zoukankan      html  css  js  c++  java
  • Linux学习 : 自己写bootloader

    一、bootloader 目标:启动内核

    基本功能:

      ①初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH

      ②image比较大需要重定位到SDRAM

      ②将内核从NAND FLASH读到 SDRAM

      ③设置“要传给内核的参数”

      ④跳转执行内核

    启动时间优化:

      ①提高CPU频率:

           1,FCLKCPU提供的时钟信号。
              2,HCLK是为AHB总线提供的时钟信号, Advanced High-performance Bus,主要用于高速外设,比如内存控制器,中断控制器,LCD控制器, DMA 等。
              3,PCLK是为APB总线提供的时钟信号,Advanced Peripherals Bus,主要用于低速外设,比如看门狗,UART控制器, IIS, I2C, SDI/MMC, GPIO,RTC and SPI等。

      ②启动ICACHE: 

        CPU将多条指令一次装入ICACHE,不用每次到SDRAM中去取指。
               另外,DCACHE主要用来取数据,需要启用MMU.

        

    1.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
    
      /* r0 r1 r2 表示给之后调用C函数传入的参数 */ mov r0, #
    0 ldr r1, =_start ldr r2, =__bss_start /*通过伪汇编获取bss段的结束地址*/ 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

    2.init.c

    /* NAND FLASH控制器 */
    #define NFCONF (*((volatile unsigned long *)0x4E000000))
    #define NFCONT (*((volatile unsigned long *)0x4E000004))
    #define NFCMMD (*((volatile unsigned char *)0x4E000008))
    #define NFADDR (*((puthexvolatile 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);
            
        }
        
    }

    3.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;
    
        params->hdr.tag = ATAG_CORE; //0x54410001,表示参数的开始
        params->hdr.size = tag_size (tag_core); //5个
    
        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; //64M
        
        params = tag_next (params); //如分多个内存存放tag,用tag_next查找
    }
    
    int strlen(char *str)
    {
        int i = 0;
        while (str[i])
        {
            i++;
        }
        return i;
    }
    
    void strcpy(char *dest, char *src)
    {
        while ((*dest++ = *src++) != '');
    }
    
    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; //向4取整
    
        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
    
    ");
        nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);
        puthex(0x1234ABCD);
        puts("
    
    ");
        puthex(*p);
        puts("
    
    ");
    
        /* 2. 设置参数 ,与内核约定参数存放地址,Uboot写个内核的遗嘱^^*/
        puts("Set boot params
    
    ");
        setup_start_tag();
        setup_memory_tags();
        setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");
        setup_end_tag();
    
        /* 3. 跳转执行 */
        puts("Boot kernel
    
    ");
        theKernel = (void (*)(int, int, unsigned int))0x30008000;
        theKernel(0, 362, 0x30000100);  //362:arm 
        /* 
         *  mov r0, #0
         *  ldr r1, =362
         *  ldr r2, =0x30000100
         *  mov pc, #0x30008000 
         */
    
        puts("Error!
    
    ");
        /* 如果一切正常, 不会执行到这里 */
    
        return -1;
    }

    4.boot.lds  :   指定代码存放的位置

    SECTIONS {
        . = 0x33f80000;  #与最高地址差512K
        .text : { *(.text) }
        
        . = ALIGN(4);
        .rodata : {*(.rodata*)} 
        
        . = ALIGN(4);
        .data : { *(.data) }
        
        . = ALIGN(4);
        __bss_start = .;  #bss段指向当前地址,保存未初始化或为0的全局变量
        .bss : { *(.bss)  *(COMMON) }
        __bss_end = .;   #bss段设定起始和结束地址,程序启动时统一初始化保存的变量
    }

    5.setup.h

    /*
     *  linux/include/asm/setup.h
     *
     *  Copyright (C) 1997-1999 Russell King
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License version 2 as
     * published by the Free Software Foundation.
     *
     *  Structure passed to kernel to tell it about the
     *  hardware it's running on.  See linux/Documentation/arm/Setup
     *  for more info.
     *
     * NOTE:
     *  This file contains two ways to pass information from the boot
     *  loader to the kernel. The old struct param_struct is deprecated,
     *  but it will be kept in the kernel for 5 years from now
     *  (2001). This will allow boot loaders to convert to the new struct
     *  tag way.
     */
    #ifndef __ASMARM_SETUP_H
    #define __ASMARM_SETUP_H
    
    #define u8  unsigned char
    #define u16 unsigned short
    #define u32 unsigned long
    
    /*
     * Usage:
     *  - do not go blindly adding fields, add them at the end
     *  - when adding fields, don't rely on the address until
     *    a patch from me has been released
     *  - unused fields should be zero (for future expansion)
     *  - this structure is relatively short-lived - only
     *    guaranteed to contain useful data in setup_arch()
     */
    #define COMMAND_LINE_SIZE 1024
    
    /* This is the old deprecated way to pass parameters to the kernel */
    struct param_struct {
        union {
        struct {
            unsigned long page_size;        /*  0 */
            unsigned long nr_pages;        /*  4 */
            unsigned long ramdisk_size;        /*  8 */
            unsigned long flags;        /* 12 */
    #define FLAG_READONLY    1
    #define FLAG_RDLOAD    4
    #define FLAG_RDPROMPT    8
            unsigned long rootdev;        /* 16 */
            unsigned long video_num_cols;    /* 20 */
            unsigned long video_num_rows;    /* 24 */
            unsigned long video_x;        /* 28 */
            unsigned long video_y;        /* 32 */
            unsigned long memc_control_reg;    /* 36 */
            unsigned char sounddefault;        /* 40 */
            unsigned char adfsdrives;        /* 41 */
            unsigned char bytes_per_char_h;    /* 42 */
            unsigned char bytes_per_char_v;    /* 43 */
            unsigned long pages_in_bank[4];    /* 44 */
            unsigned long pages_in_vram;    /* 60 */
            unsigned long initrd_start;        /* 64 */
            unsigned long initrd_size;        /* 68 */
            unsigned long rd_start;        /* 72 */
            unsigned long system_rev;        /* 76 */
            unsigned long system_serial_low;    /* 80 */
            unsigned long system_serial_high;    /* 84 */
            unsigned long mem_fclk_21285;       /* 88 */
        } s;
        char unused[256];
        } u1;
        union {
        char paths[8][128];
        struct {
            unsigned long magic;
            char n[1024 - sizeof(unsigned long)];
        } s;
        } u2;
        char commandline[COMMAND_LINE_SIZE];
    };
    
    
    /*
     * The new way of passing information: a list of tagged entries
     */
    
    /* The list ends with an ATAG_NONE node. */
    #define ATAG_NONE    0x00000000
    
    struct tag_header {
        u32 size;
        u32 tag;
    };
    
    /* The list must start with an ATAG_CORE node */
    #define ATAG_CORE    0x54410001
    
    struct tag_core {
        u32 flags;        /* bit 0 = read-only */
        u32 pagesize;
        u32 rootdev;
    };
    
    /* it is allowed to have multiple ATAG_MEM nodes */
    #define ATAG_MEM    0x54410002
    
    struct tag_mem32 {
        u32    size;
        u32    start;    /* physical start address */
    };
    
    /* VGA text type displays */
    #define ATAG_VIDEOTEXT    0x54410003
    
    struct tag_videotext {
        u8        x;
        u8        y;
        u16        video_page;
        u8        video_mode;
        u8        video_cols;
        u16        video_ega_bx;
        u8        video_lines;
        u8        video_isvga;
        u16        video_points;
    };
    
    /* describes how the ramdisk will be used in kernel */
    #define ATAG_RAMDISK    0x54410004
    
    struct tag_ramdisk {
        u32 flags;    /* bit 0 = load, bit 1 = prompt */
        u32 size;    /* decompressed ramdisk size in _kilo_ bytes */
        u32 start;    /* starting block of floppy-based RAM disk image */
    };
    
    /* describes where the compressed ramdisk image lives (virtual address) */
    /*
     * this one accidentally used virtual addresses - as such,
     * its depreciated.
     */
    #define ATAG_INITRD    0x54410005
    
    /* describes where the compressed ramdisk image lives (physical address) */
    #define ATAG_INITRD2    0x54420005
    
    struct tag_initrd {
        u32 start;    /* physical start address */
        u32 size;    /* size of compressed ramdisk image in bytes */
    };
    
    /* board serial number. "64 bits should be enough for everybody" */
    #define ATAG_SERIAL    0x54410006
    
    struct tag_serialnr {
        u32 low;
        u32 high;
    };
    
    /* board revision */
    #define ATAG_REVISION    0x54410007
    
    struct tag_revision {
        u32 rev;
    };
    
    /* initial values for vesafb-type framebuffers. see struct screen_info
     * in include/linux/tty.h
     */
    #define ATAG_VIDEOLFB    0x54410008
    
    struct tag_videolfb {
        u16        lfb_width;
        u16        lfb_height;
        u16        lfb_depth;
        u16        lfb_linelength;
        u32        lfb_base;
        u32        lfb_size;
        u8        red_size;
        u8        red_pos;
        u8        green_size;
        u8        green_pos;
        u8        blue_size;
        u8        blue_pos;
        u8        rsvd_size;
        u8        rsvd_pos;
    };
    
    /* command line:  terminated string */
    #define ATAG_CMDLINE    0x54410009
    
    struct tag_cmdline {
        char    cmdline[1];    /* this is the minimum size */
    };
    
    /* acorn RiscPC specific information */
    #define ATAG_ACORN    0x41000101
    
    struct tag_acorn {
        u32 memc_control_reg;
        u32 vram_pages;
        u8 sounddefault;
        u8 adfsdrives;
    };
    
    /* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */
    #define ATAG_MEMCLK    0x41000402
    
    struct tag_memclk {
        u32 fmemclk;
    };
    
    struct tag {
        struct tag_header hdr;
        union {
            struct tag_core        core;
            struct tag_mem32    mem;
            struct tag_videotext    videotext;
            struct tag_ramdisk    ramdisk;
            struct tag_initrd    initrd;
            struct tag_serialnr    serialnr;
            struct tag_revision    revision;
            struct tag_videolfb    videolfb;
            struct tag_cmdline    cmdline;
    
            /*
             * Acorn specific
             */
            struct tag_acorn    acorn;
    
            /*
             * DC21285 specific
             */
            struct tag_memclk    memclk;
        } u;
    };
    
    struct tagtable {
        u32 tag;
        int (*parse)(const struct tag *);
    };
    
    
    #define tag_member_present(tag,member)                
        ((unsigned long)(&((struct tag *)0L)->member + 1)    
            <= (tag)->hdr.size * 4)
    
    #define tag_next(t)    ((struct tag *)((u32 *)(t) + (t)->hdr.size))
    #define tag_size(type)    ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
    
    #define for_each_tag(t,base)        
        for (t = base; t->hdr.size; t = tag_next(t))
    
    /*
     * Memory map description
     */
    #define NR_BANKS 8
    
    struct meminfo {
        int nr_banks;
        unsigned long end;
        struct {
            unsigned long start;
            unsigned long size;
            int           node;
        } bank[NR_BANKS];
    };
    
    extern struct meminfo meminfo;
    
    #endif

    6.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 -  -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
        
  • 相关阅读:
    HotSpot VM执行引擎的实现
    解释器与JIT编译器
    执行引擎(Execution Engine)基础知识
    堆、栈、方法区的详细图解
    方法区的垃圾回收
    方法区的演进
    方法区的内部结构
    方法区内存配置
    设计模式简记-设计原则之迪米特法则
    设计模式简记-设计原则之DRY原则
  • 原文地址:https://www.cnblogs.com/blogs-of-lxl/p/5906103.html
Copyright © 2011-2022 走看看