coreboot学习9:ramstage阶段之设备初始化流程

本文对ramstage阶段的设备初始化过程进行跟踪。设备初始化是在dev_initialize函数中完成的,代码如下:

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_initialize(void)
{
struct bus *link;

printk(BIOS_INFO, "Initializing devices...\n");

#if CONFIG_ARCH_X86
/* Ensure EBDA is prepared before Option ROMs. */
setup_default_ebda();
#endif

/* First call the mainboard init. */
// 先调用mainboard的init -- 注: 如没有定义,则使用弱链接,参考static.c
init_dev(&dev_root);


/* Now initialize everything. */
for (link = dev_root.link_list; link; link = link->next)
{
init_link(link);
}
post_log_clear();

printk(BIOS_INFO, "Devices initialized\n");
show_all_devs(BIOS_SPEW, "After init.");
}

从该函数看出大概有几个步骤:
1、先调用init_dev初始化根设备dev_root。
2、遍历根设备下的所有设备,调用init_link。
3、最后将所有设备打印出来。
其中,init_dev调用调用具体设备的device_operations结构体指针函数init。当设备有link时,调用init_link函数,该函数再调用init_dev进行初始化,直到所有设备均遍历完毕。如果阅读过编译时生成的static.c文件,就会发现,有的设备link_list成员变量被赋值,有的设备则为NULL。这个文件实际上就是组成了目标板上的设备树。在ramstage阶段很多的操作,实际就是遍历这个设备树,找对应的设备类型,调用对应的操作函数。
下面跟踪一下CPU的初始化过程。CPU设备操作函数集定义如下:

1
2
3
4
5
6
7
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,
};

在调用init时,会调用到cpu_bus_init函数。该函数如下:

1
2
3
4
static void cpu_bus_init(device_t dev)
{
initialize_cpus(dev->link_list);
}

而initialize_cpus会调用到cpu_initialize。该函数主要是读取CPU信息,如family、model、stepping,然后调用对应的驱动的init函数。文中使用qemu i440fx,因而最终会调用到qemu_cpu_init函数。 代码如下:

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
59
60
61
62
63
64
65
66
67
68
69
70
71
void cpu_initialize(unsigned int index)
{
/* Because we busy wait at the printk spinlock.
* It is important to keep the number of printed messages
* from secondary cpus to a minimum, when debugging is
* disabled.
*/
struct device *cpu;
struct cpu_info *info;
struct cpuinfo_x86 c;

info = cpu_info();

printk(BIOS_INFO, "Initializing CPU #%d\n", index);

cpu = info->cpu;
if (!cpu) {
die("CPU: missing cpu device structure");
}

if (cpu->initialized)
return;

post_log_path(cpu);

/* Find what type of cpu we are dealing with */
identify_cpu(cpu); // 获取cpu信息
printk(BIOS_DEBUG, "CPU: vendor %s device 0x%x\n",
cpu_vendor_name(cpu->vendor), cpu->device);

get_fms(&c, cpu->device); // fms难道是family model stepping的意思?

/* 打印CPU family、model,例如0x06_0x37表示atom e3000系列(e3800也在其中),参考IA32手册卷3第35章表格1 */
printk(BIOS_DEBUG, "CPU: family 0x%02x, model 0x%02x, stepping 0x%02x\n",
c.x86, c.x86_model, c.x86_mask);
printk(BIOS_DEBUG, "DisplayFamily_DisplayModel: %02X_%02XH\n", c.x86, c.x86_model);
// test
msr_t platform_id = rdmsr(0x17);
printk(BIOS_DEBUG, "platform_id: %x %x\n", platform_id.hi, platform_id.lo);

// my test...
char processor_name[49];
/* Print processor name */
fill_processor_name1(processor_name);
// 打印CPU,如qemu会打印:QEMU Virtual CPU version 2.0.0
printk(BIOS_INFO, "LLDEBUG CPU: %s.\n", processor_name);

/* Lookup the cpu's operations */
set_cpu_ops(cpu);

if(!cpu->ops) {
/* mask out the stepping and try again */
cpu->device -= c.x86_mask;
set_cpu_ops(cpu); // 设置操作函数
cpu->device += c.x86_mask;
if(!cpu->ops) die("Unknown cpu");
printk(BIOS_DEBUG, "Using generic cpu ops (good)\n");
}


/* Initialize the cpu */
if (cpu->ops && cpu->ops->init) {
cpu->enabled = 1;
cpu->initialized = 1; // 已经初始化好了
cpu->ops->init(cpu); // 调用具体的init函数 如baytrail的为baytrail_init_cpus, qemu为qemu_cpu_init
}
post_log_clear();

printk(BIOS_INFO, "CPU #%d initialized\n", index);
return;
}

在前面学习CPUID指令时,实际上就是在这个函数中做试验的。
根据代码整理的流程图如下:
(此处留空)
附上此过程的打印信息:

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
[LL DEBUG]: in bs_dev_init()...
Initializing devices...
Root Device init ...
POST: 0x75
CPU_CLUSTER: 0 init ...
Initializing CPU #0
CPU: vendor Intel device 0x663
CPU: family 0x06, model 0x06, stepping 0x03
DisplayFamily_DisplayModel: 06_06H
platform_id: 0 0
LLDEBUG CPU: QEMU Virtual CPU version 2.0.0.
Setting up local apic... apic_id: 0x00 done.
POST: 0x9b
CPU #0 initialized
POST: 0x75
POST: 0x75
POST: 0x75
PCI: 00:00.0 init ...
Assigning IRQ 10 to 0:1.3
Assigning IRQ 11 to 0:3.0
POST: 0x75
PCI: 00:01.0 init ...
RTC Init
POST: 0x75
PCI: 00:01.1 init ...
IDE: Primary IDE interface: on
IDE: Secondary IDE interface: on
IDE: Access to legacy IDE ports: off
POST: 0x75
POST: 0x75
PCI: 00:02.0 init ...
POST: 0x75
PCI: 00:03.0 init ...
Devices initialized
Show all devs... After init.
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
PCI: 00:02.0: enabled 1
PCI: 00:03.0: enabled 1

李迟 2016.4.4 周一 清明节