I'm currently trying to build a small hypervisor and kernel using kvm and I struggle to get hypercalls with multiple args working correctly.
Here is what I've tried:
// guest.c
#define KVM_HYPERCALL vmcall
// #define KVM_HYPERCALL vmmcall
// #define KVM_HYPERCALL ".byte 0x0f,0x01,0xd9"
// #define KVM_HYPERCALL .byte 0x0f,0x01,0xc1"
static inline long kvm_hypercall4(int nr, unsigned long p1,
unsigned long p2, unsigned long p3,
unsigned long p4) {
long ret;
asm volatile(KVM_HYPERCALL
: "=a"(ret)
: "a"(nr), "b"(p1), "c"(p2), "d"(p3), "S"(p4)
: "memory");
return ret;
}
Any of those Hypercalls lead to vcpu->kvm_run->exit_reason
to equal 6 which is to my surprise KVM_EXIT_MMIO
instead of KVM_EXIT_HYPERCALL
switch (vcpu->kvm_run->exit_reason) {
case KVM_EXIT_MMIO:
printf("syscall: %lld\n", vcpu->kvm_run->hypercall.nr); // prints 0
printf("arg 1: %lld\n", vcpu->kvm_run->hypercall.args[1]); // prints 0
printf("arg 2: %lld\n", vcpu->kvm_run->hypercall.args[2]); // prints 0
printf("arg 3: %lld\n", vcpu->kvm_run->hypercall.args[3]); // prints 0
if(ioctl(vcpu->fd, KVM_GET_REGS, ®s)<0) exit 1;
printf("rax: %lld\n", regs.rax); // prints 0
printf("rbx: %lld\n", regs.rbx); // prints 0
printf("rcx: %lld\n", regs.rcx); // prints 0
Aside of the exit reason being KVM_EXIT_MMIO
why are the regs not set?
What is the right way to trigger a KVM_EXIT_HYPERCALL with multple arguments?
Thanks in advance
EDIT: In case it matters: I'm using 9th generation intel i7 cpu running debian with linux kernel 5.4
KVM hypercalls use the HYPCALL instruction with code 0 and the hypercall number in $2 (v0). Up to four arguments may be placed in $4-$7 (a0-a3) and the return value is placed in $2 (v0). The template for each hypercall is: 1. Hypercall name. 2. Architecture (s) 3. Status (deprecated, obsolete, active) 4. Purpose 1. KVM_HC_VAPIC_POLL_IRQ ¶
KVM hypercalls uses 4 byte opcode, that are patched with ‘hypercall-instructions’ property inside the device tree’s /hypervisor node. For more information refer to The PPC KVM paravirtual interface KVM hypercalls use the HYPCALL instruction with code 0 and the hypercall number in $2 (v0).
The hypercall number should be placed in rax and the return value will be placed in rax. No other registers will be clobbered unless explicitly stated by the particular hypercall. R2-R7 are used for parameters 1-6. In addition, R1 is used for hypercall number. The return value is written to R2.
The hypercall lets a guest send multicast IPIs, with at most 128 128 destinations per hypercall in 64-bit mode and 64 vCPUs per hypercall in 32-bit mode. The destinations are represented by a bitmap contained in the first two arguments (a0 and a1).
KVM_EXIT_HYPERCALL
is no longer used, according to the documentation:
/* KVM_EXIT_HYPERCALL */ struct { __u64 nr; __u64 args[6]; __u64 ret; __u32 longmode; __u32 pad; } hypercall;
Unused. This was once used for 'hypercall to userspace'. To implement such functionality, use KVM_EXIT_IO (x86) or KVM_EXIT_MMIO (all except s390). Note KVM_EXIT_IO is significantly faster than KVM_EXIT_MMIO.
And it seems to me that KVM_EXIT_HYPERCALL
is also not implemented. Quick and dirty search with grep. It's defined, but will never assigned as exit_reason
:
user@host:~/Linux/src> grep -R KVM_EXIT_HYPERCALL
include/uapi/linux/kvm.h:#define KVM_EXIT_HYPERCALL 3
include/uapi/linux/kvm.h: /* KVM_EXIT_HYPERCALL */
Documentation/virt/kvm/api.rst: /* KVM_EXIT_HYPERCALL */
tools/include/uapi/linux/kvm.h:#define KVM_EXIT_HYPERCALL 3
tools/include/uapi/linux/kvm.h: /* KVM_EXIT_HYPERCALL */
tools/testing/selftests/kvm/lib/kvm_util.c: {KVM_EXIT_HYPERCALL, "HYPERCALL"},
user@host:~/Linux/src>
Linux version:
user@host:~/Linux/src> git-describe --tags
v5.6-10895-g4c205c84e249
user@host:~/Linux/src>
There is an older question how to implement custom VMCALL's with two answers on this site. Have you tried them?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With