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

132 篇文章 41 订阅 ¥49.90 ¥99.00

接前一篇文章:VirtIO实现原理之VirtIO-PCI(2)

本文内容参考:

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

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

特此致谢!

一、VirtIO-PCI初始化

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

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

1. PCI初始化

(1)枚举

本回详细解析发生在pci_subsys_init()中的枚举过程。先把整体流程列出来,如下所示:

pci_subsys_init
    x86_init.pci.init    =>    x86_default_pci_init    
    pci_legacy_init
        pcibios_scan_root
            x86_pci_root_bus_resources    // 为Host bridge分配资源,通常情况下就是64K IO空间地址和内存空间地址就在这里划分
            pci_scan_root_bus            // 枚举总线树上的设备
                pci_create_root_bus        // 创建Host bridge
                pci_scan_child_bus        // 扫描总线树上所有设备,如果有pci桥,递归扫描下去
                    pci_scan_slot
                        pci_scan_single_device    // 扫描设备,读取vendor id和device id
                            pci_scan_device
                                pci_setup_device
                                    pci_read_bases
                                        __pci_read_base    // 读取bar空间大小

在原作中,就直接写读到bar空间之后的事了,这里不要一带而过,而是详细跟一下上边这个代码流程。

1)pci_subsys_init()调用x86_init.pci.init

对应上边流程中的:

027666fca7ef49318e8929690b6695c1.png

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);

x86_init的定义在arch/x86/kernel/x86_init.c中,代码如下:

/*
 * The platform setup functions are preset with the default functions
 * for standard PC hardware.
 */
struct x86_init_ops x86_init __initdata = {

	.resources = {
		.probe_roms		= probe_roms,
		.reserve_resources	= reserve_standard_io_resources,
		.memory_setup		= e820__memory_setup_default,
	},

	.mpparse = {
		.setup_ioapic_ids	= x86_init_noop,
		.find_smp_config	= default_find_smp_config,
		.get_smp_config		= default_get_smp_config,
	},

	.irqs = {
		.pre_vector_init	= init_ISA_irqs,
		.intr_init		= native_init_IRQ,
		.intr_mode_select	= apic_intr_mode_select,
		.intr_mode_init		= apic_intr_mode_init,
		.create_pci_msi_domain	= native_create_pci_msi_domain,
	},

	.oem = {
		.arch_setup		= x86_init_noop,
		.banner			= default_banner,
	},

	.paging = {
		.pagetable_init		= native_pagetable_init,
	},

	.timers = {
		.setup_percpu_clockev	= setup_boot_APIC_clock,
		.timer_init		= hpet_time_init,
		.wallclock_init		= x86_wallclock_init,
	},

	.iommu = {
		.iommu_init		= iommu_init_noop,
	},

	.pci = {
		.init			= x86_default_pci_init,
		.init_irq		= x86_default_pci_init_irq,
		.fixup_irqs		= x86_default_pci_fixup_irqs,
	},

	.hyper = {
		.init_platform		= x86_init_noop,
		.guest_late_init	= x86_init_noop,
		.x2apic_available	= bool_x86_init_noop,
		.msi_ext_dest_id	= bool_x86_init_noop,
		.init_mem_mapping	= x86_init_noop,
		.init_after_bootmem	= x86_init_noop,
	},

	.acpi = {
		.set_root_pointer	= x86_default_set_root_pointer,
		.get_root_pointer	= x86_default_get_root_pointer,
		.reduced_hw_early_init	= acpi_generic_reduced_hw_init,
	},
};

x86_init.pci相关的片段为:

	.pci = {
		.init			= x86_default_pci_init,
		.init_irq		= x86_default_pci_init_irq,
		.fixup_irqs		= x86_default_pci_fixup_irqs,
	},

那么,pci_subsys_init函数中所调用的x86_init.pci.init()其实就是x86_default_pci_init()。

这个x86_default_pci_init实际上还是个宏定义,在arch/x86/include/asm/pci_x86.h中,如下:

#ifdef CONFIG_PCI
# ifdef CONFIG_ACPI
#  define x86_default_pci_init		pci_acpi_init
# else
#  define x86_default_pci_init		pci_legacy_init
# endif
# define x86_default_pci_init_irq	pcibios_irq_init
# define x86_default_pci_fixup_irqs	pcibios_fixup_irqs
#else
# define x86_default_pci_init		NULL
# define x86_default_pci_init_irq	NULL
# define x86_default_pci_fixup_irqs	NULL
#endif

由代码可知,x86_default_pci_init根据不同宏的定义即选项使能的情况,指向了不同的函数。

如果CONFIG_PCI未被定义、也就是没有使能PCI,那么x86_default_pci_init为NULL;如果CONFIG_PCI定义了、但是CONFIG_ACPI未被定义,也就是没有使能ACPI(Advanced Configuration and Power Management Interface,高级配置和电源管理接口)的情况,那么x86_default_pci_init为pci_legacy_init;如果CONFIG_PCI和CONFIG_ACPI都被定义了,也就是既使能了PCI、又使能了ACPI,那么x86_default_pci_init为pci_acpi_init。

这里把pci_legacy_init和pci_acpi_init函数代码都贴出来,后续会重点讲解前者。

  • pci_legacy_init函数

pci_legacy_init函数在arch/x86/pci/legacy.c中,代码如下:

int __init pci_legacy_init(void)
{
	if (!raw_pci_ops)
		return 1;

	pr_info("PCI: Probing PCI hardware\n");
	pcibios_scan_root(0);
	return 0;
}
  • pci_acpi_init函数

pci_acpi_init函数在arch/x86/pci/acpi.c中,代码如下:

int __init pci_acpi_init(void)
{
	struct pci_dev *dev = NULL;

	if (acpi_noirq)
		return -ENODEV;

	printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n");
	acpi_irq_penalty_init();
	pcibios_enable_irq = acpi_pci_irq_enable;
	pcibios_disable_irq = acpi_pci_irq_disable;
	x86_init.pci.init_irq = x86_init_noop;

	if (pci_routeirq) {
		/*
		 * PCI IRQ routing is set up by pci_enable_device(), but we
		 * also do it here in case there are still broken drivers that
		 * don't use pci_enable_device().
		 */
		printk(KERN_INFO "PCI: Routing PCI interrupts for all devices because \"pci=routeirq\" specified\n");
		for_each_pci_dev(dev)
			acpi_pci_irq_enable(dev);
	}

	return 0;
}

下一回将按照(沿着)上边的流程,对于pci_legacy_init函数及其后流程进行深入解析。

评论
成就一亿技术人!
拼手气红包 6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠  查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额 3.43前往充值 >
需支付: 10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝天居士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付: ¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值