- arch_initcall
- acpi_pci_init(了解)
- subsys_initcall
- 重点描述
- acpi_init(重点)
- ACPI初始化主流程
- ACPI主桥分配( 重点 )
- 主桥的创建-1: pci_acpi_scan_root
- MCFG的资源分配:init_info函数
- 资源的扫描过程
- 资源管理接口
- mps和mrrs (这部分在mps专栏分析)
- 重点函数1
- 重点函数1
ACPI下的PCI初始化(重点)
参考:PCI总线的初始化
这里有一个ACPI(Advanced Configuration and Power Interface),我的理解是ACPI提供了电源、硬件和固件的接口。这里只关注软件角度的ACPI的结构——在屏蔽了硬件细节的同时,提供了一系列系统资源,包括:
- ACPI寄存器
- ACPI BIOS
- ACPI Tables
arch_initcall
acpi_pci_init(了解)
注:这部分虽然也在arch_initcall,但在pci 的 arch_initicall之后
注册一个全局的acpi_pci_bus总线,并添加到ACPI的全局总线链表中:bus_type_list
// pci-acpi.c:1360:arch_initcall(acpi_pci_init);acpi_pci_initregister_acpi_bus_type(&acpi_pci_bus);list_add_tail(&type->list, &bus_type_list); // 添加到全局bus_type_list总线链表中pci_set_platform_pm(&acpi_pci_platform_pm); // 设置全局的pci相关pm管理接口
subsys_initcall
重点描述
- 从ACPI获取UEFI中给PCI设备分配得资源表信息
- ECAM地址获取及ioremap
- 初始化PCI设备resouce信息(下节)
- 系统启动过程中得打印分析
acpi_init(重点)
因为PCI的枚举牵扯到了这部分,所以这里也跟踪下代码
ACPI初始化主流程
APCI关于PCI主桥设备的总线的匹配(了解)
EDK%20II之Device%20Path%20中描述了PNP0A3是%20PCI%20Host%20Bridge,%20%20所以这边用PNP0A03
//%20subsys_initcall(acpi_init);//%20这部分是ACPI的扫描部分,可以快速看下,类似platform这种acpi_init%20%20%20%20acpi_bus_init();%20%20%20%20pci_mmcfg_late_init();%20%20%20%20acpi_scan_init();%20%20%20%20%20%20%20%20acpi_pci_root_init();%20%20%20%20%20%20%20%20%20%20%20%20acpi_scan_add_handler_with_hotplug(&pci_root_handler,%20"pci_root");%20%20%20%20%20%20%20%20acpi_pci_link_init();%20%20%20%20%20%20%20%20acpi_bus_scan(ACPI_ROOT_OBJECT);%20%20%20%20%20%20%20%20%20%20%20%20acpi_bus_attach%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20acpi_scan_attach_handler%20//%20进行检测%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20handler%20=%20acpi_scan_match_handler(hwid->id,%20&devid);%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20handler->attach(device,%20devid);%20//%20匹配上边的acpi_pci_root_init,进入attach//%20匹配表static%20const%20struct%20acpi_device_id%20root_device_ids[]%20=%20{%20%20%20%20{"PNP0A03",%200},%20%20%20//%20PCIE主桥%20%20%20%20{"",%200},};static%20struct%20acpi_scan_handler%20pci_root_handler%20=%20{%20%20%20%20.ids%20=%20root_device_ids,%20%20%20%20.attach%20=%20acpi_pci_root_add,%20%20%20%20.detach%20=%20acpi_pci_root_remove,%20%20%20%20.hotplug%20=%20{%20%20%20%20%20%20%20%20.enabled%20=%20true,%20%20%20%20%20%20%20%20.scan_dependent%20=%20acpi_pci_root_scan_dependent,%20%20%20%20},};
%20
ACPI主桥分配(%20重点%20)
acpi_pci_root_add%20%20%20参考:ACPI%20PCI%20Root%20Bridge%20Driver类图参考:%20acpi_pci.drawio%20%20最好参考acpi的图进行代码阅读
小知识:判断服务器有多少主桥?每个主桥管哪些总线
dmesg%20可以看到(acpi_pci_root_add函数打印)出现了:[%20%20%20%200.181405]%20ACPI:%20PCI%20Root%20Bridge%20[PCI0]%20(domain%200000%20[bus%2000-1E])%20%20#%20[PCIn]%20[bus%2000-1E],%20那么00就是root的BUS号[%20%20%20%200.181405]%20PCI%20host%20bridge%20to%20bus%200000:00%20%20..%20主桥初始化[%20%20%20%200.181993]%20PCI%20host%20bridge%20to%20bus%200000:00%20%20..%20主桥资源的信息表%20%20%20%20[%20%20%20%200.181995]%20pci_bus%200000:00:%20root%20bus%20resource%20[bus%2000-1E][%20%20%20%200.181996]%20pci_bus%200000:00:%20root%20bus%20resource%20[io%20%200x0000-0x0cf7%20window][%20%20%20%200.181998]%20pci_bus%200000:00:%20root%20bus%20resource%20[io%20%200x0d00-0xffff%20window][%20%20%20%200.181999]%20pci_bus%200000:00:%20root%20bus%20resource%20[mem%200x000a0000-0x000bffff%20window][%20%20%20%200.182000]%20pci_bus%200000:00:%20root%20bus%20resource%20[mem%200x90000000-0xdfffffff%20window][%20%20%20%200.182001]%20pci_bus%200000:00:%20root%20bus%20resource%20[mem%200xfd000000-0xfe7fffff%20window].......[%20%20%20%200.181405]%20ACPI:%20PCI%20Root%20Bridge%20[PCI1]%20(domain%200000%20[bus%201F-FF])%20%20#%20[PCIn]%20[bus%201F-fe],%20那么17就是root的BUS号......可以看到%20%20PC1%20已经枚举到bus%20ff了,那么支持2个主桥
目前所能见到的设备大概只有一个SEG,下边挂了N个主桥,来支持0x00-0xFF的PCI%20BUS
在前边注册到设备后,每检测到一个主桥,会进入 acpi_pci_root_add中,进行初始化。又因为支持了多个主桥,所以这个函数可能会进入多次。
// 这部分是acpi_pci_root部分,需要看下acpi_pci_root_add // 分配一个acpi_pci_root,并对其进行初始化,一般情况下仅含有一个HOST桥。root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); // 分配acpi_pci_root// 下边这部分与BIOS相关:获取ROOT的主设备号acpi_evaluate_integer(&segment); // 从BIOS里边获取SEG信息, 就是pci的domain == 0try_get_root_bridge_busnr(&root->secondary); // 获取domain下边的总线个数// 内核打印定位(!!!关键点调试用):// dmesg输出: ACPI: PCI Root Bridge [PCI0] (domain 0000 [bus 00-7f])#define ACPI_PCI_ROOT_CLASS "pci_bridge"#define ACPI_PCI_ROOT_DEVICE_NAME "PCI Root Bridge"strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME);strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);pr_info(PREFIX "%s [%s] (domain %04x %pR)..."); // !!! 打印了对应主桥的SEG和所管理的BUS号:root->segment和&root->secondary// Scan the Root Bridgeroot->bus = pci_acpi_scan_root(root); // PCI总线初始化的入口函数 !!! 重点pci_bus_add_devices(root->bus);
小知识: acpi_evaluate_integer的作用: 内核ACPI函数API之acpi_evaluate_integer 和 ACPI PCI Root Bridge Driver
**
主桥的创建-1: pci_acpi_scan_root
pci_acpi_scan_root(root); // 枚举PCI设备// number of PCI domain, busnum 都从BIOS获取int domain = root->segment; // 注:这个函数给我们最大疑惑是:domain是多少?busnum是多少? 大部分情况下,domain=0(也就是SEG),根据打印可以看到int busnum = root->secondary.start; // 设备给每一个domain下的主桥分配一段总线域,总共簇成了0x00-0xFF 可以看内核中的打印(也许支持多个主桥)。bus = pci_find_bus(domain, busnum); // domain=0,busnum=0, qemu模拟没找到// 通过pci_find_bus查找HOST Bridge对应的segment,bus num有没有被注册,如果注册了就更新一下信息,// 没有注册则调用acpi_pci_root_create创建,该函数中有两个比较重要,一个是pci_create_root_busif(bus){ // 不考虑...} else { // this branchinfo->sd.domain = domain;info->sd.node = node;info->sd.companion = root->device;acpi_pci_root_create(重点)// 设置整条链路的maxpayload配置(专栏 max payload讲述)list_for_each_entry(child, &bus->children, node)pcie_bus_configure_settings(child);
acpi_pci_root_create(重点)ops->init_info(info); // acpi_pci_root_ops的 pci_acpi_root_init_infopci_acpi_root_init_info(info) // CONFIG_PCI_MMCONFIG=y,ECAM初始化// 设备资源的获取ops->prepare_resources(info); // acpi_pci_root_ops的 pci_acpi_root_prepare_resources 资源扫描pci_acpi_root_prepare_resources(info); // 从ACPI获取所有设备资源信息pci_acpi_root_add_resources(info); // 添加acpi 资源pci_add_resource(&info->resources, &root->secondary);// 创建host_bridgepci_create_root_bus // 创建host bridgepci_register_host_bridge(bridge); // 创建root bus ******************* 也是重点// 扫描child bus ** 子设备创建pci_scan_child_bus(bus); // 查看下一章节
MCFG的资源分配:init_info函数
unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2 |PCI_PROBE_MMCONF;ops->init_info(info)pci_acpi_root_init_infosetup_mcfg_map(ci);pci_mmconfig_insert(dev, seg, info->start_bus, info->end_bus,root->mcfg_addr);// 分配MCFG的 struct pci_mmcfg_region !!! 这里只分配主桥管理的BUS总线的资源,因为1个主桥并不一定全部0x00-0XFF的PCI总线cfg = pci_mmconfig_alloc(seg, start, end, addr);pci_mmcfg_check_reserved(dev, cfg, 0); // 检测主桥对MMC的预留信息(非重点)insert_resource_conflict(&iomem_resource, &cfg->res); // __insert_resource ( 非重点)pci_mmcfg_arch_map(cfg)cfg->virt = mcfg_ioremap(cfg); // !!! 映射mcfg空间list_add_sorted(cfg); // !!! pci_mmcfg_list 存放了全局所有主桥下的ECAM配置// 启动打印: PCI: MMCONFIG for domain 0000 [bus 00-ff] at [mem 0x80000000-0x8fffffff] (base 0x80000000)// PCI: MMCONFIG at [mem 0x80000000-0x8fffffff] reserved in E820 这两个地址就是前边看的acpi的地址raw_pci_ext_ops = &pci_mmcfg; // 这就是访问PCI 扩展空间的ECAM方式。
资源的扫描过程
每个PCI设备在BAR中描述自己需要占用多少地址空间,bios通过所有设备的这些信息构建一张address map,描述系统中资源的分配情况,然后在合理的将地址空间配置给每个PCI设备。
pci_acpi_scan_root 下函数的类图类图
其中,segment=0, secondary为 [bus 00-ff]
在资源扫描过程有以下几部分:
static struct acpi_pci_root_ops acpi_pci_root_ops = {.pci_ops = &pci_root_ops,.init_info = pci_acpi_root_init_info,.release_info = pci_acpi_root_release_info,.prepare_resources = pci_acpi_root_prepare_resources,};if (ops->init_info && ops->init_info(info)) // pci_acpi_root_init_info(info) MCFG的资源管理goto out_release_info;if (ops->prepare_resources)ret = ops->prepare_resources(info); // pci_acpi_root_prepare_resources(info) PCI的资源管理else......pci_acpi_root_add_resources(info);
**
资源管理接口
从ACPI中获取所有设备的资源信息,并添加到链表中
ops->prepare_resources(info);pci_acpi_root_prepare_resourcespci_acpi_root_add_resources(info);pci_add_resource(&info->resources, &root->secondary);
**pci_create_root_bus 主桥的分配
pci_create_root_bus // 重点:分配主桥struct pci_host_bridge *bridge // 主桥描述结构体pci_alloc_host_bridge(0); // 分配主桥对象 struct pci_host_bridge *bridge;list_splice_init(resources, &bridge->windows); //pci_register_host_bridge // 注册主桥dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(bus),bridge->busnr); // 这就是主桥编号是: 0000:00的来源# 打印当前主桥的内容设置当前主桥在PCI总线域的地址区范围(重点,后期设备初始化需要靠这个来确定存储区地址),注:这里明显windows offset==0[ 0.125794] PCI host bridge to bus 0000:00[ 0.125797] pci_bus 0000:00: root bus resource [io 0x0000-0x0cf7 window][ 0.125798] pci_bus 0000:00: root bus resource [io 0x0d00-0xffff window][ 0.125799] pci_bus 0000:00: root bus resource [mem 0x000a0000-0x000bffff window][ 0.125800] pci_bus 0000:00: root bus resource [mem 0xbf800000-0xfeafffff window][ 0.125801] pci_bus 0000:00: root bus resource [bus 00-3f]
**
mps和mrrs (这部分在mps专栏分析)
参考: PCIe学习笔记之Max payload size 和 PCI Express Base_r5_1.pdf 中 7.5.3章节(PCIE CAP)
struct pci_bus *child;list_for_each_entry(child, &bus->children, node)pcie_bus_configure_settings(child); // drivers/pci/probe.cenum pcie_bus_config_types {PCIE_BUS_TUNE_OFF, /* don't touch MPS at all */PCIE_BUS_DEFAULT, /* ensure MPS matches upstream bridge */PCIE_BUS_SAFE, /* use largest MPS boot-time devices support */PCIE_BUS_PERFORMANCE, /* use MPS and MRRS for best performance */PCIE_BUS_PEER2PEER, /* set MPS = 128 for all devices */};enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_DEFAULT;pcie_bus_configure_settingsif (!pci_is_pcie(bus->self)) return; // 只有PCIE支持maxpayload,因为这个配置在PCIE里边pcie_bus_configure_set(bus->self, &smpss);pci_walk_bus(bus, pcie_bus_configure_set, &smpss);
重点函数1
static int pcie_bus_configure_set(struct pci_dev *dev, void *data){int mps, orig_mps;if (!pci_is_pcie(dev)) // PCIE才只是MPS调整return 0;if (pcie_bus_config == PCIE_BUS_TUNE_OFF ||pcie_bus_config == PCIE_BUS_DEFAULT) // 这两种配置不进行设置return 0;mps = 128 << *(u8 *)data;orig_mps = pcie_get_mps(dev); // 从Device Control Register获取MPSpcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);return 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5); // MPS配置pcie_write_mps(dev, mps); // 根据参数配置mpsif (pcie_bus_config == PCIE_BUS_PERFORMANCE) { // 最优化的MPS配置mps = 128 << dev->pcie_mpss;}rc = pcie_set_mps(dev, mps); // 配置Device Control Register的MPSpcie_write_mrrs(dev); // PCIE_BUS_PERFORMANCE 才支持mrrs = pcie_get_mps(dev); // mrrs = Device Control Register的MPSwhile (mrrs != pcie_get_readrq(dev) && mrrs >= 128) { // 从Device Control Register获取MRRS,往最高性能配置MRRS,失败后依次降低rc = pcie_set_readrq(dev, mrrs);if (!rc)break;dev_warn(&dev->dev, "Failed attempting to set the MRRS\n");mrrs /= 2;}dev_info(&dev->dev, "Max Payload Size set to %4d/%4d (was %4d), Max Read Rq %4d\n",pcie_get_mps(dev), 128 << dev->pcie_mpss,orig_mps, pcie_get_readrq(dev));return 0;}
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论