简体   繁体   English

Qemu 如何模拟 PCIe 设备?

[英]How does Qemu emulate PCIe devices?

I'm writing an open source document about qemu internals so if you help me you're helping the growth of Qemu project我正在写一个关于 qemu 内部的开源文档,所以如果你帮助我,你就是在帮助 Qemu 项目的发展

The closest answer I found was: In which conditions the ioctl KVM_RUN returns?我找到的最接近的答案是: ioctl KVM_RUN 在什么情况下返回?

This is the thread loop for a single CPU running on KVM:这是在 KVM 上运行的单个 CPU 的线程循环:

static void *qemu_kvm_cpu_thread_fn(void *arg)
{
    CPUState *cpu = arg;
    int r;

    rcu_register_thread();

    qemu_mutex_lock_iothread();
    qemu_thread_get_self(cpu->thread);
    cpu->thread_id = qemu_get_thread_id();
    cpu->can_do_io = 1;
    current_cpu = cpu;

    r = kvm_init_vcpu(cpu);
    if (r < 0) {
        error_report("kvm_init_vcpu failed: %s", strerror(-r));
        exit(1);
    }

    kvm_init_cpu_signals(cpu);

    /* signal CPU creation */
    cpu->created = true;
    qemu_cond_signal(&qemu_cpu_cond);
    qemu_guest_random_seed_thread_part2(cpu->random_seed);

    do {
        if (cpu_can_run(cpu)) {
            r = kvm_cpu_exec(cpu);
            if (r == EXCP_DEBUG) {
                cpu_handle_guest_debug(cpu);
            }
        }
        qemu_wait_io_event(cpu);
    } while (!cpu->unplug || cpu_can_run(cpu));

    qemu_kvm_destroy_vcpu(cpu);
    cpu->created = false;
    qemu_cond_signal(&qemu_cpu_cond);
    qemu_mutex_unlock_iothread();
    rcu_unregister_thread();
    return NULL;
}

You can see here:你可以在这里看到:

do {
        if (cpu_can_run(cpu)) {
            r = kvm_cpu_exec(cpu);
            if (r == EXCP_DEBUG) {
                cpu_handle_guest_debug(cpu);
            }
        }
        qemu_wait_io_event(cpu);
    } while (!cpu->unplug || cpu_can_run(cpu));

that every time the KVM returns, it gives an opportunity for Qemu to emulate things.每次 KVM 返回时,它都为 Qemu 提供了模拟事物的机会。 I suppose that when the kernel on the guest tries to access a PCIe device, KVM on the host returns.我想当客户机上的 kernel 尝试访问 PCIe 设备时,主机上的 KVM 会返回。 I don't know how KVM knows how to return.不知道KVM怎么知道返回。 Maybe KVM maintains the addresses of the PCIe device and tells Intel's VT-D or AMD's IOV which addresses should generate an exception.也许 KVM 会维护 PCIe 设备的地址,并告诉 Intel 的 VT-D 或 AMD 的 IOV 哪些地址应该产生异常。 Can someone clarify this?有人可以澄清一下吗?

Well, by the look of the qemu_kvm_cpu_thread_fn , the only place where a PCIe access could be emulated, is qemu_wait_io_event(cpu) , which is defined here: https://github.com/qemu/qemu/blob/stable-4.2/cpus.c#L1266 and which calls qemu_wait_io_event_common defined here: https://github.com/qemu/qemu/blob/stable-4.2/cpus.c#L1241 which calls process_queued_cpu_work defined here:https://github.com/qemu/qemu/blob/stable-4.2/cpus-common.c#L309好吧,通过qemu_kvm_cpu_thread_fn的外观,可以模拟 PCIe 访问的唯一地方是qemu_wait_io_event(cpu) ,它在此处定义: https://github.com/qemu/qemu/blob/stable-4.2/cpus .c#L1266 and which calls qemu_wait_io_event_common defined here: https://github.com/qemu/qemu/blob/stable-4.2/cpus.c#L1241 which calls process_queued_cpu_work defined here:https://github.com/qemu/ qemu/blob/stable-4.2/cpus-common.c#L309

Let's see the code which executes the queue functions:让我们看看执行队列函数的代码:

 while (cpu->queued_work_first != NULL) {
        wi = cpu->queued_work_first;
        cpu->queued_work_first = wi->next;
        if (!cpu->queued_work_first) {
            cpu->queued_work_last = NULL;
        }
        qemu_mutex_unlock(&cpu->work_mutex);
        if (wi->exclusive) {
            /* Running work items outside the BQL avoids the following deadlock:
             * 1) start_exclusive() is called with the BQL taken while another
             * CPU is running; 2) cpu_exec in the other CPU tries to takes the
             * BQL, so it goes to sleep; start_exclusive() is sleeping too, so
             * neither CPU can proceed.
             */
            qemu_mutex_unlock_iothread();
            start_exclusive();
            wi->func(cpu, wi->data);

It looks like that the only power the VCPU thread qemu_kvm_cpu_thread_fn has when KVM returns, is to execute the queued functions:看起来,当 KVM 返回时,VCPU 线程qemu_kvm_cpu_thread_fn的唯一权力是执行排队的函数:

wi->func(cpu, wi->data);

This means that a PCIe device would have to constantly queue itself as a function for qemu to execute.这意味着 PCIe 设备必须不断地将自己排队为 function 才能执行 qemu。 I don't see how it would work.我不明白它会如何工作。

The functions that are able to queue work on this cpu have run_on_cpu on its name.能够在此 cpu 上排队工作的函数的名称上有run_on_cpu By searching it on VSCode I found some functions that queue work but none related to PCIe or even emulation.通过在 VSCode 上搜索它,我发现了一些排队工作但与 PCIe 甚至仿真无关的函数。 The nicest function I found was this one that apparently patches instructions: https://github.com/qemu/qemu/blob/stable-4.2/hw/i386/kvmvapic.c#L446 .我发现的最好的 function 是显然修补指令的这个: https://github.com/qemu/qemu/blob/stable-4.2/hw/i386/kvmvapic.c#L446 Nice, I wanted to know that also.很好,我也想知道。

Device emulation (of all devices, not just PCI) under KVM gets handled by the "case KVM_EXIT_IO" (for x86-style IO ports) and "case KVM_EXIT_MMIO" (for memory mapped IO including PCI) in the "switch (run->exit_reason)" inside kvm_cpu_exec(). Device emulation (of all devices, not just PCI) under KVM gets handled by the "case KVM_EXIT_IO" (for x86-style IO ports) and "case KVM_EXIT_MMIO" (for memory mapped IO including PCI) in the "switch (run-> exit_reason)" 在 kvm_cpu_exec() 中。 qemu_wait_io_event() is unrelated. qemu_wait_io_event() 是不相关的。

Want to know how execution gets to "emulate a register read on a PCI device"?想知道如何执行“模拟 PCI 设备上的寄存器读取”? Run QEMU under gdb, set a breakpoint on, say, the register read/write function for the ethernet PCI card you're using, and then when you get dropped into the debugger look at the stack backtrace.在 gdb 下运行 QEMU,在寄存器读/写 function 上为您正在使用的以太网 PCI 卡设置一个断点,然后当您进入调试器时查看堆栈回溯。 (Compile QEMU --enable-debug to get better debug info for this kind of thing.) (编译 QEMU --enable-debug 以获得更好的调试信息。)

PS: If you're examining QEMU internals for educational purposes, you'd be better to use the current code, not a year-old release of it. PS:如果您出于教育目的检查 QEMU 内部结构,最好使用当前代码,而不是一年前的版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM