blob: 157ba9c46df8841f1de00308499315ac25619fea [file] [log] [blame] [edit]
#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();
}