x86 pci bus 初始化流程

PCIE总线初始化 PCIe总线初始化 阅读详情

X86上pcie的初始化枚举流程,基于kernel 5.2.9分析

                  `只是一个看代码笔记,仅供参考`

pcie的代码在kernel里面大致分两部分初始化的:
一部分和cpu的框架密切相关,使用arch_initcall初始化,x86的初始化代码位于init.c arch\x86\pci,arch_initcall(pci_arch_init); 主要是检测pci type, 设置全局的config 空间read/write函数。
另一部分是pcie subsystem的初始化,使用subsys_initcall 初始化,x86的代码位于legacy.c arch\x86\pci里面,前面适合x86相关的,最终会调用pci_scan_root_bus来扫描初始化总线下所有的bridge和ep设备。

A. arch_initcall(pci_arch_init)的初始化:

探测bus总线,设置read,write函数

函数pci_arch_init:
/* arch_initcall has too random ordering, so call the initializers
   in the right sequence from here. */
static __init int pci_arch_init(void)
{
#ifdef CONFIG_PCI_DIRECT
	int type = 0;

	type = pci_direct_probe();
#endif

	if (!(pci_probe & PCI_PROBE_NOEARLY))
		pci_mmcfg_early_init();

	if (x86_init.pci.arch_init && !x86_init.pci.arch_init())
		return 0;

#ifdef CONFIG_PCI_BIOS
	pci_pcbios_init();
#endif
	/*
	 * 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.
	 */
#ifdef CONFIG_PCI_DIRECT
	pci_direct_init(type); /  下面有具体的函数  /
#endif
	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);
函数pci_direct_init:
void __init pci_direct_init(int type)
{
	if (type == 0)
		return;
	printk(KERN_INFO "PCI: Using configuration type %d for base access\n",
		 type);
	if (type == 1) {
		raw_pci_ops = &pci_direct_conf1;
		if (raw_pci_ext_ops)
			return;
		if (!(pci_probe & PCI_HAS_IO_ECS))
			return;
		printk(KERN_INFO "PCI: Using configuration type 1 "
		       "for extended access\n");
		raw_pci_ext_ops = &pci_direct_conf1;/ 结构体的函数实现在下面 /
		return;
	}
	raw_pci_ops = &pci_direct_conf2;
}
pci_direct_conf1 结构体定义及回调函数
const struct pci_raw_ops pci_direct_conf1 = {
	.read =		pci_conf1_read,
	.write =	pci_conf1_write,
};
static int pci_conf1_read(unsigned int seg, unsigned int bus,
			  unsigned int devfn, int reg, int len, u32 *value)
{
	unsigned long flags;

	if (seg || (bus > 255) || (devfn > 255) || (reg > 4095)) {
		*value = -1;
		return -EINVAL;
	}
	raw_spin_lock_irqsave(&pci_config_lock, flags);
/*
 *	#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
 *	(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
 *	| (devfn << 8) | (reg & 0xFC))
 */
	outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

	switch (len) {
	case 1:
		*value = inb(0xCFC + (reg & 3));
		break;
	case 2:
		*value = inw(0xCFC + (reg & 2));
		break;
	case 4:
		*value = inl(0xCFC);
		break;
	}

	raw_spin_unlock_irqrestore(&pci_config_lock, flags);

	return 0;
}

static int pci_conf1_write(unsigned int seg, unsigned int bus,
			   unsigned int devfn, int reg, int len, u32 value)
{
	unsigned long flags;

	if (seg || (bus > 255) || (devfn > 255) || (reg > 4095))
		return -EINVAL;

	raw_spin_lock_irqsave(&pci_config_lock, flags);

	outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

	switch (len) {
	case 1:
		outb((u8)value, 0xCFC + (reg & 3));
		break;
	case 2:
		outw((u16)value, 0xCFC + (reg & 2));
		break;
	case 4:
		outl((u32)value, 0xCFC);
		break;
	}

	raw_spin_unlock_irqrestore(&pci_config_lock, flags);

	return 0;
}

B. pcie read/write config 函数的注册流程:

pci_bus_read_config_xx and pci_bus_write_config_xx:
#define PCI_OP_READ(size, type, len) \
int noinline pci_bus_read_config_##size \
	(struct pci_bus *bus, unsigned int devfn, int pos, type *value)	\
{									\
	int res;							\
	unsigned long flags;						\
	u32 data = 0;							\
	if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;	\
	pci_lock_config(flags);						\
	res = bus->ops->read(bus, devfn, pos, len, &data);		\
	*value = (type)data;						\
	pci_unlock_config(flags);					\
	return res;							\
}

#define PCI_OP_WRITE(size, type, len) \
int noinline pci_bus_write_config_##size \
	(struct pci_bus *bus, unsigned int devfn, int pos, type value)	\
{									\
	int res;							\
	unsigned long flags;						\
	if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;	\
	pci_lock_config(flags);						\
	res = bus->ops->write(bus, devfn, pos, len, value);		\
	pci_unlock_config(flags);					\
	return res;							\
}
PCI_OP_READ(byte, u8, 1)
PCI_OP_READ(word, u16, 2)
PCI_OP_READ(dword, u32, 4)
PCI_OP_WRITE(byte, u8, 1)
PCI_OP_WRITE(word, u16, 2)
PCI_OP_WRITE(dword, u32, 4)

