首页 > 代码库 > 基于tiny210的barebox分析(二)

基于tiny210的barebox分析(二)

代码分析

在上一篇文章中,我们已经对barebox的编译、烧写和运行有了一个大致的了解,

现在我们就要开始学习代码了。

arch/arm/cpu/start.c line126

void __naked __section(.text_entry) start(void)
{
	barebox_arm_head();
}
一般的bootloader都会以一个汇编文件作为起始,但是barebox没有这样。

这个c函数作为了整个iamge的入口,关键是__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
{
 . = 0x23e00000;

 . = ALIGN(4);
 .text :
 {
  _stext = .;
  _text = .;
  *(.text_entry*)
  __bare_init_start = .;
  *(.text_bare_init*)
  __bare_init_end = .;
  __exceptions_start = .;
  KEEP(*(.text_exceptions*))
  __exceptions_stop = .;
  *(.text*)
 }
*(.text_entry*)就相当于把start函数关联进了lds文件,然后ENTRY(start)又指定整个image的入口,于是start函数就成了整个image的入口。

而0x23e00000则是dram中的地址,后面barebox会把自己拷贝到这个地址并跳转过去。

我们继续来看代码,

arch/arm/include/asm/barebox-arm-head.h line54

static inline void barebox_arm_head(void)
{
	__barebox_arm_head();
	__asm__ __volatile__ (
		"b barebox_arm_reset_vector\n"
	);
}


arch/arm/include/asm/barebox-arm-head.h line20

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"
	);
}
这两个函数都是inline函数,所以代码都会被内嵌到start函数中去。

于是c函数start其实就变成了一个汇编函数。

我们可以看到,在汇编代码的最前端,连续的8个跳转,其实是模仿arm的8个异常向量。


但在这里其实并没有什么作用,只是简单的做了下跳转到2,并在中间留了一些空间保存了一些数据。

先是一个8个字节大小的字符串barebox,然后是两个4字节变量,一个保存的是barebox的链接地址,还有个是barebox的大小,最后则是连续8个0x55555555。

从我们之前dump的barebox.bin可以看出,这和我们想的完全一致。

0000000 0012 ea00 fffe eaff fffe eaff fffe eaff
0000010 fffe eaff fffe eaff fffe eaff fffe eaff
0000020 6162 6572 6f62 0078 0000 23e0 3fe8 0001
0000030 5555 5555 5555 5555 5555 5555 5555 5555
*
0000050 0013 ea00 e000 e04e f00e e1a0 c03c e59f
这里链接地址是0x23e00000,大小是0x13fe8。

接下来,程序就会调用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();
#endif

	debug_led(0, 1);

	if (get_pc() < IRAM_CODE_BASE) /* Are we running from iRAM? */
		/* No, we don't. */
		goto boot;

	s5p_init_dram_bank_ddr2(S5P_DMC0_BASE, 0x20E00323, 0, 0);

	debug_led(1, 1);

	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);

	jump_sdram(IRAM_CODE_BASE - ld_var(_text));

	debug_led(1, 0);

boot:
	barebox_arm_entry(S3C_SDRAM_BASE, SZ_256M, 0);
}
arm_cpu_lowlevel_init只是预留了一个接口,暂时并没有实现什么功能。

所以第一步做的事情是配置pll。

然后判断当前代码的运行位置。

#define IRAM_CODE_BASE		0xD0020010


假设程序是通过rom code加载到sram运行的话,其pc值必定是从0xD0020000起,外加16个字节的校验,所以就是0xD0020010了。

如果已经是在dram中的话,那就没有必要再初始化dram以及把barebox加载到dram了。

当然,在我们这种正常启动情况下,代码会顺序往下走,进行dram的初始化配置。

dram配置完毕后,就把整个barebox从sd卡读取到dram中去。

static __bare_init bool load_stage2(void *dest, size_t size)
{
	/* TODO add other ways to boot */
	return s5p_irom_load_mmc(dest, 1, (size+ 511) / 512);
}

#define ADDR_V210_SDMMC_BASE	0xD0037488
#define ADDR_CopySDMMCtoMem	0xD0037F98

int __bare_init s5p_irom_load_mmc(void *dest, uint32_t start_block, uint16_t block_count)
{
	typedef uint32_t (*func_t) (int32_t, uint32_t, uint16_t, uint32_t*, int8_t);
	uint32_t chbase = readl(ADDR_V210_SDMMC_BASE);
	func_t func = (func_t)readl(ADDR_CopySDMMCtoMem);
	int chan = (chbase - 0xEB000000) >> 20;
	if (chan != 0 && chan != 2)
		return 0;
	return func(chan, start_block, block_count, (uint32_t*)dest, 0) ? 1 : 0;
}
这里又要用到s5pv210的一个特殊功能,它的rom code提供了从sd卡读取数据的函数


所以只要直接调用这个地址就可以实现barebox的加载了。

最后一步就是通过jump_sdram跳转到dram中去,这之后运行的barebox_arm_entry函数,就是运行在dram中的了。

static __bare_init __naked void jump_sdram(unsigned long offset)
{
	__asm__ __volatile__ (
			"sub lr, lr, %0;"
			"mov pc, lr;" : : "r"(offset)
			);
}

这里还有一点需要注意,我们可以发现,在跳转到dram之前运行的函数都是有__bare_init的

#define __bare_init          __section(.text_bare_init.text)
在lds文件中是紧随*(.text_entry*)之后的。

估计是因为barebox的链接地址是dram中的地址,所以刚开始在sram中运行时被调用的函数都需要靠近以保证跳转时使用的是相对偏移而不是绝对地址。

有尝试过去掉某一函数的__bare_init修饰符,会直接导致barebox无法正常运行。