:QEMU virt machine 与 Device Tree](http://pic.xiahunao.cn/yaotu/[Virtualization](九):QEMU virt machine 与 Device Tree)
第八篇讨论了 SBI 在 RISC-V Guest 中如何被虚拟化。本篇回到 QEMU-machine virt到底如何把 RAM、CPU、UART、virtio、interrupt controller 和 device tree 组织成 Guest Linux 眼中的一台 RISC-V 机器1. 本篇要回答的问题启动 RISC-V Linux guest 时我们经常写qemu-system-riscv64\-machinevirt\-m1G\-smp4\-nographic\-kernelImage\-appendconsolettyS0其中-machine virt不是一句泛泛的“虚拟机器”而是 QEMU 里一个具体的 RISC-V machine model。它要回答的问题是Guest Linux 启动时看到的那台 RISC-V 机器是 QEMU 如何描述、摆放和交付的2. machine model 是什么QEMU 不是只模拟 CPU。它要提供一整台机器的外观RAMCPU hartinterrupt controllertimerUARTvirtio-mmio 设备flash/ROMfirmwarekernel/initrd 放置位置device tree这些对象加起来才构成 Guest Linux 能启动的“平台”。可以把 QEMU machine model 理解成一张虚拟硬件原理图。真实板卡靠硬件手册描述设备QEMUvirtmachine 靠代码和 device tree 描述设备。3.virtmachine 的基本布局QEMU RISC-Vvirtmachine 通常会定义一套固定或可配置的地址布局。简化模型Guest physical address space 0x00000000 ------------------- | ROM / firmware | ------------------- | CLINT / AIA | ------------------- | PLIC / interrupt | ------------------- | UART | ------------------- | virtio-mmio | ------------------- 0x80000000 | RAM | | kernel / initrd | | device tree | -------------------实际地址会随 QEMU 版本和配置变化但思路不变QEMU 把一组设备放进 guest physical address space再告诉 Guest Linux 这些设备在哪里。4. CPU hart 如何创建RISC-V 多核里常说 hart。-smp 4会让 QEMU 创建多个 guest hart。每个 hart 对 Guest Linux 来说像一个 CPU。在 KVM 模式下hart 通常对应 vCPU。Guest hart 0 - KVM vCPU 0 - QEMU vCPU thread 0 Guest hart 1 - KVM vCPU 1 - QEMU vCPU thread 1 Guest hart 2 - KVM vCPU 2 - QEMU vCPU thread 2 Guest hart 3 - KVM vCPU 3 - QEMU vCPU thread 3Device tree 中的 CPU 节点会告诉 Guest Linux 有哪些 hart、hart id 是什么、支持哪些 ISA 扩展。5. RAM 如何交给 Guest-m 1G告诉 QEMU 给 guest 准备 1GB RAM。QEMU 会创建对应 memory region并把它放到virtmachine 的 RAM 地址范围中。在 KVM 模式下QEMU 还会把这段内存注册给 KVM成为 memory slot。QEMU memory region | | if KVM: KVM_SET_USER_MEMORY_REGION v KVM memory slot | v Guest RAM in GPA spaceGuest Linux 最终通过 device tree 的memory节点知道 RAM 起点和大小。6. Firmware、kernel 和 initrdRISC-V Linux 启动通常涉及 firmware/SBI例如 OpenSBI。QEMU 需要安排firmware 从哪里开始执行kernel Image 放在哪里initrd 放在哪里device tree 放在哪里启动参数如何传给 kernel简化启动交接reset vector | v firmware / SBI | | pass hart id and DTB address v Linux kernel entryGuest Linux 不需要知道 QEMU 内部如何加载文件。它只需要在启动入口拿到符合 RISC-V boot protocol 的参数。7. UART 与-nographic-nographic常常和consolettyS0一起使用。QEMUvirtmachine 会提供一个 UART 设备。Guest Linux 通过 device tree 发现 UART然后把 console 输出到这个设备。Guest printk | v Guest UART driver | | MMIO access v QEMU UART device model | v host terminal这就是为什么串口配置正确时guest kernel log 会出现在当前终端中。如果早期日志依赖earlyconsbi则可能走 SBI console 路径而不是完整 UART driver。8. virtio-mmio 设备窗口QEMUvirtmachine 常使用 virtio-mmio 给 guest 提供虚拟设备。每个 virtio-mmio 设备会占用一段 MMIO 地址。Guest Linux 通过 device tree 发现这些设备然后加载 virtio driver。例如 virtio blockDTB virtio-mmio node | v Guest virtio-mmio driver | v Guest virtio-blk driver | v QEMU virtio-blk device model因此 device tree 里的 virtio-mmio 节点是 guest driver 和 QEMU device model 对上的关键。9. interrupt controllerGuest 设备需要中断。QEMUvirtmachine 会为 guest 提供中断控制器模型。不同配置可能涉及PLICAIAIMSICACLINT/CLINT 相关组件Device tree 会告诉 Guest Linux 中断控制器是什么、设备 interrupt line 如何连接。virtio device completes request | v virtual interrupt controller | v Guest Linux interrupt handler中断控制器是 QEMU machine model、KVM interrupt injection 和 guest driver 之间的交汇点。10. Device Tree 是硬件契约RISC-V Linux 依赖 DTB 认识硬件。QEMUvirtmachine 创建完虚拟硬件后会生成一份 device tree交给 guest。DTB 通常包含/memory/cpus/choseninterrupt controller 节点UART 节点virtio-mmio 节点reserved-memoryinitrd 起止地址bootargs可以把 DTB 理解成 QEMU 写给 Guest Linux 的硬件说明书。11.chosen节点chosen节点常包含启动参数。例如chosen { bootargs consolettyS0 earlyconsbi; linux,initrd-start ...; linux,initrd-end ...; };-append里的内容通常会进入 bootargs。-initrd对应的地址范围也会通过 DTB 告诉 guest。Guest Linux 早期启动解析 DTB 后就能知道 command line 和 initrd 位置。12. dumpdtb观察 QEMU 生成了什么理解virtmachine 最实用的方法之一是 dump DTB。qemu-system-riscv64\-machinevirt,dumpdtbvirt.dtb\-nographic\-kernelImage然后反编译dtc-Idtb-Odts virt.dtbvirt.dts通过virt.dts可以直接看到 QEMU 告诉 guest 的硬件布局。这比只读文字描述更可靠。13. QEMU 源码阅读入口本篇对应的 QEMU 源码入口hw/riscv/virt.cRISC-V virt machine 主体target/riscv/CPU model、ISA、CSR、TCG/KVM 相关架构代码target/riscv/kvm/RISC-V KVM accelerator 相关hw/intc/中断控制器模型hw/char/UART 等字符设备hw/virtio/virtio 框架hw/block/virtio-blk.cvirtio block阅读问题virtmachine 在哪里注册RAM base 和 device base 在哪里定义CPU hart 在哪里创建DTB 在哪里生成和填充virtio-mmio 设备如何挂到地址空间interrupt line 如何连接14. 本篇小结这一篇把 QEMUvirtmachine 和 Device Tree 放在一起看。第一-machine virt是一个具体的 RISC-V 虚拟平台模型。第二QEMU 负责创建 RAM、CPU hart、UART、virtio、中断控制器等虚拟硬件。第三Device Tree 是 QEMU 和 Guest Linux 之间的硬件契约。第四Guest Linux 通过 DTB 发现内存、CPU、中断控制器和设备。第五读hw/riscv/virt.c时应该同时观察设备创建和 DTB 生成。可以把本篇压缩成一句话QEMUvirtmachine 负责把虚拟硬件拼出来Device Tree 负责把这台 RISC-V 机器说明白Guest Linux 则根据这份说明书完成启动和驱动绑定。15. 下一篇预告virtio 与 vhost下一篇进入 I/O 路径。会讨论virtio 为什么适合虚拟化virtqueue 的基本结构virtio-mmio 和 virtio-pcivirtio-blk、virtio-net 的请求路径QEMU device model 与 host backendvhost-kernel 和 vhost-user 如何减少 QEMU 数据面开销