Mini2440学习日记(1)
--U-boot简介及启动流程分析
一、U-boot的引入
我们来回忆一下PC的体系结构:PC机中的引导加载程序是由BIOS和位于硬盘MBR中的OS Boot Loader(比如LILO和GRUB等)一起组成的,BIOS在完成硬件检测和资源分配后,将硬盘MBR中的Boot Loader读到系统的RAM中,然后将控制权交给OS Boot Loader。Boot Loader的主要运行任务就是将内核映象从硬盘上读到RAM中,然后跳转到内核的入口点去运行,即开始启动操作系统。在嵌入式系统中,通常并没有像BIOS那样的固件程序(注:有的嵌入式cpu也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由Boot Loader来完成。比如在一个基于ARM7TDMI core的嵌入式系统中,系统在上电或复位时通常都从地址0x00000000处开始执行,而在这个地址处安排的通常就是系统的Boot Loader程序。
二、一个嵌入式的存储设备的分区
图1 一个典型的NandFlash分区
三、BootLoader的启动方式
Bootloader的启动方式主要有网络启动方式、磁盘启动方式和Flash启动方式。
图2 网络启动方式
四、U-boot的源码目录结构
图3 U-boot的源码目录结构及作用
五、U-boot的启动流程图
u-boot的stage1代码通常放在cpu/xxxx/start.S文件中,用汇编语言写成,u-boot的stage2代码通常放在lib_xxxx/board.c文件中,用C语言写成。
图4 U-boot的启动流程图和内存搬运图
六、U-boot最小代码量分析
最小源代码目录:
Start.s文件(U-boot的第一阶段启动代码)
.text .global _start _start: /*1 设置栈 */ ldr sp,=4096 /*1 关闭看门狗 */ bl disable_watchdog bl disable_interrupt /*1 设置时钟 */ bl clock_init /*1 初始化SDRAM */ bl sdram_init bl nand_init /*1 初始化NAND FLASH */ /*1 确定三个参数 */ mov r0, #0 ldr r1, =_start ldr r2, = __bss_start sub r2,r2,r1 /*1 将UBOOT 从NAND FLASH 拷贝到SDRAM*/ bl copy_uboot_to_sdram /*1 清除BSS */ bl clear_bss /*1 跳转到MIAN函数*/ ldr lr, =halt ldr sp, =0x34000000 ldr pc, =main halt: b halt |
Boot.c文件(U-boot的第二阶段启动代码):
#include "setup.h" #define PWTCON *(volatile unsigned int *)0x5300000 #define GPBCON *(volatile unsigned long *)0X56000010 #define GPBDAT *(volatile unsigned long *)0x56000014 #define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x1<<4)|(0x01<<0)) #define MPLLCON *(volatile unsigned int *)0x4c000004 #define CLKDIVN *(volatile unsigned int *)0x4C000014 #define ULCON0 *(volatile unsigned long *)0x50000000 #define UCON0 *(volatile unsigned long *)0x50000004 #define UTXH0 *(volatile unsigned long *)0x50000020 #define UTRSTAT0 *(volatile unsigned long *)0x50000010 #define UBRDIV0 *(volatile unsigned long *)0x50000028 #define GPHCON *(volatile unsigned long *)0x56000070 #define GPHUP *(volatile unsigned long *)0x56000078 #define PCLK 50000000 // init.c中的clock_init函数设置PCLK为50MHz #define UART_CLK PCLK // UART0的时钟源设为PCLK #define UART_BAUD_RATE 115200 // 波特率 #define UART_BRD ((PCLK/(UART_BAUD_RATE * 16)) - 1) void disable_watchdog(void) { PWTCON = 0; } #define INTMSK *(volatile unsigned int *)0x4A000008 void disable_interrupt(void) { INTMSK = 0xffffffff; } void uart_init(void) { ULCON0 =0x3; //8 N 1 UCON0 =0x5; //中断或者查询模式 GPHCON =0xa0; //设置GPH2为TX0 GPHUP =0x0c; //设置上拉 UBRDIV0 =UART_BRD; } void putc(char c) { while(0 == (UTRSTAT0 & 0x4)); UTXH0 = c; } void puts(char *p) { if(!p) return; while(*p !='\0') { putc(*p); p++; } } void printout() { static char i=0; i++; if(i>4) i=0; putc(0x30+i); putc('\n'); } void clock_init(void) { CLKDIVN = 0x5; __asm__( "mrc p15, 0, r1, c1, c0, 0\n" /* 读出控制寄存器 */ "orr r1, r1, #0xc0000000\n" /* 设置为“asynchronous bus mode” */ "mcr p15, 0, r1, c1, c0, 0\n" /* 写入控制寄存器 */ ); MPLLCON = S3C2440_MPLL_400MHZ; } void sdram_init(void) { volatile unsigned int *p = (volatile unsigned int *)0x48000000; p[0] =0x22011110, //BWSCON p[1] =0x00000700, //BANKCON0 p[2] =0x00000700, //BANKCON1 p[3] =0x00000700, //BANKCON2 p[4] =0x00000700, //BANKCON3 p[5] =0x00000700, //BANKCON4 p[6] =0x00000700, //BANKCON5 p[7] =0x00018005, //BANKCON6 p[8] =0x00018005, //BANKCON7 p[9] =0x008C04F4, //REFRESH p[10]=0x000000B1, //BANKSIZE p[11]=0x00000030, //MRSRB6 p[12]=0x00000030; //MRSRB7 } #define NFCONF *(volatile unsigned int *)0x4e000000 #define NFCONT *(volatile unsigned int *)0x4e000004 #define NFCMMD *(volatile unsigned char*)0x4e000008 #define NFADDR *(volatile unsigned int *)0x4e00000C #define NFDATA *(volatile unsigned char *)0x4e000010 #define NFSTAT *(volatile unsigned int *)0x4e000020 #if 1 void nand_init(void) { #define TACLS 0 #define TWRPH0 1 #define TWRPH1 0 NFCONF |= (TACLS << 12)|(TWRPH0 << 8)|(TWRPH1 <<4); NFCONT |= ((1 << 4) | (1 << 1) | (1 << 0)); } void nand_select(void) { NFCONT &= ~(1<<1); }
void nand_disselect(void) { NFCONT |= (1<<1); } void nand_command(unsigned char cmd) { unsigned int i = 0 ; NFCMMD = cmd; for(i = 20 ; i>0;i--); }
#define NAND_PAGE_SIZE 2048 void nand_address(unsigned int addr) { unsigned int page = addr/NAND_PAGE_SIZE; unsigned int col = addr%NAND_PAGE_SIZE; unsigned i = 0 ;
NFADDR = (col & 0xff); for(i = 20 ; i>0;i--); NFADDR = ((col >> 8) & 0xff); for(i = 20 ; i>0;i--);
NFADDR = (page & 0xff); for(i = 20 ; i>0;i--);
NFADDR = ((page >> 8) & 0xff); for(i = 20 ; i>0;i--); NFADDR = ((page >> 16) & 0xff); for(i = 20 ; i>0;i--);
}
unsigned char nand_data(void) { return NFDATA; } void nand_ready(void) { while(0 == (NFSTAT&0x1)); }
void nand_read1(unsigned char* src , unsigned char* dest ,unsigned int len) {
int col = (unsigned long)src % NAND_PAGE_SIZE; int i = 0 ; nand_select(); while(i < len) { nand_command(0x00); nand_address((unsigned int)src); nand_command(0x30); nand_ready(); for(; (col < NAND_PAGE_SIZE)&&(i<len);col++) { dest[i] = nand_data(); i++; src++; } col = 0; } nand_disselect(); } void nand_read2(unsigned char* src , unsigned char* dest ,unsigned int len) { puts("tioashi1\r\n"); int col = (unsigned long)src % NAND_PAGE_SIZE; int i = 0 ; nand_select(); puts("tioashi2\r\n"); while(i < len) { nand_command(0x00); nand_address((unsigned int)src); nand_command(0x30); nand_ready(); for(; (col < NAND_PAGE_SIZE)&&(i<len);col++) { dest[i] = nand_data(); i++; src++; } col = 0; puts("tioashi3-------------------------------\r\n"); putc(0x30+i); } puts("tioashi4\r\n"); nand_disselect(); } #endif int BootFromNandFlash_or_NorFlash(void) { volatile int *p = (volatile int *)0; int val = *p; *p = 0x12345678; if(*p==0x12345678) { *p = val; return 1; } else return 0; } int copy_uboot_to_sdram(unsigned char* src , unsigned char *dest , unsigned int len) { int i = 0 ; if( BootFromNandFlash_or_NorFlash()==1) { nand_read1(src,dest,len); } else { while(i < len) { dest[i]= src[i]; dest++; src++; i++; } } return 0; } void clear_bss(void) { extern unsigned int __bss_start,__bss_end; volatile unsigned int *p = &__bss_start; while(p < &__bss_end) { *p = 0; p++; } } static struct tag *params; static void setup_start_tag (void) { params = (struct tag*)0x30000100; params->hdr.tag = ATAG_CORE ; params->hdr.size = tag_size (tag_core);//3 params->u.core.flags = 0;//read-only params->u.core.pagesize = 0;// params->u.core.rootdev = 0; params = tag_next (params); } static void setup_memory_tags (void) {
params->hdr.tag = ATAG_MEM; params->hdr.size = tag_size (tag_mem32);// params->u.mem.start = 0x30000000;//内存的其实地址0x30000000 params->u.mem.size = 64*1024*1024;//64M params = tag_next (params); } /*uboot不依赖任何标准库程序,因此字符串处理函数还需要自己写*/ char * strcpy(char * dest,const char *src) { char *tmp = dest; while ((*dest++ = *src++) != '\0') /* nothing */; return tmp; }
unsigned int strlen(const char * s) { const char *sc;
for (sc = s; *sc != '\0'; ++sc) /* nothing */; return sc - s; } static void setup_commandline_tag (char *commandline) { char *p = commandline;
if (!commandline) return;
params->hdr.tag = ATAG_CMDLINE ; params->hdr.size =(sizeof (struct tag_header) + strlen (p) + 1 + 3) >> 2;
strcpy (params->u.cmdline.cmdline, p);
params = tag_next (params); }
static void setup_end_tag (void) { params->hdr.tag = 0x00000000; params->hdr.size = 0; } void len_on(void) { GPBCON |=(0x1<<10); GPBDAT =0; } int main(void) { /*定义一个函数指针*/ void (* thekernel)(int zero,int arch,unsigned int params); /*初始化串口,内核启动后打印内核信息*/ uart_init(); /*将内核拷贝到SDRAM*/ puts("copy kernel to SDRAN...\r\n"); nand_read2((unsigned char*)(0x60000+64),(unsigned char *)0x30008000,(unsigned int)0x200000); /*设置启动参数*/ puts("shezhiqidongcanshu...\r\n"); setup_start_tag(); setup_memory_tags(); setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"); setup_end_tag(); /*跳到内核的入口地址去执行*/ puts("boot the kernel...\r\n"); thekernel = (void(*)(int,int,unsigned int))0x30008000; thekernel(0,263,0x30000100); puts("error\r\n"); return 0; } |
Boot.lds(U-boot代码的链接文件):
SECTIONS{ . = 0x33f80000; .text : {*(.text)} . =ALIGN(4); .rodata : {*(.rodata)} . =ALIGN(4); .data : {*(.data)} . =ALIGN(4); __bss_start = .; .bss : {*(.bss) *(COMMON)} __bss_end = .; } |
MakeFile文件(U-boot的编译文件):
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 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 *.bin *.elf *.dis *.o |
Setup.h文件(U-boot中一些结构体的定义):
/* * 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 int /* * 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 __attribute__((unused, __section__(".taglist"))) #define __tagtable(tag, fn) \ static struct tagtable __tagtable_##fn __tag = { tag, fn }
#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 |