本文主要针对ramstage阶段的设备枚举的过程进行分析。限于精力,就直接使用qemu-i440fx作为分析,baytrail就免了吧。在分析时,不一定会根据顺序,也不一定会详细到每个函数。如果要详细的信息,请查阅代码。也建议根据前文给出的主干流程图进行参照分析。
枚举的函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void dev_enumerate(void) { struct device *root; printk(BIOS_INFO, "Enumerating buses...\n"); root = &dev_root; show_all_devs(BIOS_SPEW, "Before device enumeration."); printk(BIOS_SPEW, "Compare with tree...\n"); show_devs_tree(root, BIOS_SPEW, 0, 0); // 在enable_dev函数中赋值scan_bus的 比如baytrail和qemu i440fx if (root->chip_ops && root->chip_ops->enable_dev) root->chip_ops->enable_dev(root); if (!root->ops || !root->ops->scan_bus) { printk(BIOS_ERR, "dev_root missing scan_bus operation"); return; } // 扫描总线 从root开始 scan_bus(root); post_log_clear(); printk(BIOS_INFO, "done\n"); }
从该函数中看出,主要有3个步骤:一是打印设备树;二是调用chip_ops的使能设备函数enable_dev;最后是调用scan_bus函数扫描总线。 阅读过前文的相信已经知道在i440fx中是没有芯片级别的初始化和使能函数的,为了做试验,在mainboard.c中做了空实现函数。但是,在北桥芯片northbridge.c文件中,同样有chip_operations结构体,同样有使能的函数:
1 2 3 4 struct chip_operations mainboard_emulation_qemu_i440fx_ops = { CHIP_NAME("QEMU Northbridge i440fx") .enable_dev = northbridge_enable, };
northbridge_enable函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 static void northbridge_enable(struct device *dev) { ll_printk("in %s() dev type: %d...\n", __func__, dev->path.type); /* Set the operations if it is a special bus type */ if (dev->path.type == DEVICE_PATH_DOMAIN) { dev->ops = &pci_domain_ops; } else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) { dev->ops = &cpu_bus_ops; } }
其中pci域设备操作函数集结构体定义如下:
1 2 3 4 5 6 7 8 9 10 11 static struct device_operations pci_domain_ops = { .read_resources = cpu_pci_domain_read_resources, // 读资源调用此函数 .set_resources = cpu_pci_domain_set_resources, // 设置资源调用此函数 .enable_resources = NULL, .init = NULL, .scan_bus = pci_domain_scan_bus, // 扫描总线 .ops_pci_bus = pci_bus_default_ops, #if CONFIG_GENERATE_SMBIOS_TABLES .get_smbios_data = qemu_get_smbios_data, #endif };
注意,如果要讲pci域,则需要涉及到存储器域、PCI总线域,还有南北桥方面的东西了。那不是当前笔者力之能及的事,建议看看PCI体系结构的书籍。(但谁又能确定未来的笔者不会被领导安排做PCI有关的事务呢?) 另外,cpu设备操作函数集定义如下:
1 2 3 4 5 6 7 8 // cpu初始化、总线扫描在这里定义 static struct device_operations cpu_bus_ops = { .read_resources = DEVICE_NOOP, .set_resources = DEVICE_NOOP, .enable_resources = DEVICE_NOOP, .init = cpu_bus_init, .scan_bus = cpu_bus_scan, };
值得注意的是,pci_domain_ops和cpu_bus_ops同是device_operations变量,一个“设备类型”为DEVICE_PATH_DOMAIN,另一个为DEVICE_PATH_CPU_CLUSTER,它们分别对应PCI域和CPU设备,在接下来的文章中将会看到CPU初始化时,就会调用到cpu_bus_init函数。在coreboot中,很多操作实际上是遍历设备树,然后调用对应的device_operations指针,而设备树上不同的设备,使用枚举类型device_path_type来区分。 PCI的扫描比较复杂。总体而言,在扫描时遇到PCI桥时,再递归调用扫描函数。从而完成所有PCI设备的扫描工作。
根据代码整理的流程图如下: (此处留空)
附上此过程的打印信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 [LL DEBUG]: in bs_dev_enumerate()... Enumerating buses... Show all devs... Before device enumeration. Root Device: enabled 1 CPU_CLUSTER: 0: enabled 1 APIC: 00: enabled 1 DOMAIN: 0000: enabled 1 PCI: 00:00.0: enabled 1 PCI: 00:01.0: enabled 1 PCI: 00:01.1: enabled 1 PCI: 00:01.3: enabled 1 Compare with tree... Root Device: enabled 1 CPU_CLUSTER: 0: enabled 1 APIC: 00: enabled 1 DOMAIN: 0000: enabled 1 PCI: 00:00.0: enabled 1 PCI: 00:01.0: enabled 1 PCI: 00:01.1: enabled 1 PCI: 00:01.3: enabled 1 [LL DEBUG]: in mainboard_i440fx_enable()... Root Device scanning... root_dev_scan_bus for Root Device CPU_CLUSTER: 0 enabled DOMAIN: 0000 enabled CPU_CLUSTER: 0 scanning... QEMU: firmware config interface detected QEMU: max_cpus is 1 CPU: APIC: 00 enabled scan_bus: scanning of bus CPU_CLUSTER: 0 took 0 usecs DOMAIN: 0000 scanning... PCI: pci_scan_bus for bus 00 POST: 0x24 PCI: 00:00.0 [8086/1237] ops PCI: 00:00.0 [8086/1237] enabled PCI: 00:01.0 [8086/7000] bus ops PCI: 00:01.0 [8086/7000] enabled PCI: 00:01.1 [8086/7010] ops PCI: 00:01.1 [8086/7010] enabled PCI: 00:01.3 [8086/7113] bus ops PCI: 00:01.3 [8086/7113] enabled PCI: 00:02.0 [1013/00b8] ops PCI: 00:02.0 [1013/00b8] enabled PCI: 00:03.0 [8086/100e] enabled POST: 0x25 PCI: 00:01.0 scanning... scan_lpc_bus for PCI: 00:01.0 scan_lpc_bus for PCI: 00:01.0 done scan_bus: scanning of bus PCI: 00:01.0 took 0 usecs PCI: 00:01.3 scanning... scan_smbus for PCI: 00:01.3 scan_smbus for PCI: 00:01.3 done scan_bus: scanning of bus PCI: 00:01.3 took 0 usecs POST: 0x55 scan_bus: scanning of bus DOMAIN: 0000 took 0 usecs root_dev_scan_bus for Root Device done scan_bus: scanning of bus Root Device took 0 usecs done
注: 由于coreboot方面资料较少,笔者第一次尝试分析代码,还有众多未能参透的地方,难免出错。任何问题,欢迎一起交流学习。
李迟 2016.4.3 周日 上午