Skip to content

Commit 0186dd2

Browse files
yamahatabonzini
authored andcommitted
KVM: TDX: add ioctl to initialize VM with TDX specific parameters
After the crypto-protection key has been configured, TDX requires a VM-scope initialization as a step of creating the TDX guest. This "per-VM" TDX initialization does the global configurations/features that the TDX guest can support, such as guest's CPUIDs (emulated by the TDX module), the maximum number of vcpus etc. Because there is no room in KVM_CREATE_VM to pass all the required parameters, introduce a new ioctl KVM_TDX_INIT_VM and mark the VM as TD_STATE_UNINITIALIZED until it is invoked. This "per-VM" TDX initialization must be done before any "vcpu-scope" TDX initialization; KVM_TDX_INIT_VM IOCTL must be invoked before the creation of vCPUs. Co-developed-by: Xiaoyao Li <xiaoyao.li@intel.com> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com> Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent a656dac commit 0186dd2

File tree

3 files changed

+299
-10
lines changed

3 files changed

+299
-10
lines changed

arch/x86/include/uapi/asm/kvm.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,7 @@ struct kvm_hyperv_eventfd {
930930
/* Trust Domain eXtension sub-ioctl() commands. */
931931
enum kvm_tdx_cmd_id {
932932
KVM_TDX_CAPABILITIES = 0,
933+
KVM_TDX_INIT_VM,
933934

934935
KVM_TDX_CMD_NR_MAX,
935936
};
@@ -961,4 +962,27 @@ struct kvm_tdx_capabilities {
961962
struct kvm_cpuid2 cpuid;
962963
};
963964

965+
struct kvm_tdx_init_vm {
966+
__u64 attributes;
967+
__u64 xfam;
968+
__u64 mrconfigid[6]; /* sha384 digest */
969+
__u64 mrowner[6]; /* sha384 digest */
970+
__u64 mrownerconfig[6]; /* sha384 digest */
971+
972+
/* The total space for TD_PARAMS before the CPUIDs is 256 bytes */
973+
__u64 reserved[12];
974+
975+
/*
976+
* Call KVM_TDX_INIT_VM before vcpu creation, thus before
977+
* KVM_SET_CPUID2.
978+
* This configuration supersedes KVM_SET_CPUID2s for VCPUs because the
979+
* TDX module directly virtualizes those CPUIDs without VMM. The user
980+
* space VMM, e.g. qemu, should make KVM_SET_CPUID2 consistent with
981+
* those values. If it doesn't, KVM may have wrong idea of vCPUIDs of
982+
* the guest, and KVM may wrongly emulate CPUIDs or MSRs that the TDX
983+
* module doesn't virtualize.
984+
*/
985+
struct kvm_cpuid2 cpuid;
986+
};
987+
964988
#endif /* _ASM_X86_KVM_H */

arch/x86/kvm/vmx/tdx.c

Lines changed: 249 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ static u64 tdx_get_supported_xfam(const struct tdx_sys_info_td_conf *td_conf)
6969
return val;
7070
}
7171

72+
static int tdx_get_guest_phys_addr_bits(const u32 eax)
73+
{
74+
return (eax & GENMASK(23, 16)) >> 16;
75+
}
76+
7277
static u32 tdx_set_guest_phys_addr_bits(const u32 eax, int addr_bits)
7378
{
7479
return (eax & ~GENMASK(23, 16)) | (addr_bits & 0xff) << 16;
@@ -350,7 +355,11 @@ static void tdx_reclaim_td_control_pages(struct kvm *kvm)
350355

351356
void tdx_vm_destroy(struct kvm *kvm)
352357
{
358+
struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);
359+
353360
tdx_reclaim_td_control_pages(kvm);
361+
362+
kvm_tdx->state = TD_STATE_UNINITIALIZED;
354363
}
355364

356365
static int tdx_do_tdh_mng_key_config(void *param)
@@ -369,10 +378,10 @@ static int tdx_do_tdh_mng_key_config(void *param)
369378
return 0;
370379
}
371380