EXPORT_SYMBOL(pci_bus_read_config_byte);
EXPORT_SYMBOL(pci_bus_read_config_word);
EXPORT_SYMBOL(pci_bus_read_config_dword);
EXPORT_SYMBOL(pci_bus_write_config_byte);
EXPORT_SYMBOL(pci_bus_write_config_word);
EXPORT_SYMBOL(pci_bus_write_config_dword);

C. pcie subsystem的初始化流程

1.下面是pcie scan and add device的大概函数调用流程

	subsys_initcall(pci_subsys_init);
		pci_subsys_init()
			pci_legacy_init()
				pcibios_scan_root(0);
					pci_scan_root_bus();
						pci_create_root_bus(parent, bus, ops, sysdata, resources);
							pci_alloc_host_bridge(0);//分配buffer
							pci_register_host_bridge(bridge);//注册bridge的设备
						pci_scan_child_bus(b);
							pci_scan_child_bus_extend(bus, 0);
	/ Scan devices below a bus,子函数太多,另起一行 /	
	pci_scan_child_bus_extend(bus, 0);//Scan devices below a bus	
		for (devfn = 0; devfn < 256; devfn += 8) {
			pci_scan_slot(bus, devfn);
				pci_scan_single_device(bus, devfn);
					pci_get_slot(bus, devfn);
					pci_scan_device(bus, devfn); /---- 很重要,下面有详细讲解 ---/
						pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)
							pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);
								pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l)
						pci_alloc_dev(bus);
						pci_setup_device(dev);
							set_pcie_port_type(dev);//读配置空间读capability获取信息
							pci_dev_assign_slot(dev);
							dev_set_name();	
							/接下来的代码是根据header type来设置或者pci 信息 /
					pci_device_add(dev, bus); /--- 很重要,下面有详细讲解,根据pci的信息,配置pci设备,最后device_add(&dev->dev);  ---/
		pci_iov_bus_range(bus);/* find bus range used by Virtual Function,Reserve buses for SR-IOV capability */
		for_each_pci_bridge(dev, bus) {  //Scan bridges that are already configured. We don't touch them unless they are misconfigured
			pci_scan_bridge_extend(bus, dev, max, 0, 0);
				pci_find_bus(pci_domain_nr(bus), secondary);
				pci_scan_child_bus(child); /重新执行一遍上面的初始化扫码过程,一级一级递归扫描设备直到最后/
		for_each_pci_bridge(dev, bus) /* Scan bridges that need to be reconfigured */
			pci_scan_bridge_extend(bus, dev, cmax, buses, 1);
				

2.下面是一些重要函数的详细代码

2.1 pci_subsys_init:

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

2.2 pci_legacy_init:

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

2.3 pcibios_scan_root:

void pcibios_scan_root(int busnum)
{
	struct pci_bus *bus;
	struct pci_sysdata *sd;
	LIST_HEAD(resources);

	sd = kzalloc(sizeof(*sd), GFP_KERNEL);
	if (!sd) {
		printk(KERN_ERR "PCI: OOM, skipping PCI bus %02x\n", busnum);
		return;
	}
	sd->node = x86_pci_root_bus_node(busnum);//获取当前bus所在的NUMA node 号
	x86_pci_root_bus_resources(busnum, &resources);
	printk(KERN_DEBUG "PCI: Probing PCI hardware (bus %02x)\n", busnum);
	bus = pci_scan_root_bus(NULL, busnum, &pci_root_ops, sd, &resources);// 探测当前总线设备以及子总线、子总线设备
	if (!bus) {
		pci_free_resource_list(&resources);
		kfree(sd);
		return;
	}
	pci_bus_add_devices(bus);// 全部设备探测完毕,注册设备。	
}

3. 关键函数的讲解:

3.00 pci_scan_device流程:

Read the config data for a PCI device, sanity-check it, and fill in the dev structure.

pci_scan_device();
	pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000);
		pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);
			pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l)
	pci_alloc_dev(bus);
		dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
		INIT_LIST_HEAD(&dev->bus_list);
		dev->dev.type = &pci_dev_type;
		dev->bus = pci_bus_get(bus);
	pci_setup_device(dev);
		pci_hdr_type(dev);
		set_pcie_port_type(dev);
		pci_dev_assign_slot(dev);//通过devfn来计算出阿里一个slot号,保存到dev中
				list_for_each_entry(slot, &dev->bus->slots, list)
					if (PCI_SLOT(dev->devfn) == slot->number)
						dev->slot = slot;		
		dev_set_name();
		set_pcie_thunderbolt(dev); 	
		set_pcie_untrusted(dev);
		pci_fixup_device(pci_fixup_early, dev);/ Early fixups, before probing the BARs /
		pci_intx_mask_broken(dev);/test whether PCI_COMMAND_INTX_DISABLE is writable /
		case PCI_HEADER_TYPE_NORMAL:		    /* standard header */
			pci_read_irq(dev);/Read interrupt line and base address registers./	
			pci_read_bases(dev, 6, PCI_ROM_ADDRESS);/get BARs info /
			pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);
		case PCI_HEADER_TYPE_BRIDGE:
			pci_read_irq(dev);
			pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
			pci_read_bridge_windows(dev);/get IO and MEMORY windows size /
			set_pcie_hotplug_bridge(dev); /check whether support hotplug /
			pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);
			pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);
		case PCI_HEADER_TYPE_CARDBUS:		    /* CardBus bridge header */
			pci_read_irq(dev);
			pci_read_bases(dev, 1, 0);
			pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID,&dev->subsystem_vendor);
			pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device);
