版权声明:本文为博主原创文章,未经博主允许不得转载。
https://blog.csdn.net/huangweiqing80/article/details/83185955
四、PCI设备的枚举探测过程
在内核启动过程中,PCI设备的探测过程是完全自动的,内核已经集成好了方法,我们无需更改,在这里还是分析一边代码作为了解。
分析之前,先看一下全部的函数调用关系,大致了解一下
pci_arch_init /* 判断host/pci桥的类型 */
pci_direct_probe
pci_check_type1
pci_sanity_check
pci_direct_init
raw_pci_ops = &pci_direct_conf1;
raw_pci_ext_ops = &pci_direct_conf1;
/* 第二个过程,枚举各级总线上的设备 */
pci_subsys_init
pci_legacy_init
pcibios_scan_root
pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd);
pci_create_bus(parent, bus, ops, sysdata); // 创建 0 级总线
pci_scan_child_bus(b); // 探测当前总线设备以及子总线、子总线设备
pci_scan_slot(bus, devfn); // 探测当前总线的设备
pci_scan_single_device(bus, devfn); // 探测单功能设备
pci_scan_single_device(bus, devfn + fn); //探测多功能设备
pci_scan_device(bus, devfn); //通过配置空间 枚举设备
pci_setup_device //根据配置空间信息,设置pci_dev
pci_device_add(dev, bus);
list_add_tail(&dev->bus_list, &bus->devices); // 将探测到的设备加入到当前总线的设备链表
pci_scan_bridge //此时已经完成当前总线设备的探测,如果这些设备里有PCI桥,那么进入下一级,探测桥下的设备
child = pci_add_new_bus(bus, dev, busnr);
pci_scan_child_bus(child); // 进入下一级探测
pci_bus_add_devices // 全部设备探测完毕,注册设备。
pci_bus_add_device(dev);
device_add // 将设备注册到 pci_bus_type
pci_bus_add_devices(child); //它最终也会调用到 device_add 将各个子总线上的设备注册到 pci_bus_type
下面来看具体的探测过程。
static __init int pci_arch_init(void)
{
#ifdef CONFIG_PCI_DIRECT
int type = 0;
type = pci_direct_probe();
#endif
#ifdef CONFIG_PCI_BIOS
pci_pcbios_init();
#endif
#ifdef CONFIG_PCI_DIRECT
pci_direct_init(type);
#endif
dmi_check_pciprobe();
dmi_check_skip_isa_align();
return 0;
}
arch_initcall(pci_arch_init);
这个函数是放在 init 段中,内核启动时会调用。
int __init pci_direct_probe(void)
{
struct resource *region, *region2;
/* 申请IO资源 */
region = request_region(0xCF8, 8, "PCI conf1");
/* 探测那种类型 ,0型(PCI设备)和1型(PCI桥) */
if (pci_check_type1()) {
raw_pci_ops = &pci_direct_conf1;
port_cf9_safe = true;
return 1;
}
release_resource(region);
type2:
if ((pci_probe & PCI_PROBE_CONF2) == 0)
return 0;
if (!request_region(0xCF8, 4, "PCI conf2"))
return 0;
if (!request_region(0xC000, 0x1000, "PCI conf2"))
goto fail2;
if (pci_check_type2()) {
raw_pci_ops = &pci_direct_conf2;
port_cf9_safe = true;
return 2;
}
release_region(0xC000, 0x1000);
fail2:
release_region(0xCF8, 4);
return 0;
}
在pci规范中,定义了两种操作配置空间的方法,即type1 和type2.在新的设计中,type2的配置机制不会被采用,通常会使用type1.因此,在代码中pci_direct_probe()一般会返回1,即使用type1.
static int __init pci_check_type1(void)
{
unsigned long flags;
unsigned int tmp;
int works = 0;
local_irq_save(flags);
/* i386 pci地址寄存器 0xcfb 写 0x01 */
outb(0x01, 0xCFB);
tmp = inl(0xCF8);
outl(0x80000000, 0xCF8);
/* 判断设备类型 */
if (inl(0xCF8) == 0x80000000 && pci_sanity_check(&pci_direct_conf1)) {
works = 1;
}
outl(tmp, 0xCF8);
local_irq_restore(flags);
return works;
}
static int __init pci_sanity_check(struct pci_raw_ops *o)
{
u32 x = 0;
int year, devfn;
/* Assume Type 1 works for newer systems.
This handles machines that don't have anything on PCI Bus 0. */
dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL);
if (year >= 2001)
return 1;
for (devfn = 0; devfn < 0x100; devfn++) {
/* 读 CLASS_DEVICE ,PCI_CLASS_DEVICE 是片内偏移地址 */
if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x)) //pci_direct_conf1->read
continue;
/* 如果 CLASS_DEVICE 为 HOST-PCI桥(北桥),PCI-PCI桥,PCI-ISA桥(南桥)正确返回 */
if (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)
return 1;
/* 读 VENDOR_ID 制造商ID */
if (o->read(0, 0, devfn, PCI_VENDOR_ID, 2, &x))
continue;
/* 如果 VENDOR_ID 为 INTEL 或 COMPAQ 正常返回 */
if (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)
return 1;
}
DBG(KERN_WARNING "PCI: Sanity check failed\n");
return 0;
}
检测完是“0型”还是“1型”设备之后,在 raw_pci_ops 中指定对应的读写配置空间的方法。
/* 设置全局的 配置空间读写函数 */
void __init pci_direct_init(int type)
{
if (type == 1) {
raw_pci_ops = &pci_direct_conf1;
raw_pci_ext_ops = &pci_direct_conf1;
return;
}
}
/* 地址是由 总线编号、设备号、片内地址 组成 */
#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
| (devfn << 8) | (reg & 0xFC))
static int pci_conf1_read(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 *value)
{
unsigned long flags;
/* 最多256个总线 ,256个设备 片内寄存器范围 0~4095 */
if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
*value = -1;
return -EINVAL;
}
spin_lock_irqsave(&pci_config_lock, flags);
/* 向地址寄存器 写要读取的地址 */
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;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
struct pci_raw_ops {
int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 *val);
int (*write)(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 val);
};
struct pci_raw_ops *raw_pci_ops;
五:pci设备的枚举过程
int __init pci_subsys_init(void)
{
#ifdef CONFIG_X86_NUMAQ
pci_numaq_init();
#endif
#ifdef CONFIG_ACPI
pci_acpi_init();
#endif
#ifdef CONFIG_X86_VISWS
pci_visws_init();
#endif
pci_legacy_init();
pcibios_fixup_peer_bridges();
pcibios_irq_init();
pcibios_init();
return 0;
}
subsys_initcall(pci_subsys_init);
struct pci_bus *pci_root_bus;
static int __init pci_legacy_init(void)
{
pci_root_bus = pcibios_scan_root(0);//创建0级总线
if (pci_root_bus)
pci_bus_add_devices(pci_root_bus);
return 0;
}
extern struct list_head pci_root_buses; /* list of all known PCI buses */
struct pci_bus * __devinit pcibios_scan_root(int busnum)
{
struct pci_bus *bus = NULL;
struct pci_sysdata *sd;
/* 在全局 pci_root_buses 链表寻找 总线编号为 busnum 的总线 */
while ((bus = pci_find_next_bus(bus)) != NULL) {
if (bus->number == busnum) {
/* 如果已经存在,返回它 */
return bus;
}
}
/* 如果这个总线编号不存在, 那么创建这个Bus */
sd = kzalloc(sizeof(*sd), GFP_KERNEL);
sd->node = get_mp_bus_to_node(busnum);
bus = pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd);
return bus;
}
struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent,
int bus, struct pci_ops *ops, void *sysdata)
{
struct pci_bus *b;
/* 为对应总线号构建pci_bus,然后将其挂入到pci_root_buses链表 */
b = pci_create_bus(parent, bus, ops, sysdata);
if (b)
b->subordinate = pci_scan_child_bus(b);
return b;
}
unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
{
unsigned int devfn, pass, max = bus->secondary;
struct pci_dev *dev;
/* 探测总线上的设备,按功能号扫描设备号对应的pci 设备 */
for (devfn = 0; devfn < 0x100; devfn += 8)
pci_scan_slot(bus, devfn);
/* Reserve buses for SR-IOV capability. */
max += pci_iov_bus_range(bus);
/*
* After performing arch-dependent fixup of the bus, look behind
* all PCI-to-PCI bridges on this bus.
*/
if (!bus->is_added) {
pr_debug("PCI: Fixups for bus %04x:%02x\n",
pci_domain_nr(bus), bus->number);
pcibios_fixup_bus(bus);
if (pci_is_root_bus(bus))
bus->is_added = 1;
}
/* 探测 pci 桥上的设备,创建子Bus,挂到父 bus->child */
for (pass=0; pass < 2; pass++)
list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
max = pci_scan_bridge(bus, dev, max, pass);
}
/*
* We've scanned the bus and so we know all about what's on
* the other side of any bridges that may be on this bus plus
* any devices.
*
* Return how far we've got finding sub-buses.
*/
pr_debug("PCI: Bus scan for %04x:%02x returning with max=%02x\n",
pci_domain_nr(bus), bus->number, max);
return max;
}
这节的难点就是在这个地方了,从我们之前分析的pci设备配置空间的读写方式可得知.对特定总线.下面最多个32个设备号.每个设备号又对应8 个功能号.我们可以将设备号和功能号放到一起,即占8~15位.在这面的代码中.对每个设备号调用pci_scan_slot()去扫描它下面的8个功能号对应的设备.总而言之,把该总线下面的所有设备都要枚举完.
int pci_scan_slot(struct pci_bus *bus, int devfn)
{
int fn, nr = 0;
struct pci_dev *dev;
dev = pci_scan_single_device(bus, devfn);
/* 如果是多功能设备 */
if (dev && dev->multifunction) {
for (fn = 1; fn < 8; fn++) {
dev = pci_scan_single_device(bus, devfn + fn);
if (dev) {
if (!dev->is_added)
nr++;
dev->multifunction = 1;
}
}
}
return nr;
}
struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn)
{
struct pci_dev *dev;
/* 遍历 bus->devices 设备链表,查找是否有 devfn 号设备存在 */
dev = pci_get_slot(bus, devfn);
/* 如果已经存在,返回它 */
if (dev) {
pci_dev_put(dev);
return dev;
}
/* 通过访问配置空间,探测设备 */
dev = pci_scan_device(bus, devfn);
/* 探测失败 返回Null */
if (!dev)
return NULL;
/* 探测成功 */
pci_device_add(dev, bus);
return dev;
}
调用pci_scan_device()执行扫描的过程,如果该设备存在,就会将该设备加入到所属总线的devices链表上.这是在pci_device_add()函数中完成的,这个函数比较简单.这里不做详细分析.我们把注意力集中到pci_scan_device(),这函数有点长,分段分析如下:
static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
{
struct pci_dev *dev;
u32 l;
int delay = 1;
/* 读 PCI_VENDOR_ID 制造商ID */
if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
return NULL;
/* id 等于这些值,认为探测失败 ,返回 */
if (l == 0xffffffff || l == 0x00000000 ||
l == 0x0000ffff || l == 0xffff0000)
return NULL;
....
/* 探测成功,分配一个 pci_dev 结构 */
dev = alloc_pci_dev();
dev->bus = bus;
dev->devfn = devfn;
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
/* 读取配置空间,更详细的设置,指定 dev->bus 等 */
if (pci_setup_device(dev)) {
kfree(dev);
return NULL;
}
return dev;
}
从配置空间中读取该设备对应的vendor id和device id.如果读出来的值,有一个是空的,则说明该功能号对应的设备不存在,或者是配置非法.
如果读出来的是0xffff0001.则需要重新读一次,如果重读次数过多,也会退出
//对特定类型的设备配置都行读取操作
int pci_setup_device(struct pci_dev *dev)
{
u32 class;
u8 hdr_type;
struct pci_slot *slot;
dev->sysdata = dev->bus->sysdata;
dev->dev.parent = dev->bus->bridge;
/* 设置 dev 所属的总线 */
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);
list_for_each_entry(slot, &dev->bus->slots, list)
if (PCI_SLOT(dev->devfn) == slot->number)
dev->slot = slot;
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));
/* 设备类型 */
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
dev->revision = class & 0xff;
class >>= 8; /* upper 3 bytes */
dev->class = class;
class >>= 8;
/* need to have dev->class ready */
dev->cfg_size = pci_cfg_space_size(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;
switch (dev->hdr_type) { /* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */
...
case PCI_HEADER_TYPE_BRIDGE: /* bridge header */
/* 设置 dev->irq */
pci_read_irq(dev);
dev->transparent = ((dev->class & 0xff) == 1);
/* 设置 dev->rom_base_reg */
pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
set_pcie_hotplug_bridge(dev);
break;
case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */
...
break;
return 0;
}
pci_setup_deivce()对特定类型的设备配置都行读取操作了
将不同类型设备的共同头部配置读出来,然后赋值给pci_dev的相应成员.这里有个特别要值得注意的地方: dev->dev.bus = &pci_bus_type.即将pci_dev里面封装的device结构的bus设置为了pci_bus_type.这个是很核心的一个步骤.我们先将它放到这里,之后的再来详细分析
特别的, HEADER_TYPE的最高位为0,表示该设备是一个单功能设备
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
{
device_initialize(&dev->dev);
dev->dev.release = pci_release_dev;
pci_dev_get(dev);
dev->dev.dma_mask = &dev->dma_mask;
dev->dev.dma_parms = &dev->dma_parms;
dev->dev.coherent_dma_mask = 0xffffffffull;
pci_set_dma_max_seg_size(dev, 65536);
pci_set_dma_seg_boundary(dev, 0xffffffff);
/* Fix up broken headers */
pci_fixup_device(pci_fixup_header, dev);
/* Clear the state_saved flag. */
dev->state_saved = false;
/* Initialize various capabilities */
pci_init_capabilities(dev);
/* 将设备挂入 bus->devices链表 */
down_write(&pci_bus_sem);
list_add_tail(&dev->bus_list, &bus->devices);
up_write(&pci_bus_sem);
}
void pci_bus_add_devices(const struct pci_bus *bus)
{
struct pci_dev *dev;
struct pci_bus *child;
int retval;
/* 遍历当前总线的 dev ,注册设备 */
list_for_each_entry(dev, &bus->devices, bus_list) {
/* Skip already-added devices */
if (dev->is_added)
continue;
retval = pci_bus_add_device(dev);
if (retval)
dev_err(&dev->dev, "Error adding device, continuing\n");
}
/* 遍历子总线的dev,注册设备 */
list_for_each_entry(dev, &bus->devices, bus_list) {
BUG_ON(!dev->is_added);
child = dev->subordinate;
/*
* If there is an unattached subordinate bus, attach
* it and then scan for unattached PCI devices.
*/
if (!child)
continue;
if (list_empty(&child->node)) {
down_write(&pci_bus_sem);
list_add_tail(&child->node, &dev->bus->children);
up_write(&pci_bus_sem);
}
pci_bus_add_devices(child);
/*
* register the bus with sysfs as the parent is now
* properly registered.
*/
if (child->is_added)
continue;
retval = pci_bus_add_child(child);
if (retval)
dev_err(&dev->dev, "Error adding bus, continuing\n");
}
}
int pci_bus_add_device(struct pci_dev *dev)
{
int retval;
/* 将设备注册到 pci_bus_type */
retval = device_add(&dev->dev);
if (retval)
return retval;
dev->is_added = 1;
pci_proc_attach_device(dev);
pci_create_sysfs_dev_files(dev);
return 0;
}
参考Pci设备驱动0:设备枚举