韦东山老师讲解自己动手写bootloader的笔记记录:
看一看几个相关文件:
start.S文件:启动流程代码
bootloader启动的第一阶段:
1. 关闭看门狗
2. 设置时钟频率,设置分频系数等;
3. 初始化SDRAM, 因为需要快速启动,所以需要将代码从flash上拷贝到内存SDRAM中启动
4. 代码重定向 (注意nor启动和nand启动的不同点)
5. 执行main函数,跳转到启动的第二阶段。
-
#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))
-
#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
-
#define MEM_CTL_BASE 0x48000000
-
-
.text
-
.global _start
-
_start:
-
-
/* 1. 关看门狗 */
-
ldr r0, =0x53000000
-
mov r1, #0
-
str r1, [r0]
-
-
/* 2. 设置时钟 */
-
ldr r0, =0x4c000014
-
// mov r1, #0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
-
mov r1, #0x05; // FCLK:HCLK:PCLK=1:4:8
-
str r1, [r0]
-
-
/* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
-
mrc p15, 0, r1, c1, c0, 0 /* 读出控制寄存器 */
-
orr r1, r1, #0xc0000000 /* 设置为“asynchronous bus mode” */
-
mcr p15, 0, r1, c1, c0, 0 /* 写入控制寄存器 */
-
-
/* MPLLCON = S3C2440_MPLL_200MHZ */
-
ldr r0, =0x4c000004
-
ldr r1, =S3C2440_MPLL_400MHZ
-
str r1, [r0]
-
-
/* 启动ICACHE, 开启ICACHE将加快启动速度 */
-
mrc p15, 0, r0, c1, c0, 0 @ read control reg
-
orr r0, r0, #(1<<12)
-
mcr p15, 0, r0, c1, c0, 0 @ write it back
-
-
-
/* 3. 初始化SDRAM */
-
ldr r0, =MEM_CTL_BASE
-
adr r1, sdram_config /* sdram_config的当前地址 */
-
add r3, r0, #(13*4)
-
1:
-
ldr r2, [r1], #4
-
str r2, [r0], #4
-
cmp r0, r3
-
bne 1b
-
-
/* 4. 重定位 : 把bootloader本身的代码从flash复制到它的链接地址去 */
-
ldr sp, =0x34000000 /* 将SP放在SDRAM的最高地址处,堆栈式满递减堆栈。在调用C函数之前,必须设置好堆栈 */
-
-
bl nand_init
-
-
mov r0, #0
-
ldr r1, =_start /* _start是起始地址,是链接地址, 由链接文件.lds 指定 */
-
ldr r2, =__bss_start /* __bss_start 同样是链接地址,在链接文件中指定 */
-
sub r2, r2, r1
-
-
-
/*
-
* copy_code_to_sdram: 被调用的C函数,
-
* 第一个参数是R0:表示拷贝数据的源地址,这里从0地址(源地址)处拷贝数据到链接地址(真正运行的地址,也就是目的地址)
-
* 第二个参数是R1:表示拷贝数据的目的地址,就是链接地址
-
* 第三个参数是R2:表示拷贝数据的长度 = bss段起始地址 - 链接的起始地址。(都在链接文件中定义。)
-
*/
-
bl copy_code_to_sdram
-
-
-
/*
-
* 由上面的拷贝代码知道,在bss段中的数据并没有拷贝到内存当中去,这是因为这些数据初始化都是0,所以只需要在初始化的时候,显示的把bss段清零,就OK了。
-
*/
-
bl clear_bss
-
-
-
-
-
/* 5. 执行main */
-
ldr lr, =halt
-
ldr pc, =main
-
halt:
-
b halt
-
-
-
-
sdram_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
链接文件 boot.lds:
-
SECTIONS {
-
. = 0x33f80000;
-
.text : { *(.text) }
-
-
. = ALIGN(4);
-
.rodata : {*(.rodata*)}
-
-
. = ALIGN(4);
-
.data : { *(.data) }
-
-
. = ALIGN(4);
-
__bss_start = .;
-
.bss : { *(.bss) *(COMMON) }
-
__bss_end = .;
-
}
3. Makefile
-
CC = arm-linux-gcc
-
LD = arm-linux-ld
-
AR = arm-linux-ar
-
OBJCOPY = arm-linux-objcopy
-
OBJDUMP = arm-linux-objdump
-
-
CFLAGS := -Wall -O2
-
CPPFLAGS := -nostdinc -nostdlib -fno-builtin
-
-
objs := start.o init.o boot.o
-
-
boot.bin: $(objs)
-
${LD} -Tboot.lds -o boot.elf $^
-
${OBJCOPY} -O binary -S boot.elf $@
-
${OBJDUMP} -D -m arm boot.elf > boot.dis
-
-
%.o:%.c
-
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
-
-
%.o:%.S
-
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
-
-
clean:
-
rm -f *.o *.bin *.elf *.dis
4. boot.c
-
#include "setup.h"
-
-
extern void uart0_init(void);
-
extern void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);
-
extern void puts(char *str);
-
extern void puthex(unsigned int val);
-
-
-
static struct tag *params;
-
-
void setup_start_tag(void)
-
{
-
params = (struct tag *)0x30000100; //这里设置了内核参数放置的位置为: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; // 表示内存的大小,这里为64MB
-
-
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;
-
}
-
-
-
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 flash的地址0x60064处拷贝大小为0x200000的代码到内存地址为0x30008000处。即:从nand flash上拷贝内核到SDRAM中。
-
nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);
-
// 0x30008000为内核需要运行的开始地址(就是链接地址,在SDRAM当中),0x200000为整个内核的长度大小。
-
// uImage = 64bytes + zImage; zImage为真正的内核。uImage存在0x60000处。
-
// 所以zImage的起始地址为:0x60000 + 64
-
-
puthex(0x1234ABCD);
-
puts("\n\r");
-
puthex(*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); // 向内核启动的函数,传递了三个参数,
-
// 第一个参数为0
-
// 第二个参数为362, 为机器号
-
// 第三个参数为0x30000100:这个是内核需要的启动参数的起始地址。内核启动需要从这个地方获取所需的启动参 数,比如内存的起始地址,大小等等。
-
-
/*
-
* mov r0, #0
-
* ldr r1, =362
-
* ldr r2, =0x30000100
-
* mov pc, #0x30008000
-
*/
-
-
puts("Error!\n\r");
-
/* 如果一切正常, 不会执行到这里 */
-
-
return -1;
-
}
分析这个还是蛮有意思的。启动需要的文件也不多。
主要要清楚知道uboot的工作目的:就是启动内核
1. 从flash上把内核读入到内存中
引申的功能: 能够读取flash(那么就需要初始化flash)
初始化内存,时钟等
2. 启动内核
a 设置参数(启动时候,需要的参数)
b 跳转执行
最简单的bootloader的编写步骤:
1. 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH
2. 如果bootloader比较大,要把它重定位到SDRAM
3. 把内核从NAND FLASH读到SDRAM
4. 设置"要传给内核的参数"
5. 跳转执行内核
改进:
1. 提高CPU频率, 200MHZ ==> 400MHZ
2. 启动ICACHE
阅读(1934) | 评论(0) | 转发(0) |