【问题标题】:kvm hypercall with multiple args具有多个参数的 kvm 超级调用
【发布时间】:2020-07-10 04:57:15
【问题描述】:

我目前正在尝试使用 kvm 构建一个小型虚拟机管理程序和内核,但我很难让具有多个 args 的超级调用正常工作。

这是我尝试过的:

// 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;
}

这些超调用中的任何一个都会导致 vcpu->kvm_run->exit_reason 等于 6,令我惊讶的是 KVM_EXIT_MMIO 而不是 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, &regs)<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

除了退出原因是KVM_EXIT_MMIO,为什么没有设置regs? 使用多个参数触发 KVM_EXIT_HYPERCALL 的正确方法是什么?

提前致谢

编辑: 万一这很重要: 我使用的是第 9 代 intel i7 cpu,运行 debian 和 linux 内核 5.4

【问题讨论】:

    标签: c virtual-machine virtualization kvm


    【解决方案1】:

    KVM_EXIT_HYPERCALL不再使用,根据documentation

    /* KVM_EXIT_HYPERCALL */
          struct {
              __u64 nr;
              __u64 args[6];
              __u64 ret;
              __u32 longmode;
              __u32 pad;
          } hypercall;
    

    未使用。这曾经被用于“对用户空间的超级调用”。到 实现这样的功能,使用 KVM_EXIT_IO (x86) 或 KVM_EXIT_MMIO (除 s390 之外的所有)。注意 KVM_EXIT_IO 明显快于 KVM_EXIT_MMIO。

    在我看来,KVM_EXIT_HYPERCALL 也没有实现。使用 grep 进行快速而肮脏的搜索。它已定义,但永远不会分配为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 版本:

    user@host:~/Linux/src> git-describe --tags
    v5.6-10895-g4c205c84e249
    user@host:~/Linux/src>
    

    有一个较旧的question 如何在此站点上实现自定义 VMCALL,并提供两个答案。你试过了吗?

    【讨论】:

    • 答案仅演示了如何使用 kvm_hypercall0 或程序集创建具有 0 个参数的超级调用。但是,我试图让它适用于多个参数,并且我的系统提供的 kvm_hypercallN 似乎没有按预期运行。例如,我希望 regs.rax 具有我在来宾程序中分配的值
    【解决方案2】:

    来自内核文档kvm/api

    如果exit_reason是KVM_EXIT_MMIO,那么vcpu已经执行了一个 kvm 无法满足的内存映射 I/O 指令。 如果 'is_write' 为真,则 'data' 成员包含写入的数据,并且 否则应由应用程序代码填写。

    您触发的超级调用引发了此类失败。
    这取决于您调用的超级调用的代码。

    【讨论】:

    • 1) 我执行了 vmcall 而不是 IO 2) 如何正确设置寄存器和 VMM,以便可以通过寄存器将多个参数传递给 VMM? - 我希望我能够通过 ioctl(KVM_GET_REGS, &regs) 读取寄存器,但所有 regs 似乎都是零
    • @Gaetano 我猜 vmcall 将 KVM_EXIT_MMIO 提升为一个例外,但仍然不明白为什么您要重新定义 KVM_HYPERCALL 而不是使用 kvm_para.h 中的定义或相应架构中的定义
    • 感谢您的回复。我正在使用 kvm_para.h 但出于故障排除的目的,我已将其复制并粘贴到我的项目中,并在此处可能使具有非 kvm 背景的其他人能够加入讨论。最后是关于正确使用 VT-X / AMD-V
    【解决方案3】:

    我在KVM API documentation 中找到了这个:

    /* KVM_EXIT_HYPERCALL */
    结构 {
    __u64 nr;
    __u64 参数[6]; __u64 ret;
    __u32 长模式;
    __u32 垫;
    } 超级调用;

    未使用。这曾经被用于“对用户空间的超级调用”。到 实现这样的功能,使用 KVM_EXIT_IO (x86) 或 KVM_EXIT_MMIO (除 s390 之外的所有)。注意 KVM_EXIT_IO 明显快于 KVM_EXIT_MMIO。

    所以,也许您应该尝试使用 KVM_EXIT_IO 或 KVM_EXIT_MMIO 来实现 Hypercall。

    【讨论】:

      猜你喜欢
      • 2016-02-09
      • 2021-02-15
      • 1970-01-01
      • 2013-06-13
      • 2011-11-21
      • 1970-01-01
      • 2022-01-17
      • 2013-10-17
      • 1970-01-01
      相关资源
      最近更新 更多