Bootloader的作用与实现

一、为什么要用Bootloader?

1、对于不同的CPU体系结构都有不同的Bootloader,例如ARM、MIPS都有自己的Bootloader。除了依赖于CPU的体系结构外,Bootloader还依赖于具体的嵌入式板级设备的配置,比如ARM架构CPU会有不同的板卡,而板卡的硬件地址分配,外设芯片类型等又大不相同。因此针对不同的板卡,尽管他们的CPU架构一样,但是还是需要针对硬件资源的配置去写不同的Bootloader。

2、看到这里,有人不禁会问,竟然Bootloader这么复杂,它是不是必要的?答案当然不是了,因为完全可以上电后直接运行操作系统,当然这个操作系统的开头必须要包含上述bootloader的功能。

3、如果不用Bootloader会有存在一些问题,假设你将产品已经卖到世界各地去了,但是有一天你发现产品程序存在一些问题,在解决这个问题之后想要升级软件就需要用下载器去重新下载程序,你不可能拿着一堆东西(电脑、下载器等)去找人家升级程序吧?但是有Bootloader之后,我们只需要将升级后的操作系统放到“硬盘”或Nand Flash中的某个位置,然后断电重启一下,bootloader就能在引导系统的同时,完成了对操作系统的升级。

二、Bootloader主要做什么事?

1、Bootloader就是系统上电之后运行的一段小程序,通过该程序能够实现关闭看门狗、配置时钟、初始化存储器等硬件设备,准备好软件环境,让软硬件环境达到一个最佳状态,最后启动操作系统内核。

2、大多数 Bootloader 都分为 stage1stage2 两大部分。

(1)、对于 stage1 ,它通常都用汇编语言来实现,以达到短小精悍的目的,它的主要包括:

  • 硬件设备初始化
  • 为加载Bootlodader的stage2准备RAM空间
  • 拷贝Bootloader的stage2到RAM空间
  • 设置好堆栈段为stager2的C语言环境做准备
  • 跳转到 stage2 的 C 入口点。

(2)、对于stage2, 则通常用C语言来实现,这样可以实现比较复杂的功能,且代码会有更好的可读性和可移植性,它主要包括:

  • 初始化本阶段要使用到的硬件设备。
  • 检测系统内存映射(memory map)。
  • 将 内核映像和根文件系统映像从 flash 上读到 RAM 空间中。
  • 为内核设置启动参数。
  • 调用内核

三、基于ARM从0写一个Bootloader

这里使用JZ2440开发板,SOC为s3c2440,外部有64MSDRAM,一个Nand Flash、一个Nor Flash。

1、stage1
(1)、关闭看门狗
ldr r0, =0x53000000	/* 0x53000000是看门狗寄存器地址 */
ldr r1, =0
str r1, [r0]
(2)、配置时钟
	/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
	/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
	ldr r0, =0x4C000000
	ldr r1, =0xFFFFFFFF
	str r1, [r0]

	/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */
	ldr r0, =0x4C000014
	ldr r1, =0x5
	str r1, [r0]

	/* 设置CPU工作于异步模式 */
	mrc p15,0,r0,c1,c0,0
	orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA
	mcr p15,0,r0,c1,c0,0

	/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) 
	 *  m = MDIV+8 = 92+8=100
	 *  p = PDIV+2 = 1+2 = 3
	 *  s = SDIV = 1
	 *  FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
	 */
	ldr r0, =0x4C000004
	ldr r1, =(0x5c<<12)|(0x01<<4)|(0x01)	/* 400MHz */
	str r1, [r0]

	/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
	 * 然后CPU工作于新的频率FCLK
	 */
(3)、初始化SDRAM
bl sdram_init	/* 跳转执行,用C语言实现 */
(4)、代码重定位
ldr sp, =0x34000000	/* 设置栈 */

bl nand_init	/* 初始化Nand Flash */

bl copy2sdram	/* 重定位text, rodata, data段整个程序 */

bl clean_bss	/* 清除BSS段 */

(5)、跳转执行main
ldr lr, =halt
ldr pc, =main

halt:
	b halt
2、stage2
int main(void)
{
	void (*theKernel)(int zero, int arch, unsigned int params);
	volatile unsigned int *p = (volatile unsigned int *)0x30008000;

	/* 0. 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口 */
	uart0_init();
	
	/* 1. 从NAND FLASH里把内核读入内存 */
	puts("Copy kernel from nand\n\r");
	nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);
	printHex(0x1234ABCD);
	puts("\n\r");
	printHex(*p);
	puts("\n\r");

	/* 2. 设置参数 */
	puts("Set boot params\n\r");
	setup_start_tag();
	setup_memory_tags();
	setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");
	setup_end_tag();

	/* 3. 跳转执行 */
	puts("Boot kernel\n\r");
	theKernel = (void (*)(int, int, unsigned int))0x30008000;
	theKernel(0, 362, 0x30000100);  

	puts("Error!\n\r");
	/* 如果一切正常, 不会执行到这里 */
}

3、启动内核成功

启动内核成功

附录代码
1、sdram初始化
/**************************************************************
函数名称:sdram_init
函数功能:sdram初始化
输入参数:无
返 回 值:无
备     注:无
**************************************************************/
void sdram_init(void)
{
	/* 根据SDRAM和s3c2440芯片手册配置各寄存器值 */
	BWSCON = 0x22000000;	/* 总线宽度和等待控制寄存器 */
	BANKCON6 = 0x18001;		/* BANK控制寄存器 */
	REFRESH  = 0x8404f5;	/* 刷新控制寄存器 */
	BANKSIZE = 0xb1;		/* BANKSIZE寄存器 */
	MRSRB6   = 0x20;		/* SDRAM模式寄存器设置寄存器 */
}
2、设置参数
static struct tag *params;

void setup_start_tag(void)
{
	params = (struct tag *)0x30000100;

	params->hdr.tag = ATAG_CORE;
	params->hdr.size = tag_size (tag_core);

	params->u.core.flags = 0;
	params->u.core.pagesize = 0;
	params->u.core.rootdev = 0;

	params = tag_next (params);
}

void setup_memory_tags(void)
{
	params->hdr.tag = ATAG_MEM;
	params->hdr.size = tag_size (tag_mem32);
	
	params->u.mem.start = 0x30000000;
	params->u.mem.size  = 64*1024*1024;
	
	params = tag_next (params);
}

int strlen(char *str)
{
	int i = 0;
	while (str[i])
	{
		i++;
	}
	return i;
}

void strcpy(char *dest, char *src)
{
	while ((*dest++ = *src++) != '\0');
}

void setup_commandline_tag(char *cmdline)
{
	int len = strlen(cmdline) + 1;
	
	params->hdr.tag  = ATAG_CMDLINE;
	params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2;

	strcpy (params->u.cmdline.cmdline, cmdline);

	params = tag_next (params);
}

void setup_end_tag(void)
{
	params->hdr.tag = ATAG_NONE;
	params->hdr.size = 0;
}

参考文献

韦东山:《嵌入式系统 Boot Loader 技术内幕》
猪哥-嵌入式:为什么需要bootloader

  • 6
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值