372-
static int __tdx_td_init(struct kvm *kvm);
373-
374381
int tdx_vm_init(struct kvm *kvm)
375382
{
383+
struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);
384+
376385
kvm->arch.has_protected_state = true;
377386
kvm->arch.has_private_mem = true;
378387

@@ -389,8 +398,9 @@ int tdx_vm_init(struct kvm *kvm)
389398
*/
390399
kvm->max_vcpus = min_t(int, kvm->max_vcpus, num_present_cpus());
391400

392-
/* Place holder for TDX specific logic. */
393-
return __tdx_td_init(kvm);
401+
kvm_tdx->state = TD_STATE_UNINITIALIZED;
402+
403+
return 0;
394404
}
395405

396406
static int tdx_get_capabilities(struct kvm_tdx_cmd *cmd)
@@ -441,15 +451,151 @@ static int tdx_get_capabilities(struct kvm_tdx_cmd *cmd)
441451
return ret;
442452
}
443453

444-
static int __tdx_td_init(struct kvm *kvm)
454+
/*
455+
* KVM reports guest physical address in CPUID.0x800000008.EAX[23:16], which is
456+
* similar to TDX's GPAW. Use this field as the interface for userspace to
457+
* configure the GPAW and EPT level for TDs.
458+
*
459+
* Only values 48 and 52 are supported. Value 52 means GPAW-52 and EPT level
460+
* 5, Value 48 means GPAW-48 and EPT level 4. For value 48, GPAW-48 is always
461+
* supported. Value 52 is only supported when the platform supports 5 level
462+
* EPT.
463+
*/
464+
static int setup_tdparams_eptp_controls(struct kvm_cpuid2 *cpuid,
465+
struct td_params *td_params)
466+
{
467+
const struct kvm_cpuid_entry2 *entry;
468+
int guest_pa;
469+
470+
entry = kvm_find_cpuid_entry2(cpuid->entries, cpuid->nent, 0x80000008, 0);
471+
if (!entry)
472+
return -EINVAL;
473+
474+
guest_pa = tdx_get_guest_phys_addr_bits(entry->eax);
475+
476+
if (guest_pa != 48 && guest_pa != 52)
477+
return -EINVAL;
478+
479+
if (guest_pa == 52 && !cpu_has_vmx_ept_5levels())
480+
return -EINVAL;
481+
482+
td_params->eptp_controls = VMX_EPTP_MT_WB;
483+
if (guest_pa == 52) {
484+
td_params->eptp_controls |= VMX_EPTP_PWL_5;
485+
td_params->config_flags |= TDX_CONFIG_FLAGS_MAX_GPAW;
486+
} else {
487+
td_params->eptp_controls |= VMX_EPTP_PWL_4;
488+
}
489+
490+
return 0;
491+
}
492+
493+
static int setup_tdparams_cpuids(struct kvm_cpuid2 *cpuid,
494+
struct td_params *td_params)
495+
{
496+
const struct tdx_sys_info_td_conf *td_conf = &tdx_sysinfo->td_conf;
497+
const struct kvm_cpuid_entry2 *entry;
498+
struct tdx_cpuid_value *value;
499+
int i, copy_cnt = 0;
500+
501+
/*
502+
* td_params.cpuid_values: The number and the order of cpuid_value must
503+
* be same to the one of struct tdsysinfo.{num_cpuid_config, cpuid_configs}
504+
* It's assumed that td_params was zeroed.
505+
*/
506+
for (i = 0; i < td_conf->num_cpuid_config; i++) {
507+
struct kvm_cpuid_entry2 tmp;
508+
509+
td_init_cpuid_entry2(&tmp, i);
510+
511+
entry = kvm_find_cpuid_entry2(cpuid->entries, cpuid->nent,
512+
tmp.function, tmp.index);
513+
if (!entry)
514+
continue;
515+
516+
copy_cnt++;
517+
518+
value = &td_params->cpuid_values[i];
519+
value->eax = entry->eax;
520+
value->ebx = entry->ebx;
521+
value->ecx = entry->ecx;
522+
value->edx = entry->edx;
523+
524+
/*
525+
* TDX module does not accept nonzero bits 16..23 for the
526+
* CPUID[0x80000008].EAX, see setup_tdparams_eptp_controls().
527+
*/
528+
if (tmp.function == 0x80000008)
529+
value->eax = tdx_set_guest_phys_addr_bits(value->eax, 0);
530+
}
531+
532+
/*
533+
* Rely on the TDX module to reject invalid configuration, but it can't
534+
* check of leafs that don't have a proper slot in td_params->cpuid_values
535+
* to stick then. So fail if there were entries that didn't get copied to
536+
* td_params.
537+
*/
538+
if (copy_cnt != cpuid->nent)
539+
return -EINVAL;
540+
541+
return 0;
542+
}
543+
544+
static int setup_tdparams(struct kvm *kvm, struct td_params *td_params,
545+
struct kvm_tdx_init_vm *init_vm)
546+
{
547+
const struct tdx_sys_info_td_conf *td_conf = &tdx_sysinfo->td_conf;
548+
struct kvm_cpuid2 *cpuid = &init_vm->cpuid;
549+
int ret;
550+
551+
if (kvm->created_vcpus)
552+
return -EBUSY;
553+
554+
if (init_vm->attributes & ~tdx_get_supported_attrs(td_conf))
555+
return -EINVAL;
556+
557+
if (init_vm->xfam & ~tdx_get_supported_xfam(td_conf))
558+
return -EINVAL;
559+
560+
td_params->max_vcpus = kvm->max_vcpus;
561+
td_params->attributes = init_vm->attributes | td_conf->attributes_fixed1;
562+
td_params->xfam = init_vm->xfam | td_conf->xfam_fixed1;
563+
564+
td_params->config_flags = TDX_CONFIG_FLAGS_NO_RBP_MOD;
565+
td_params->tsc_frequency = TDX_TSC_KHZ_TO_25MHZ(kvm->arch.default_tsc_khz);
566+
567+
ret = setup_tdparams_eptp_controls(cpuid, td_params);
568+
if (ret)
569+
return ret;
570+
571+
ret = setup_tdparams_cpuids(cpuid, td_params);
572+
if (ret)
573+
return ret;
574+
575+
#define MEMCPY_SAME_SIZE(dst, src) \
576+
do { \
577+
BUILD_BUG_ON(sizeof(dst) != sizeof(src)); \
578+
memcpy((dst), (src), sizeof(dst)); \
579+
} while (0)
580+
581+
MEMCPY_SAME_SIZE(td_params->mrconfigid, init_vm->mrconfigid);
582+
MEMCPY_SAME_SIZE(td_params->mrowner, init_vm->mrowner);
583+
MEMCPY_SAME_SIZE(td_params->mrownerconfig, init_vm->mrownerconfig);
584+
585+
return 0;
586+
}
587+
588+
static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params,
589+
u64 *seamcall_err)
445590
{
446591
struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);
447592
cpumask_var_t packages;
448593
struct page **tdcs_pages = NULL;
449594
struct page *tdr_page;
450595
int ret, i;
451-
u64 err;
596+
u64 err, rcx;
452597