3.01 pci_setup_device 具体函数内容

Initialize the device structure with information about the device's
vendor,class,memory and IO-space addresses, IRQ lines etc.

/**
 * pci_setup_device - Fill in class and map information of a device
 * @dev: the device structure to fill
 *
 * Initialize the device structure with information about the device's
 * vendor,class,memory and IO-space addresses, IRQ lines etc.
 * Called at initialisation of the PCI subsystem and by CardBus services.
 * Returns 0 on success and negative if unknown type of device (not normal,
 * bridge or CardBus).
 */
int pci_setup_device(struct pci_dev *dev)
{
	u32 class;
	u16 cmd;
	u8 hdr_type;
	int pos = 0;
	struct pci_bus_region region;
	struct resource *res;

	hdr_type = pci_hdr_type(dev);

	dev->sysdata = dev->bus->sysdata;
	dev->dev.parent = dev->bus->bridge;
	dev->dev.bus = &pci_bus_type;
	dev->hdr_type = hdr_type & 0x7f;
	dev->multifunction = !!(hdr_type & 0x80);
	dev->error_state = pci_channel_io_normal;
	set_pcie_port_type(dev);

	pci_dev_assign_slot(dev);

	/*
	 * Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer)
	 * set this higher, assuming the system even supports it.
	 */
	dev->dma_mask = 0xffffffff;

	dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
		     dev->bus->number, PCI_SLOT(dev->devfn),
		     PCI_FUNC(dev->devfn));

	class = pci_class(dev);

	dev->revision = class & 0xff;
	dev->class = class >> 8;		    /* upper 3 bytes */

	pci_info(dev, "[%04x:%04x] type %02x class %#08x\n",
		   dev->vendor, dev->device, dev->hdr_type, dev->class);

	if (pci_early_dump)
		early_dump_pci_device(dev);

	/* Need to have dev->class ready */
	dev->cfg_size = pci_cfg_space_size(dev);

	/* Need to have dev->cfg_size ready */
	set_pcie_thunderbolt(dev);

	set_pcie_untrusted(dev);

	/* "Unknown power state" */
	dev->current_state = PCI_UNKNOWN;

	/* Early fixups, before probing the BARs */
	pci_fixup_device(pci_fixup_early, dev);

	/* Device class may be changed after fixup */
	class = dev->class >> 8;

	if (dev->non_compliant_bars) {
		pci_read_config_word(dev, PCI_COMMAND, &cmd);
		if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
			pci_info(dev, "device has non-compliant BARs; disabling IO/MEM decoding\n");
			cmd &= ~PCI_COMMAND_IO;
			cmd &= ~PCI_COMMAND_MEMORY;
			pci_write_config_word(dev, PCI_COMMAND, cmd);
		}
	}

	dev->broken_intx_masking = pci_intx_mask_broken(dev);

	switch (dev->hdr_type) {		    /* header type */
	case PCI_HEADER_TYPE_NORMAL:		    /* standard header */
		if (class == PCI_CLASS_BRIDGE_PCI)
			goto bad;
		pci_read_irq(dev);
		pci_read_bases(dev, 6, PCI_ROM_ADDRESS);

		pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);

		/*
		 * Do the ugly legacy mode stuff here rather than broken chip
		 * quirk code. Legacy mode ATA controllers have fixed
		 * addresses. These are not always echoed in BAR0-3, and
		 * BAR0-3 in a few cases contain junk!
		 */
		if (class == PCI_CLASS_STORAGE_IDE) {
			u8 progif;
			pci_read_config_byte(dev, PCI_CLASS_PROG, &progif);
			if ((progif & 1) == 0) {
				region.start = 0x1F0;
				region.end = 0x1F7;
				res = &dev->resource[0];
				res->flags = LEGACY_IO_RESOURCE;
				pcibios_bus_to_resource(dev->bus, res, &region);
				pci_info(dev, "legacy IDE quirk: reg 0x10: %pR\n",
					 res);
				region.start = 0x3F6;
				region.end = 0x3F6;
				res = &dev->resource[1];
				res->flags = LEGACY_IO_RESOURCE;
				pcibios_bus_to_resource(dev->bus, res, &region);
				pci_info(dev, "legacy IDE quirk: reg 0x14: %pR\n",
					 res);
			}
			if ((progif & 4) == 0) {
				region.start = 0x170;
				region.end = 0x177;
				res = &dev->resource[2];
				res->flags = LEGACY_IO_RESOURCE;
				pcibios_bus_to_resource(dev->bus, res, &region);
				pci_info(dev, "legacy IDE quirk: reg 0x18: %pR\n",
					 res);
				region.start = 0x376;
				region.end = 0x376;
				res = &dev->resource[3];
				res->flags = LEGACY_IO_RESOURCE;
				pcibios_bus_to_resource(dev->bus, res, &region);
				pci_info(dev, "legacy IDE quirk: reg 0x1c: %pR\n",
					 res);
			}
		}
		break;

	case PCI_HEADER_TYPE_BRIDGE:		    /* bridge header */
		/*
		 * The PCI-to-PCI bridge spec requires that subtractive
		 * decoding (i.e. transparent) bridge must have programming
		 * interface code of 0x01.
		 */
		pci_read_irq(dev);
		dev->transparent = ((dev->class & 0xff) == 1);
		pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
		pci_read_bridge_windows(dev);
		set_pcie_hotplug_bridge(dev);
		pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);
		if (pos) {
			pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);
			pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);
		}
		break;

	case PCI_HEADER_TYPE_CARDBUS:		    /* CardBus bridge header */
		if (class != PCI_CLASS_BRIDGE_CARDBUS)
			goto bad;
		pci_read_irq(dev);
		pci_read_bases(dev, 1, 0);
		pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
		pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device);
		break;

	default:				    /* unknown header */
		pci_err(dev, "unknown header type %02x, ignoring device\n",
			dev->hdr_type);
		return -EIO;

	bad:
		pci_err(dev, "ignoring class %#08x (doesn't match header type %02x)\n",
			dev->class, dev->hdr_type);
		dev->class = PCI_CLASS_NOT_DEFINED << 8;
	}

	/* We found a fine healthy device, go go go... */
	return 0;
}
3.02 pci_read_bases 函数内容:
static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
{
	unsigned int pos, reg;

	if (dev->non_compliant_bars)
		return;

	/* Per PCIe r4.0, sec 9.3.4.1.11, the VF BARs are all RO Zero */
	if (dev->is_virtfn)
		return;
	/采用循环的方式调用__pci_read_base,第一个bar的地址是PCI_BASE_ADDRESS_0(0x10)/
	for (pos = 0; pos < howmany; pos++) {
		struct resource *res = &dev->resource[pos];
		reg = PCI_BASE_ADDRESS_0 + (pos << 2);
		pos += __pci_read_base(dev, pci_bar_unknown, res, reg); //分配资源的具体函数
	}

	if (rom) {
		struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
		dev->rom_base_reg = rom;
		res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH |
				IORESOURCE_READONLY | IORESOURCE_SIZEALIGN;
		__pci_read_base(dev, pci_bar_mem32, res, rom);
	}
}

