VirtIO实现原理之VirtIO-PCI(1)

132 篇文章 41 订阅 yen49.90 yen99.00

本文内容参考:

VirtIO实现原理——PCI基础_virtio-pci-CSDN博客

QEMU源码全解析 —— virtio(3)_qemu virtio block bus-CSDN博客

特此致谢!

序言

本系列文章是笔者在看了网名为“享乐主”的VirtIO系列文章后决心要写的。这位博主的“VirtIO专栏”中博文的内容从技术层面来讲写得非常好,既有广度又有深度。但是如果论文笔以及行文方式和易理解程度,就还需要仔细打磨了。

ac86e5988a9740f9974d33b4c568300c.png

笔者就用本系列文章,以“享乐主”的“VirtIO”专栏文章为蓝本以及主线脉络,以自己的理解和行文方式撰写这一系列文章。一方面,可以算作对原系列文章的“注”(当然,不仅仅是注);另一方面,也会结合笔者自己的理解、笔者既有文章中的知识点以及QEMU/KVM源码进行补充和拓展。本系列文章将收录到笔者的“QEMU/KVM”专栏中。

在开始正式文章之前,再次表达对“享乐主”的谢意!

一、VirtIO-PCI初始化

virtio设备首先需要创建一个PCI设备,叫作virtio PCI代理设备,这个代理设备挂到PCI总线上

接着,virtio代理设备再创建一条virtio总线,这样virtio设备就可以挂到这条virtio总线上了

f8f27718eb2a4a478c6b5489dc09cc17.png

按照上边的逻辑,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总线树的枚举和配置。

更多内容请看下回。