kvm io设备管理架构采用IO总线方式,涉及到guest os陷入到hypervisor进行IO的设备都挂载在总线上。kvm提供四种总线:
- mmio(memory map input/output)
- pio(port input/output)
- virtio
- fast mmio
mmio和pio是常见的访问硬件设备寄存器的两种方式,所以mmio和pio总线是用来挂载支持硬件虚拟化的硬件设备,比如GICV3就是挂载在mmio总线上的。virtio总线则是挂载支持并实现相关virtio标准的设备,比如virtio-net、virtio-console、virtio-block等等。
以gicv3为例,kvm实现了三种结构体以支持IO。分别是:
- struct kvm_io_bus
- struct kvm_io_device
- struct vgic_io_device
其中vgic_io_device是gicv3设备私有的,不同的设备有着不同的结构体。本文第一部分简介读写gicv3的整个流程,第二部分介绍io总线和io设备,最后介绍vgic设备的IO。
1. kvm io总线读写流程
guest os访问gicv3的distributor寄存器会触发同步异常。相应的guest os访问的数据地址会保存在far_el2寄存器中。假设改地址是addr,那么kvm把addr交给io总线去处理,处理流程如下:
将地址信息和读/写信息传给io总线,io总线通过addr匹配找到相应的kvm_io_device,然后执行read/write。通过container_of函数找到私有的vgic_io_device,vgic_io_device将guest os可以修改的寄存器划分为不同的region。通过偏移地址(addr-gic_base_addr)和region的地址进行比较找到其所在的region,最后执行相应的读写操作。
2. io总线和io设备
kvm_io_bus结构体中dev_count是统计当前设备总数(一个vm的设备);ioeventfd_count是统计当前io的次数;range则指示一个设备的所处地址范围,以便于io总线根据地址找到相应的设备。
struct kvm_io_bus {
int dev_count;
int ioeventfd_count;
struct kvm_io_range range[];
};
struct kvm_io_range {
gpa_t addr;
int len;
struct kvm_io_device *dev;
};
io总线的api如下:
int kvm_io_bus_write(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,
int len, const void *val);
int kvm_io_bus_write_cookie(struct