3.03 __pci_read_base :

/**
 * pci_read_base - Read a PCI BAR
 * @dev: the PCI device
 * @type: type of the BAR
 * @res: resource buffer to be filled in
 * @pos: BAR position in the config space
 *
 * Returns 1 if the BAR is 64-bit, or 0 if 32-bit.
 */
int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
		    struct resource *res, unsigned int pos)
{
	u32 l = 0, sz = 0, mask;
	u64 l64, sz64, mask64;
	u16 orig_cmd;
	struct pci_bus_region region, inverted_region;

	mask = type ? PCI_ROM_ADDRESS_MASK : ~0;/ 判断是device还是bridge /

	/* No printks while decoding is disabled! */
	if (!dev->mmio_always_on) {
		pci_read_config_word(dev, PCI_COMMAND, &orig_cmd);
		if (orig_cmd & PCI_COMMAND_DECODE_ENABLE) {
			pci_write_config_word(dev, PCI_COMMAND,
				orig_cmd & ~PCI_COMMAND_DECODE_ENABLE);
		}
	}

	res->name = pci_name(dev);
	
	`获取bar空间大小,x86上,bar的地址已经在bios里面设置到寄存器里面了,读出来的就是bar的首地址,`
	`全部写1来确定size, 读取resource的flag`
	pci_read_config_dword(dev, pos, &l);
	pci_write_config_dword(dev, pos, l | mask);
	pci_read_config_dword(dev, pos, &sz);
	pci_write_config_dword(dev, pos, l);

	/*
	 * All bits set in sz means the device isn't working properly.
	 * If the BAR isn't implemented, all bits must be 0.  If it's a
	 * memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit
	 * 1 must be clear.
	 */
	if (sz == 0xffffffff)
		sz = 0;

	/*
	 * I don't know how l can have all bits set.  Copied from old code.
	 * Maybe it fixes a bug on some ancient platform.
	 */
	if (l == 0xffffffff)
		l = 0;

	if (type == pci_bar_unknown) {
		res->flags = decode_bar(dev, l);
		res->flags |= IORESOURCE_SIZEALIGN;
		/根据flag判断是io还是mem/
		if (res->flags & IORESOURCE_IO) {
			l64 = l & PCI_BASE_ADDRESS_IO_MASK;
			sz64 = sz & PCI_BASE_ADDRESS_IO_MASK;
			mask64 = PCI_BASE_ADDRESS_IO_MASK & (u32)IO_SPACE_LIMIT;
		} else {
			l64 = l & PCI_BASE_ADDRESS_MEM_MASK;
			sz64 = sz & PCI_BASE_ADDRESS_MEM_MASK;
			mask64 = (u32)PCI_BASE_ADDRESS_MEM_MASK;
		}
	} else {
		if (l & PCI_ROM_ADDRESS_ENABLE)
			res->flags |= IORESOURCE_ROM_ENABLE;
		l64 = l & PCI_ROM_ADDRESS_MASK;
		sz64 = sz & PCI_ROM_ADDRESS_MASK;
		mask64 = PCI_ROM_ADDRESS_MASK;
	}
	/如果是64 bit的话,就要继续读取高32bit/
	if (res->flags & IORESOURCE_MEM_64) {
		pci_read_config_dword(dev, pos + 4, &l);
		pci_write_config_dword(dev, pos + 4, ~0);
		pci_read_config_dword(dev, pos + 4, &sz);
		pci_write_config_dword(dev, pos + 4, l);

		l64 |= ((u64)l << 32);
		sz64 |= ((u64)sz << 32);
		mask64 |= ((u64)~0 << 32);
	}

