本文内容参考:
VirtIO实现原理——PCI基础_virtio-pci-CSDN博客
QEMU源码全解析 —— virtio(3)_qemu virtio block bus-CSDN博客
特此致谢!
序言
本系列文章是笔者在看了网名为“享乐主”的VirtIO系列文章后决心要写的。这位博主的“VirtIO专栏”中博文的内容从技术层面来讲写得非常好,既有广度又有深度。但是如果论文笔以及行文方式和易理解程度,就还需要仔细打磨了。
笔者就用本系列文章,以“享乐主”的“VirtIO”专栏文章为蓝本以及主线脉络,以自己的理解和行文方式撰写这一系列文章。一方面,可以算作对原系列文章的“注”(当然,不仅仅是注);另一方面,也会结合笔者自己的理解、笔者既有文章中的知识点以及QEMU/KVM源码进行补充和拓展。本系列文章将收录到笔者的“QEMU/KVM”专栏中。
在开始正式文章之前,再次表达对“享乐主”的谢意!
一、VirtIO-PCI初始化
virtio设备首先需要创建一个PCI设备,叫作virtio PCI代理设备,这个代理设备挂到PCI总线上。
接着,virtio代理设备再创建一条virtio总线,这样virtio设备就可以挂到这条virtio总线上了。
按照上边的逻辑,virtio-blk基于virtio-pci,virtio-pci基于pci,所以virtio-blk初始化要从pci设备初始化说起。
1. PCI初始化
PCI驱动框架初始化在内核中有两个入口,分别如下:
(1)arch_initcall(pci_arch_init)
在arch/x86/pci/init.c中(笔者选用的内核版本为linux-6.1.10,后面都是此版本)。代码如下:
/* arch_initcall has too random ordering, so call the initializers
in the right sequence from here. */
static __init int pci_arch_init(void)
{
int type, pcbios = 1;
type = pci_direct_probe();
if (!(pci_probe & PCI_PROBE_NOEARLY))
pci_mmcfg_early_init();
if (x86_init.pci.arch_init)
pcbios = x86_init.pci.arch_init();
/*
* Must happen after x86_init.pci.arch_init(). Xen sets up the
* x86_init.irqs.create_pci_msi_domain there.
*/
x86_create_pci_msi_domain();
if (!pcbios)
return 0;
pci_pcbios_init();
/*
* don't check for raw_pci_ops here because we want pcbios as last
* fallback, yet it's needed to run first to set pcibios_last_bus
* in case legacy PCI probing is used. otherwise detecting peer busses
* fails.
*/
pci_direct_init(type);
if (!raw_pci_ops && !raw_pci_ext_ops)
printk(KERN_ERR
"PCI: Fatal: No config space access function found\n");
dmi_check_pciprobe();
dmi_check_skip_isa_align();
return 0;
}
arch_initcall(pci_arch_init);
体系结构的初始化,包括初始化IO地址0xCF8。
(2)subsys_initcall(pci_subsys_init)
在arch/x86/pci/legacy.c中。代码如下:
static int __init pci_subsys_init(void)
{
/*
* The init function returns an non zero value when
* pci_legacy_init should be invoked.
*/
if (x86_init.pci.init()) {
if (pci_legacy_init()) {
pr_info("PCI: System does not support PCI\n");
return -ENODEV;
}
}
pcibios_fixup_peer_bridges();
x86_init.pci.init_irq();
pcibios_init();
return 0;
}
subsys_initcall(pci_subsys_init);
PCI子系统初始化,这个过程会完成PCI总线树上设备的枚举,Host bridge会为PCI设备分配地址空间并将其写入BAR寄存器。
体系结构的初始化不作介绍,主要介绍PCI总线树的枚举和配置。
更多内容请看下回。