Linux

自己写bootloader

2017-06-07  本文已影响116人  li_justit

自己写 bootloader,或者移植 uboot ,需要知道的一些信息

第一阶段:

1 关看门狗

2 设置时钟

3 初始化SDRAM

4 重定位:bootloader可能大于4K,把flash中的bootloader拷贝到SDRAM中。

5 跳转到main

第二阶段:

6 初始化串口,内核启动打印参数

7 设置启动参数,供内核启动时解析使用

8 跳转启动内核

实现第一阶段

关看门狗

设置时钟

设置SDRAM

初始化NAND FLASH

硬件初始化 start.S

#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


Rom初始化


/* NAND FLASH控制器 */
#define NFCONF (*((volatile unsigned long *)0x4E000000))
#define NFCONT (*((volatile unsigned long *)0x4E000004))
#define NFCMMD (*((volatile unsigned char *)0x4E000008))
#define NFADDR (*((volatile unsigned char *)0x4E00000C))
#define NFDATA (*((volatile unsigned char *)0x4E000010))
#define NFSTAT (*((volatile unsigned char *)0x4E000020))

/* GPIO */
#define GPHCON              (*(volatile unsigned long *)0x56000070)
#define GPHUP               (*(volatile unsigned long *)0x56000078)

/* UART registers*/
#define ULCON0              (*(volatile unsigned long *)0x50000000)
#define UCON0               (*(volatile unsigned long *)0x50000004)
#define UFCON0              (*(volatile unsigned long *)0x50000008)
#define UMCON0              (*(volatile unsigned long *)0x5000000c)
#define UTRSTAT0            (*(volatile unsigned long *)0x50000010)
#define UTXH0               (*(volatile unsigned char *)0x50000020)
#define URXH0               (*(volatile unsigned char *)0x50000024)
#define UBRDIV0             (*(volatile unsigned long *)0x50000028)

#define TXD0READY   (1<<2)


void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);


int isBootFromNorFlash(void)
{
    volatile int *p = (volatile int *)0;
    int val;

    val = *p;
    *p = 0x12345678;
    if (*p == 0x12345678)
    {
        /* 写成功, 是nand启动 */
        *p = val;
        return 0;
    }
    else
    {
        /* NOR不能像内存一样写 */
        return 1;
    }
}

void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
{   
    int i = 0;
    
    /* 如果是NOR启动 */
    if (isBootFromNorFlash())
    {
        while (i < len)
        {
            dest[i] = src[i];
            i++;
        }
    }
    else
    {
        //nand_init();
        nand_read((unsigned int)src, dest, len);
    }
}

void clear_bss(void)
{
    extern int __bss_start, __bss_end;
    int *p = &__bss_start;
    
    for (; p < &__bss_end; p++)
        *p = 0;
}

void nand_init(void)
{
#define TACLS   0
#define TWRPH0  1
#define TWRPH1  0
    /* 设置时序 */
    NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
    /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
    NFCONT = (1<<4)|(1<<1)|(1<<0);  
}

void nand_select(void)
{
    NFCONT &= ~(1<<1);  
}

void nand_deselect(void)
{
    NFCONT |= (1<<1);   
}

void nand_cmd(unsigned char cmd)
{
    volatile int i;
    NFCMMD = cmd;
    for (i = 0; i < 10; i++);
}

void nand_addr(unsigned int addr)
{
    unsigned int col  = addr % 2048;
    unsigned int page = addr / 2048;
    volatile int i;

    NFADDR = col & 0xff;
    for (i = 0; i < 10; i++);
    NFADDR = (col >> 8) & 0xff;
    for (i = 0; i < 10; i++);
    
    NFADDR  = page & 0xff;
    for (i = 0; i < 10; i++);
    NFADDR  = (page >> 8) & 0xff;
    for (i = 0; i < 10; i++);
    NFADDR  = (page >> 16) & 0xff;
    for (i = 0; i < 10; i++);   
}

void nand_wait_ready(void)
{
    while (!(NFSTAT & 1));
}

unsigned char nand_data(void)
{
    return NFDATA;
}

void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
    int col = addr % 2048;
    int i = 0;
        
    /* 1. 选中 */
    nand_select();

    while (i < len)
    {
        /* 2. 发出读命令00h */
        nand_cmd(0x00);

        /* 3. 发出地址(分5步发出) */
        nand_addr(addr);

        /* 4. 发出读命令30h */
        nand_cmd(0x30);

        /* 5. 判断状态 */
        nand_wait_ready();

        /* 6. 读数据 */
        for (; (col < 2048) && (i < len); col++)
        {
            buf[i] = nand_data();
            i++;
            addr++;
        }
        
        col = 0;
    }

    /* 7. 取消选中 */       
    nand_deselect();
}

#define PCLK            50000000    // init.c中的clock_init函数设置PCLK为50MHz
#define UART_CLK        PCLK        //  UART0的时钟源设为PCLK
#define UART_BAUD_RATE  115200      // 波特率
#define UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)

/*
 * 初始化UART0
 * 115200,8N1,无流控
 */
void uart0_init(void)
{
    GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0
    GPHUP   = 0x0c;     // GPH2,GPH3内部上拉

    ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)
    UCON0   = 0x05;     // 查询方式,UART时钟源为PCLK
    UFCON0  = 0x00;     // 不使用FIFO
    UMCON0  = 0x00;     // 不使用流控
    UBRDIV0 = UART_BRD; // 波特率为115200
}

/*
 * 发送一个字符
 */
void putc(unsigned char c)
{
    /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
    while (!(UTRSTAT0 & TXD0READY));
    
    /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
    UTXH0 = c;
}

void puts(char *str)
{
    int i = 0;
    while (str[i])
    {
        putc(str[i]);
        i++;
    }
}

void puthex(unsigned int val)
{
    /* 0x1234abcd */
    int i;
    int j;
    
    puts("0x");

    for (i = 0; i < 8; i++)
    {
        j = (val >> ((7-i)*4)) & 0xf;
        if ((j >= 0) && (j <= 9))
            putc('0' + j);
        else
            putc('A' + j - 0xa);
        
    }
    
}

链接文档

SECTIONS {
    . = 0x33f80000;
    .text : { *(.text) }
    
    . = ALIGN(4);
    .rodata : {*(.rodata*)} 
    
    . = ALIGN(4);
    .data : { *(.data) }
    
    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss)  *(COMMON) }
    __bss_end = .;
}

软件初始化


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

    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;
    
    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_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);
    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);  
    /* 
     *  mov r0, #0
     *  ldr r1, =362
     *  ldr r2, =0x30000100
     *  mov pc, #0x30008000 
     */

    puts("Error!\n\r");
    /* 如果一切正常, 不会执行到这里 */

    return -1;
}

上一篇下一篇

猜你喜欢

热点阅读