自己写BootLoader

来源:互联网 发布:原材料价格查询软件 编辑:程序博客网 时间:2024/06/05 06:01

关于BootLoader是什么、有什么用、启动流程这些内容百度百科讲的很详细查阅即可:bootloader百度百科

自己写bootloader能使我们更好的的理解bootloader。

bootloader得最终目的是什么?肯定是引导内核。围绕引导内核,在引导内核之前肯定需要一些硬件设备的初始化,这样才能正确的读取到内核并加载到RAM中。

不同的目标机有有着不同的硬件结构。所以不同的目标机所需要初始化的内容也不是完全相同的。本文针对本人所使用的jz2440开发板。

在jz2440上有两种启动方式,norflash启动和nandflash启动。在这里针对nandflash启动。

针对jz2440开发板,写最简单的bootloader步骤:

1、初始化硬件:关闭看门狗、设置时钟、初始化SDRAM、初始化NAND FLASH

2、把内核从NAND FLASH读到SDRAM

3、设置“要传给内核的参数”

4、跳转执行内核

s3c2440在NAND FLASH启动过程中,是将NAND FLASH前4K的内容拷贝到地址0x0000 0000启动的,如果我们的bootloader代码大于4K则需要将代码从nandflash读到SDRAM中去,然后跳转执行。为了便于扩展,还是需要将代码重定位。

注意:在代码重定位之前,我们使用的代码需要是位置无关码。

第一阶段代码:

#define MPLL_200MHZ ((0X5C<<12)|(0X01<<4)|(0X02))#define MEM_CTL_BASE 0x48000000#define GPFCON 0x56000050#define GPFDAT 0x56000054.text.global _start_start:/*1、关闭看门狗*/ldr r0, =0x53000000mov r1, #0str r1, [r0]/*2、初始化时钟*/ldr r0, =0x4C000014mov r1, #0x03//HDIVN = 1, PDIVN = 1; FCLK:HCLK:PCLK = 1:2:4str r1, [r0]/*If HDIVN is not 0, the CPU bus mode has to be changed from the fast bus mode to the asynchronous*bus mode using following instructions(S3C2440 does not support synchronous bus mode).*/mrc p15,0,r0,c1,c0,0orr r0,r0,#0xc0000000mcr p15,0,r0,c1,c0,0ldr r0, =0x4C000004ldr r1, =MPLL_200MHZstr r1, [r0]/*3、初始化SDRAM*/ldr r0, =MEM_CTL_BASEadr r1, sdram_config   //不能用ldr需要使用相对地址,不能使用绝对地址add r3, r0, #(13*4)1:ldr r2, [r1], #4str r2, [r0], #4cmp r0, r3bne 1b/*4、重定位*/ldr sp, =0x34000000bl nand_initmov r0, #0ldr r1, =_startldr r2, =__bss_startsub r2, r2, r1bl copy_code_to_sdrambl clear_bss#if 0/*******************************/ldr r0, =GPFCONmov r1, #0x500str r1, [r0]ldr r0, =GPFDATmov r1, #0str r1, [r0]/*******************************/#endif/*5、执行main,进入第二阶段*/ldr lr, =haltldr pc, =mainhalt:b haltsdram_config:.long 0x22011110 //BWSCON.long 0x00000700 //BANKCON0.long 0x00000700 //BANKCON1.long 0x00000700 //BANKCON2.long 0x00000700 //BANKCON3  .long 0x00000700 //BANKCON4.long 0x00000700 //BANKCON5.long 0x00018005 //BANKCON6.long 0x00018005 //BANKCON7.long 0x008C04F4 // REFRESH.long 0x000000B1 //BANKSIZE.long 0x00000030 //MRSRB6.long 0x00000030 //MRSRB7

第二阶段代码:

int main(void){void (*theKernel)(int zero, int arch, unsigned int params);/* *初始化串口 *内核刚开始并没有初始化串口,所以要帮串口初始化串口 */s3c2440_uart_init();uart_puts("init uart succese\n\r");GPFCON = 0x500;GPFDAT = 0;/*从NAND FLASH读取内核到SDRAM*/uart_puts("Copy kernel from nand flash\n\r");nand_read(0x60000+64,(unsigned char *)0x30008000,0x200000);/*为内核设置参数*/uart_puts("Set kernel params\n\r");setup_start_tag();setup_memory_tags();setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");setup_end_tag();/*跳转执行内核*/uart_puts("Boot kernel\n\r");theKernel = (void (*)(int, int, unsigned int))0x30008000;theKernel(0, 362, 0x30000100); uart_puts("Error!\n\r");return -1;}

下面细说每一步:

1、初始化硬件设备:关闭看门狗:s3c2440的看门狗默认是打开的,如果不关闭运行一段时间将自动重启,所以需要关闭看门狗。

初始化时钟:外接12M晶振,为了让系统跑的更快,让CPU充分发挥性能,使用MPLL倍频。S3C2440最大支持系统时钟400MHZ。

初始化SDRAM:我们需要将代码拷贝到SDRAM运行,所以需要初始化SDRAM。

2、重定位:初始化NAND FLASH:如果我们是NAND FLASH启动,我们需要将代码从NAND FLASH拷贝到指定地址,所以需要初始化NAND FLASH。

拷贝代码到指定地址(链接地址):保证代码在链接地址上运行,这样就可以使用位置相关的代码了,可以使用编译时的生成符号表。进入第二阶段完成更复杂的功能。

3、初始化串口:刚加载内核时,内核并没有对串口进行初始化,但它又需要使用串口打印一些系统信息,所以我们需要“帮它”初始化串口。

4、拷贝内核镜像到SDRAM:内核需要在SDRAM上运行。

5、设置内核需要的参数:怎么将参数传递给内核呢?约定一个地址,约定一个方式(tag)。这样内核就知道参数在哪里,知道怎么去识别参数。

6、跳转到内核执行:启动内核。


我们可以对这个bootloader进行一些改进:

1、提高系统时钟,S3C2440最大支持400MHZ的FCLK。我们可以系统时钟,将系统时钟提高到400MHZ,修改时需要注意HCLK的最大频率和PLCK的最大频率限制,适当更改分频系数。

/*2、初始化时钟*/ldr r0, =0x4C000014mov r1, #0x05//HDIVN = 4, PDIVN = 1; FCLK:HCLK:PCLK = 1:4:8str r1, [r0]/*If HDIVN is not 0, the CPU bus mode has to be changed from the fast bus mode to the asynchronous*bus mode using following instructions(S3C2440 does not support synchronous bus mode).*/mrc p15,0,r0,c1,c0,0orr r0,r0,#0xc0000000mcr p15,0,r0,c1,c0,0ldr r0, =0x4C000004ldr r1, =MPLL_400MHZstr r1, [r0]

2、启动CACHE:只能启动ICACHE,启动DCACHE需要使能MMU。启动ICACHE后将大大提升速度。

/*启动ICACHE*/mrc p15,0,r0,c1,c0,0@从协处理器读取一个寄存器到r0orr r0,r0,#(1<<12)mcr p15,0,r0,c1,c0,0@将r0的值写入协处理器


0 0
原创粉丝点击