coreboot学习6:ramstage阶段之芯片初始化流程

从本文开始,就根据前文给出的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 周六 夜