sea_to_user.c 9.4 KB

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