目录
title: 简单BootLoader
tags: linux date: 2018-09-28 23:23:05 ---简单BootLoader
概述
目标: 启动内核,也就是需要读取内核到内存,也就是操作flash和内存
一个最基本的BootLoader应该有以下步骤:
- 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH
- 如果bootloader比较大,要把它重定位到SDRAM
- 把内核从NAND FLASH读到SDRAM
- 设置要传给内核的参数
- 跳转执行内核
优化: 为了加快运行速度,应该提高主频,打开Icach
/* 启动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
NOR与NAND启动
参考文档 ARM裸机>启动流程
NAND 启动自动复制到4KRAM
NOR启动,片内RAM为0x4000,0000
链接脚本规划
我们从上图可以看到,SDRAM的起始地址为0x3000,0000
,SDRAM大小为64M,也就是地址顶端为0x3400,0000
.预留512K给Bootloader,也就是规划hex(0x34000000-512*1024)=0x33f8,0000
为Bootloader的起始地址.链接脚本考虑字节对齐.
SECTIONS { . = 0x33f80000; .text : { *(.text) } . = ALIGN(4); .rodata : {*(.rodata*)} . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); __bss_start = .; .bss : { *(.bss) *(COMMON) } __bss_end = .;}
链接脚本中有个COMMON段,这是未初始化的全局变量,一般情况下也是直接在链接阶段放在bss段进行清零.但是注意未初始化的全局变量与初始化为0的全局变量,并不对等.
对于全局变量来说,如果初始化了不为0的值,那么该全局变量则被保存在data段,如果初始化的值为0,那么将其保存在bss段,如果没有初始化,则将其保存在common段,等到链接时再将其放入到BSS段。关于第三点不同编译器行为会不同,有的编译器会把没有初始化的全局变量直接放到BSS段。
链接脚本中值的获取,具体参考./链接脚本.md
//汇编方法1.global _bss_start_bss_start: .word __bss_startldr r1, _bss_start //读取内存,这里是读取label所在内存的数据//汇编方法2ldr r1, =__bss_start//C方法extern int __bss_start;int val =&__bss_start; //!< 获得__bss_start的值
初始化规划
#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 */ 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 bl nand_init mov r0, #0 ldr r1, =_start ldr r2, =__bss_start sub r2, r2, r1 bl copy_code_to_sdram bl clear_bss /* 5. 执行main */ ldr lr, =halt ldr 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
参数设置
nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);setup_start_tag();setup_memory_tags();setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");setup_end_tag();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);}
内核存储在0x60000+64
,读取到运行地址0x30008000
,参数存储在0x30000100
,最后传入机器ID=362
theKernel = (void (*)(int, int, unsigned int))0x30008000;theKernel(0, 362, 0x30000100);