	if (!dev->mmio_always_on && (orig_cmd & PCI_COMMAND_DECODE_ENABLE))
		pci_write_config_word(dev, PCI_COMMAND, orig_cmd);

	if (!sz64)
		goto fail;

	sz64 = pci_size(l64, sz64, mask64);/获取size大小,对bar空间全部写一,回读,最低位为1的地方就是size/
	if (!sz64) {
		pci_info(dev, FW_BUG "reg 0x%x: invalid BAR (can't size)\n",
			 pos);
		goto fail;
	}
	/合法性检测/
	if (res->flags & IORESOURCE_MEM_64) {
		if ((sizeof(pci_bus_addr_t) < 8 || sizeof(resource_size_t) < 8)
		    && sz64 > 0x100000000ULL) {
			res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
			res->start = 0;
			res->end = 0;
			pci_err(dev, "reg 0x%x: can't handle BAR larger than 4GB (size %#010llx)\n",
				pos, (unsigned long long)sz64);
			goto out;
		}

		if ((sizeof(pci_bus_addr_t) < 8) && l) {
			/* Above 32-bit boundary; try to reallocate */
			res->flags |= IORESOURCE_UNSET;
			res->start = 0;
			res->end = sz64 - 1;
			pci_info(dev, "reg 0x%x: can't handle BAR above 4GB (bus address %#010llx)\n",
				 pos, (unsigned long long)l64);
			goto out;
		}
	}

	region.start = l64;
	region.end = l64 + sz64 - 1;

	pcibios_bus_to_resource(dev->bus, res, &region); /下面有具体函数/
	pcibios_resource_to_bus(dev->bus, &inverted_region, res);

	/*
	 * If "A" is a BAR value (a bus address), "bus_to_resource(A)" is
	 * the corresponding resource address (the physical address used by
	 * the CPU.  Converting that resource address back to a bus address
	 * should yield the original BAR value:
	 *
	 *     resource_to_bus(bus_to_resource(A)) == A
	 *
	 * If it doesn't, CPU accesses to "bus_to_resource(A)" will not
	 * be claimed by the device.
	 */
	if (inverted_region.start != region.start) {
		res->flags |= IORESOURCE_UNSET;
		res->start = 0;
		res->end = region.end - region.start;
		pci_info(dev, "reg 0x%x: initial BAR value %#010llx invalid\n",
			 pos, (unsigned long long)region.start);
	}

	goto out;


fail:
	res->flags = 0;
out:
	if (res->flags)
		pci_info(dev, "reg 0x%x: %pR\n", pos, res);

	return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
}

检查地址的合法性:pcibios_resource_to_bus 和 pcibios_bus_to_resource
/* True iff r1 completely contains r2 */
static inline bool resource_contains(struct resource *r1, struct resource *r2)
{
	if (resource_type(r1) != resource_type(r2))
		return false;
	if (r1->flags & IORESOURCE_UNSET || r2->flags & IORESOURCE_UNSET)
		return false;
	return r1->start <= r2->start && r1->end >= r2->end;
}

void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region,
			     struct resource *res)
{
	struct pci_host_bridge *bridge = pci_find_host_bridge(bus);
	struct resource_entry *window;
	resource_size_t offset = 0;

	resource_list_for_each_entry(window, &bridge->windows) {
		if (resource_contains(window->res, res)) {
			offset = window->offset;
			break;
		}
	}

	region->start = res->start - offset;
	region->end = res->end - offset;
}
EXPORT_SYMBOL(pcibios_resource_to_bus);

static bool region_contains(struct pci_bus_region *region1,
			    struct pci_bus_region *region2)
{
	return region1->start <= region2->start && region1->end >= region2->end;
}

void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res,
			     struct pci_bus_region *region)
{
	struct pci_host_bridge *bridge = pci_find_host_bridge(bus);
	struct resource_entry *window;
	resource_size_t offset = 0;

