简单BootLoader

Posted zongzi10010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单BootLoader相关的知识,希望对你有一定的参考价值。

目录


title: 简单BootLoader
tags: linux
date: 2018-09-28 23:23:05
---

简单BootLoader

概述

目标: 启动内核,也就是需要读取内核到内存,也就是操作flash和内存

一个最基本的BootLoader应该有以下步骤:

  1. 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH
  2. 如果bootloader比较大,要把它重定位到SDRAM
  3. 把内核从NAND FLASH读到SDRAM
  4. 设置要传给内核的参数
  5. 跳转执行内核

优化: 为了加快运行速度,应该提高主频,打开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的全局变量,并不对等.[未初始化的全局变量(COMMON段)为弱符号,如果有同名的全局变量,可能会存在覆盖的情况]

对于全局变量来说,如果初始化了不为0的值,那么该全局变量则被保存在data段,如果初始化的值为0,那么将其保存在bss段,如果没有初始化,则将其保存在common段,等到链接时再将其放入到BSS段。关于第三点不同编译器行为会不同,有的编译器会把没有初始化的全局变量直接放到BSS段。

链接脚本中值的获取,具体参考./链接脚本.md

//汇编方法1
.global _bss_start
_bss_start:
    .word __bss_start
ldr r1, _bss_start  //读取内存,这里是读取label所在内存的数据
//汇编方法2
ldr 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, =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

参数设置

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);  



以上是关于简单BootLoader的主要内容,如果未能解决你的问题,请参考以下文章

stm32的bootloader需要自己写吗

stm32最简单的实现BootLoader

最简单的bootloader的编写步骤

u-boot学习:自己写bootloader

为啥这段代码会泄露? (简单的代码片段)

如何简单快速地合并 bootloader 和 APP 文件?