Happy to help!
Nope it wasn't any special technique, I was working on the selftests and
made a mistake configuring the TD while initializing it.
While setting up struct kvm_tdx_init_vm, I did not copy the entire
struct kvm_cpuid2.
This reliably reproduces the crash:
--------
// SPDX-License-Identifier: GPL-2.0-only
#include "kvm_util_base.h"
#include <processor.h>
static int tdx_ioctl(int fd, int ioctl_no, uint32_t flags, void *data)
{
struct kvm_tdx_cmd tdx_cmd;
memset(&tdx_cmd, 0x0, sizeof(tdx_cmd));
tdx_cmd.id = ioctl_no;
tdx_cmd.flags = flags;
tdx_cmd.data = (uint64_t)data;
return ioctl(fd, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd);
}
#define XFEATURE_LBR 15
#define XFEATURE_XTILECFG 17
#define XFEATURE_XTILEDATA 18
#define XFEATURE_CET_U 11
#define XFEATURE_CET_S 12
#define XFEATURE_MASK_LBR (1 << XFEATURE_LBR)
#define XFEATURE_MASK_CET_U (1 << XFEATURE_CET_U)
#define XFEATURE_MASK_CET_S (1 << XFEATURE_CET_S)
#define XFEATURE_MASK_CET (XFEATURE_MASK_CET_U | XFEATURE_MASK_CET_S)
#define XFEATURE_MASK_XTILECFG (1 << XFEATURE_XTILECFG)
#define XFEATURE_MASK_XTILEDATA (1 << XFEATURE_XTILEDATA)
#define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILECFG |
XFEATURE_MASK_XTILEDATA)
static void apply_tdx_restrictions(struct kvm_cpuid2 *cpuid_data)
{
for (int i = 0; i < 100; i++) {
struct kvm_cpuid_entry2 *e = &cpuid_data->entries[i];
if (e->function == 0xd && e->index == 0) {
/*
* TDX module requires both XTILE_{CFG, DATA} to be set.
* Both bits are required for AMX to be functional.
*/
if ((e->eax & XFEATURE_MASK_XTILE) != XFEATURE_MASK_XTILE) {
e->eax &= ~XFEATURE_MASK_XTILE;
}
}
if (e->function == 0xd && e->index == 1) {
/*
* TDX doesn't support LBR yet.
* Disable bits from the XCR0 register.
*/
e->ecx &= ~XFEATURE_MASK_LBR;
/*
* TDX modules requires both CET_{U, S} to be set even
* if only one is supported.
*/
if (e->ecx & XFEATURE_MASK_CET)
e->ecx |= XFEATURE_MASK_CET;
}
}
}
void initialize_td_bad(struct kvm_vm *vm)
{
const struct kvm_cpuid2 *cpuid;
int rc;
/* No guest VMM controlled cpuid information yet. */
struct kvm_tdx_init_vm init_vm;
rc = kvm_check_cap(KVM_CAP_X2APIC_API);
TEST_ASSERT(rc, "TDX: KVM_CAP_X2APIC_API is not supported!");
rc = kvm_check_cap(KVM_CAP_SPLIT_IRQCHIP);
TEST_ASSERT(rc, "TDX: KVM_CAP_SPLIT_IRQCHIP is not supported!");
vm_enable_cap(vm, KVM_CAP_X2APIC_API,
KVM_X2APIC_API_USE_32BIT_IDS |
KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK);
vm_enable_cap(vm, KVM_CAP_SPLIT_IRQCHIP, 24);
/* Allocate and setup memory for the td guest. */
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, 1, 0);
cpuid = kvm_get_supported_cpuid();
apply_tdx_restrictions((struct kvm_cpuid2 *) cpuid);
memset(&init_vm, 0, sizeof(init_vm));
init_vm.attributes = 0;
/*
* Should have been kvm_cpuid2_size(cpuid->nent) instead of
* sizeof(*cpuid)
*/
memcpy(&init_vm.cpuid, cpuid, sizeof(*cpuid));
rc = tdx_ioctl(vm->fd, KVM_TDX_INIT_VM, 0, &init_vm);
TEST_ASSERT(rc == 0, "KVM_TDX_INIT_VM failed: %d %d", rc, errno);
}
static int crash(void)
{
struct kvm_vm *vm;
/* Create a TD VM with no memory.*/
vm = ____vm_create(VM_MODE_DEFAULT, 0, KVM_X86_TDX_VM);
/* Allocate TD guest memory and initialize the TD.*/
initialize_td_bad(vm);
return 0;
}
int main(int argc, char **argv)
{
/* Disable stdout buffering */
setbuf(stdout, NULL);
return crash();
}