1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Test KVM returns to userspace with KVM_EXIT_ARM_SEA if host APEI fails 4 * to handle SEA and userspace has opt-ed in KVM_CAP_ARM_SEA_TO_USER. 5 * 6 * After reaching userspace with expected arm_sea info, also test userspace 7 * injecting a synchronous external data abort into the guest. 8 * 9 * This test utilizes EINJ to generate a REAL synchronous external data 10 * abort by consuming a recoverable uncorrectable memory error. Therefore 11 * the device under test must support EINJ in both firmware and host kernel, 12 * including the notrigger feature. Otherwise the test will be skipped. 13 * The under-test platform's APEI should be unable to claim SEA. Otherwise 14 * the test will also be skipped. 15 */ 16 17 #include <signal.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <unistd.h> 21 22 #include "test_util.h" 23 #include "kvm_util.h" 24 #include "processor.h" 25 #include "guest_modes.h" 26 27 #define PAGE_PRESENT (1ULL << 63) 28 #define PAGE_PHYSICAL 0x007fffffffffffffULL 29 #define PAGE_ADDR_MASK (~(0xfffULL)) 30 31 /* Group ISV and ISS[23:14]. */ 32 #define ESR_ELx_INST_SYNDROME ((ESR_ELx_ISV) | (ESR_ELx_SAS) | \ 33 (ESR_ELx_SSE) | (ESR_ELx_SRT_MASK) | \ 34 (ESR_ELx_SF) | (ESR_ELx_AR)) 35 36 #define EINJ_ETYPE "/sys/kernel/debug/apei/einj/error_type" 37 #define EINJ_ADDR "/sys/kernel/debug/apei/einj/param1" 38 #define EINJ_MASK "/sys/kernel/debug/apei/einj/param2" 39 #define EINJ_FLAGS "/sys/kernel/debug/apei/einj/flags" 40 #define EINJ_NOTRIGGER "/sys/kernel/debug/apei/einj/notrigger" 41 #define EINJ_DOIT "/sys/kernel/debug/apei/einj/error_inject" 42 /* Memory Uncorrectable non-fatal. */ 43 #define ERROR_TYPE_MEMORY_UER 0x10 44 /* Memory address and mask valid (param1 and param2). */ 45 #define MASK_MEMORY_UER 0b10 46 47 /* Guest virtual address region = [2G, 3G). */ 48 #define START_GVA 0x80000000UL 49 #define VM_MEM_SIZE 0x40000000UL 50 /* Note: EINJ_OFFSET must < VM_MEM_SIZE. */ 51 #define EINJ_OFFSET 0x01234badUL 52 #define EINJ_GVA ((START_GVA) + (EINJ_OFFSET)) 53 54 static vm_paddr_t einj_gpa; 55 static void *einj_hva; 56 static uint64_t einj_hpa; 57 static bool far_invalid; 58 59 static uint64_t translate_to_host_paddr(unsigned long vaddr) 60 { 61 uint64_t pinfo; 62 int64_t offset = vaddr / getpagesize() * sizeof(pinfo); 63 int fd; 64 uint64_t page_addr; 65 uint64_t paddr; 66 67 fd = open("/proc/self/pagemap", O_RDONLY); 68 if (fd < 0) 69 ksft_exit_fail_perror("Failed to open /proc/self/pagemap"); 70 if (pread(fd, &pinfo, sizeof(pinfo), offset) != sizeof(pinfo)) { 71 close(fd); 72 ksft_exit_fail_perror("Failed to read /proc/self/pagemap"); 73 } 74 75 close(fd); 76 77 if ((pinfo & PAGE_PRESENT) == 0) 78 ksft_exit_fail_perror("Page not present"); 79 80 page_addr = (pinfo & PAGE_PHYSICAL) << MIN_PAGE_SHIFT; 81 paddr = page_addr + (vaddr & (getpagesize() - 1)); 82 return paddr; 83 } 84 85 static void write_einj_entry(const char *einj_path, uint64_t val) 86 { 87 char cmd[256] = {0}; 88 FILE *cmdfile = NULL; 89 90 sprintf(cmd, "echo %#lx > %s", val, einj_path); 91 cmdfile = popen(cmd, "r"); 92 93 if (pclose(cmdfile) == 0) 94 ksft_print_msg("echo %#lx > %s - done\n", val, einj_path); 95 else 96 ksft_exit_fail_perror("Failed to write EINJ entry"); 97 } 98 99 static void inject_uer(uint64_t paddr) 100 { 101 if (access("/sys/firmware/acpi/tables/EINJ", R_OK) == -1) 102 ksft_test_result_skip("EINJ table no available in firmware"); 103 104 if (access(EINJ_ETYPE, R_OK | W_OK) == -1) 105 ksft_test_result_skip("EINJ module probably not loaded?"); 106 107 write_einj_entry(EINJ_ETYPE, ERROR_TYPE_MEMORY_UER); 108 write_einj_entry(EINJ_FLAGS, MASK_MEMORY_UER); 109 write_einj_entry(EINJ_ADDR, paddr); 110 write_einj_entry(EINJ_MASK, ~0x0UL); 111 write_einj_entry(EINJ_NOTRIGGER, 1); 112 write_einj_entry(EINJ_DOIT, 1); 113 } 114 115 /* 116 * When host APEI successfully claims the SEA caused by guest_code, kernel 117 * will send SIGBUS signal with BUS_MCEERR_AR to test thread. 118 * 119 * We set up this SIGBUS handler to skip the test for that case. 120 */ 121 static void sigbus_signal_handler(int sig, siginfo_t *si, void *v) 122 { 123 ksft_print_msg("SIGBUS (%d) received, dumping siginfo...\n", sig); 124 ksft_print_msg("si_signo=%d, si_errno=%d, si_code=%d, si_addr=%p\n", 125 si->si_signo, si->si_errno, si->si_code, si->si_addr); 126 if (si->si_code == BUS_MCEERR_AR) 127 ksft_test_result_skip("SEA is claimed by host APEI\n"); 128 else 129 ksft_test_result_fail("Exit with signal unhandled\n"); 130 131 exit(0); 132 } 133 134 static void setup_sigbus_handler(void) 135 { 136 struct sigaction act; 137 138 memset(&act, 0, sizeof(act)); 139 sigemptyset(&act.sa_mask); 140 act.sa_sigaction = sigbus_signal_handler; 141 act.sa_flags = SA_SIGINFO; 142 TEST_ASSERT(sigaction(SIGBUS, &act, NULL) == 0, 143 "Failed to setup SIGBUS handler"); 144 } 145 146 static void guest_code(void) 147 { 148 uint64_t guest_data; 149 150 /* Consumes error will cause a SEA. */ 151 guest_data = *(uint64_t *)EINJ_GVA; 152 153 GUEST_FAIL("Poison not protected by SEA: gva=%#lx, guest_data=%#lx\n", 154 EINJ_GVA, guest_data); 155 } 156 157 static void expect_sea_handler(struct ex_regs *regs) 158 { 159 u64 esr = read_sysreg(esr_el1); 160 u64 far = read_sysreg(far_el1); 161 bool expect_far_invalid = far_invalid; 162 163 GUEST_PRINTF("Handling Guest SEA\n"); 164 GUEST_PRINTF("ESR_EL1=%#lx, FAR_EL1=%#lx\n", esr, far); 165 166 GUEST_ASSERT_EQ(ESR_ELx_EC(esr), ESR_ELx_EC_DABT_CUR); 167 GUEST_ASSERT_EQ(esr & ESR_ELx_FSC_TYPE, ESR_ELx_FSC_EXTABT); 168 169 if (expect_far_invalid) { 170 GUEST_ASSERT_EQ(esr & ESR_ELx_FnV, ESR_ELx_FnV); 171 GUEST_PRINTF("Guest observed garbage value in FAR\n"); 172 } else { 173 GUEST_ASSERT_EQ(esr & ESR_ELx_FnV, 0); 174 GUEST_ASSERT_EQ(far, EINJ_GVA); 175 } 176 177 GUEST_DONE(); 178 } 179 180 static void vcpu_inject_sea(struct kvm_vcpu *vcpu) 181 { 182 struct kvm_vcpu_events events = {}; 183 184 events.exception.ext_dabt_pending = true; 185 vcpu_events_set(vcpu, &events); 186 } 187 188 static void run_vm(struct kvm_vm *vm, struct kvm_vcpu *vcpu) 189 { 190 struct ucall uc; 191 bool guest_done = false; 192 struct kvm_run *run = vcpu->run; 193 u64 esr; 194 195 /* Resume the vCPU after error injection to consume the error. */ 196 vcpu_run(vcpu); 197 198 ksft_print_msg("Dump kvm_run info about KVM_EXIT_%s\n", 199 exit_reason_str(run->exit_reason)); 200 ksft_print_msg("kvm_run.arm_sea: esr=%#llx, flags=%#llx\n", 201 run->arm_sea.esr, run->arm_sea.flags); 202 ksft_print_msg("kvm_run.arm_sea: gva=%#llx, gpa=%#llx\n", 203 run->arm_sea.gva, run->arm_sea.gpa); 204 205 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_ARM_SEA); 206 207 esr = run->arm_sea.esr; 208 TEST_ASSERT_EQ(ESR_ELx_EC(esr), ESR_ELx_EC_DABT_LOW); 209 TEST_ASSERT_EQ(esr & ESR_ELx_FSC_TYPE, ESR_ELx_FSC_EXTABT); 210 TEST_ASSERT_EQ(ESR_ELx_ISS2(esr), 0); 211 TEST_ASSERT_EQ((esr & ESR_ELx_INST_SYNDROME), 0); 212 TEST_ASSERT_EQ(esr & ESR_ELx_VNCR, 0); 213 214 if (!(esr & ESR_ELx_FnV)) { 215 ksft_print_msg("Expect gva to match given FnV bit is 0\n"); 216 TEST_ASSERT_EQ(run->arm_sea.gva, EINJ_GVA); 217 } 218 219 if (run->arm_sea.flags & KVM_EXIT_ARM_SEA_FLAG_GPA_VALID) { 220 ksft_print_msg("Expect gpa to match given KVM_EXIT_ARM_SEA_FLAG_GPA_VALID is set\n"); 221 TEST_ASSERT_EQ(run->arm_sea.gpa, einj_gpa & PAGE_ADDR_MASK); 222 } 223 224 far_invalid = esr & ESR_ELx_FnV; 225 226 /* Inject a SEA into guest and expect handled in SEA handler. */ 227 vcpu_inject_sea(vcpu); 228 229 /* Expect the guest to reach GUEST_DONE gracefully. */ 230 do { 231 vcpu_run(vcpu); 232 switch (get_ucall(vcpu, &uc)) { 233 case UCALL_PRINTF: 234 ksft_print_msg("From guest: %s", uc.buffer); 235 break; 236 case UCALL_DONE: 237 ksft_print_msg("Guest done gracefully!\n"); 238 guest_done = 1; 239 break; 240 case UCALL_ABORT: 241 ksft_print_msg("Guest aborted!\n"); 242 guest_done = 1; 243 REPORT_GUEST_ASSERT(uc); 244 break; 245 default: 246 TEST_FAIL("Unexpected ucall: %lu\n", uc.cmd); 247 } 248 } while (!guest_done); 249 } 250 251 static struct kvm_vm *vm_create_with_sea_handler(struct kvm_vcpu **vcpu) 252 { 253 size_t backing_page_size; 254 size_t guest_page_size; 255 size_t alignment; 256 uint64_t num_guest_pages; 257 vm_paddr_t start_gpa; 258 enum vm_mem_backing_src_type src_type = VM_MEM_SRC_ANONYMOUS_HUGETLB_1GB; 259 struct kvm_vm *vm; 260 261 backing_page_size = get_backing_src_pagesz(src_type); 262 guest_page_size = vm_guest_mode_params[VM_MODE_DEFAULT].page_size; 263 alignment = max(backing_page_size, guest_page_size); 264 num_guest_pages = VM_MEM_SIZE / guest_page_size; 265 266 vm = __vm_create_with_one_vcpu(vcpu, num_guest_pages, guest_code); 267 vm_init_descriptor_tables(vm); 268 vcpu_init_descriptor_tables(*vcpu); 269 270 vm_install_sync_handler(vm, 271 /*vector=*/VECTOR_SYNC_CURRENT, 272 /*ec=*/ESR_ELx_EC_DABT_CUR, 273 /*handler=*/expect_sea_handler); 274 275 start_gpa = (vm->max_gfn - num_guest_pages) * guest_page_size; 276 start_gpa = align_down(start_gpa, alignment); 277 278 vm_userspace_mem_region_add( 279 /*vm=*/vm, 280 /*src_type=*/src_type, 281 /*guest_paddr=*/start_gpa, 282 /*slot=*/1, 283 /*npages=*/num_guest_pages, 284 /*flags=*/0); 285 286 virt_map(vm, START_GVA, start_gpa, num_guest_pages); 287 288 ksft_print_msg("Mapped %#lx pages: gva=%#lx to gpa=%#lx\n", 289 num_guest_pages, START_GVA, start_gpa); 290 return vm; 291 } 292 293 static void vm_inject_memory_uer(struct kvm_vm *vm) 294 { 295 uint64_t guest_data; 296 297 einj_gpa = addr_gva2gpa(vm, EINJ_GVA); 298 einj_hva = addr_gva2hva(vm, EINJ_GVA); 299 300 /* Populate certain data before injecting UER. */ 301 *(uint64_t *)einj_hva = 0xBAADCAFE; 302 guest_data = *(uint64_t *)einj_hva; 303 ksft_print_msg("Before EINJect: data=%#lx\n", 304 guest_data); 305 306 einj_hpa = translate_to_host_paddr((unsigned long)einj_hva); 307 308 ksft_print_msg("EINJ_GVA=%#lx, einj_gpa=%#lx, einj_hva=%p, einj_hpa=%#lx\n", 309 EINJ_GVA, einj_gpa, einj_hva, einj_hpa); 310 311 inject_uer(einj_hpa); 312 ksft_print_msg("Memory UER EINJected\n"); 313 } 314 315 int main(int argc, char *argv[]) 316 { 317 struct kvm_vm *vm; 318 struct kvm_vcpu *vcpu; 319 320 TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SEA_TO_USER)); 321 322 setup_sigbus_handler(); 323 324 vm = vm_create_with_sea_handler(&vcpu); 325 vm_enable_cap(vm, KVM_CAP_ARM_SEA_TO_USER, 0); 326 vm_inject_memory_uer(vm); 327 run_vm(vm, vcpu); 328 kvm_vm_free(vm); 329 330 return 0; 331 } 332