1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2022, Google LLC.
7 * This work is licensed under the terms of the GNU GPL, version 2.
9 * Test that user space can inject UnCorrectable No Action required (UCNA)
10 * memory errors to the guest.
12 * The test starts one vCPU with the MCG_CMCI_P enabled. It verifies that
13 * proper UCNA errors can be injected to a vCPU with MCG_CMCI_P and
14 * corresponding per-bank control register (MCI_CTL2) bit enabled.
15 * The test also checks that the UCNA errors get recorded in the
16 * Machine Check bank registers no matter the error signal interrupts get
17 * delivered into the guest or not.
27 #include "processor.h"
28 #include "test_util.h"
31 #define SYNC_FIRST_UCNA 9
32 #define SYNC_SECOND_UCNA 10
34 #define FIRST_UCNA_ADDR 0xdeadbeef
35 #define SECOND_UCNA_ADDR 0xcafeb0ba
38 * Vector for the CMCI interrupt.
39 * Value is arbitrary. Any value in 0x20-0xFF should work:
40 * https://wiki.osdev.org/Interrupt_Vector_Table
42 #define CMCI_VECTOR 0xa9
44 #define UCNA_BANK 0x7 // IMC0 bank
46 #define MCI_CTL2_RESERVED_BIT BIT_ULL(29)
48 static uint64_t supported_mcg_caps
;
51 * Record states about the injected UCNA.
52 * The variables started with the 'i_' prefixes are recorded in interrupt
53 * handler. Variables without the 'i_' prefixes are recorded in guest main
56 static volatile uint64_t i_ucna_rcvd
;
57 static volatile uint64_t i_ucna_addr
;
58 static volatile uint64_t ucna_addr
;
59 static volatile uint64_t ucna_addr2
;
61 struct thread_params
{
62 struct kvm_vcpu
*vcpu
;
63 uint64_t *p_i_ucna_rcvd
;
64 uint64_t *p_i_ucna_addr
;
65 uint64_t *p_ucna_addr
;
66 uint64_t *p_ucna_addr2
;
69 static void verify_apic_base_addr(void)
71 uint64_t msr
= rdmsr(MSR_IA32_APICBASE
);
72 uint64_t base
= GET_APIC_BASE(msr
);
74 GUEST_ASSERT(base
== APIC_DEFAULT_GPA
);
77 static void ucna_injection_guest_code(void)
80 verify_apic_base_addr();
83 /* Sets up the interrupt vector and enables per-bank CMCI sigaling. */
84 xapic_write_reg(APIC_LVTCMCI
, CMCI_VECTOR
| APIC_DM_FIXED
);
85 ctl2
= rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK
));
86 wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK
), ctl2
| MCI_CTL2_CMCI_EN
);
88 /* Enables interrupt in guest. */
91 /* Let user space inject the first UCNA */
92 GUEST_SYNC(SYNC_FIRST_UCNA
);
94 ucna_addr
= rdmsr(MSR_IA32_MCx_ADDR(UCNA_BANK
));
96 /* Disables the per-bank CMCI signaling. */
97 ctl2
= rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK
));
98 wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK
), ctl2
& ~MCI_CTL2_CMCI_EN
);
100 /* Let the user space inject the second UCNA */
101 GUEST_SYNC(SYNC_SECOND_UCNA
);
103 ucna_addr2
= rdmsr(MSR_IA32_MCx_ADDR(UCNA_BANK
));
107 static void cmci_disabled_guest_code(void)
109 uint64_t ctl2
= rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK
));
110 wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK
), ctl2
| MCI_CTL2_CMCI_EN
);
115 static void cmci_enabled_guest_code(void)
117 uint64_t ctl2
= rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK
));
118 wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK
), ctl2
| MCI_CTL2_RESERVED_BIT
);
123 static void guest_cmci_handler(struct ex_regs
*regs
)
126 i_ucna_addr
= rdmsr(MSR_IA32_MCx_ADDR(UCNA_BANK
));
127 xapic_write_reg(APIC_EOI
, 0);
130 static void guest_gp_handler(struct ex_regs
*regs
)
135 static void run_vcpu_expect_gp(struct kvm_vcpu
*vcpu
)
141 TEST_ASSERT_KVM_EXIT_REASON(vcpu
, KVM_EXIT_IO
);
142 TEST_ASSERT(get_ucall(vcpu
, &uc
) == UCALL_SYNC
,
143 "Expect UCALL_SYNC");
144 TEST_ASSERT(uc
.args
[1] == SYNC_GP
, "#GP is expected.");
145 printf("vCPU received GP in guest.\n");
148 static void inject_ucna(struct kvm_vcpu
*vcpu
, uint64_t addr
) {
150 * A UCNA error is indicated with VAL=1, UC=1, PCC=0, S=0 and AR=0 in
151 * the IA32_MCi_STATUS register.
152 * MSCOD=1 (BIT[16] - MscodDataRdErr).
153 * MCACOD=0x0090 (Memory controller error format, channel 0)
155 uint64_t status
= MCI_STATUS_VAL
| MCI_STATUS_UC
| MCI_STATUS_EN
|
156 MCI_STATUS_MISCV
| MCI_STATUS_ADDRV
| 0x10090;
157 struct kvm_x86_mce mce
= {};
161 * MCM_ADDR_PHYS indicates the reported address is a physical address.
162 * Lowest 6 bits is the recoverable address LSB, i.e., the injected MCE
163 * is at 4KB granularity.
165 mce
.misc
= (MCM_ADDR_PHYS
<< 6) | 0xc;
167 mce
.bank
= UCNA_BANK
;
169 vcpu_ioctl(vcpu
, KVM_X86_SET_MCE
, &mce
);
172 static void *run_ucna_injection(void *arg
)
174 struct thread_params
*params
= (struct thread_params
*)arg
;
179 r
= pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS
, &old
);
181 "pthread_setcanceltype failed with errno=%d",
184 vcpu_run(params
->vcpu
);
186 TEST_ASSERT_KVM_EXIT_REASON(params
->vcpu
, KVM_EXIT_IO
);
187 TEST_ASSERT(get_ucall(params
->vcpu
, &uc
) == UCALL_SYNC
,
188 "Expect UCALL_SYNC");
189 TEST_ASSERT(uc
.args
[1] == SYNC_FIRST_UCNA
, "Injecting first UCNA.");
191 printf("Injecting first UCNA at %#x.\n", FIRST_UCNA_ADDR
);
193 inject_ucna(params
->vcpu
, FIRST_UCNA_ADDR
);
194 vcpu_run(params
->vcpu
);
196 TEST_ASSERT_KVM_EXIT_REASON(params
->vcpu
, KVM_EXIT_IO
);
197 TEST_ASSERT(get_ucall(params
->vcpu
, &uc
) == UCALL_SYNC
,
198 "Expect UCALL_SYNC");
199 TEST_ASSERT(uc
.args
[1] == SYNC_SECOND_UCNA
, "Injecting second UCNA.");
201 printf("Injecting second UCNA at %#x.\n", SECOND_UCNA_ADDR
);
203 inject_ucna(params
->vcpu
, SECOND_UCNA_ADDR
);
204 vcpu_run(params
->vcpu
);
206 TEST_ASSERT_KVM_EXIT_REASON(params
->vcpu
, KVM_EXIT_IO
);
207 if (get_ucall(params
->vcpu
, &uc
) == UCALL_ABORT
) {
208 TEST_ASSERT(false, "vCPU assertion failure: %s.",
209 (const char *)uc
.args
[0]);
215 static void test_ucna_injection(struct kvm_vcpu
*vcpu
, struct thread_params
*params
)
217 struct kvm_vm
*vm
= vcpu
->vm
;
219 params
->p_i_ucna_rcvd
= (uint64_t *)addr_gva2hva(vm
, (uint64_t)&i_ucna_rcvd
);
220 params
->p_i_ucna_addr
= (uint64_t *)addr_gva2hva(vm
, (uint64_t)&i_ucna_addr
);
221 params
->p_ucna_addr
= (uint64_t *)addr_gva2hva(vm
, (uint64_t)&ucna_addr
);
222 params
->p_ucna_addr2
= (uint64_t *)addr_gva2hva(vm
, (uint64_t)&ucna_addr2
);
224 run_ucna_injection(params
);
226 TEST_ASSERT(*params
->p_i_ucna_rcvd
== 1, "Only first UCNA get signaled.");
227 TEST_ASSERT(*params
->p_i_ucna_addr
== FIRST_UCNA_ADDR
,
228 "Only first UCNA reported addr get recorded via interrupt.");
229 TEST_ASSERT(*params
->p_ucna_addr
== FIRST_UCNA_ADDR
,
230 "First injected UCNAs should get exposed via registers.");
231 TEST_ASSERT(*params
->p_ucna_addr2
== SECOND_UCNA_ADDR
,
232 "Second injected UCNAs should get exposed via registers.");
234 printf("Test successful.\n"
235 "UCNA CMCI interrupts received: %ld\n"
236 "Last UCNA address received via CMCI: %lx\n"
237 "First UCNA address in vCPU thread: %lx\n"
238 "Second UCNA address in vCPU thread: %lx\n",
239 *params
->p_i_ucna_rcvd
, *params
->p_i_ucna_addr
,
240 *params
->p_ucna_addr
, *params
->p_ucna_addr2
);
243 static void setup_mce_cap(struct kvm_vcpu
*vcpu
, bool enable_cmci_p
)
245 uint64_t mcg_caps
= MCG_CTL_P
| MCG_SER_P
| MCG_LMCE_P
| KVM_MAX_MCE_BANKS
;
247 mcg_caps
|= MCG_CMCI_P
;
249 mcg_caps
&= supported_mcg_caps
| MCG_CAP_BANKS_MASK
;
250 vcpu_ioctl(vcpu
, KVM_X86_SETUP_MCE
, &mcg_caps
);
253 static struct kvm_vcpu
*create_vcpu_with_mce_cap(struct kvm_vm
*vm
, uint32_t vcpuid
,
254 bool enable_cmci_p
, void *guest_code
)
256 struct kvm_vcpu
*vcpu
= vm_vcpu_add(vm
, vcpuid
, guest_code
);
257 setup_mce_cap(vcpu
, enable_cmci_p
);
261 int main(int argc
, char *argv
[])
263 struct thread_params params
;
265 struct kvm_vcpu
*ucna_vcpu
;
266 struct kvm_vcpu
*cmcidis_vcpu
;
267 struct kvm_vcpu
*cmci_vcpu
;
269 kvm_check_cap(KVM_CAP_MCE
);
271 vm
= __vm_create(VM_SHAPE_DEFAULT
, 3, 0);
273 kvm_ioctl(vm
->kvm_fd
, KVM_X86_GET_MCE_CAP_SUPPORTED
,
274 &supported_mcg_caps
);
276 if (!(supported_mcg_caps
& MCG_CMCI_P
)) {
277 print_skip("MCG_CMCI_P is not supported");
281 ucna_vcpu
= create_vcpu_with_mce_cap(vm
, 0, true, ucna_injection_guest_code
);
282 cmcidis_vcpu
= create_vcpu_with_mce_cap(vm
, 1, false, cmci_disabled_guest_code
);
283 cmci_vcpu
= create_vcpu_with_mce_cap(vm
, 2, true, cmci_enabled_guest_code
);
285 vm_install_exception_handler(vm
, CMCI_VECTOR
, guest_cmci_handler
);
286 vm_install_exception_handler(vm
, GP_VECTOR
, guest_gp_handler
);
288 virt_pg_map(vm
, APIC_DEFAULT_GPA
, APIC_DEFAULT_GPA
);
290 test_ucna_injection(ucna_vcpu
, ¶ms
);
291 run_vcpu_expect_gp(cmcidis_vcpu
);
292 run_vcpu_expect_gp(cmci_vcpu
);