U-boot启动详解cpu/arm920t/start.S
/* ************************************************************************* * * Jump vector table as in table 3.1 in [1] * ************************************************************************* */ ;定义变量_start,然后跳转到处理器复位代码
.globl _start _start: b reset ;产生中断则利用pc来跳转到对应的中断处理程序中 ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq ;利用.word来在当前位置放置一个值,这个值实际上就用对应的中断处理函数的地址 ;.word的意义为在当前地址处放入一个16bits值 _undefined_instruction: .word undefined_instruction _software_interrupt: .word software_interrupt _prefetch_abort: .word prefetch_abort _data_abort: .word data_abort _not_used: .word not_used _irq: .word irq _fiq: .word fiq .balignl 16,0xdeadbeef
/* ************************************************************************* * * Startup Code (reset vector) * * do important init only if we don't start from memory! * relocate armboot to ram * setup stack * jump to second stage * ************************************************************************* */ ;定义变量 _TEXT_BASE: .word TEXT_BASE .globl _armboot_start
_armboot_start: .word _start /*
* These are defined in the board-specific linker script. */ .globl _bss_start _bss_start: .word __bss_start .globl _bss_end
_bss_end: .word _end #ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */ .globl IRQ_STACK_START IRQ_STACK_START: .word 0x0badc0de /* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START FIQ_STACK_START: .word 0x0badc0de #endif /* * the actual reset code */ ;实际处理代码 reset: /* * set the cpu to SVC32 mode */ mrs r0,cpsr ;bic清除指定为1的位 bic r0,r0,#0x1f ;orr逻辑或操作 orr r0,r0,#0xd3 ;经过以上两步r0值控制位位11010011,第0~4位标识处理器当前所处模式为10011(32位管理模式),第6、7位 ;为1标识禁止IRQ和FIQ中断,第5位为0标识程序运行为arm状态,若其为1则运行在thumb状态 ;设置处理器为32位管理模式,并运行与arm状态 msr cpsr,r0 /* turn off the watchdog */
#if defined(CONFIG_S3C2400) # define pWTCON 0x15300000 # define INTMSK 0x14400008 /* Interupt-Controller base addresses */ # define CLKDIVN 0x14800014 /* clock divisor register */ #elif defined(CONFIG_S3C2410) ;看门狗寄存器地址 # define pWTCON 0x53000000 ;中断掩码寄存器,决定那个中断源被屏蔽,某位为1则屏蔽中断源,初始值为0xFFFFFFFF,屏蔽所有中断 # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */ ;中断子掩码寄存器,该寄存器只能屏蔽11个中断源,因此其仅低11位有效,初始值为0x7FF # define INTSUBMSK 0x4A00001C ;时钟分频控制寄存器 # define CLKDIVN 0x4C000014 /* clock divisor register */ #endif #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
;将看门狗寄存器清空,其各位含义为,第0位为1则当看门狗定时器溢出时重启,为0则不重启,初值为1 ;第2位为中断使能位,初值为0 ;第3、4位为时钟分频因子,初值为00, ;第5位为看门狗的使能位初值为1 ;第8~15位为比例因子,初值为0x80 ldr r0, =pWTCON mov r1, #0x0 ;将看门狗寄存器所有位置零,关闭看门狗,其实只要将第5位置0即可 str r1, [r0] ;屏蔽所有中断,实际上中断屏蔽掩码寄存器初值即为0xFFFFFFF mov r1, #0xffffffff ldr r0, =INTMSK str r1, [r0] # if defined(CONFIG_S3C2410) ;设置子中断掩码寄存器 ldr r1, =0x3ff ldr r0, =INTSUBMSK str r1, [r0] # endif ;设置时钟寄存器,CLKDIVN第0位为PDIVN,为0则PCLK=HCLK,为1则PCLK=HCLK/2 ;第1位为HDIVN,为0则HCLK=FCLK,为1则HCLK=FCLK/2 ;这里两位均为1,则FCLK:HCLK:PCLK = 4:2:1 /* default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN mov r1, #3 str r1, [r0] #endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */ /*
* we do sys-critical inits only at reboot, * not when booting from ram! */ ;对临界寄存器的初始化,如果从ram中启动则不执行,如果重启则执行 #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif ;重定向代码,也就是从flash中复制到ram中 #ifndef CONFIG_SKIP_RELOCATE_UBOOT relocate: /* relocate U-Boot to RAM */ ;当前代码地址,adr获取当前代码的地址信息,若从ram运行则_start=TEXT_BASE,否则_start=0x00000000 adr r0, _start /* r0 <- current position of code */ ;获取_TEXT_BASE ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* don't reloc during debug */ ;两者相等,表示从ram运行则跳转到堆栈设置 beq stack_setup ;不相等则表示从flash中运行,重定向代码 ldr r2, _armboot_start ;获取未初始化数据段地址 ldr r3, _bss_start ;计算代码段大小 sub r2, r3, r2 /* r2 <- size of armboot */ ;计算代码段终止地址 add r2, r0, r2 /* r2 <- source end address */ ;复制代码,r0为代码的起始地址,r1为ram中地址,r2为代码的终止地址 ;每次copy后将r0值递增同r2比较来判断是否复制完成 copy_loop: ldmia r0!, {r3-r10} /* copy from source address [r0] */ stmia r1!, {r3-r10} /* copy to target address [r1] */ cmp r0, r2 /* until source end addreee [r2] */ ble copy_loop #endif /* CONFIG_SKIP_RELOCATE_UBOOT */ /* Set up the stack */
stack_setup: ;获取_TEXT_BASE ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ ;获取分配区域起始指针,CFG_MALLOC_LEN=128*1024+CFG_ENV_SIZE=128*1024+0x10000=192K sub r0, r0, #CFG_MALLOC_LEN /* malloc area */ ;另外分配128bytes来存储开发板信息 sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQ sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif ;再减去12bytes用于栈起点 sub sp, r0, #12 /* leave 3 words for abort-stack */ ;清空未初始化数据段 clear_bss: ldr r0, _bss_start /* find start of bss segment */ ldr r1, _bss_end /* stop here */ mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4 cmp r0, r1 ble clbss_l #if 0
;关闭看门狗 /* try doing this stuff after the relocation */ ldr r0, =pWTCON mov r1, #0x0 str r1, [r0] /*
* mask all IRQs by setting all bits in the INTMR - default */ ;禁止中断 mov r1, #0xffffffff ldr r0, =INTMR str r1, [r0] ;设置时钟 /* FCLK:HCLK:PCLK = 1:2:4 */ /* default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN mov r1, #3 str r1, [r0] /* END stuff after relocation */ #endif ;完成复制后跳转到start_armboot,到这里就进入函数lib_arm/board.c的start_armboot函数中 ldr pc, _start_armboot _start_armboot: .word start_armboot
; 这里指的从flash中运行是指的从flash rom中运行,也就是常说的从nor flash中运行程序,现在有很多开发板;都是利用SDRAM和NAND-FLASH共同工作,所以需要添加从nand flash启动的代码。在ldr pc,_start_armboot前;;添加如下代码: #ifdef CONFIG_S3C2410_NAND_BOOT bl copy_myself @ jump to ram ldr r1, =on_the_ram add pc, r1, #0 nop nop 1: b 1b @ infinite loop on_the_ram: #endif ;开始就跳转到函数copy_myself中,
#ifdef CONFIG_S3C2410_NAND_BOOT copy_myself: ;保存断点地址 mov r10, lr @ reset NAND ;NAND_CTRL_BASE为nand flash的寄存器基址为0x4E000000 ;根据flash手册来复位flash mov r1, #NAND_CTL_BASE ;通过nand flash配置寄存器配置nand flash,该寄存器仅低16位有效 ;第15位为nand flash控制器使能位,第12位为初始化ECC使能位,第11位为nand falsh memory nFCE使能 ldr r2, =0xf830 @ initial value str r2, [r1, #oNFCONF] ldr r2, [r1, #oNFCONF] ;将第11位清零,使能芯片 bic r2, r2, #0x800 @ enable chip str r2, [r1, #oNFCONF] ;写入命令 mov r2, #0xff @ RESET command strb r2, [r1, #oNFCMD] ;循环延时 mov r3, #0 @ wait 1: add r3, r3, #0x1 cmp r3, #0xa blt 1b ;利用状态寄存器测试flash内部操作是否完成,如果完成则其状态寄存器将返回1 ;等待flash操作完成 2: ldr r2, [r1, #oNFSTAT] @ wait ready tst r2, #0x1 beq 2b ;禁止芯片 ldr r2, [r1, #oNFCONF] orr r2, r2, #0x800 @ disable chip str r2, [r1, #oNFCONF] @ get read to call C functions (for nand_read())
;建立堆栈,栈起点为0x33f00000,大小为0x8000 ldr sp, DW_STACK_START @ setup stack pointer mov fp, #0 @ no previous frame, so fp=0 @ copy U-BOOT to RAM
;UBOOT_RAM_BASE应该同TEXT_BASE相同 ldr r0, =UBOOT_RAM_BASE mov r1, #0x0 @address mov r2, #0x30000 @size ;这里设置大小为192K,为什么取这个值?为什么这三个寄存器的值就作为参数传递进去了? ;跳转到board/smdk2410/nand_read.c中的nand_read_ll函数,代码见后面 ;该函数需要三个参数,r0为其在ram中的起始地址,r1为在源地址也就是flash中的起始地址,r2为需要 ;复制的中大小 bl nand_read_ll tst r0, #0x0 ;r0为返回值 ;如果成功完成复制则跳转到ok_nand_read beq ok_nand_read #ifdef CONFIG_DEBUG_LL
bad_nand_read: ldr r0, STR_FAIL ldr r1, SerBase bl PrintWord 1: b 1b @ infinite loop #endif ;打印信息 ok_nand_read: #ifdef CONFIG_DEBUG_LL ldr r0, STR_OK ldr r1, SerBase bl PrintWord #endif ;校验,因为从nand flash启动只需要将bootloader的前4K代码复制到stepping stone处即可 @ verify mov r0, #0 ldr r1, =UBOOT_RAM_BASE mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes go_next: ldr r3, [r0], #4 ldr r4, [r1], #4 teq r3, r4 bne notmatch subs r2, r2, #4 beq done_nand_read bne go_next notmatch:
#ifdef CONFIG_DEBUG_LL sub r0, r0, #4 ldr r1, SerBase bl PrintHexWord ldr r0, STR_FAIL ldr r1, SerBase bl PrintWord #endif 1: b 1b done_nand_read:
#ifdef CONFIG_DEBUG_LL ldr r0, STR_OK ldr r1, SerBase bl PrintWord #endif ;恢复断点,程序继续运行 mov pc, r10 ;内存清零???
@ clear memory @ r0: start address @ r1: length mem_clear: mov r2, #0 mov r3, r2 mov r4, r2 mov r5, r2 mov r6, r2 mov r7, r2 mov r8, r2 mov r9, r2 clear_loop: stmia r0!, {r2-r9} subs r1, r1, #(8 * 4) bne clear_loop mov pc, lr #endif @ CONFIG_S3C2410_NAND_BOOT
/* ************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * ************************************************************************* */ ;对临界寄存器的初始化
#ifndef CONFIG_SKIP_LOWLEVEL_INIT cpu_init_crit: /* * flush v4 I/D caches */ ;清空指令和数据caches mov r0, #0 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ /*
* disable MMU stuff and caches */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) orr r0, r0, #0x00000002 @ set bit 2 (A) Align orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache mcr p15, 0, r0, c1, c0, 0 /*
* before relocating, we have to setup RAM timing * because memory timing is board-dependend, you will * find a lowlevel_init.S in your board directory. */ ;在重定向代码之前,必须初始化内存时序,因为重定向时需要将flash中的代码复制到内存中 ;内存初始化的代码在开发板目录下 mov ip, lr bl lowlevel_init mov lr, ip mov pc, lr #endif /* CONFIG_SKIP_LOWLEVEL_INIT */ /*
************************************************************************* * * Interrupt handling * ************************************************************************* */ ;中断处理 @ @ IRQ stack frame. @ #define S_FRAME_SIZE 72 #define S_OLD_R0 68
#define S_PSR 64 #define S_PC 60 #define S_LR 56 #define S_SP 52 #define S_IP 48
#define S_FP 44 #define S_R10 40 #define S_R9 36 #define S_R8 32 #define S_R7 28 #define S_R6 24 #define S_R5 20 #define S_R4 16 #define S_R3 12 #define S_R2 8 #define S_R1 4 #define S_R0 0 #define MODE_SVC 0x13
#define I_BIT 0x80 /*
* use bad_save_user_regs for abort/prefetch/undef/swi ... * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling */ .macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ Calling r0-r12 ldr r2, _armboot_start sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN) sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack ldmia r2, {r2 - r3} @ get pc, cpsr add r0, sp, #S_FRAME_SIZE @ restore sp_SVC add r5, sp, #S_SP
mov r1, lr stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr mov r0, sp .endm .macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ Calling r0-r12 add r8, sp, #S_PC stmdb r8, {sp, lr}^ @ Calling SP, LR str lr, [r8, #0] @ Save calling PC mrs r6, spsr str r6, [r8, #4] @ Save CPSR str r0, [r8, #8] @ Save OLD_R0 mov r0, sp .endm .macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr mov r0, r0 ldr lr, [sp, #S_PC] @ Get PC add sp, sp, #S_FRAME_SIZE subs pc, lr, #4 @ return & move spsr_svc into cpsr .endm .macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN) sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack str lr, [r13] @ save caller lr / spsr
mrs lr, spsr str lr, [r13, #4] mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13 msr spsr, r13 mov lr, pc movs pc, lr .endm .macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START .endm .macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START .endm /*
* exception handlers */ .align 5 undefined_instruction: get_bad_stack bad_save_user_regs bl do_undefined_instruction .align 5
software_interrupt: get_bad_stack bad_save_user_regs bl do_software_interrupt .align 5
prefetch_abort: get_bad_stack bad_save_user_regs bl do_prefetch_abort .align 5
data_abort: get_bad_stack bad_save_user_regs bl do_data_abort .align 5
not_used: get_bad_stack bad_save_user_regs bl do_not_used #ifdef CONFIG_USE_IRQ
.align 5
irq: get_irq_stack irq_save_user_regs bl do_irq irq_restore_user_regs .align 5
fiq: get_fiq_stack /* someone ought to write a more effiction fiq_save_user_regs */ irq_save_user_regs bl do_fiq irq_restore_user_regs #else
.align 5
irq: get_bad_stack bad_save_user_regs bl do_irq .align 5
fiq: get_bad_stack bad_save_user_regs bl do_fiq #endif
#ifdef CONFIG_S3C2410_NAND_BOOT .align 2 DW_STACK_START: .word STACK_BASE+STACK_SIZE-4 #endif //该函数来自于board/smdk2410/nand_read.c //通过r0,r1,r2三个寄存器传递参数过来,返回值为r0 int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size) { int i, j; if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
return -1; /* invalid alignment */ } //按照芯片的时序来进行读写操作 /* chip Enable */ NFCONF &= ~0x800; for(i=0; i<10; i++); for(i=start_addr; i < (start_addr + size);) {
/* READ0 */ NFCMD = 0; /* Write Address */
NFADDR = i & 0xff; NFADDR = (i >> 9) & 0xff; NFADDR = (i >> 17) & 0xff; NFADDR = (i >> 25) & 0xff; wait_idle();
for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {
*buf = (NFDATA & 0xff); buf++; } } /* chip Disable */
NFCONF |= 0x800; /* chip disable */ return 0;
} board/qt2410/lowlevel_init.S
;总线宽度和等待寄存器 #define BWSCON 0x48000000 ;宽度8bits、16bits、32bits /* BWSCON */ #define DW8 (0x0) #define DW16 (0x1) #define DW32 (0x2) ;等待 #define WAIT (0x1<<2) ;UBLB标识引脚信号的类型,为0则为nWBE,为1则为nBE #define UBLB (0x1<<3) ;定义总线类型 #define B1_BWSCON (DW32) #define B2_BWSCON (DW16) #define B3_BWSCON (DW16 + WAIT + UBLB) #define B4_BWSCON (DW16)1 #define B5_BWSCON (DW16) #define B6_BWSCON (DW32) #define B7_BWSCON (DW32) ;bank0寄存器 /* BANK0CON */ #define B0_Tacs 0x0 /* 0clk */ #define B0_Tcos 0x0 /* 0clk */ #define B0_Tacc 0x7 /* 14clk */ #define B0_Tcoh 0x0 /* 0clk */ #define B0_Tah 0x0 /* 0clk */ #define B0_Tacp 0x0 #define B0_PMC 0x0 /* normal */ ;bank1寄存器 /* BANK1CON */ #define B1_Tacs 0x0 /* 0clk */ #define B1_Tcos 0x0 /* 0clk */ #define B1_Tacc 0x7 /* 14clk */ #define B1_Tcoh 0x0 /* 0clk */ #define B1_Tah 0x0 /* 0clk */ #define B1_Tacp 0x0 #define B1_PMC 0x0 ;bank2寄存器 #define B2_Tacs 0x0 #define B2_Tcos 0x0 #define B2_Tacc 0x7 #define B2_Tcoh 0x0 #define B2_Tah 0x0 #define B2_Tacp 0x0 #define B2_PMC 0x0 ;bank3寄存器 #define B3_Tacs 0x0 /* 0clk */ #define B3_Tcos 0x3 /* 4clk */ #define B3_Tacc 0x7 /* 14clk */ #define B3_Tcoh 0x1 /* 1clk */ #define B3_Tah 0x0 /* 0clk */ #define B3_Tacp 0x3 /* 6clk */ #define B3_PMC 0x0 /* normal */ ;bank4寄存器 #define B4_Tacs 0x0 /* 0clk */ #define B4_Tcos 0x0 /* 0clk */ #define B4_Tacc 0x7 /* 14clk */ #define B4_Tcoh 0x0 /* 0clk */ #define B4_Tah 0x0 /* 0clk */ #define B4_Tacp 0x0 #define B4_PMC 0x0 /* normal */ ;bank5寄存器 #define B5_Tacs 0x0 /* 0clk */ #define B5_Tcos 0x0 /* 0clk */ #define B5_Tacc 0x7 /* 14clk */ #define B5_Tcoh 0x0 /* 0clk */ #define B5_Tah 0x0 /* 0clk */ #define B5_Tacp 0x0 #define B5_PMC 0x0 /* normal */ ;bank6寄存器 #define B6_MT 0x3 /* SDRAM */ #define B6_Trcd 0x1 #define B6_SCAN 0x1 /* 9bit */ ;bank7寄存器 #define B7_MT 0x3 /* SDRAM */ #define B7_Trcd 0x1 /* 3clk */ #define B7_SCAN 0x1 /* 9bit */ /* REFRESH parameter */
#define REFEN 0x1 /* Refresh enable */ #define TREFMD 0x0 /* CBR(CAS before RAS)/Auto refresh */ #define Trp 0x0 /* 2clk */ #define Trc 0x3 /* 7clk */ #define Tchr 0x2 /* 3clk */ #define REFCNT 1113 /* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */ /**************************************/ _TEXT_BASE:
.word TEXT_BASE .globl lowlevel_init
lowlevel_init: /* memory control configuration */ /* make r0 relative the current location so that it */ /* reads SMRDATA out of FLASH rather than memory ! */ ldr r0, =SMRDATA ldr r1, _TEXT_BASE sub r0, r0, r1 ldr r1, =BWSCON /* Bus Width Status Controller */ add r2, r0, #13*4 /*added by kyle*/ lmov r3,pc lldr r4,=0x3FFF0000 land r3,r3,r4 laad r0,r0,r3 ladd r2,r2,r3 0: ldr r3, [r0], #4
str r3, [r1], #4 cmp r2, r0 bne 0b /* everything is fine now */
mov pc, lr .ltorg
/* the literal pools origin */ SMRDATA:
.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) .word 0x32 .word 0x30 .word 0x30 ;定义函数指针 typedef int (init_fnc_t) (void); ;定义函数指针数组,对硬件初始化按照该数组进行 init_fnc_t *init_sequence[] = { cpu_init, //cpu/arm920t/cpu.c中定义,该函数为空,因为没有采用IRQ或FIQ模式 board_init, //board/smdk2410/smdk2410.c interrupt_init, //cpu/arm920t/s3c24x0/interrupt.c env_init, //tools/env/FW_env.c init_baudrate, //lib_arm/board.c serial_init, //cpu/arm920t/s3c24x0/serial.c console_init_f, //common/console.c display_banner, //lib_arm/board.c #if defined(CONFIG_DISPLAY_CPUINFO) print_cpuinfo, // #endif #if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, // #endif dram_init, //board/smdk2410/smdk2410.c display_dram_config,//lib_arm/board.c NULL, }; int board_init (void) { ;将时间相关的寄存器定义为结构体S3C24X0_CLOCK_POWER,S3C24X0_GPIO也是一样 S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO(); ;设置cpu时钟 /* to reduce PLL lock time, adjust the LOCKTIME register */ clk_power->LOCKTIME = 0xFFFFFF; /* configure MPLL */
//M_MDIV=0xA1,M_PDIV=0x3,M_SDIV=0x1 //这样系统时钟为202.80M clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV); /* some delay between MPLL and UPLL */
delay (4000); ;USB时钟为48M /* configure UPLL */ clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV); /* some delay between MPLL and UPLL */
delay (8000); ;设置GPIO /* set up the I/O ports */ gpio->GPACON = 0x007FFFFF; gpio->GPBCON = 0x00044555; gpio->GPBUP = 0x000007FF; gpio->GPCCON = 0xAAAAAAAA; gpio->GPCUP = 0x0000FFFF; gpio->GPDCON = 0xAAAAAAAA; gpio->GPDUP = 0x0000FFFF; gpio->GPECON = 0xAAAAAAAA; gpio->GPEUP = 0x0000FFFF; gpio->GPFCON = 0x000055AA; gpio->GPFUP = 0x000000FF; gpio->GPGCON = 0xFF95FFBA; gpio->GPGUP = 0x0000FFFF; gpio->GPHCON = 0x002AFAAA; gpio->GPHUP = 0x000007FF; ;初始化bd结构体中的bi_arch_number和bi_boot_params /* arch number of SMDK2410-Board */ gd->bd->bi_arch_number = MACH_TYPE_SMDK2410; /* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100; ;启用指令和数据cache ;通过对协处理器的操作了实现cache的使能 icache_enable(); dcache_enable(); return 0;
} int interrupt_init (void) { ;获取计时控制寄存器 S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS(); ;使用PWM定时器4 /* use PWM Timer 4 because it has no output */ /* prescaler for Timer 4 is 16 */ timers->TCFG0 = 0x0f00; if (timer_load_val == 0) { /* * for 10 ms clock period @ PCLK with 4 bit divider = 1/2 * (default) and prescaler = 16. Should be 10390 * @33.25MHz and 15625 @ 50 MHz */ timer_load_val = get_PCLK()/(2 * 16 * 100); } /* load value for 10 ms timeout */ lastdec = timers->TCNTB4 = timer_load_val; /* auto load, manual update of Timer 4 */ timers->TCON = (timers->TCON & ~0x0700000) | 0x600000; /* auto load, start Timer 4 */ timers->TCON = (timers->TCON & ~0x0700000) | 0x500000; timestamp = 0; return (0);
} static int env_init (void) { int crc1, crc1_ok; uchar *addr1; int crc2, crc2_ok;
uchar flag1, flag2, *addr2; //解析参数,定义了两个envdev_t型变量 //typedef struct envdev_s { //uchar devname[16]; /* Device name */ //ulong devoff; /* Device offset */ //ulong env_size; /* environment size */ //ulong erase_size; /* device erase size */ //} envdev_t; //程序中定义了/dev/mtd1和/dev/mtd2两个,parse_config函数用来初始化这两个结构体,并利用stat函数 //初始化一个struct stat结构体 if (parse_config ()) /* should fill envdevices */ return 1; //为参数分配空间,ENV_SIZE=CFG_ENV_SIZE - ENV_HEADER_SIZE=0x10000-sizeof(unsigned long) if ((addr1 = calloc (1, ENV_SIZE)) == NULL) { fprintf (stderr, "Not enough memory for environment (%ld bytes)n", ENV_SIZE); return (errno); } //从flash中读取参数 //typedef struct environment_s { //ulong crc; /* CRC32 over data bytes */ //uchar flags; /* active or obsolete */ //uchar *data; //} env_t; /* read environment from FLASH to local buffer */ //确定其指针 environment.data = addr1; curdev = 0; ;该函数用来将旧的参数从flash中擦除,然后将新的参数写入flash if (flash_io (O_RDONLY)) { return (errno); } ;进行crc校验 crc1_ok = ((crc1 = crc32 (0, environment.data, ENV_SIZE)) == environment.crc); if (!HaveRedundEnv) { if (!crc1_ok) { fprintf (stderr, "Warning: Bad CRC, using default environmentn"); memcpy(environment.data, default_environment, sizeof default_environment); } } else { flag1 = environment.flags; curdev = 1;
if ((addr2 = calloc (1, ENV_SIZE)) == NULL) { fprintf (stderr, "Not enough memory for environment (%ld bytes)n", ENV_SIZE); return (errno); } environment.data = addr2; if (flash_io (O_RDONLY)) {
return (errno); } crc2_ok = ((crc2 = crc32 (0, environment.data, ENV_SIZE))
== environment.crc); flag2 = environment.flags; if (crc1_ok && !crc2_ok) {
environment.data = addr1; environment.flags = flag1; environment.crc = crc1; curdev = 0; free (addr2); } else if (!crc1_ok && crc2_ok) { environment.data = addr2; environment.flags = flag2; environment.crc = crc2; curdev = 1; free (addr1); } else if (!crc1_ok && !crc2_ok) { fprintf (stderr, "Warning: Bad CRC, using default environmentn"); memcpy(environment.data, default_environment, sizeof default_environment); curdev = 0; free (addr1); } else if (flag1 == active_flag && flag2 == obsolete_flag) { environment.data = addr1; environment.flags = flag1; environment.crc = crc1; curdev = 0; free (addr2); } else if (flag1 == obsolete_flag && flag2 == active_flag) { environment.data = addr2; environment.flags = flag2; environment.crc = crc2; curdev = 1; free (addr1); } else if (flag1 == flag2) { environment.data = addr1; environment.flags = flag1; environment.crc = crc1; curdev = 0; free (addr2); } else if (flag1 == 0xFF) { environment.data = addr1; environment.flags = flag1; environment.crc = crc1; curdev = 0; free (addr2); } else if (flag2 == 0xFF) { environment.data = addr2; environment.flags = flag2; environment.crc = crc2; curdev = 1; free (addr1); } } return (0); } //如果参数中设置了波特率则利用参数用设置的波特率,否则利用默认的CONFIG_BAUDRATE(115200) static int init_baudrate (void) { char tmp[64]; /* long enough for environment variables */ int i = getenv_r ("baudrate", tmp, sizeof (tmp)); gd->bd->bi_baudrate = gd->baudrate = (i > 0) (int) simple_strtoul (tmp, NULL, 10) : CONFIG_BAUDRATE; return (0);
} int serial_init (void)
{ //该函数设置uart的几个寄存器,包括FIFO寄存器、控制寄存器、列控制寄存器和波特率约数寄存器等 serial_setbrg (); return (0); } ;设置gd->have_console int console_init_f (void) { gd->have_console = 1; #ifdef CONFIG_SILENT_CONSOLE
if (getenv("silent") != NULL) gd->flags |= GD_FLG_SILENT; #endif return (0);
} //打印当前一些状态信息 static int display_banner (void) { printf ("nn%snn", version_string); debug ("U-Boot code: %08lX -> %08lX BSS: -> %08lXn", _armboot_start, _bss_start, _bss_end); #ifdef CONFIG_MODEM_SUPPORT debug ("Modem Support enabledn"); #endif #ifdef CONFIG_USE_IRQ debug ("IRQ Stack: %08lxn", IRQ_STACK_START); debug ("FIQ Stack: %08lxn", FIQ_STACK_START); #endif return (0);
} //初始化ram信息,设置起始地址和大小,从include/configs/smdk2410.h中获取这些信息 int dram_init (void) { gd->bd->bi_dram[0].start = PHYS_SDRAM_1; gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; return 0;
} //显示ram信息,其中的宏也是从include/configs/smdk2410.h中读取 static int display_dram_config (void) { int i; #ifdef DEBUG
puts ("RAM Configuration:n"); for(i=0; i<CONFIG_NR_DRAM_BANKS; i++) {
printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start); print_size (gd->bd->bi_dram[i].size, "n"); } #else ulong size = 0; for (i=0; i<CONFIG_NR_DRAM_BANKS; i++) {
size += gd->bd->bi_dram[i].size; } puts("DRAM: "); print_size(size, "n"); #endif return (0);
} //以上都是一些初始化的函数,可以看出以上这些函数都是为了初始化一个全局的结构体变量gd而执行的, //该变量地址由寄存器r8指向,该结构体定义了开发板的相关硬件配置,在include/asm-arm/global_data.h中 //定义 /* typedef struct global_data { bd_t *bd;//开发板参数 unsigned long flags; unsigned long baudrate; unsigned long have_console; /* serial_init() was called */ unsigned long reloc_off; /* Relocation Offset */ unsigned long env_addr; /* Address of Environment struct */ unsigned long env_valid; /* Checksum of Environment valid? */ unsigned long fb_base; /* base address of frame buffer */ #ifdef CONFIG_VFD unsigned char vfd_type; /* display type */ #endif #if 0 unsigned long cpu_clk; /* CPU clock in Hz! */ unsigned long bus_clk; unsigned long ram_size; /* RAM size */ unsigned long reset_status; /* reset status register at boot */ #endif void **jt; /* jump table */ } gd_t; */ void start_armboot (void)
{ init_fnc_t **init_fnc_ptr; char *s; #ifndef CFG_NO_FLASH ulong size; #endif #if defined(CONFIG_VFD) || defined(CONFIG_LCD) unsigned long addr; #endif /* Pointer is writable since we allocated a register for it */
//获取全局gd指针 gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("": : :"memory"); //清空该结构体 memset ((void*)gd, 0, sizeof (gd_t)); //获取bd_info结构体指针 gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); memset (gd->bd, 0, sizeof (bd_t)); //整个代码区的长度 monitor_flash_len = _bss_start - _armboot_start; //调用初始化函数,用来初始化gd结构体 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } } #ifndef CFG_NO_FLASH
/* configure available FLASH banks */ //board/smdk2410/flash.c配置flash //从其实现来看,好像只是配置nor flash size = flash_init (); //显示flash信息 display_flash_config (size); #endif /* CFG_NO_FLASH */ //定义显示类型 #ifdef CONFIG_VFD # ifndef PAGE_SIZE # define PAGE_SIZE 4096 # endif /* * reserve memory for VFD display (always full pages) */ /* bss_end is defined in the board-specific linker script */ //按页对其方式保留显存 addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); size = vfd_setmem (addr); gd->fb_base = addr; #endif /* CONFIG_VFD */ //显示器为LCD,同上 #ifdef CONFIG_LCD # ifndef PAGE_SIZE # define PAGE_SIZE 4096 # endif /* * reserve memory for LCD display (always full pages) */ /* bss_end is defined in the board-specific linker script */ addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); size = lcd_setmem (addr); gd->fb_base = addr; #endif /* CONFIG_LCD */ //初始化CFG_MALLOC_LEN大小空间 /* armboot_start is defined in the board-specific linker script */ mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); //初始化nand flash,这是在nand flash启动的s3c2410移植u-boot的关键,根据flash时序编写函数即可
//在include/configs/smdk2410.h中的command definition中增加CONFIG_COMMANDS和CFG_CMD_NAND命令 #if (CONFIG_COMMANDS & CFG_CMD_NAND) puts ("NAND: "); nand_init(); //board/smdk2410/smdk2410.c #endif #ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit(); dataflash_print_info(); #endif /* initialize environment */
//初始化环境参数 env_relocate (); //framebuffer初始化 #ifdef CONFIG_VFD /* must do this after the framebuffer is allocated */ drv_vfd_init(); #endif /* CONFIG_VFD */ //通过命令行参数传递获取ip地址 /* IP Address */ gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); //通过命令行参数传递获取物理地址 /* MAC Address */ { int i; ulong reg; char *s, *e; char tmp[64]; i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL; for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0; if (s) s = (*e) ? e + 1 : e; } #ifdef CONFIG_HAS_ETH1
i = getenv_r ("eth1addr", tmp, sizeof (tmp)); s = (i > 0) ? tmp : NULL; for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0; if (s) s = (*e) ? e + 1 : e; } #endif } //调用相应驱动函数对硬件设备进行初始化 devices_init (); /* get the devices list going. */ #ifdef CONFIG_CMC_PU2
load_sernum_ethaddr (); #endif /* CONFIG_CMC_PU2 */ jumptable_init ();
//初始化串口 console_init_r (); /* fully init console as a device */ #if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */ misc_init_r (); #endif /* enable exceptions */
//启用中断 enable_interrupts (); /* Perform network card initialisation if necessary */
//初始化网卡 #ifdef CONFIG_DRIVER_CS8900 cs8900_get_enetaddr (gd->bd->bi_enetaddr); #endif #if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
if (getenv ("ethaddr")) { smc_set_mac_addr(gd->bd->bi_enetaddr); } #endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */ /* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) { load_addr = simple_strtoul (s, NULL, 16); } #if (CONFIG_COMMANDS & CFG_CMD_NET) if ((s = getenv ("bootfile")) != NULL) { copy_filename (BootFile, s, sizeof (BootFile)); } #endif /* CFG_CMD_NET */ #ifdef BOARD_LATE_INIT
board_late_init (); #endif #if (CONFIG_COMMANDS & CFG_CMD_NET) #if defined(CONFIG_NET_MULTI) puts ("Net: "); #endif eth_initialize(gd->bd); #endif /* main_loop() can return to retry autoboot, if so just run it again. */ for (;;) { main_loop (); } /* NOTREACHED - no way out of command loop except booting */
} //在board/smdk2410/smdk2410.c中增加nand flash初始化代码 /* * NAND flash initialization. */ // 在include/s3c24x0.h中由S3C2410_NAND结构体的定义,也就是定义了NFCONF、NFCMD、NFADDR和NFSTAT这 四个//寄存器,由于2410有flash控制器,因此可以直接通过flash控制器来对flash器件进行操作,如果芯片没有//flash控制器,有 的利用I/O端口来同flash交换数据,就必须利用I/O口来模式时序。比如44B0X就是利用GPIO //来进行flash读写的 #if (CONFIG_COMMANDS & CFG_CMD_NAND) typedef enum {
NFCE_LOW, NFCE_HIGH, } NFCE_STATE; extern unsigned long nand_probe(unsigned long physadr);
static inline void NF_Reset(void)
{ int i; //使能 NF_SetCE(NFCE_LOW); //写入命令 NF_Cmd(0xFF); // reset command //延时 for(i = 0; i < 10; i++); // tWB = 100ns. //通过R/B信号线,判断flash内部操作是否完成, NF_WaitRB(); // wait 200~500us; //芯片禁用 NF_SetCE(NFCE_HIGH); } static inline void NF_Init(void) { #if 0 // a little bit too optimistic #define TACLS 0 #define TWRPH0 3 #define TWRPH1 0 #else #define TACLS 0 #define TWRPH0 4 #define TWRPH1 2 #endif //首先根据NFCONF寄存器的各位定义对Nand flash进行初始化 NF_Conf((1<<15)|(0<<14)|(0<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0)); NF_Reset(); } void nand_init(void)
{ S3C2410_NAND * const nand = S3C2410_GetBase_NAND(); NF_Init(); //利用drivers/nand_legacy/nand_legacy.c,在该文件中也添加这几个同nand flash相关的函数 printf ("%4lu MBn", nand_probe((ulong)nand) >> 20); } //操作控制寄存器,该寄存器为16位寄存器 static void NF_Conf(u16 conf) { S3C2410_NAND * const nand = S3C2410_GetBase_NAND(); nand->NFCONF = conf; } //flash只有8位的输入、输出端口,直接向命令寄存器赋值即可写入命令 static void NF_Cmd(u8 cmd) { S3C2410_NAND * const nand = S3C2410_GetBase_NAND(); nand->NFCMD = cmd; } static void NF_CmdW(u8 cmd)
{ NF_Cmd(cmd); udelay(1); } //写入地址 static void NF_Addr(u8 addr) { S3C2410_NAND * const nand = S3C2410_GetBase_NAND(); nand->NFADDR = addr; } //芯片使能 static void NF_SetCE(NFCE_STATE s) { S3C2410_NAND * const nand = S3C2410_GetBase_NAND(); //Nand flash memroy chip enable,如果NFCONF的第11位为1则nFCE使能,为1则禁止 switch (s) { case NFCE_LOW: nand->NFCONF &= ~(1<<11); break; case NFCE_HIGH: nand->NFCONF |= (1<<11); break; } } //等待flash内部操作完成 static void NF_WaitRB(void) { S3C2410_NAND * const nand = S3C2410_GetBase_NAND(); while (!(nand->NFSTAT & 0x01)); } //向数据寄存器写入数据 static void NF_Write(u8 data) { S3C2410_NAND * const nand = S3C2410_GetBase_NAND(); nand->NFDATA = data; } //读取数据 static u8 NF_Read(void) { S3C2410_NAND * const nand = S3C2410_GetBase_NAND(); return(nand->NFDATA); } //校验 static void NF_Init_ECC(void) { S3C2410_NAND * const nand = S3C2410_GetBase_NAND(); nand->NFCONF |= (1<<12); } //获取ECC code static u32 NF_Read_ECC(void) { S3C2410_NAND * const nand = S3C2410_GetBase_NAND(); return(nand->NFECC); } #endif 通过上面的描述要移植U-Boot到s3c2410上,并要从nand flash启动,则需要修改的地方有: 1、在start.S中增加将U-Boot从flash复制到ram中的代码 2、在include/configs/smdk2410.h中增加相关定义,主要是nand flash寄存器的定义,并针对具体平台对其中的参数进行配置,比如ram的大小等。 3、在board/smdk2410/smdk2410.c中增加nand flash的相关操作函数,如复位、写指令、读数据、查看状态等。 再编译U-Boot则可以看到flash的定义,并能通过网络下载内核和文件系统等。 当U-Boot进入main_loop后将等待用户输入,或者在定时时间超过时调用do_bootm_linux来启动linux内核, U-Boot为各个处理器的do_bootm_linux定义了固定的类型,定义了boot_os_Fcn函数类型。 typedef void boot_os_Fcn (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[], ulong addr, /* of image to boot */ ulong *len_ptr, /* multi-file image length table */ int verify); /* getenv("verify")[0] != 'n' */ cmdtp结构体用来保存命令以及参数,addr为启动镜像的地址,程序首先进入do_bootm函数 int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { ulong iflag; ulong addr; ulong data, len, checksum; ulong *len_ptr; //定义最大的解压空间为8M uint unc_len = CFG_BOOTM_LEN; int i, verify; char *name, *s; int (*appl)(int, char *[]); //镜像文件的头部信息,传递给do_bootm_linux函数 image_header_t *hdr = &header; s = getenv ("verify");
verify = (s && (*s == 'n')) ? 0 : 1; //如果没有指定运行地址,则采用默认的地址,该地址在include/configs/smdk2410.h中定义为0x30008000 if (argc < 2) { addr = load_addr; } else { //如果参数中指定了地址,则从指定地址开始运行 addr = simple_strtoul(argv[1], NULL, 16); } //在文件开头定义CONFIG_SHOW_BOOT_PROGRESS函数用来显示当前内核运行到哪一步了 SHOW_BOOT_PROGRESS (1); printf ("## Booting image at %08lx ...n", addr); //系统中没有atmel的数据flash /* Copy header so we can blank CRC field for re-calculation */ #ifdef CONFIG_HAS_DATAFLASH if (addr_dataflash(addr)){ read_dataflash(addr, sizeof(image_header_t), (char *)&header); } else #endif //从镜像读取头部到结构header中 memmove (&header, (char *)addr, sizeof(image_header_t)); //通过头部结构体来获取magic、crc等数据 if (ntohl(hdr->ih_magic) != IH_MAGIC) { //i386 #ifdef __I386__ /* correct image format not implemented yet - fake it */ //通过头部结构体,检测镜像的格式是否正确 if (fake_header(hdr, (void*)addr, -1) != NULL) { /* to compensate for the addition below */ addr -= sizeof(image_header_t); /* turnof verify, * fake_header() does not fake the data crc */ verify = 0; } else #endif /* __I386__ */ //对于非i386平台 { puts ("Bad Magic Numbern"); SHOW_BOOT_PROGRESS (-1); return 1; } } //magic正确为第2步 SHOW_BOOT_PROGRESS (2); //校验crc data = (ulong)&header; len = sizeof(image_header_t); checksum = ntohl(hdr->ih_hcrc);
hdr->ih_hcrc = 0; if (crc32 (0, (uchar *)data, len) != checksum) {
puts ("Bad Header Checksumn"); SHOW_BOOT_PROGRESS (-2); return 1; } //校验成功为第3步 SHOW_BOOT_PROGRESS (3); #ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash(addr)){ len = ntohl(hdr->ih_size) + sizeof(image_header_t); read_dataflash(addr, len, (char *)CFG_LOAD_ADDR); addr = CFG_LOAD_ADDR; } #endif //对于多文件镜像的处理,这个部分是很有用的,特别对于容量很小的系统,有时候需要将代码段和数据段放//在不同的文件中
//显示镜像文件的信息,包括文件大小、创建时间、入口等 print_image_hdr ((image_header_t *)addr); //data为镜像文件头部后的第1个ulong数据,用来完成crc校验 data = addr + sizeof(image_header_t); //len为镜像问价的大小 len = ntohl(hdr->ih_size); //是否进行crc校验 if (verify) { puts (" Verifying Checksum ... "); if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) { printf ("Bad Data CRCn"); SHOW_BOOT_PROGRESS (-3); return 1; } puts ("OKn"); } //校验成功为第4步 SHOW_BOOT_PROGRESS (4); //数据指针 len_ptr = (ulong *)data; //校验cpu的体系结构 #if defined(__PPC__) if (hdr->ih_arch != IH_CPU_PPC) #elif defined(__ARM__) if (hdr->ih_arch != IH_CPU_ARM) #elif defined(__I386__) if (hdr->ih_arch != IH_CPU_I386) #elif defined(__mips__) if (hdr->ih_arch != IH_CPU_MIPS) #elif defined(__nios__) if (hdr->ih_arch != IH_CPU_NIOS) #elif defined(__M68K__) if (hdr->ih_arch != IH_CPU_M68K) #elif defined(__microblaze__) if (hdr->ih_arch != IH_CPU_MICROBLAZE) #elif defined(__nios2__) if (hdr->ih_arch != IH_CPU_NIOS2) #elif defined(__blackfin__) if (hdr->ih_arch != IH_CPU_BLACKFIN) #else # error Unknown CPU type #endif { printf ("Unsupported Architecture 0x%xn", hdr->ih_arch); SHOW_BOOT_PROGRESS (-4); return 1; } //体系结构校验成功为第5步 SHOW_BOOT_PROGRESS (5); //判断镜像文件类型 switch (hdr->ih_type) { //如果是独立的应用程序则利用第2个参数来作为加载地址 case IH_TYPE_STANDALONE: name = "Standalone Application"; /* A second argument overwrites the load address */ if (argc > 2) { hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16)); } break; //单文件类型内核 case IH_TYPE_KERNEL: name = "Kernel Image"; break; //多文件类型内核 case IH_TYPE_MULTI: name = "Multi-File Image"; //获取内核镜像大小 len = ntohl(len_ptr[0]); /* OS kernel is always the first image */ data += 8; /* kernel_len + terminator */ for (i=1; len_ptr[i]; ++i) data += 4; break; default: printf ("Wrong Image Type for %s commandn", cmdtp->name); SHOW_BOOT_PROGRESS (-5); return 1; } //类型判断成功为第6步 SHOW_BOOT_PROGRESS (6); /*
* We have reached the point of no return: we are going to * overwrite all exception vector code, so we cannot easily * recover from any failures any more... */ //关闭所有中断 iflag = disable_interrupts(); //处理cache #ifdef CONFIG_AMIGAONEG3SE /* * We've possible left the caches enabled during * bios emulation, so turn them off again */ icache_disable(); invalidate_l1_instruction_cache(); flush_data_cache(); dcache_disable(); #endif //判断镜像压缩类型,并利用对应程序对内核压缩镜像进行解压 switch (hdr->ih_comp) { case IH_COMP_NONE: if(ntohl(hdr->ih_load) == addr) { printf (" XIP %s ... ", name); } else { #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) size_t l = len; void *to = (void *)ntohl(hdr->ih_load); void *from = (void *)data; printf (" Loading %s ... ", name);
while (l > 0) {
size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l; WATCHDOG_RESET(); memmove (to, from, tail); to += tail; from += tail; l -= tail; } #else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */ memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len); #endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ } break; case IH_COMP_GZIP: printf (" Uncompressing %s ... ", name); if (gunzip ((void *)ntohl(hdr->ih_load), unc_len, (uchar *)data, &len) != 0) { puts ("GUNZIP ERROR - must RESET board to recovern"); SHOW_BOOT_PROGRESS (-6); do_reset (cmdtp, flag, argc, argv); } break; #ifdef CONFIG_BZIP2 case IH_COMP_BZIP2: printf (" Uncompressing %s ... ", name); /* * If we've got less than 4 MB of malloc() space, * use slower decompression algorithm which requires * at most 2300 KB of memory. */ i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load), &unc_len, (char *)data, len, CFG_MALLOC_LEN < (4096 * 1024), 0); if (i != BZ_OK) { printf ("BUNZIP2 ERROR %d - must RESET board to recovern", i); SHOW_BOOT_PROGRESS (-6); udelay(100000); do_reset (cmdtp, flag, argc, argv); } break; #endif /* CONFIG_BZIP2 */ default: //重新打开中断 if (iflag) enable_interrupts(); //不能识别的压缩类型 printf ("Unimplemented compression type %dn", hdr->ih_comp); SHOW_BOOT_PROGRESS (-7); return 1; } puts ("OKn"); //解压成功为第7步 SHOW_BOOT_PROGRESS (7); //如果是独立的应用程序,则根据autostart来确定是否自动启动程序 switch (hdr->ih_type) { case IH_TYPE_STANDALONE: if (iflag) enable_interrupts(); /* load (and uncompress), but don't start if "autostart"
* is set to "no" */ if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) { char buf[32]; sprintf(buf, "%lX", len); setenv("filesize", buf); return 0; } //hdr->ih_ep为程序的入口 appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep); //启动程序 (*appl)(argc-1, &argv[1]); return 0; case IH_TYPE_KERNEL: case IH_TYPE_MULTI: /* handled below */ break; default: if (iflag) enable_interrupts(); printf ("Can't boot image type %dn", hdr->ih_type); SHOW_BOOT_PROGRESS (-8); return 1; } //成功为第8步 SHOW_BOOT_PROGRESS (8); //判断操作系统类型 switch (hdr->ih_os) { default: /* handled by (original) Linux case */ case IH_OS_LINUX: #ifdef CONFIG_SILENT_CONSOLE fixup_silent_linux(); #endif printf("boot linuxn"); //进入armlinux.c中的do_bootm_linux函数 do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify); break; case IH_OS_NETBSD: do_bootm_netbsd (cmdtp, flag, argc, argv, addr, len_ptr, verify); break; #ifdef CONFIG_LYNXKDI
case IH_OS_LYNXOS: do_bootm_lynxkdi (cmdtp, flag, argc, argv, addr, len_ptr, verify); break; #endif case IH_OS_RTEMS:
do_bootm_rtems (cmdtp, flag, argc, argv, addr, len_ptr, verify); break; #if (CONFIG_COMMANDS & CFG_CMD_ELF)
case IH_OS_VXWORKS: do_bootm_vxworks (cmdtp, flag, argc, argv, addr, len_ptr, verify); break; case IH_OS_QNX: do_bootm_qnxelf (cmdtp, flag, argc, argv, addr, len_ptr, verify); break; #endif /* CFG_CMD_ELF */ #ifdef CONFIG_ARTOS case IH_OS_ARTOS: do_bootm_artos (cmdtp, flag, argc, argv, addr, len_ptr, verify); break; #endif } SHOW_BOOT_PROGRESS (-9);
#ifdef DEBUG puts ("n## Control returned to monitor - resetting...n"); do_reset (cmdtp, flag, argc, argv); #endif return 1; } 进入文件lib_arm/arm_linux.c中的do_bootm_linux
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[], ulong addr, ulong *len_ptr, int verify) { ulong len = 0, checksum; ulong initrd_start, initrd_end; ulong data; //内核调用入口 void (*theKernel)(int zero, int arch, uint params); //获取镜像的头部信息,header在cmd_bootm.c中定义 image_header_t *hdr = &header; //获取开发板信息 bd_t *bd = gd->bd; #ifdef CONFIG_CMDLINE_TAG
//获取bootargs参数 char *commandline = getenv ("bootargs"); #endif //调用内核入口函数,将内核入口函数指针赋给theKernel theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); /*
* Check if there is an initrd image */ //看是否为initrd镜像 if (argc >= 3) { //如果为initrd为第9步 SHOW_BOOT_PROGRESS (9); //获取initrd镜像地址 addr = simple_strtoul (argv[2], NULL, 16); printf ("## Loading Ramdisk Image at %08lx ...n", addr);
/* Copy header so we can blank CRC field for re-calculation */ #ifdef CONFIG_HAS_DATAFLASH if (addr_dataflash (addr)) { read_dataflash (addr, sizeof (image_header_t), (char *) &header); } else #endif //获取initrd头部西你想 memcpy (&header, (char *) addr, sizeof (image_header_t)); //校验magic if (ntohl (hdr->ih_magic) != IH_MAGIC) { printf ("Bad Magic Numbern"); SHOW_BOOT_PROGRESS (-10); do_reset (cmdtp, flag, argc, argv); } //校验crc data = (ulong) & header; len = sizeof (image_header_t); checksum = ntohl (hdr->ih_hcrc);
hdr->ih_hcrc = 0; if (crc32 (0, (unsigned char *) data, len) != checksum) {
printf ("Bad Header Checksumn"); SHOW_BOOT_PROGRESS (-11); do_reset (cmdtp, flag, argc, argv); } //校验成功为第10步 SHOW_BOOT_PROGRESS (10); print_image_hdr (hdr);
data = addr + sizeof (image_header_t);
len = ntohl (hdr->ih_size); #ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash (addr)) { read_dataflash (data, len, (char *) CFG_LOAD_ADDR); data = CFG_LOAD_ADDR; } #endif //checksum if (verify) { ulong csum = 0; printf (" Verifying Checksum ... ");
csum = crc32 (0, (unsigned char *) data, len); if (csum != ntohl (hdr->ih_dcrc)) { printf ("Bad Data CRCn"); SHOW_BOOT_PROGRESS (-12); do_reset (cmdtp, flag, argc, argv); } printf ("OKn"); } //成功为第11步 SHOW_BOOT_PROGRESS (11); //判断操作系统类型 if ((hdr->ih_os != IH_OS_LINUX) || (hdr->ih_arch != IH_CPU_ARM) || (hdr->ih_type != IH_TYPE_RAMDISK)) { printf ("No Linux ARM Ramdisk Imagen"); SHOW_BOOT_PROGRESS (-13); do_reset (cmdtp, flag, argc, argv); } #if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO)
/* *we need to copy the ramdisk to SRAM to let Linux boot */ memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len); data = ntohl(hdr->ih_load); #endif /* CONFIG_B2 || CONFIG_EVB4510 */ /*
* Now check if we have a multifile image */ } //判断是否为多文件内核镜像 else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) { ulong tail = ntohl (len_ptr[0]) % 4; int i; SHOW_BOOT_PROGRESS (13);
/* skip kernel length and terminator */
data = (ulong) (&len_ptr[2]); /* skip any additional image length fields */ for (i = 1; len_ptr[i]; ++i) data += 4; /* add kernel length, and align */ data += ntohl (len_ptr[0]); if (tail) { data += 4 - tail; } len = ntohl (len_ptr[1]);
} else {
/* * no initrd image */ //无initrd镜像 SHOW_BOOT_PROGRESS (14); len = data = 0;
} #ifdef DEBUG
if (!data) { printf ("No initrdn"); } #endif //确定initrd的起始地址和终止地址 if (data) { initrd_start = data; initrd_end = initrd_start + len; } else { initrd_start = 0; initrd_end = 0; } //成功为第15步 SHOW_BOOT_PROGRESS (15); //进入linux内核 debug ("## Transferring control to Linux (at address %08lx) ...n", (ulong) theKernel); #if defined (CONFIG_SETUP_MEMORY_TAGS) ||
defined (CONFIG_CMDLINE_TAG) || defined (CONFIG_INITRD_TAG) || defined (CONFIG_SERIAL_TAG) || defined (CONFIG_REVISION_TAG) || defined (CONFIG_LCD) || defined (CONFIG_VFD) //开始设置启动参数,该函数也在本文件中定义,见下面 setup_start_tag (bd); #ifdef CONFIG_SERIAL_TAG //设置串口参数 setup_serial_tag (¶ms); #endif #ifdef CONFIG_REVISION_TAG //设置修订参数 setup_revision_tag (¶ms); #endif #ifdef CONFIG_SETUP_MEMORY_TAGS //设置内存参数 setup_memory_tags (bd); #endif #ifdef CONFIG_CMDLINE_TAG //设置命令行参数 setup_commandline_tag (bd, commandline); #endif #ifdef CONFIG_INITRD_TAG //设置initrd参数 if (initrd_start && initrd_end) setup_initrd_tag (bd, initrd_start, initrd_end); #endif #if defined (CONFIG_VFD) || defined (CONFIG_LCD) //设置vfd、lcd参数 setup_videolfb_tag ((gd_t *) gd); #endif //结束参数设置 setup_end_tag (bd); #endif //启动内核 /* we assume that the kernel is in place */ printf ("nStarting kernel ...nn"); #ifdef CONFIG_USB_DEVICE
{ extern void udc_disconnect (void); udc_disconnect (); } #endif //在内核启动前的清除工作 cleanup_before_linux (); //调用内核 theKernel (0, bd->bi_arch_number, bd->bi_boot_params); } //开始设置启动参数 static void setup_start_tag (bd_t *bd) { params = (struct tag *) bd->bi_boot_params; 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);
}
|
|
来自: langhuayipian > 《bootloader》