	resource_list_for_each_entry(window, &bridge->windows) {
		struct pci_bus_region bus_region;

		/遍历brideg的地址范围,check类型是否一致:io or mem/
		if (resource_type(res) != resource_type(window->res))
			continue;

		bus_region.start = window->res->start - window->offset;
		bus_region.end = window->res->end - window->offset;
		/判断当前的region是否和已有的region是否有重叠/
		if (region_contains(&bus_region, region)) {
			offset = window->offset;
			break;
		}
	}
	/ 更新 res = &dev->resource[pos];/
	res->start = region->start + offset;
	res->end = region->end + offset;
}
3.10 pci_device_add(dev, bus) 流程:
pci_device_add(dev, bus);
	pci_configure_device(dev);
		pci_configure_mps(dev); /设置payload size, 以下4个都是通过pcie capability structure(id:10)来配置device/
		pci_configure_extended_tags(dev, NULL);/config externed tags /
		pci_configure_relaxed_ordering(dev);/config relaxed ordering /
		pci_configure_ltr(dev);
		pci_configure_eetlp_prefix(dev);
		pci_configure_serr(dev);/ 和bridge的SERR#有关 /
		pci_acpi_program_hp_params(dev, &hp_ops);
	device_initialize(&dev->dev); /init device structure./
	dev->dev.release = pci_release_dev;/设置release函数/
	set_dev_node(&dev->dev, pcibus_to_node(bus));//设置numa_node
	dma_set_max_seg_size(&dev->dev, 65536);
	dma_set_seg_boundary(&dev->dev, 0xffffffff);
	pci_fixup_device(pci_fixup_header, dev);

 	/ This function disables memory decoding and releases memory resources /
 	/ of the device specified by kernel's boot parameter 'pci=resource_alignment=' /
	/ It also rounds up size to specified alignment./
 	/ Later on, the kernel will assign page-aligned memory resource back/
 	/ to the device./
	pci_reassigndev_resource_alignment(dev);
	
	pci_init_capabilities(dev);
	list_add_tail(&dev->bus_list, &bus->devices);/ Add the device to our list of discovered devices and the bus list for fixup functions /
	pcibios_add_device(dev); /Initialize various capabilities 比较重要,下面附详细函数/
	pci_set_msi_domain(dev);/Set up MSI IRQ domain/
	device_add(&dev->dev);/add device to device hierarchy./
3.11pci_init_capabilities 具体函数内容

进一步地根据capability 进行初始化,包括sr-iov, 下面的函数都是通过函数pci_find_capability();来超找capability ID地址的:从64——config的配置空间里面的获取第一个capability的地址,然后逐个遍历查找,找到对应的ID,获取地址。

static void pci_init_capabilities(struct pci_dev *dev)
{
	/* Enhanced Allocation */
	pci_ea_init(dev);
				
	/* Setup MSI caps & disable MSI/MSI-X interrupts */
	pci_msi_setup_pci_dev(dev);

	/* Buffers for saving PCIe and PCI-X capabilities */
	pci_allocate_cap_save_buffers(dev);

	/* Power Management */
	pci_pm_init(dev);

	/* Vital Product Data */
	pci_vpd_init(dev);

	/* Alternative Routing-ID Forwarding */
	pci_configure_ari(dev);

	/* Single Root I/O Virtualization */
	pci_iov_init(dev); / initialize the IOV capability,获取sriov信息,计算最大的busnum, 获取VF 的total值,获取每个bar空间的size,分配资源池size=total*bar_size, 后续vf分配的bar资源就是从这里来的, 计算最大的bus号,为后面的vf使用。 /

	/* Address Translation Services */
	pci_ats_init(dev);

	/* Enable ACS P2P upstream forwarding */
	pci_enable_acs(dev);

	/* Precision Time Measurement */
	pci_ptm_init(dev);

	/* Advanced Error Reporting */
	pci_aer_init(dev);

	pcie_report_downtraining(dev);

	if (pci_probe_reset_function(dev) == 0)/check whether the device can be safely reset/
		dev->reset_fn = 1;
}

pci_bus_read_config_dword实现说明 在学习linux 内核启动pci总线枚举的过程中,发现跟踪到底层扫描总线上每个设备,是通过读取每个设备的vendor id来确定设备的有无,在这里遇到了一个问题,就是函数pci_bus_read_config_dword没有找到实现,只是找到了EXPORT_SYMBOL(pci_bus_read_config_dword),没有具体的实现,仔细看了一下access.c文件才发现这个问题所在,先贴出 阅读详情

相关推荐

linx pci 的初始化过程

单纯从一种处理器系统的角度来看, linux pci的实现机制远非完美。在pci_scan_bus_parented 函数调用pci_scan_child_bus 函数时,其输入参数为host 主桥的pci_bus结构, 些pci_scan_slot 函数首先初始化与host 主桥直接相加的pci 设备, 即bus 号为0r pci 设备。pci 总线树枚举由pci_scan_child_bus 函数完成, 该函数的主要作用是分配pci 总线树的pci 总线号, 而并初始化pci 设备使用的bar 空间。

Helping BIOSer become Awesome! 1109

Linux设备驱动——PCI总线的初始化

目录 一、概述 二、初始化分析 2.1pcibus_class_init 2.2pci_driver_init 2.3acpi_pci_init 2.3.1 基于ACPI的PCI设备枚举过程 2.4 pci_arch_init 2.5pci_slot_init 2.6pci_subsys_init 三、参考 一、概述 二、初始化分析 pci初始化涉及很多函数,这...

whenloce的专栏 3988

Linux系统总线之PCI扫描流程

Linux系统总线之PCI扫描流程

多云转晴,适合debug的博客 4784

慢慢欣赏linux PCI-PCIE初始化学习

powerpc的PCI/PCIE控制器枚举PCI设备实在内核里面实现的,依赖的资源主要是设备树。 设备数提供了PCI/PCIE池的空间基地址/大小,用于分配PCI/PCIE的memory空间; 如果是传统PCI设备,设备树还提供了PCI设备的中断路由表。 例如: powerpc CPU 的PCIE3对接了一个PCIE->PCI桥片,桥片接了一个PCI设备,其设备树可以如下描述:pcie@fe......