598+
*seamcall_err = 0;
453599
ret = tdx_guest_keyid_alloc();
454600
if (ret < 0)
455601
return ret;
@@ -561,10 +707,23 @@ static int __tdx_td_init(struct kvm *kvm)
561707
}
562708
}
563709

564-
/*
565-
* Note, TDH_MNG_INIT cannot be invoked here. TDH_MNG_INIT requires a dedicated
566-
* ioctl() to define the configure CPUID values for the TD.
567-
*/
710+
err = tdh_mng_init(&kvm_tdx->td, __pa(td_params), &rcx);
711+
if ((err & TDX_SEAMCALL_STATUS_MASK) == TDX_OPERAND_INVALID) {
712+
/*
713+
* Because a user gives operands, don't warn.
714+
* Return a hint to the user because it's sometimes hard for the
715+
* user to figure out which operand is invalid. SEAMCALL status
716+
* code includes which operand caused invalid operand error.
717+
*/
718+
*seamcall_err = err;
719+
ret = -EINVAL;
720+
goto teardown;
721+
} else if (WARN_ON_ONCE(err)) {
722+
pr_tdx_error_1(TDH_MNG_INIT, err, rcx);
723+
ret = -EIO;
724+
goto teardown;
725+
}
726+
568727
return 0;
569728

