活到老,学到老!
分类: 嵌入式
2014-08-20 22:52:44
一般的bootloader都会以一个汇编文件作为起始,但是barebox没有
这个c函数作为了整个image的入口,关键是__section(.text_entry)和lds文件起了作用。
lds文件是arch/arm/lib/barebox.lds.S,它会被编译成barebox.lds并最终参与链接。
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(start)
SECTIONS
{
#ifdef CONFIG_RELOCATABLE
. = 0x0;
#else
. = TEXT_BASE;
#endif
. = ALIGN(4);
.text :
{
_stext = .;
_text = .;
*(.text_entry*)
__bare_init_start = .;
*(.text_bare_init*)
__bare_init_end = .;
__exceptions_start = .;
KEEP(*(.text_exceptions*))
__exceptions_stop = .;
*(.text*)
}
BAREBOX_BARE_INIT_SIZE
*(.text_entry*)就相当于把start函数关联进了lds文件,然后ENTRY(start)又指定整个image的入口,于是start函数就成了整个image的入口。
而TEXT_BASE则是dram中的地址,后面barebox会把自己拷贝到这个地址并跳转过去。
//第一条语句调用内联函数barebox_arm_head ()
1. arch/arm/include/asm/barebox-arm-head.h
static inline void __barebox_arm_head(void)
{
__asm__ __volatile__ (
#ifdef CONFIG_THUMB2_BAREBOX
".arm\n"
"adr r9, 1f + 1\n"
"bx r9\n"
".thumb\n"
"1:\n"
"bl 2f\n"
".rept 10\n"
"1: b 1b\n"
".endr\n"
#else
"b 2f\n"
"1: b 1b\n"
"1: b 1b\n"
"1: b 1b\n"
"1: b 1b\n"
"1: b 1b\n"
"1: b 1b\n"
"1: b 1b\n"
#endif
".asciz \"barebox\"\n"
".word _text\n" /* text base. If copied there,
* barebox can skip relocation
*/
".word _barebox_image_size\n" /* image size to copy */
".rept 8\n"
".word 0x55555555\n"
".endr\n"
"2:\n"
);
}
static inline void barebox_arm_head(void)
{
__barebox_arm_head();
__asm__ __volatile__ (
"b barebox_arm_reset_vector\n"
);
}
这两个函数都是inline函数,所以代码都会被内嵌到start函数中去。
于是c函数start其实就变成了一个汇编函数。
我们可以看到,在汇编代码的最前端,连续的8个跳转,其实是模仿arm的8个异常向量。
这里其实并没有什么作用,只是简单的做了下跳转到2。
用UltraEdit打开barebox.bin,即看到先是一个8个字节大小的字符串barebox,然后是两个4字节变量,一个保存的是barebox的链接地址0x23e00000(TEXT_BASE),还有个是barebox的大小,最后则是连续8个0x55555555。
接下来,程序就会调用arch/arm/boards/friendlyarm-tiny210/lowlevel.c中的barebox_arm_reset_vector函数。
void __bare_init barebox_arm_reset_vector(void)
{
arm_cpu_lowlevel_init();
#ifdef CONFIG_S3C_PLL_INIT
s5p_init_pll(); //初始化PLL
#endif
debug_led(0, 1); //点亮LED1
if (get_pc() < IRAM_CODE_BASE) /* Are we running from iRAM? */
/* No, we don't. */ //从SD卡启动时,PC= IRAM_CODE_BASE
goto boot;
s5p_init_dram_bank_ddr2(S5P_DMC0_BASE, 0x20E00323, 0, 0); //初始化DDR
debug_led(1, 1); //点亮LED2
//将SD卡中的barebox装载到TEXT_BASE位置,+/-16是因为加了16字节的校验头。
/*此处用到s5pv210的一个特殊功能,它的rom code提供了从sd卡读取数据的函数,所以只要直接调用这个地址就可以实现barebox的加载了*/
if (! load_stage2((void*)(ld_var(_text) - 16),
ld_var(_barebox_image_size) + 16)) {
debug_led(3, 1);
while (1) { } /* hang */
}
debug_led(2, 1); //点亮LED3
jump_sdram(IRAM_CODE_BASE - ld_var(_text)); //跳转offset到TEXT_BASE执行
debug_led(1, 0);
boot:
barebox_arm_entry(S3C_SDRAM_BASE, SZ_256M, 0);
}
arm_cpu_lowlevel_init只是预留了一个接口,暂时并没有实现什么功能。
所以第一步做的事情是配置pll。然后判断当前代码的运行位置。
假设程序是通过rom code加载到sram运行的话,其pc值必定是从0xD0020000起,外加16个字节的校验,所以就是0xD0020010了。
如果已经是在dram中的话,那就没有必要再初始化dram以及把barebox加载到dram了。
当然,在我们这种正常启动情况下,代码会顺序往下走,进行dram的初始化配置。
dram配置完毕后,就把整个barebox从sd卡读取到dram中去。
最后一步就是通过jump_sdram跳转到dram中去,这之后运行的barebox_arm_entry函数,就是运行在dram中的了。
这里还有一点需要注意,我们可以发现,在跳转到dram之前运行的函数都是有__bare_init的
#define __bare_init __section(.text_bare_init.text)
在lds文件中是紧随*(.text_entry*)之后的。
估计是因为barebox的链接地址是dram中的地址,所以刚开始在sram中运行时被调用的函数都需要靠近以保证跳转时使用的是相对偏移而不是绝对地址。
arm_setup_stack() 设置栈SP指针,指向0x20000000+0x10000000-16
(S3C_SDRAM_BASE+SZ_256M -16)
接着跳转到__start,还在arch/arm/cpu/start.c中
最后,执行start_barebox()
调用经典的initcall(),所有的设备、驱动、文件系统都在这里完成,类似linux。
读取环境变量
最后执行死循环run_shell()
打开barebox的DEBUG选项
Make menuconfig->Debugging,都改为7即可
配合barebox.map分析打印信息
pure_initcall(globalvar_init); // register_device
pure_initcall(platform_init); // bus_register -> register_device
pure_initcall(spi_bus_init); // bus_register -> register_device
pure_initcall(fs_bus_init); // bus_register -> register_device
core_initcall(s3c_clk_src_init); //Timer4(需要理解)
postcore_initcall(hist_init); //
postcore_initcall(init_fs); //
console_platform_driver(s3c_serial_driver); //
console_initcall(tiny210_console_init); //将s3c_serial作为控制台,并分配缓冲区
0xe2900000:0xe29003ff
//这中间有个cs0 没有弄明白
mem_initcall(tiny210_mem_init); //注册mem0,空间为0x20000000:0x3fffffff
coredevice_initcall(mem_malloc_resource); //划分空间如下
bss 0x23e16910:0x23e19d87
barebox data 0x23e1591a:0x23e1690f
barebox 0x23e00000:0x23e15919
malloc space 0x23a00000:0x23dfffff
coredevice_initcall(bootsource_init); //boot,引导启动相关
coredevice_initcall(reset_source_init); //复位源初始化
coredevice_initcall(ramfs_init); //ramfs
coredevice_initcall(devfs_init); //devfs
coredevice_initcall(arm_request_stack); //分配stack,0x2fff8000:0x2fffffff
fs_initcall(mount_root); //挂载ramfs-> / devfs->/dev
fs_initcall(binfmt_sh_init); //shell脚本解释器
fs_initcall(binfmt_uimage_init); //通过shell调用bootm语法解释
device_initcall(loglevel_init); //调试信息等级
device_initcall(null_init); //创建一个null的字符设备,null_write
device_initcall(full_init); //创建一个full的字符设备,full_read
device_initcall(zero_init); //创建一个zero的字符设备,zero_read
device_initcall(mem_init); // Memory Functions
device_initcall(tiny210_devices_init); //板文件,平台初始化(GPIO。。。)
device_initcall(s3c_detect_reset_source); //检测复位源
late_initcall(console_global_init); //控制台相关(允许交互)
late_initcall(bootm_init); //初始化bootm需要传递的参数(image,initrd)
late_initcall(init_command_list); //初始化命令列表
late_initcall(display_meminfo); //打印内存信息
barebox code: 0x23e00000 -> 0x23e15919
bss segment: 0x23e16910 -> 0x23e19d87
malloc space: 0x23a00000 -> 0x23dfffff (size 4 MiB)
late_initcall(trigger_init); //trigger led
late_initcall(s5pcxx_dump_clocks); //时钟
late_initcall(armlinux_register_image_handler); // register_image_handler,根据不同的image类型,bootm指令调用不同的函数
Done