shipinsky的博客 5672

PCIE初始化时,下游设备还未分配到bus,如何访问它们?

学BIOS的都知道,访问PCI设备是通过bus:device:function来访问的。如果是CF8/CFC的方式,bus:device:function是向CF8里写,然后从CFC里读,或者向CFC里写数据;如果是memory map的方式,则直接去PCIEBase+bus:device:function的方式去读写。这里的访问是在设备已经初始化,分配了bus之后,那么,在整个系统刚启动时,是如

zeewee1986的专栏 3194

pci设备学习笔记

水平有限,错误难免 ^_^ 参考资料: 1) 《Linux内核源代码情景分析》 2)  Linux内核源代码(2.6.32)。 本文只讨论比较简单的软硬件配置场景。 系统中的第一条PCI总线(即主PCI总线),挂在“宿主—PCI桥”上。 CPU通过“宿主——PCI桥”就可以访问主PCI总线了。 PC机中通常只有一个“宿主—PCI桥”。但是,通过引入其他类型的P

无忧老猪 6899

【PCIe小知识】PCIe的初始化流程

PCIe EP设备外部会连接许多模块实现上述流程,比如应用层设置app_ltssm_enable和app_hold_phy_rst,一般会通过power_up模块实现。上图为PCIe初始化的基本流程,需要注意的是,配置请求只能有CPU发起,即对应PCIe的RC端(Root Complex)。应用层设置app_hold_phy_rst=1,重新配置PHY的寄存器参数。应用层设置app_ltssm_enable=1,使能PCIe的链路训练。开始应用层传输,完成初始化流程。如GEN3,GEN4的切换等。

itnan110的博客 5287

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

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

phmatthaus的专栏 1027

基于Linux的PCIe设备驱动开发入门到精通--2.2 PCI子系统在内核中的角色与初始化

本文深入解析Linux内核中PCI子系统的初始化流程。PCI子系统负责管理PCI/PCIe设备,主要功能包括总线枚举、资源分配、设备抽象和提供驱动API。初始化始于start_kernel(),通过pci_subsys_init()完成核心注册工作,其中关键步骤pci_scan_root_bus()会深度扫描总线并发现设备。现代系统通过ACPI与固件交互获取总线信息。初始化完成后,所有PCI设备都注册到设备驱动模型,为驱动加载做好准备。理解这一流程对开发PCIe驱动至关重要。

MHD0815的博客 1244

Jupyter Lab 无法启动 Kernel 问题排查与解决总结

使用 curl,telnet,或 Python websocket-client 库可以本地模拟 WebSocket 握手行为,帮助分析服务端响应状态。避免使用 http:// + ws:// 组合方式部署 Jupyter Lab,特别是在远程或跨域访问场景。建议统一采用 https:// + wss:// 方式部署,确保浏览器信任并兼容。确认 WebSocket 已升级为 wss://,内核可正常启动。但在远程访问时,浏览器出于安全策略,默认拒绝 ws:// 协议。

weixin_43866043的博客 1858

视频监控设备(NVR、IPC等)如何通过国标GB28181接入到视频监控平台,以及接入过程如何测试视频接入是否成功

