| #include "libcflat.h" |
| #include "vm.h" |
| #include "desc.h" |
| #include "alloc_page.h" |
| #include "fwcfg.h" |
| |
| #define KVM_HYPERCALL_VMCALL 0x0f,0x01,0xc1 /* Intel */ |
| #define KVM_HYPERCALL_VMMCALL 0x0f,0x01,0xd9 /* AMD */ |
| |
| #define test_hypercall(type, may_fail) \ |
| do { \ |
| const char ref_insn[] = { KVM_HYPERCALL_##type }; \ |
| bool extra; \ |
| extern const char hc_##type[]; \ |
| \ |
| asm volatile goto(ASM_TRY("%l["xstr(fault_##type)"]") \ |
| xstr(hc_##type) ":\n\t" \ |
| ".byte " xstr(KVM_HYPERCALL_##type) \ |
| : /* no outputs allowed */ \ |
| : "a"(-1) \ |
| : "memory" \ |
| : fault_##type); \ |
| extra = memcmp(hc_##type, ref_insn, sizeof(ref_insn)); \ |
| report(true, "Hypercall via " #type ": OK%s", \ |
| extra ? " (patched)" : ""); \ |
| break; \ |
| \ |
| fault_##type: \ |
| extra = exception_vector() != PF_VECTOR && \ |
| exception_vector() != UD_VECTOR; \ |
| report((may_fail) && !extra, \ |
| "Hypercall via " #type ": %s%s(%u)", \ |
| extra ? "unexpected " : "", \ |
| exception_mnemonic(exception_vector()), \ |
| exception_error_code()); \ |
| } while (0) |
| |
| #ifdef __x86_64__ |
| #define NON_CANON_START (1UL << 47) |
| |
| static bool test_edge(bool may_fail) |
| { |
| const char *addr = (void *)(NON_CANON_START - 3); |
| char insn[3] = { addr[0], addr[1], addr[2] }; |
| |
| static_assert(NON_CANON_START == 0x800000000000UL); |
| asm volatile goto("jmpq *%[addr]; 1:" |
| ASM_EX_ENTRY("0x7ffffffffffd", "%l[insn_failed]") |
| ASM_EX_ENTRY("0x800000000000", "1b") |
| : /* no outputs allowed */ |
| : "a"(-1), [addr]"r"(addr) |
| : "memory" |
| : insn_failed); |
| printf("Return from %s(%u) with RIP = %lx%s\n", |
| exception_mnemonic(exception_vector()), exception_error_code(), |
| NON_CANON_START, memcmp(addr, insn, 3) ? ", patched" : ""); |
| return true; |
| |
| insn_failed: |
| printf("KVM hypercall failed%s\n", |
| may_fail ? ", as expected" : " unexpectedly!"); |
| return may_fail; |
| } |
| #endif |
| |
| int main(int ac, char **av) |
| { |
| /* VMCALL may be patched by KVM on AMD or fail with #UD on bare metal */ |
| test_hypercall(VMCALL, !is_intel()); |
| |
| /* VMMCALL may be patched on Intel or fail with #UD in bare metal */ |
| test_hypercall(VMMCALL, is_intel()); |
| |
| #ifdef __x86_64__ |
| setup_vm(); |
| |
| u8 *topmost = (void *) (NON_CANON_START - PAGE_SIZE); |
| |
| install_page(current_page_table(), virt_to_phys(alloc_page()), topmost); |
| memset(topmost, 0xcc, PAGE_SIZE); |
| |
| memcpy(topmost + PAGE_SIZE - 3, (char []){ KVM_HYPERCALL_VMCALL }, 3); |
| report(test_edge(!is_intel()), |
| "VMCALL on edge of canonical address space (Intel)"); |
| |
| memcpy(topmost + PAGE_SIZE - 3, (char []){ KVM_HYPERCALL_VMMCALL }, 3); |
| report(test_edge(is_intel()), |
| "VMMCALL on edge of canonical address space (AMD)"); |
| #endif |
| |
| return report_summary(); |
| } |