romstage是coreboot的第二个执行阶段。本文分别介绍基于qemu模拟环境的x86的跟踪,以及基于Intel baytrail平台的跟踪。 在romstage阶段,由于内存还未初始化好,所以使用cache作为内存,此项技术称为“cache as ram”,简称为“CAR”。网络上有较多文章涉及此方面,可以查阅以了解更多。
一、qemu-i440fx cache_as_ram文件:src\mainboard\emulation\qemu-i440fx\cache_as_ram.inc
1、保存BIST BIST值会作为romstage主函数的参数。
1 2 /* Save the BIST result. */ movl %eax, %ebp
2、设置CAR (注:这段代码还不是太理解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 cache_as_ram: post_code(0x20) /* Clear the cache memory region. This will also fill up the cache */ movl $CACHE_AS_RAM_BASE, %esi movl %esi, %edi movl $(CACHE_AS_RAM_SIZE >> 2), %ecx // movl $0x23322332, %eax xorl %eax, %eax /* 使用 EAX 填写位于 ESI:EDI 的 ECX 个双字 */ /* 将CACHE_AS_RAM_BASE地址的大小为CACHE_AS_RAM_SIZE区域初始化为0 */ rep stosl post_code(0x21) /* Set up the stack pointer. */ movl $(CACHE_AS_RAM_SIZE + CACHE_AS_RAM_BASE - 4), %eax movl %eax, %esp
3、恢复BIST值 1 2 3 4 5 6 /* Restore the BIST result. */ movl %ebp, %eax movl %esp, %ebp /* eax为BIST值,将其压栈,作为main的参数,然后调用main函数 */ pushl %eax /*测试 pushl $0xdeadbeaf*/
4、调用romstage主函数 上段代码恢复BIST值到eax寄存器后,即将eax压入栈中,接着调用main函数,这个函数在romstage.c文件中定义。
1 2 3 4 5 before_romstage: post_code(0x29) /* 跳转到romstage的主函数 */ /* Call romstage.c main function. */ call main
5、跳转到ramstage阶段 在main函数返回后,调用copy_and_run函数,之后就到了ramstage阶段了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /* 调用完main后,就到了copy_and_run,即ramstage阶段了 */ __main: post_code(POST_PREPARE_RAMSTAGE) cld /* Clear direction flag. */ movl $CONFIG_RAMTOP, %esp movl %esp, %ebp /* 此处为调试用 movl $9, %eax pushl %eax */ /* 调用copy_and_run */ call copy_and_run .Lhlt: post_code(POST_DEAD_CODE) hlt jmp .Lhlt
至此,分析结束。
下面看看romstage的主函数。main函数 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // bist为blockboot传递的参数,intel在启动时会进行自检,正常情况下bist为0 // 在cache_as_ram.inc中会调用到此处的main void main(unsigned long bist) { int cbmem_was_initted; /* init_timer(); */ post_code(0x05); console_init(); ll_printk("qemu-i440fx romstage --BIST: 0x%x\n", (unsigned int)bist); /* Halt if there was a built in self test failure */ report_bist_failure(bist); //print_pci_devices(); //dump_pci_devices(); cbmem_was_initted = !cbmem_recovery(0); timestamp_init(timestamp_get()); timestamp_add_now(TS_START_ROMSTAGE); }
很简单,调用console_init函数初始化终端,在该函数中会打印coreboot的版本号以及编译时间。如果传入的参数的bist值不为0,则出错,直接挂机。
注:ll_printk为代码使用的打印函数,本文中使用bist值(即cache_as_ram.inc的eax寄存器)以跟踪执行流程。 流程图示如下:
二、baytrail-fsp (李迟按:留空待写)
注: 由于coreboot方面资料较少,笔者第一次尝试分析代码,还有众多未能参透的地方,难免出错。任何问题,欢迎一起交流学习。
李迟 2016.3.15 周二 夜