GB/T28181标准广泛的应用,视频监控设备通过国标GB28181接入视频监控平台的过程变成程序化、标准化的互联过程,确保了视频监控设备能够在不同系统、不同网络环境下实现远程、跨区域、跨级别的访问和控制。版本有GB28181-2011、2016、2022几个常用版本,威迪斯特的视频监控平台完全支持GB28181的这几个版本,能够实现不同品牌视频监控设备的国标视频接入(国标视频监控接入),或者为国标28181视频接入(国标28181视频监控接入),也为国标281818接入(国标GB/T28181接入。

weixin_70208651的博客 1万+

linux ACPI PCIe扫描

acpi_pci_root_create()在调用acpi_pci_probe_root_resources()处理完IO资源后,调用pci_scan_child_bus()开始取扫描root bus下的devices了(1066行),我们接着看这个扫描函数pci_scan_child_bus().1404行:acpi_bus_init()->acpi_load_tables()->acpi_tb_load_namespace()->acpi_ns_load_table()->

shaohui973的专栏 1061

从零构建SOEM主站:基于STM32的EtherCAT伺服控制实战指南

本文详细介绍了如何从零构建基于STM32的SOEM主站系统,实现EtherCAT伺服电机的高精度控制。通过硬件选型、SOEM协议栈移植、PDO/SDO配置优化等实战步骤,帮助开发者掌握工业自动化中的关键运动控制技术,特别适用于23位编码器伺服电机的应用场景。

weixin_29186249的博客 666

x86 pci初始化过程

在《PCI-E体系结构导读》+linux3.15的基础,大致过了下x86 linux pci的初始化过程 =========do_initcalls完成一系列驱动初始化,包括PCI的初始化也在这个过程中================= kernel_init->kernel_init_freeable->do_basic_setup->do_initcalls ==============

fridayLL的专栏 2992

不同网段共享打印机的方法

不同网段怎么共享打印机,跨路由怎么共享打印机

weixin_44686104的博客 1万+

实战指南:彻底清除TIA Portal卸载后遗留的SQL Server WinCC实例

本文详细介绍了如何彻底清除TIA Portal卸载后遗留的SQL Server WinCC实例,解决重新安装时的冲突问题。通过控制面板排查、注册表验证、安全卸载操作流程和注册表深度清理等步骤,确保完全移除残留组件,避免系统崩溃。文章还提供了安装环境重建策略和预防措施,帮助工程师高效管理西门子自动化软件。

weixin_30482181的博客 405

【PCI】初始化

系统对于PCI的初始化大体分为4个阶段:1.BIOS 对PCI设备进行初次枚举:在系统启动时,BIOS 会扫描系统中的PCI总线,识别和枚举连接在总线上的所有PCI设备,并为它们分配资源(如IO端口、内存地址等)。2.PCI设备系统枚举的前期准备:在操作系统启动后,系统会进行更深入的PCI设备枚举工作。这个阶段会包括文件系统相关目录的建立(如/sys/bus/pci/devices/)、访问方法的初始化等准备工作。3.PCI设备的系统阶段枚举:在系统准备就绪后,会对PCI设备进行系统级别的枚举。

qq_45527937的博客 2876

Python+Tableau 全流程数据分析实战:从数据清洗到商业洞察可视化

本文通过Python+Tableau完整演示电商销售数据分析流程。首先使用Python进行数据清洗(缺失值处理、异常值过滤等)和探索性分析(商品类别、时间趋势等),然后导入Tableau创建交互式可视化仪表盘。分析发现电子产品销售额最高但利润率低,美妆类利润率最优,东部地区销售表现最好。最终提出优化供应链、加强美妆推广等商业建议,展示了Python数据处理与Tableau可视化的高效配合。所有代码可直接运行,适合数据分析初学者和从业者实战参考。

weixin_44520581的博客 337

PCI设备初始化(一)

我们知道开启分页后,CPU可以直接访问虚拟地址,也可以通过MMU间接访问物理地址,进而访问总线地址(对x86架构来说,物理地址和总线地址是等同的),那CPU是怎么访问网卡的存储空间的?每个网卡都有自己的存储空间,这些空间的卡上地址(在网卡上的地址)本质上是局部的,所以都从0开始,它们不与总线直接相连,在把网卡插上总线并加电之初,从总线上还访问不到这些空间。

Jiajun Zhu 1万+

汽车OTA 测试用例

汽车OTA 测试用例

天赐好车的博客 974

Python + Playwright:集成 Applitools 进行视觉回归测试(快速入门)

Applitools是一款基于人工智能的视觉验证测试工具,专门用于自动化和验证应用程序的用户界面(UI);Applitools使用视觉回归测试来检测应用程序界面的变化,确保 UI 在不同版本之间的一致性;Applitools的核心功能是通过视觉比较,检测 UI 元素的差异,而无需手动编写复杂的界面检查代码;通过集成Applitools,测试人员可以轻松捕捉和验证界面中的视觉差异,无论是在Web移动端或桌面应用的测试环境;

blues_C的博客 2921

Linux源码阅读——PCI总线驱动代码(三)PCI设备枚举过程

linux源码阅读,PCI设备枚举过程。

咸鱼弟的博客 5244

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

PCI总线驱动代码梳理(三)--PCI设备的枚举

PCI总线驱动代码梳理(三)–PCI设备的枚举 注:本文档分析的PCI总线驱动框架基于4.4版本内核 在旧版本的内核(以2.6.16版本为例)中系统会调用pci_legacy_init(内核加载等级为4)中调用 pcibios_scan_root来完成对于PCI设备的枚举过程,但是对于现在的x86架构的SOC来说由于ACPI机制的普遍支持,所以对于PCI设备枚举的整个过程就移交至ACPI中来完成。在此我们只基于新机制下的PCI设备枚举的过程进行分析。 1.枚举过程梳理 由于在ACPI机制在x86平台的广泛应

SangDeYG的博客 3551

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

lspci 详解 pci 拓扑结构 与 pci 树形结构

一、PCIE 拓扑结构 硬盘是大家都很熟悉的设备,一路走来,从HDD到SSD,从SATA到NVMe,作为NVMe SSD的前端接口,PCIe再次进入我们的视野。作为x86体系关键的一环,PCIe标准历经PCI,PCI-X和PCIe,走过近30年时光。其中Host发现与查找设备的方式却一脉沿袭,今天我们先来聊一聊PCIe设备在一个系统中是如何发现与访问的。 首先我们来看一下在x86系统中,PCIe是什么样的一个体系架构。下图是一个PCIe的拓扑结构示例,PCIe协议支持256个Bus, 每条Bus最多支持32

weixin_42109053的博客 1万+

Linux源码阅读——PCI总线驱动代码(一)整体框架

linux 源码分析pci设备初始化流程。

咸鱼弟的博客 5513

qemu virtio设备模拟与初始化流程

virtio则是属于一种半虚拟化解决方案,它是一种前后端架构,虚拟机内部需要安装特定的virtio设备驱动作为前端,模拟的设备作为后端,后端可以放在用户空间模拟,也可以放在内核空间模拟。放在内核空间模拟就是vhost的实现,如DPDK,SPDK等。

Eddy的博客 3621
上一篇: pcie SRIOV linux 调用流程
下一篇: U-boot 启动流程分析 1
Runner_Linux
博客等级 码龄6年 17粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值