Boot Loader 的 stage2 通常包括以下步骤(以执行的先后顺序):
- 初始化本阶段要使用到的硬件设备。
- 检测系统内存映射(memory map)。
- 将 kernel 映像和根文件系统映像从 flash 上读到 RAM 空间中。
- 为内核设置启动参数。
- 调用内核。
根据此通用步骤来编写我们的BootLoader引导程序。
1、初始化本阶段需要用到硬件设备,在E-boot中需要用到串口,同时根据硬件初始化GPIO设置。
- uart0_init(); //串口初始化
- printf("\n\rGPIO initialize ...\n\r");//然后即可使用以前编写的printf()函数来进行串口调试
-
- Port_Init(); //通用GPIO接口设置
- void Port_Init(void)
- {
- GPACON = 0x7fffff;
- GPBCON = 0x044555;
- GPBUP = 0x7ff; // The pull up function is disabled GPB[10:0]
- GPCCON = 0xaaaaaaaa;
- GPCUP = 0xffff; // The pull up function is disabled GPC[15:0]
- GPDCON = 0x00151544;
- GPDDAT = 0x0430;
- GPDUP = 0x877A;
- GPECON = 0xaa2aaaaa;
- GPEUP = 0xf7ff; // GPE11 is NC
- GPFCON = 0x55aa;
- GPFUP = 0xff; // The pull up function is disabled GPF[7:0]
- GPGCON = 1<<8;
- GPGDAT = 0;
- GPHCON = 0x16faaa;
- GPHUP = 0x7ff; // The pull up function is disabled GPH[10:0]
- EXTINT0 = 0x22222222; // EINT[7:0]
- EXTINT1 = 0x22222222; // EINT[15:8]
- EXTINT2 = 0x22222222; // EINT[23:16]
- }
2、MMU设置,据网上所说,MMU和Dcache必须关闭, Dcache 随意,在以下关闭了MMU,
但是Dcahe打开也能成功引导Linux系统。
- MMU_EnableICache();
- MMU_EnableDCache();
- static inline void MMU_EnableICache(void)
- {
- asm (
- "mrc p15,0,r0,c1,c0,0\n"
- "orr r0,r0,#(1<<12)\n"
- "mcr p15,0,r0,c1,c0,0\n"
- );
- }
- static inline void MMU_EnableDCache(void)
- {
- asm (
- "mrc p15,0,r0,c1,c0,0\n"
- "orr r0,r0,#(1<<2)\n"
- "mcr p15,0,r0,c1,c0,0\n"
- );
- }
3、设置Linux系统内核启动参数,此处主要需参考Linux内核中的arch/arm/include/asm/setup.h和
arch/arm/kernel/setup.c这两个文件来设置。其中用到的结构体在setup.h中均有定义。此处只设置
其最基本的参数。page_size和nr_page。参数保存在0x30000100这个位置。
- printf("\n\rset parameters ...\n\r");
- SetParameters();
- static inline void SetParameters(void)
- {
- struct param_struct *p = (struct param_struct *)0x30000100;
- memset(p, 0, sizeof(*p));
- memcpy(p->commandline, LINUX_CMD_LINE, sizeof(LINUX_CMD_LINE));
- p->u1.s.page_size = 4 * 1024;//MMU中用段式页表,一级页表中有4096个描述符表示4G空间
- p->u1.s.nr_pages = 64 * 1024 * 1024 / (4 * 1024);//共64M内存
-
- }
4、从Nand Flash的内核存放区拷贝Linux镜像到SDRAM中。
- LoadImageFromNand();
printf("\n\rload Linux image...\n\r");
- LoadImageFromNand()
- {
- NandInit(); //Nand Flash 初始化
- unsigned int Length;
- U8 *RAM;
- unsigned BlockNum;
- unsigned pos;
- Length = 0x500000; //Nand Flash 中内核存放区的总大小
- Length = (Length + BLOCK_SIZE - 1) >> (BYTE_SECTOR_SHIFT + SECTOR_BLOCK_SHIFT) << (BYTE_SECTOR_SHIFT + SECTOR_BLOCK_SHIFT); // align to Block Size
-
- /** Nand Flash 中内核存放区的开始位置0X60000 **/
- BlockNum = 0x60000 >> (BYTE_SECTOR_SHIFT + SECTOR_BLOCK_SHIFT);
- RAM = (U8 *) 0x30008000; //拷贝到内存中的位置
- for (pos = 0; pos < Length; pos += BLOCK_SIZE) {
- unsigned int i;
- // skip badblock
- for (;;) {
- if (NandIsGoodBlock
- (BlockNum <<
- (BYTE_SECTOR_SHIFT + SECTOR_BLOCK_SHIFT))) {
- break;
- }
- BlockNum++; //try next
- }
- for (i = 0; i < BLOCK_SIZE; i += SECTOR_SIZE) {
- int ret =
- NandReadOneSector(RAM,
- (BlockNum <<
- (BYTE_SECTOR_SHIFT +
- SECTOR_BLOCK_SHIFT)) + i);
- RAM += SECTOR_SIZE;
- ret = 0;
- }
- BlockNum++;
- }
- }
5、跳转到内核运行。r0必须设为0,r1为机器ID码,r2为内核引导参数存放地址
- theKernel(0,1999,0x30008000);
- //内核跳转程序
- void (*theKernel)(int zero, int arch, U32 params_addr) = (void (*)(int, int, U32))0x30008000;
实验发现,第三个参数传递进去的TAG首地址似乎没有起到作用,因为启动时总是找不到正确的启动参数。后来发现内核有个默认的TAG首地址0x30000100,它总是到0x30000100去寻找启动参数,而不理会我们传进来的第三个参数。所以在 SetParameters()中我们将参数放在0x30000100处。
6、运行结果可以看到“,Uncompressing Linux........................................ done, booting the kernel.”即可。
以下为能直接运行的二进制文件,下载到Nand Flash Black0 直接以Nand Flash运行。
阅读(1520) | 评论(0) | 转发(0) |