关于Bootloader的一些看法

来源:互联网 发布:注册会计师考试 知乎 编辑:程序博客网 时间:2024/04/29 22:02

          关于bootloader初步解析
bootloader 的只要程序是包括三个部分,是:主程序boot.c,初始化程序:init.c,引导程序(汇编语言编写)start.s
首先来看下start.s程序:
#define S3C2440_MPLL_200MHZ     ((0x5c<<12)|(0x01<<4)|(0x02))
#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
 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_200MHZ
 str r1, [r0]

/* 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
然后是bootloader的主要部分,如下
/*
 *  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: \0 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
#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;
}

最后是bootloader的初始化程序:
关于控制器包括:NAND FLASH控制器,GPIO 接口,UART registers等

/* 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);
  
 }
 
}
综合和以上可以看出Bootloader的框架基本是
1. 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH
2. 如果bootloader比较大,要把它重定位到SDRAM
3. 把内核从NAND FLASH读到SDRAM
4. 设置"要传给内核的参数"
5. 跳转执行内核

改进:
1. 提高CPU频率, 200MHZ ==> 400MHZ
2. 启动ICACHE

       2013/12/5摘自韦老大视频

 

原创粉丝点击