Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * ucna_injection_test
0004  *
0005  * Copyright (C) 2022, Google LLC.
0006  *
0007  * This work is licensed under the terms of the GNU GPL, version 2.
0008  *
0009  * Test that user space can inject UnCorrectable No Action required (UCNA)
0010  * memory errors to the guest.
0011  *
0012  * The test starts one vCPU with the MCG_CMCI_P enabled. It verifies that
0013  * proper UCNA errors can be injected to a vCPU with MCG_CMCI_P and
0014  * corresponding per-bank control register (MCI_CTL2) bit enabled.
0015  * The test also checks that the UCNA errors get recorded in the
0016  * Machine Check bank registers no matter the error signal interrupts get
0017  * delivered into the guest or not.
0018  *
0019  */
0020 
0021 #define _GNU_SOURCE /* for program_invocation_short_name */
0022 #include <pthread.h>
0023 #include <inttypes.h>
0024 #include <string.h>
0025 #include <time.h>
0026 
0027 #include "kvm_util_base.h"
0028 #include "kvm_util.h"
0029 #include "mce.h"
0030 #include "processor.h"
0031 #include "test_util.h"
0032 #include "apic.h"
0033 
0034 #define SYNC_FIRST_UCNA 9
0035 #define SYNC_SECOND_UCNA 10
0036 #define SYNC_GP 11
0037 #define FIRST_UCNA_ADDR 0xdeadbeef
0038 #define SECOND_UCNA_ADDR 0xcafeb0ba
0039 
0040 /*
0041  * Vector for the CMCI interrupt.
0042  * Value is arbitrary. Any value in 0x20-0xFF should work:
0043  * https://wiki.osdev.org/Interrupt_Vector_Table
0044  */
0045 #define CMCI_VECTOR  0xa9
0046 
0047 #define UCNA_BANK  0x7  // IMC0 bank
0048 
0049 #define MCI_CTL2_RESERVED_BIT BIT_ULL(29)
0050 
0051 static uint64_t supported_mcg_caps;
0052 
0053 /*
0054  * Record states about the injected UCNA.
0055  * The variables started with the 'i_' prefixes are recorded in interrupt
0056  * handler. Variables without the 'i_' prefixes are recorded in guest main
0057  * execution thread.
0058  */
0059 static volatile uint64_t i_ucna_rcvd;
0060 static volatile uint64_t i_ucna_addr;
0061 static volatile uint64_t ucna_addr;
0062 static volatile uint64_t ucna_addr2;
0063 
0064 struct thread_params {
0065     struct kvm_vcpu *vcpu;
0066     uint64_t *p_i_ucna_rcvd;
0067     uint64_t *p_i_ucna_addr;
0068     uint64_t *p_ucna_addr;
0069     uint64_t *p_ucna_addr2;
0070 };
0071 
0072 static void verify_apic_base_addr(void)
0073 {
0074     uint64_t msr = rdmsr(MSR_IA32_APICBASE);
0075     uint64_t base = GET_APIC_BASE(msr);
0076 
0077     GUEST_ASSERT(base == APIC_DEFAULT_GPA);
0078 }
0079 
0080 static void ucna_injection_guest_code(void)
0081 {
0082     uint64_t ctl2;
0083     verify_apic_base_addr();
0084     xapic_enable();
0085 
0086     /* Sets up the interrupt vector and enables per-bank CMCI sigaling. */
0087     xapic_write_reg(APIC_LVTCMCI, CMCI_VECTOR | APIC_DM_FIXED);
0088     ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK));
0089     wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK), ctl2 | MCI_CTL2_CMCI_EN);
0090 
0091     /* Enables interrupt in guest. */
0092     asm volatile("sti");
0093 
0094     /* Let user space inject the first UCNA */
0095     GUEST_SYNC(SYNC_FIRST_UCNA);
0096 
0097     ucna_addr = rdmsr(MSR_IA32_MCx_ADDR(UCNA_BANK));
0098 
0099     /* Disables the per-bank CMCI signaling. */
0100     ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK));
0101     wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK), ctl2 & ~MCI_CTL2_CMCI_EN);
0102 
0103     /* Let the user space inject the second UCNA */
0104     GUEST_SYNC(SYNC_SECOND_UCNA);
0105 
0106     ucna_addr2 = rdmsr(MSR_IA32_MCx_ADDR(UCNA_BANK));
0107     GUEST_DONE();
0108 }
0109 
0110 static void cmci_disabled_guest_code(void)
0111 {
0112     uint64_t ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK));
0113     wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK), ctl2 | MCI_CTL2_CMCI_EN);
0114 
0115     GUEST_DONE();
0116 }
0117 
0118 static void cmci_enabled_guest_code(void)
0119 {
0120     uint64_t ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK));
0121     wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK), ctl2 | MCI_CTL2_RESERVED_BIT);
0122 
0123     GUEST_DONE();
0124 }
0125 
0126 static void guest_cmci_handler(struct ex_regs *regs)
0127 {
0128     i_ucna_rcvd++;
0129     i_ucna_addr = rdmsr(MSR_IA32_MCx_ADDR(UCNA_BANK));
0130     xapic_write_reg(APIC_EOI, 0);
0131 }
0132 
0133 static void guest_gp_handler(struct ex_regs *regs)
0134 {
0135     GUEST_SYNC(SYNC_GP);
0136 }
0137 
0138 static void run_vcpu_expect_gp(struct kvm_vcpu *vcpu)
0139 {
0140     unsigned int exit_reason;
0141     struct ucall uc;
0142 
0143     vcpu_run(vcpu);
0144 
0145     exit_reason = vcpu->run->exit_reason;
0146     TEST_ASSERT(exit_reason == KVM_EXIT_IO,
0147             "exited with unexpected exit reason %u-%s, expected KVM_EXIT_IO",
0148             exit_reason, exit_reason_str(exit_reason));
0149     TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_SYNC,
0150             "Expect UCALL_SYNC\n");
0151     TEST_ASSERT(uc.args[1] == SYNC_GP, "#GP is expected.");
0152     printf("vCPU received GP in guest.\n");
0153 }
0154 
0155 static void inject_ucna(struct kvm_vcpu *vcpu, uint64_t addr) {
0156     /*
0157      * A UCNA error is indicated with VAL=1, UC=1, PCC=0, S=0 and AR=0 in
0158      * the IA32_MCi_STATUS register.
0159      * MSCOD=1 (BIT[16] - MscodDataRdErr).
0160      * MCACOD=0x0090 (Memory controller error format, channel 0)
0161      */
0162     uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN |
0163               MCI_STATUS_MISCV | MCI_STATUS_ADDRV | 0x10090;
0164     struct kvm_x86_mce mce = {};
0165     mce.status = status;
0166     mce.mcg_status = 0;
0167     /*
0168      * MCM_ADDR_PHYS indicates the reported address is a physical address.
0169      * Lowest 6 bits is the recoverable address LSB, i.e., the injected MCE
0170      * is at 4KB granularity.
0171      */
0172     mce.misc = (MCM_ADDR_PHYS << 6) | 0xc;
0173     mce.addr = addr;
0174     mce.bank = UCNA_BANK;
0175 
0176     vcpu_ioctl(vcpu, KVM_X86_SET_MCE, &mce);
0177 }
0178 
0179 static void *run_ucna_injection(void *arg)
0180 {
0181     struct thread_params *params = (struct thread_params *)arg;
0182     struct ucall uc;
0183     int old;
0184     int r;
0185     unsigned int exit_reason;
0186 
0187     r = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
0188     TEST_ASSERT(r == 0,
0189             "pthread_setcanceltype failed with errno=%d",
0190             r);
0191 
0192     vcpu_run(params->vcpu);
0193 
0194     exit_reason = params->vcpu->run->exit_reason;
0195     TEST_ASSERT(exit_reason == KVM_EXIT_IO,
0196             "unexpected exit reason %u-%s, expected KVM_EXIT_IO",
0197             exit_reason, exit_reason_str(exit_reason));
0198     TEST_ASSERT(get_ucall(params->vcpu, &uc) == UCALL_SYNC,
0199             "Expect UCALL_SYNC\n");
0200     TEST_ASSERT(uc.args[1] == SYNC_FIRST_UCNA, "Injecting first UCNA.");
0201 
0202     printf("Injecting first UCNA at %#x.\n", FIRST_UCNA_ADDR);
0203 
0204     inject_ucna(params->vcpu, FIRST_UCNA_ADDR);
0205     vcpu_run(params->vcpu);
0206 
0207     exit_reason = params->vcpu->run->exit_reason;
0208     TEST_ASSERT(exit_reason == KVM_EXIT_IO,
0209             "unexpected exit reason %u-%s, expected KVM_EXIT_IO",
0210             exit_reason, exit_reason_str(exit_reason));
0211     TEST_ASSERT(get_ucall(params->vcpu, &uc) == UCALL_SYNC,
0212             "Expect UCALL_SYNC\n");
0213     TEST_ASSERT(uc.args[1] == SYNC_SECOND_UCNA, "Injecting second UCNA.");
0214 
0215     printf("Injecting second UCNA at %#x.\n", SECOND_UCNA_ADDR);
0216 
0217     inject_ucna(params->vcpu, SECOND_UCNA_ADDR);
0218     vcpu_run(params->vcpu);
0219 
0220     exit_reason = params->vcpu->run->exit_reason;
0221     TEST_ASSERT(exit_reason == KVM_EXIT_IO,
0222             "unexpected exit reason %u-%s, expected KVM_EXIT_IO",
0223             exit_reason, exit_reason_str(exit_reason));
0224     if (get_ucall(params->vcpu, &uc) == UCALL_ABORT) {
0225         TEST_ASSERT(false, "vCPU assertion failure: %s.\n",
0226                 (const char *)uc.args[0]);
0227     }
0228 
0229     return NULL;
0230 }
0231 
0232 static void test_ucna_injection(struct kvm_vcpu *vcpu, struct thread_params *params)
0233 {
0234     struct kvm_vm *vm = vcpu->vm;
0235     params->vcpu = vcpu;
0236     params->p_i_ucna_rcvd = (uint64_t *)addr_gva2hva(vm, (uint64_t)&i_ucna_rcvd);
0237     params->p_i_ucna_addr = (uint64_t *)addr_gva2hva(vm, (uint64_t)&i_ucna_addr);
0238     params->p_ucna_addr = (uint64_t *)addr_gva2hva(vm, (uint64_t)&ucna_addr);
0239     params->p_ucna_addr2 = (uint64_t *)addr_gva2hva(vm, (uint64_t)&ucna_addr2);
0240 
0241     run_ucna_injection(params);
0242 
0243     TEST_ASSERT(*params->p_i_ucna_rcvd == 1, "Only first UCNA get signaled.");
0244     TEST_ASSERT(*params->p_i_ucna_addr == FIRST_UCNA_ADDR,
0245             "Only first UCNA reported addr get recorded via interrupt.");
0246     TEST_ASSERT(*params->p_ucna_addr == FIRST_UCNA_ADDR,
0247             "First injected UCNAs should get exposed via registers.");
0248     TEST_ASSERT(*params->p_ucna_addr2 == SECOND_UCNA_ADDR,
0249             "Second injected UCNAs should get exposed via registers.");
0250 
0251     printf("Test successful.\n"
0252            "UCNA CMCI interrupts received: %ld\n"
0253            "Last UCNA address received via CMCI: %lx\n"
0254            "First UCNA address in vCPU thread: %lx\n"
0255            "Second UCNA address in vCPU thread: %lx\n",
0256            *params->p_i_ucna_rcvd, *params->p_i_ucna_addr,
0257            *params->p_ucna_addr, *params->p_ucna_addr2);
0258 }
0259 
0260 static void setup_mce_cap(struct kvm_vcpu *vcpu, bool enable_cmci_p)
0261 {
0262     uint64_t mcg_caps = MCG_CTL_P | MCG_SER_P | MCG_LMCE_P | KVM_MAX_MCE_BANKS;
0263     if (enable_cmci_p)
0264         mcg_caps |= MCG_CMCI_P;
0265 
0266     mcg_caps &= supported_mcg_caps | MCG_CAP_BANKS_MASK;
0267     vcpu_ioctl(vcpu, KVM_X86_SETUP_MCE, &mcg_caps);
0268 }
0269 
0270 static struct kvm_vcpu *create_vcpu_with_mce_cap(struct kvm_vm *vm, uint32_t vcpuid,
0271                          bool enable_cmci_p, void *guest_code)
0272 {
0273     struct kvm_vcpu *vcpu = vm_vcpu_add(vm, vcpuid, guest_code);
0274     setup_mce_cap(vcpu, enable_cmci_p);
0275     return vcpu;
0276 }
0277 
0278 int main(int argc, char *argv[])
0279 {
0280     struct thread_params params;
0281     struct kvm_vm *vm;
0282     struct kvm_vcpu *ucna_vcpu;
0283     struct kvm_vcpu *cmcidis_vcpu;
0284     struct kvm_vcpu *cmci_vcpu;
0285 
0286     kvm_check_cap(KVM_CAP_MCE);
0287 
0288     vm = __vm_create(VM_MODE_DEFAULT, 3, 0);
0289 
0290     kvm_ioctl(vm->kvm_fd, KVM_X86_GET_MCE_CAP_SUPPORTED,
0291           &supported_mcg_caps);
0292 
0293     if (!(supported_mcg_caps & MCG_CMCI_P)) {
0294         print_skip("MCG_CMCI_P is not supported");
0295         exit(KSFT_SKIP);
0296     }
0297 
0298     ucna_vcpu = create_vcpu_with_mce_cap(vm, 0, true, ucna_injection_guest_code);
0299     cmcidis_vcpu = create_vcpu_with_mce_cap(vm, 1, false, cmci_disabled_guest_code);
0300     cmci_vcpu = create_vcpu_with_mce_cap(vm, 2, true, cmci_enabled_guest_code);
0301 
0302     vm_init_descriptor_tables(vm);
0303     vcpu_init_descriptor_tables(ucna_vcpu);
0304     vcpu_init_descriptor_tables(cmcidis_vcpu);
0305     vcpu_init_descriptor_tables(cmci_vcpu);
0306     vm_install_exception_handler(vm, CMCI_VECTOR, guest_cmci_handler);
0307     vm_install_exception_handler(vm, GP_VECTOR, guest_gp_handler);
0308 
0309     virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
0310 
0311     test_ucna_injection(ucna_vcpu, &params);
0312     run_vcpu_expect_gp(cmcidis_vcpu);
0313     run_vcpu_expect_gp(cmci_vcpu);
0314 
0315     kvm_vm_free(vm);
0316 }