自制简易BootLoader思路分析及实现
2017-08-31 16:53
465 查看
1,BootLoader功能分析:BootLoader最终要达到的目标是把Linux内核引导起来(相当于用户空间的Shell程序启动用户进程,但是Shell不仅仅具有启动用户进程的作用)
为了实现这个最终的目标,BootLoader具有以下必须的功能(以U-Boot为例);
Step-1:
1),设置CPU;
2),关闭Watchdog;
3),设置Clock;
4),屏蔽所有的Interrupt;
5),初始化SDRAM;
6),初始化nandflash(如果u-boot代码在nandflash上);
7);重定位BootLoader的代码到SDRAM;
8);设置栈(C语言函数调用需要先设置栈,栈要在SDRAM初始化之后设置);
9),清BSS段;
10),进入BootLoader的Step-2阶段启动内核;
Step-2:
11),设置BootLoader要传递给内核的参数;
12),从Nor 或 Nand flash 上复制内核代码到SDRAM的0x30007FC0的地方(这个地址不是固定的,可以任意指定,只要不破坏u-boot使用的内存空间,u-boot会根据uImage的Header中的Load Addr判断内核是否装载到Load Addr的地址处,Load Addr在u-boot中默认为0x30007FC0,如果不在,u-boot会移动内核代码到改地址处);
13),跳转到uImage的Header中Entry Point指定的入口地址处开始执行内核代码(内核执行后,u-boot不在执行);
2,自制BootLoader思路:
实现上述必须功能;
以S3C2440平台为例;
硬件参数查S3C2440 Datasheet;
内存布局以及内核参数传递参考u-boot.1.1.6版本代码;
总体功能实现(start.S , init.c , boot.c , u-boot.lds , Makefile 附件会提供完整源码):
1,第一阶段的实现:
设置CPU为管理模式;
开启CPU的I-Cache,经测试开启之前和开启之后复制内核代码时间缩短8s;
关闭Watchdog;
屏蔽所有中断;
初始化时钟;
2,设置总线模式为异步模式(手册中要求);
3,设置MPLL寄存器,将12M晶振频率变为200M;
初始化内存;
0: 循环设置内存控制器13个寄存器的值;
设置栈,后面的初始化由C语言来完成所以需要设置栈;
初始化Nand Flash控制器;
重定位u-boot代码;
进入u-boot第二阶段准备启动内核;
设置内存控制器寄存器时使用的值;
第二阶段主要介绍传递给内核的参数设置部分(参考u-boot源码中的struct tag结构体):
参数的内存分布如图:
最重要的几个参数设置如下
完整源代码下载地址
start.S 第一阶段启动实现;
init.c 主要硬件初始化及相关操作实现(串口 , Nand Flash);
boot.c 从Nand Flash的Kernel分区(0x60040–不包括uImage的header部分所以0x60000需要加上64字节)中复制Kernel代码到SDRAM的0x30008000的地址处,设置传递给内核的参数,参数存放在内存的0x30000100地址处,然后启动内核;
为了实现这个最终的目标,BootLoader具有以下必须的功能(以U-Boot为例);
Step-1:
1),设置CPU;
2),关闭Watchdog;
3),设置Clock;
4),屏蔽所有的Interrupt;
5),初始化SDRAM;
6),初始化nandflash(如果u-boot代码在nandflash上);
7);重定位BootLoader的代码到SDRAM;
8);设置栈(C语言函数调用需要先设置栈,栈要在SDRAM初始化之后设置);
9),清BSS段;
10),进入BootLoader的Step-2阶段启动内核;
Step-2:
11),设置BootLoader要传递给内核的参数;
12),从Nor 或 Nand flash 上复制内核代码到SDRAM的0x30007FC0的地方(这个地址不是固定的,可以任意指定,只要不破坏u-boot使用的内存空间,u-boot会根据uImage的Header中的Load Addr判断内核是否装载到Load Addr的地址处,Load Addr在u-boot中默认为0x30007FC0,如果不在,u-boot会移动内核代码到改地址处);
13),跳转到uImage的Header中Entry Point指定的入口地址处开始执行内核代码(内核执行后,u-boot不在执行);
在第二阶段中还可以包括一些辅助开发的功能(不分先后次序): 14),Nor 或者 Nand flash烧写功能; 15),USB初始化; 16),UART串口初始化; 17),网络功能初始化;
2,自制BootLoader思路:
实现上述必须功能;
以S3C2440平台为例;
硬件参数查S3C2440 Datasheet;
内存布局以及内核参数传递参考u-boot.1.1.6版本代码;
总体功能实现(start.S , init.c , boot.c , u-boot.lds , Makefile 附件会提供完整源码):
1,第一阶段的实现:
设置CPU为管理模式;
mrs r0,cpsr bic r0,r0,#0x1f orr r0,r0,#0xd3 msr cpsr,r0
开启CPU的I-Cache,经测试开启之前和开启之后复制内核代码时间缩短8s;
mrc p15, 0, r1, c1, c0, 0 orr r1,r1,#0x1000 mcr p15, 0, r1, c1, c0, 0
关闭Watchdog;
ldr r0,=0x53000000 mov r1,#0 str r1,[r0]
屏蔽所有中断;
ldr r0,=0x4A000008 ldr r1,=0xFFFFFFFF str r1,[r0]
ldr r1,=0x3FF ldr r0,=0x4A00001C str r1,[r0]
初始化时钟;
1,设置 FCLK:HCLK:PCLK=1:2:4 ldr r0,=0x4C000014 mov r1,#0x3 str r1,[r0]
2,设置总线模式为异步模式(手册中要求);
mrc p15, 0, r1, c1, c0, 0 orr r1, r1, #0xc0000000 mcr p15, 0, r1, c1, c0, 0
3,设置MPLL寄存器,将12M晶振频率变为200M;
ldr r0,=0x4c000004 ldr r2,=0x0005c012 str r2,[r0]
初始化内存;
ldr r0,=SDRDATA ldr r1,=_text_start sub r0,r0,r1 @data address for initing sdram start; add r2,r0,#13*4 @data address end; ldr r1,=0x48000000 @sdram control registers address start;
0: 循环设置内存控制器13个寄存器的值;
ldr r3,[r0],#4 str r3,[r1],#4 cmp r0,r2 bne 0b
设置栈,后面的初始化由C语言来完成所以需要设置栈;
ldr sp,=0x34000000
初始化Nand Flash控制器;
bl nand_init
重定位u-boot代码;
bl copy_to_sdram
进入u-boot第二阶段准备启动内核;
ldr lr,=halt ldr pc,=main @ bl main @jump to step 2 for starting linux kernel; halt: bl halt
设置内存控制器寄存器时使用的值;
.align 4 SDRDATA: .long 0x32333330 .long 0x00002a60 .long 0x00002a60 .long 0x00002a60 .long 0x00002a60 .long 0x00002a60 .long 0x00002a60 .long 0x00018001 .long 0x00018001 .long 0x008404f5 .long 0x000000b1 .long 0x00000020 .long 0x00000020
第二阶段主要介绍传递给内核的参数设置部分(参考u-boot源码中的struct tag结构体):
参数的内存分布如图:
最重要的几个参数设置如下
/* 起始tag设置 */ static unsigned long * setup_start_tag (unsigned long *params) { *params = 0x5; //start tag的大小,以4字节对齐; *(params+1) = ATAG_CORE; //start tag的name标示; *(params+2) = 0; *(params+3) = 0; *(params+4) = 0; return (params+5); }
/* 内存tag设置 */ static unsigned long * setup_memory_tags (unsigned long *params) { *params = 4; *(params+1) = ATAG_MEM; *(params+2) = 0x4000000; //内存空间大小; *(params+3) = 0x30000000; //内存起始地址; return (params+4); }
/* 命令行tag设置 */ static unsigned long * setup_commandline_tag (unsigned long *params,char *commandline) { int len,slen=strlen(commandline); //命令行参数长度 char *tmp; len = (8+slen+4)>>2; //命令行参数tag的总大小,4字节对齐; *params = len; *(params+1) = ATAG_CMDLINE; tmp = (char *)(params+2); strcpy(tmp,commandline); //将命令行参数内容放入tag内存空间; *(tmp+slen)='\0'; return params+len; }
/* 结束tag设置 */ static void setup_end_tag (unsigned long *params) { *params = 0x0; *(params+1) = ATAG_NONE; }
完整源代码下载地址
start.S 第一阶段启动实现;
init.c 主要硬件初始化及相关操作实现(串口 , Nand Flash);
boot.c 从Nand Flash的Kernel分区(0x60040–不包括uImage的header部分所以0x60000需要加上64字节)中复制Kernel代码到SDRAM的0x30008000的地址处,设置传递给内核的参数,参数存放在内存的0x30000100地址处,然后启动内核;
相关文章推荐
- 权限管理 (一) 设计思路分析和实现授权、认证
- 基于s3c2440的简易bootloader实现
- Nodejs进阶:readline实现日志分析+简易命令行工具
- 35.top10热门品类之需求回顾以及实现思路分析
- n个2*1小矩形无重叠覆盖2*n大矩形(递归,思路分析及代码实现,斐波那契数列衍生)
- 翻翻git之---自定义邮件发送按钮SendButton(流程分析,实现思路可以学习下)
- 用js写“算24”游戏的思路分析与实现代码
- enode框架step by step之框架要实现的目标的分析思路剖析2
- PHP简易的缓存实现思路
- 最佳路径分析实现思路及优化研究
- Android轮播图原理思路分析+实现方案
- enode框架step by step之框架要实现的目标的分析思路剖析1
- S5PV210开发系列三_简易Bootloader的实现
- 如何整合Spring和Mybatis的思路分析(使用面向接口的mapper代理,不用再去写Dao的实现类)
- Gis分析POI空间聚合设计思路及示例实现源码
- 简易记事本实现与分析(三)主界面
- PHP实现自动登陆的思路分析
- Android轮播图原理思路分析+实现方案
- 实现复选框,单选框附加上美图并实行动画思路分析