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

本文深入探讨了X86架构下PCIe的初始化枚举流程,重点分析了kernel5.2.9版本中PCIe初始化的两大部分:与CPU框架紧密相关的初始化,以及PCI子系统的初始化。文章详细解释了初始化函数pci_arch_init和pci_subsys_init的工作原理,以及PCI设备的发现和配置过程。
最低0.47元/天 解锁文章
1872

被折叠的 条评论
为什么被折叠?



