从本文开始,就根据前文给出的ramstage的主干线索分析每个小阶段执行的过程。依然以qemu-i440fx为主做分析——因为当前条件只有这个“主板”才能在通过打印跟踪其过程。另外,也会列出baytrail的相关函数,但可能会存在部分瑕疵。在分析时,不一定会根据顺序,也不一定会详细到每个函数。如果要详细的信息,请查阅代码。也建议根据前文给出的主干流程图进行参照分析。
由代码得知,第一个启动状态BS_PRE_DEVICE实现为空,因此实际上最先执行的状态为BS_DEV_INIT_CHIPS,即芯片初始化。该状态对应的函数为bs_dev_init_chips(),该函数调用dev_initialize_chips(),该函数会调到到具体芯片平台的init函数:
1 2 3 4 5 6 7 8 /* Initialize chip if we haven't yet. */ if (dev->chip_ops && dev->chip_ops->init && !dev->chip_ops->initialized) { ll_printk("in %s()\n", __func__); post_log_path(dev); dev->chip_ops->init(dev->chip_info); // 调用chip_operations的init函数 dev->chip_ops->initialized = 1; }
注意,其中的chip_ops为结构体chip_operations的指针。下面将分析这个过程。
一、qemu-i440fx 该“主板”目录位于src\mainboard\emulation\qemu-i440fx。主板入口文件为mainboard.c。如果对比其它主板的入口文件代码,就会发现其它的主板定义了一个叫mainboard_ops的结构体,结构体为chip_operations(与上文提到的结构体对应起来)。但qemu-i440fx的入口文件没有定义。原来,一个叫sconfig的设备树编译工具会生成一个叫static.c的文件(注:关于sconfig不在此处展开了)。这个文件位于build\mainboard\emulation\qemu-i440fx\,这个static.c文件十分重要,里面定义了许多在ramstage阶段使用到的全局变量。比如根设备dev_root(这个结构体将在设备枚举时使用到):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ROMSTAGE_CONST struct device dev_root = { #ifndef __PRE_RAM__ .ops = &default_dev_ops_root, #endif .bus = &dev_root_links[0], .path = { .type = DEVICE_PATH_ROOT }, .enabled = 1, .on_mainboard = 1, .link_list = &dev_root_links[0], #ifndef __PRE_RAM__ .chip_ops = &mainboard_ops, .name = mainboard_name, #endif .next=&_dev2 };
可以看到,这个结构体定义中,将mainboard_ops赋值给结构体成员函数指针chip_ops。这样,dev_initialize_chips()最终将调用到static.c被赋值的mainboard_ops中的init函数。在文件开始处,可以看到定义了几个结构体的弱链接:
1 2 3 4 __attribute__((weak)) struct chip_operations mainboard_ops = {}; __attribute__((weak)) struct chip_operations cpu_qemu_x86_ops = {}; __attribute__((weak)) struct chip_operations mainboard_emulation_qemu_i440fx_ops = {}; __attribute__((weak)) struct chip_operations southbridge_intel_i82371eb_ops = {};
这些弱链接就是为了保证编译的顺利通过而定义的。 为了验证猜想,我们在qemu-i440fx的mainboard.c文件自行定义chip_operations结构体,测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ////////////////////// // 下面是chip_operations的测试,主要是演示在枚举设备前的芯片级别的初始化 // 在util/sconfig(coreboot设备树编译器工具)中生成static.c文件,将chip_operations赋值,如果不定义,则使用弱链接 static void mainboard_i440fx_enable(device_t dev) { ll_printk("in %s()...\n", __func__); } static void mainboard_i440fx_chip_init(void *chip_info) { ll_printk("in %s()...\n", __func__); } // 从static.c文件看,这个是root设备 struct chip_operations mainboard_ops = { CHIP_NAME("Late Lee qemu i440fx soc") .enable_dev = mainboard_i440fx_enable, .init = mainboard_i440fx_chip_init, // 在dev_initialize_chips函数中调用 };
mainboard_ops结构体定义了2个函数,其中一个为init,另一个为设备使能enable_dev。这个函数将在设备枚举dev_enumerate中被调用,代码如下:
1 2 3 4 5 6 7 8 9 void dev_enumerate(void) { ... ll_printk("Enable device...\n"); // 在enable_dev函数中赋值scan_bus的 比如baytrail和qemu i440fx if (root->chip_ops && root->chip_ops->enable_dev) root->chip_ops->enable_dev(root); ... }
使用qemu运行,可以看到已经执行上述代码了。部分日志如下:
1 2 3 4 5 POST: 0x71 [LL DEBUG]: in bs_dev_init_chips()... [LL DEBUG]: in dev_initialize_chips() [LL DEBUG]: in mainboard_i440fx_chip_init()... [LL DEBUG]: bs_walk_state_machine() state 2 start==============================
二、baytrail-fsp (故意留空不写)
注: 由于coreboot方面资料较少,笔者第一次尝试分析代码,还有众多未能参透的地方,难免出错。任何问题,欢迎一起交流学习。
李迟 2016.3.19 周六 夜