570729
/*
@@ -612,6 +771,83 @@ static int __tdx_td_init(struct kvm *kvm)
612771
return ret;
613772
}
614773

774+
static int tdx_td_init(struct kvm *kvm, struct kvm_tdx_cmd *cmd)
775+
{
776+
struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);
777+
struct kvm_tdx_init_vm *init_vm;
778+
struct td_params *td_params = NULL;
779+
int ret;
780+
781+
BUILD_BUG_ON(sizeof(*init_vm) != 256 + sizeof_field(struct kvm_tdx_init_vm, cpuid));
782+
BUILD_BUG_ON(sizeof(struct td_params) != 1024);
783+
784+
if (kvm_tdx->state != TD_STATE_UNINITIALIZED)
785+
return -EINVAL;
786+
787+
if (cmd->flags)
788+
return -EINVAL;
789+
790+
init_vm = kmalloc(sizeof(*init_vm) +
791+
sizeof(init_vm->cpuid.entries[0]) * KVM_MAX_CPUID_ENTRIES,
792+
GFP_KERNEL);
793+
if (!init_vm)
794+
return -ENOMEM;
795+
796+
if (copy_from_user(init_vm, u64_to_user_ptr(cmd->data), sizeof(*init_vm))) {
797+
ret = -EFAULT;
798+
goto out;
799+
}
800+
801+
if (init_vm->cpuid.nent > KVM_MAX_CPUID_ENTRIES) {
802+
ret = -E2BIG;
803+
goto out;
804+
}
805+
806+
if (copy_from_user(init_vm->cpuid.entries,
807+
u64_to_user_ptr(cmd->data) + sizeof(*init_vm),
808+
flex_array_size(init_vm, cpuid.entries, init_vm->cpuid.nent))) {
809+
ret = -EFAULT;
810+
goto out;
811+
}
812+
813+
if (memchr_inv(init_vm->reserved, 0, sizeof(init_vm->reserved))) {
814+
ret = -EINVAL;
815+
goto out;
816+
}
817+
818+
if (init_vm->cpuid.padding) {
819+
ret = -EINVAL;
820+
goto out;
821+
}
822+
823+
td_params = kzalloc(sizeof(struct td_params), GFP_KERNEL);
824+
if (!td_params) {
825+
ret = -ENOMEM;
826+
goto out;
827+
}
828+
829+
ret = setup_tdparams(kvm, td_params, init_vm);
830+
if (ret)
831+
goto out;
832+
833+
ret = __tdx_td_init(kvm, td_params, &cmd->hw_error);
834+
if (ret)
835+
goto out;
836+
837+
kvm_tdx->tsc_offset = td_tdcs_exec_read64(kvm_tdx, TD_TDCS_EXEC_TSC_OFFSET);
838+
kvm_tdx->tsc_multiplier = td_tdcs_exec_read64(kvm_tdx, TD_TDCS_EXEC_TSC_MULTIPLIER);
839+
kvm_tdx->attributes = td_params->attributes;
840+
kvm_tdx->xfam = td_params->xfam;
841+
842+
kvm_tdx->state = TD_STATE_INITIALIZED;
843+
out:
844+
/* kfree() accepts NULL. */
845+
kfree(init_vm);
846+
kfree(td_params);
847+
848+
return ret;
849+
}
850+
615851
int tdx_vm_ioctl(struct kvm *kvm, void __user *argp)
616852
{
617853
struct kvm_tdx_cmd tdx_cmd;
@@ -633,6 +869,9 @@ int tdx_vm_ioctl(struct kvm *kvm, void __user *argp)
633869
case KVM_TDX_CAPABILITIES:
634870
r = tdx_get_capabilities(&tdx_cmd);
635871
break;
872+
case KVM_TDX_INIT_VM:
873+
r = tdx_td_init(kvm, &tdx_cmd);
874+
break;
636875
default:
637876
r = -EINVAL;
638877
goto out;

0 commit comments

Comments
 (0)