From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.16]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A118E2FA0C7; Thu, 14 Aug 2025 11:48:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.16 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755172138; cv=none; b=li5IcvhZTlwF1kWMOBQzf9OThU8Wv4OsrhsU2wXgWlu/uf/PjnfVhe4xQJCzLltB2ffx735HO/XBRwgIzBDFx+L2xDPW8bv/JFvGJboR1305SZJ8VuqFcRsvIb0X7121CZT8RSiGw7/pJaiEuQrrXQYAHmpI4+VKlaBynQaekJE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755172138; c=relaxed/simple; bh=gu4zFAplBgPGHOV6DtfYlkgF3aC4M8p9AQmESbr3BKQ=; h=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From: In-Reply-To:Content-Type; b=EKG547f1JYUNCGvqG4uvi3s4cctnxWdzeiqgmBf5o1QnUnHRK32PQ9VrNjizITufeBwSOewQISA3VWgD6cZRvv/HWD25TUfy4XJhHGlYd6lsdQAB3lyms6fqBs0klT8g6t2P3AKiMDAzXYuGQ2/kfOg3865QvFuv0o1pkgRCIgk= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=none smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=B6F/o2th; arc=none smtp.client-ip=192.198.163.16 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="B6F/o2th" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1755172135; x=1786708135; h=message-id:date:mime-version:subject:to:cc:references: from:in-reply-to:content-transfer-encoding; bh=gu4zFAplBgPGHOV6DtfYlkgF3aC4M8p9AQmESbr3BKQ=; b=B6F/o2thqrccY/b6s16cizw8B70w07EUrmkgvyJjtzm83o+W4o4knmLy FhNq5Yyg6Hzn9VS7GAMrbEoTJbHT/lZ62tmGSASjQu+Azf5uoIyw3nZT8 O8QAuKkE52UjH0x7+UY4a6aZC1iY8nXH+HVQ8llGwsyc7/8ZfZ9UPn1Y1 /Mu0LmaMuA9Id7LOba+A6n47BEskyiYI9mbAW1DX40w7Xegcntg1eUyw1 JGp1E3zNVETeJVM9g95CdF1QeeLhnw5BBimBhgdeY5KmfPJBXDJE9zukq NrIQgBTDMZIF7CW7CE0GVbbveVlJFoaYlxkVoDhiJvtePlN1uF7s/1DDE Q==; X-CSE-ConnectionGUID: bNP/k1V0QAO1LbdN/oeuKA== X-CSE-MsgGUID: ygu2oef8Q5+WGwwGothg4Q== X-IronPort-AV: E=McAfee;i="6800,10657,11520"; a="45062502" X-IronPort-AV: E=Sophos;i="6.17,287,1747724400"; d="scan'208";a="45062502" Received: from orviesa007.jf.intel.com ([10.64.159.147]) by fmvoesa110.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Aug 2025 04:48:54 -0700 X-CSE-ConnectionGUID: uVJP7rpYSMucul+jFkbWwQ== X-CSE-MsgGUID: VLAJKu8bQ1iPFVgvlRBrsg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.17,287,1747724400"; d="scan'208";a="166725347" Received: from unknown (HELO [10.238.0.107]) ([10.238.0.107]) by orviesa007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Aug 2025 04:48:50 -0700 Message-ID: <410c14cd-edab-4dd0-8ac1-a33496744590@linux.intel.com> Date: Thu, 14 Aug 2025 19:48:48 +0800 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v8 22/30] KVM: selftests: TDX: Add TDG.VP.INFO test To: Sagi Shahar Cc: linux-kselftest@vger.kernel.org, Paolo Bonzini , Shuah Khan , Sean Christopherson , Ackerley Tng , Ryan Afranji , Andrew Jones , Isaku Yamahata , Erdem Aktas , Rick Edgecombe , Roger Wang , Oliver Upton , "Pratik R. Sampat" , Reinette Chatre , Ira Weiny , linux-kernel@vger.kernel.org, kvm@vger.kernel.org References: <20250807201628.1185915-1-sagis@google.com> <20250807201628.1185915-23-sagis@google.com> Content-Language: en-US From: Binbin Wu In-Reply-To: <20250807201628.1185915-23-sagis@google.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit On 8/8/2025 4:16 AM, Sagi Shahar wrote: > From: Roger Wang > > Adds a test for TDG.VP.INFO. > > Introduce __tdx_module_call() that does needed shuffling from function > parameters to registers used by the TDCALL instruction that is used by the > guest to communicate with the TDX module. The first function parameter is > the leaf number indicating which guest side function should be run, for > example, TDG.VP.INFO. I think __tdx_hypercall() can be combined into __tdx_module_call(), it's just another leaf TDG.VP.VMCALL for tdcall. To avoid two copies of assembly code doing similar things? > > The guest uses new __tdx_module_call() to call TDG.VP.INFO to obtain TDX > TD execution environment information from the TDX module. All returned > registers are passed back to the host that verifies values for > correctness. > > Co-developed-by: Sagi Shahar > Signed-off-by: Sagi Shahar > Signed-off-by: Roger Wang > Signed-off-by: Sagi Shahar > --- > .../selftests/kvm/include/x86/tdx/tdcall.h | 19 +++ > .../selftests/kvm/include/x86/tdx/tdx.h | 5 + > .../selftests/kvm/lib/x86/tdx/tdcall.S | 68 +++++++++ > tools/testing/selftests/kvm/lib/x86/tdx/tdx.c | 27 ++++ > tools/testing/selftests/kvm/x86/tdx_vm_test.c | 133 +++++++++++++++++- > 5 files changed, 251 insertions(+), 1 deletion(-) > > diff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdcall.h b/tools/testing/selftests/kvm/include/x86/tdx/tdcall.h > index e7440f7fe259..ab1a97a82fa9 100644 > --- a/tools/testing/selftests/kvm/include/x86/tdx/tdcall.h > +++ b/tools/testing/selftests/kvm/include/x86/tdx/tdcall.h > @@ -32,4 +32,23 @@ struct tdx_hypercall_args { > /* Used to request services from the VMM */ > u64 __tdx_hypercall(struct tdx_hypercall_args *args, unsigned long flags); > > +/* > + * Used to gather the output registers values of the TDCALL and SEAMCALL > + * instructions when requesting services from the TDX module. > + * > + * This is a software only structure and not part of the TDX module/VMM ABI. > + */ > +struct tdx_module_output { > + u64 rcx; > + u64 rdx; > + u64 r8; > + u64 r9; > + u64 r10; > + u64 r11; > +}; > + > +/* Used to communicate with the TDX module */ > +u64 __tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9, > + struct tdx_module_output *out); > + > #endif // SELFTESTS_TDX_TDCALL_H > diff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx.h b/tools/testing/selftests/kvm/include/x86/tdx/tdx.h > index 060158cb046b..801ca879664e 100644 > --- a/tools/testing/selftests/kvm/include/x86/tdx/tdx.h > +++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx.h > @@ -6,6 +6,8 @@ > > #include "kvm_util.h" > > +#define TDG_VP_INFO 1 > + > #define TDG_VP_VMCALL_GET_TD_VM_CALL_INFO 0x10000 > #define TDG_VP_VMCALL_REPORT_FATAL_ERROR 0x10003 > > @@ -31,5 +33,8 @@ uint64_t tdg_vp_vmcall_ve_request_mmio_write(uint64_t address, uint64_t size, > uint64_t tdg_vp_vmcall_instruction_cpuid(uint32_t eax, uint32_t ecx, > uint32_t *ret_eax, uint32_t *ret_ebx, > uint32_t *ret_ecx, uint32_t *ret_edx); > +uint64_t tdg_vp_info(uint64_t *rcx, uint64_t *rdx, > + uint64_t *r8, uint64_t *r9, > + uint64_t *r10, uint64_t *r11); > > #endif // SELFTEST_TDX_TDX_H > diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdcall.S b/tools/testing/selftests/kvm/lib/x86/tdx/tdcall.S > index b10769d1d557..c393a0fb35be 100644 > --- a/tools/testing/selftests/kvm/lib/x86/tdx/tdcall.S > +++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdcall.S > @@ -91,5 +91,73 @@ __tdx_hypercall: > pop %rbp > ret > > +#define TDX_MODULE_rcx 0 /* offsetof(struct tdx_module_output, rcx) */ > +#define TDX_MODULE_rdx 8 /* offsetof(struct tdx_module_output, rdx) */ > +#define TDX_MODULE_r8 16 /* offsetof(struct tdx_module_output, r8) */ > +#define TDX_MODULE_r9 24 /* offsetof(struct tdx_module_output, r9) */ > +#define TDX_MODULE_r10 32 /* offsetof(struct tdx_module_output, r10) */ > +#define TDX_MODULE_r11 40 /* offsetof(struct tdx_module_output, r11) */ > + > +.globl __tdx_module_call > +.type __tdx_module_call, @function > +__tdx_module_call: > + /* Set up stack frame */ > + push %rbp > + movq %rsp, %rbp > + > + /* Callee-saved, so preserve it */ > + push %r12 > + > + /* > + * Push output pointer to stack. > + * After the operation, it will be fetched into R12 register. > + */ > + push %r9 > + > + /* Mangle function call ABI into TDCALL/SEAMCALL ABI: */ > + /* Move Leaf ID to RAX */ > + mov %rdi, %rax > + /* Move input 4 to R9 */ > + mov %r8, %r9 > + /* Move input 3 to R8 */ > + mov %rcx, %r8 > + /* Move input 1 to RCX */ > + mov %rsi, %rcx > + /* Leave input param 2 in RDX */ > + > + tdcall > + > + /* > + * Fetch output pointer from stack to R12 (It is used > + * as temporary storage) > + */ > + pop %r12 > + > + /* > + * Since this macro can be invoked with NULL as an output pointer, > + * check if caller provided an output struct before storing output > + * registers. > + * > + * Update output registers, even if the call failed (RAX != 0). > + * Other registers may contain details of the failure. > + */ > + test %r12, %r12 > + jz .Lno_output_struct > + > + /* Copy result registers to output struct: */ > + movq %rcx, TDX_MODULE_rcx(%r12) > + movq %rdx, TDX_MODULE_rdx(%r12) > + movq %r8, TDX_MODULE_r8(%r12) > + movq %r9, TDX_MODULE_r9(%r12) > + movq %r10, TDX_MODULE_r10(%r12) > + movq %r11, TDX_MODULE_r11(%r12) > + > +.Lno_output_struct: > + /* Restore the state of R12 register */ > + pop %r12 > + > + pop %rbp > + ret > + > /* Disable executable stack */ > .section .note.GNU-stack,"",%progbits > diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdx.c b/tools/testing/selftests/kvm/lib/x86/tdx/tdx.c > index fb391483d2fa..ab6fd3d7ae4b 100644 > --- a/tools/testing/selftests/kvm/lib/x86/tdx/tdx.c > +++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdx.c > @@ -162,3 +162,30 @@ uint64_t tdg_vp_vmcall_instruction_cpuid(uint32_t eax, uint32_t ecx, > > return ret; > } > + > +uint64_t tdg_vp_info(uint64_t *rcx, uint64_t *rdx, > + uint64_t *r8, uint64_t *r9, > + uint64_t *r10, uint64_t *r11) > +{ > + struct tdx_module_output out; > + uint64_t ret; > + > + memset(&out, 0, sizeof(struct tdx_module_output)); > + > + ret = __tdx_module_call(TDG_VP_INFO, 0, 0, 0, 0, &out); > + > + if (rcx) > + *rcx = out.rcx; > + if (rdx) > + *rdx = out.rdx; > + if (r8) > + *r8 = out.r8; > + if (r9) > + *r9 = out.r9; > + if (r10) > + *r10 = out.r10; > + if (r11) > + *r11 = out.r11; > + > + return ret; > +} > diff --git a/tools/testing/selftests/kvm/x86/tdx_vm_test.c b/tools/testing/selftests/kvm/x86/tdx_vm_test.c > index b6ef0348746c..82acc17a66ab 100644 > --- a/tools/testing/selftests/kvm/x86/tdx_vm_test.c > +++ b/tools/testing/selftests/kvm/x86/tdx_vm_test.c > @@ -1038,6 +1038,135 @@ void verify_host_reading_private_mem(void) > printf("\t ... PASSED\n"); > } > > +/* > + * Do a TDG.VP.INFO call from the guest > + */ > +void guest_tdcall_vp_info(void) > +{ > + uint64_t rcx, rdx, r8, r9, r10, r11; > + uint64_t err; > + > + err = tdg_vp_info(&rcx, &rdx, &r8, &r9, &r10, &r11); > + tdx_assert_error(err); > + > + /* return values to user space host */ > + err = tdx_test_report_64bit_to_user_space(rcx); > + tdx_assert_error(err); > + > + err = tdx_test_report_64bit_to_user_space(rdx); > + tdx_assert_error(err); > + > + err = tdx_test_report_64bit_to_user_space(r8); > + tdx_assert_error(err); > + > + err = tdx_test_report_64bit_to_user_space(r9); > + tdx_assert_error(err); > + > + err = tdx_test_report_64bit_to_user_space(r10); > + tdx_assert_error(err); > + > + err = tdx_test_report_64bit_to_user_space(r11); > + tdx_assert_error(err); > + > + tdx_test_success(); > +} > + > +/* > + * TDG.VP.INFO call from the guest. Verify the right values are returned > + */ > +void verify_tdcall_vp_info(void) > +{ > + const struct kvm_cpuid_entry2 *cpuid_entry; > + uint32_t ret_num_vcpus, ret_max_vcpus; > + uint64_t rcx, rdx, r8, r9, r10, r11; > + const int num_vcpus = 2; > + struct kvm_vcpu *vcpus[num_vcpus]; > + uint64_t attributes; > + struct kvm_vm *vm; > + int gpa_bits = -1; > + uint32_t i; > + > + vm = td_create(); > + > +#define TDX_TDPARAM_ATTR_SEPT_VE_DISABLE_BIT BIT(28) > + /* Setting attributes parameter used by TDH.MNG.INIT to 0x10000000 */ > + attributes = TDX_TDPARAM_ATTR_SEPT_VE_DISABLE_BIT; > + > + td_initialize(vm, VM_MEM_SRC_ANONYMOUS, attributes); > + > + for (i = 0; i < num_vcpus; i++) > + vcpus[i] = td_vcpu_add(vm, i, guest_tdcall_vp_info); > + > + td_finalize(vm); > + > + printf("Verifying TDG.VP.INFO call:\n"); > + > + /* Get KVM CPUIDs for reference */ > + > + for (i = 0; i < num_vcpus; i++) { > + struct kvm_vcpu *vcpu = vcpus[i]; > + > + cpuid_entry = vcpu_get_cpuid_entry(vcpu, 0x80000008); > + TEST_ASSERT(cpuid_entry, "CPUID entry missing\n"); > + gpa_bits = (cpuid_entry->eax & GENMASK(23, 16)) >> 16; > + TEST_ASSERT_EQ((1UL << (gpa_bits - 1)), tdx_s_bit); > + > + /* Wait for guest to report rcx value */ > + tdx_run(vcpu); > + rcx = tdx_test_read_64bit_report_from_guest(vcpu); > + > + /* Wait for guest to report rdx value */ > + tdx_run(vcpu); > + rdx = tdx_test_read_64bit_report_from_guest(vcpu); > + > + /* Wait for guest to report r8 value */ > + tdx_run(vcpu); > + r8 = tdx_test_read_64bit_report_from_guest(vcpu); > + > + /* Wait for guest to report r9 value */ > + tdx_run(vcpu); > + r9 = tdx_test_read_64bit_report_from_guest(vcpu); > + > + /* Wait for guest to report r10 value */ > + tdx_run(vcpu); > + r10 = tdx_test_read_64bit_report_from_guest(vcpu); > + > + /* Wait for guest to report r11 value */ > + tdx_run(vcpu); > + r11 = tdx_test_read_64bit_report_from_guest(vcpu); > + > + ret_num_vcpus = r8 & 0xFFFFFFFF; > + ret_max_vcpus = (r8 >> 32) & 0xFFFFFFFF; > + > + /* first bits 5:0 of rcx represent the GPAW */ > + TEST_ASSERT_EQ(rcx & 0x3F, gpa_bits); > + /* next 63:6 bits of rcx is reserved and must be 0 */ > + TEST_ASSERT_EQ(rcx >> 6, 0); > + TEST_ASSERT_EQ(rdx, attributes); > + TEST_ASSERT_EQ(ret_num_vcpus, num_vcpus); > + TEST_ASSERT_EQ(ret_max_vcpus, vm_check_cap(vm, KVM_CAP_MAX_VCPUS)); > + /* VCPU_INDEX = i */ > + TEST_ASSERT_EQ(r9, i); > + /* > + * verify reserved bits are 0 > + * r10 bit 0 (SYS_RD) indicates that the TDG.SYS.RD/RDM/RDALL > + * functions are available and can be either 0 or 1. > + */ > + TEST_ASSERT_EQ(r10 & ~1, 0); > + TEST_ASSERT_EQ(r11, 0); > + > + /* Wait for guest to complete execution */ > + tdx_run(vcpu); > + > + tdx_test_assert_success(vcpu); > + > + printf("\t ... Guest completed run on VCPU=%u\n", i); > + } > + > + kvm_vm_free(vm); > + printf("\t ... PASSED\n"); > +} > + > int main(int argc, char **argv) > { > ksft_print_header(); > @@ -1045,7 +1174,7 @@ int main(int argc, char **argv) > if (!is_tdx_enabled()) > ksft_exit_skip("TDX is not supported by the KVM. Exiting.\n"); > > - ksft_set_plan(14); > + ksft_set_plan(15); > ksft_test_result(!run_in_new_process(&verify_td_lifecycle), > "verify_td_lifecycle\n"); > ksft_test_result(!run_in_new_process(&verify_report_fatal_error), > @@ -1074,6 +1203,8 @@ int main(int argc, char **argv) > "verify_td_cpuid_tdcall\n"); > ksft_test_result(!run_in_new_process(&verify_host_reading_private_mem), > "verify_host_reading_private_mem\n"); > + ksft_test_result(!run_in_new_process(&verify_tdcall_vp_info), > + "verify_tdcall_vp_info\n"); > > ksft_finished(); > return 0;