首页 / 操作系统 / Linux / U-Boot系列之三: u-boot整体结构、移植步骤以及启动代码分析
本篇文章首先介绍u-boot的整体代码结构,移植的基本步骤,然后分析启动的代码(start.S)中 其中代码结构和移植步骤是参考了下面两个连接的文章,作者是 焦玉全 黄乡生 鲍玉军,题目是
第7540篇:U-Boot在S3C2410上的移植(具体URL忘了,我文章是下载的,知道的朋友告诉一声) 一、整体结构 首先下载u-boot的源代码(www.denx.de),解压缩,你可以看到下面的目录: - board 目标板相关文件,主要包含SDRAM、FLASH驱动; - common 独立于处理器体系结构的通用代码,如内存大小探测与故障检测; - cpu 与处理器相关的文件。如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件; - driver 通用设备驱动,如CFI FLASH驱动(目前对INTEL FLASH支持较好) - doc U-Boot的说明文档; - examples可在U-Boot下运行的示例程序;如hello_world.c,timer.c; - include U-Boot头文件;尤其configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件; - lib_xxx 处理器体系相关的文件,如lib_ppc, lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件; - net 与网络功能相关的文件目录,如bootp,nfs,tftp; - post 上电自检文件目录。尚有待于进一步完善; - rtc RTC驱动程序; - tools 用于创建U-Boot S-RECORD和BIN镜像文件的工具; 二、移植步骤 为了使U-Boot支持新的开发板,一种简便的做法是在U-Boot已经支持的开发板中选择一种和目标板接近的,并在其基础上进行修改。代码修改的步骤如下: 1)在board目录下创建smdk2410目录,添加smdk2410.c、flash.c、memsetup.s、u-boot.lds和config.mk等; 2)在cpu目录下创建arm920t目录,主要包含start.s、interrupts.c、cpu.c、serial.c和speed.c等文件; 3)在include/configs目录下添加smdk2410.h,它定义了全局的宏定义等; 4)修改u-boot根目录下的Makefile文件: smdk2410_config : unconfig@./mkconfig $(@:_config=) arm arm920t smdk2410 5)运行make smdk2410_config,如果没有错误,就可以开始进行与硬件相关的代码移植工作。由于这部分代码与硬件紧密相关,所以要熟悉开发板的硬件配置,可参考各芯片的用户手册。 当然,这个是一般步骤,后面我们做的可能具体文件名还和这个不一样,等到那时候在交待,这里先介绍的目的是在开始的时候给个大概的思路,要不直接分析源代码,有点在原始森林的感觉,耐心的看吧:) 三、start.S分析 首先介绍start.S中的代码的具体作用,由于该代码是系统最开始执行的,这时,u-boot对系统一无所知,必须要初始化一些东西,比如设置异常的入口地址和异常处理函数;配置PLLCON寄存器,确定系统的主频;屏蔽看门狗和中断;初始化I/O寄存器;关闭MMU功能;调用 /board/smdk2410中的memsetup.s,初始化存储器空间,设置刷新频率;将U-Boot的内容复制到SDRAM中;设置堆栈的大小, 然后ldr pc, _start_armboot,跳到C函数 代码来自华恒的板子上面的资料 /* CPU clcok */ /* 50.00 MHz */ #define MDIV_50 0x5c #define PDIV_50 0x4 #define SDIV_50 0x2 /* 100.00 MHz */ #define MDIV_100 0xa1 #define PDIV_100 0x3 #define SDIV_100 0x1 /* 200.00 MHz */ /*CPU clock = 202.800000 Mhz, HCLK = 101.400000 Mhz, PCLK = 50.700000 Mhz*/ /*0xa1 3 1*/ /*180Mhz 90Mhz 45Mhz 0x52 1 1*/ /*152MHZ 0x44 1 1*/ #define MDIV_200 0xa1 #define PDIV_200 0x3 #define SDIV_200 0x1 #define vMPLLCON_50 ((MDIV_50 << 12) | (PDIV_50 << 4) | (SDIV_50)) #define vMPLLCON_100 ((MDIV_100 << 12) | (PDIV_100 << 4) | (SDIV_100)) #define vMPLLCON_200 ((MDIV_200 << 12) | (PDIV_200 << 4) | (SDIV_200)) /* ************************************************************************* * * Jump vector table as in table 3.1 in [1] * ************************************************************************* */ .globl _start _start: b reset======================================〉程序从这里开始 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 _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 * ************************************************************************* */ /* * CFG_MEM_END is in the board dependent config-file (configs/config_BOARD.h) */ _TEXT_BASE: .word TEXT_BASE .globl _armboot_start _armboot_start: .word _start /* * Note: _armboot_end_data and _armboot_end are defined * by the (board-dependent) linker script. * _armboot_end_data is the first usable FLASH address after armboot */ .globl _armboot_end_data _armboot_end_data: .word armboot_end_data .globl _armboot_end _armboot_end: .word armboot_end /* * _armboot_real_end is the first usable RAM address behind armboot * and the various stacks */ .globl _armboot_real_end _armboot_real_end: .word 0x0badc0de #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:=============================================〉程序开始的时候跳转到这里 ldr r0, =pWTCON mov r1, #0x0 str r1, [r0];0x0写到0x53000000地址上,其实该地址是看门狗的控制寄存器,该指令就是关闭看门狗,具体对应位请看2410的用户手册。 /* * mask all IRQs by setting all bits in the INTMR - default */ mov r1, #0xffffffff ldr r0, =INTMSK str r1, [r0];将0xffffffff写到INTMSK代表的地址上,屏蔽中断 #if defined(CONFIG_S3C2410) ldr r1, =0x7ff ldr r0, =INTSUBMSK str r1, [r0];对于2410还要设置此寄存器 #endif @ initialise system clocks;下面没什么要讲的,具体参看2410的用户手册 mov r1, #CLK_CTL_BASE mvn r2, #0xff000000 str r2, [r1, #0x0] /*oLOCKTIME*/ @ldr r2, mpll_50mhz @str r2, [r1, #0x4] /*oMPLLCON*/ mov r1, #CLK_CTL_BASE /* mov r2, #0x3*/ mov r2, #0x3 str r2, [r1, #0x14] /*oCLKDIVN*/ mrc p15, 0, r1, c1, c0, 0 @ read ctrl register orr r1, r1, #0xc0000000 @ Asynchronous mcr p15, 0, r1, c1, c0, 0 @ write ctrl register @ now, CPU clock is 200 Mhz mov r1, #CLK_CTL_BASE ldr r2, mpll_200mhz str r2, [r1, #0x4] /*oMPLLCON*/ /* * we do sys-critical inits only at reboot, * not when booting from ram! */ #ifdef CONFIG_INIT_CRITICAL bl cpu_init_crit ;跳到下面的子过程 #endif relocate:;??面代码的作用是把u-boot的后续代码搬运到内存中 /* * relocate armboot to RAM */ adr r0, _start /* r0 <- current position of code */ ldr r2, _armboot_start ldr r3, _armboot_end sub r2, r3, r2 /* r2 <- size of armboot */ ldr r1, _TEXT_BASE oardsmdk2410config.mk中定义了_TEXT_BASE,表示把u-boot搬到内存中的相应位置,在这里是0x33F00000,相对于0x30000000来说是63MB的地方,朋友们可能要疑惑了,前面的文章不是介绍了我们用的板子是32M内存吗?那怎么是63M的地方呢,其实2410支持地址循环,这里其实就是31M的地方,哈哈,那u-boot不是把自己放在SDRAM中的最高的地方吗?的确是这样的。 add r2, r0, r2 /* r2 <- source end address */ /* * r0 = source address * r1 = target address * r2 = source end address */ copy_loop:;开始拷贝 ldmia r0!, {r3-r10} stmia r1!, {r3-r10} cmp r0, r2 ble copy_loop /* set up the stack */;最后我们在u-boot之后建立堆栈,为C语言的执行创建环境,否则是不允许执行C程序的,大小为128*1024 ldr r0, _armboot_end add r0, r0, #CONFIG_STACKSIZE 该宏在includeconfigssmdk2410.h中定义 sub sp, r0, #12 /* leave 3 words for abort-stack */ ldr pc, _start_armboot _start_armboot: .word start_armboot;跳转到/lib_arm/board.c中的start_armboot()函数中运行了,到此我们完成了第一阶段,初始汇编代码的运行 /* ************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * ************************************************************************* */ cpu_init_crit: /* * flush v4 I/D 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 memsetup.S in your board directory. */ mov ip, lr bl memsetup ==〉上面英文注释就是,在重新定位之前,要初始化RAM,以及设置刷新频率 我们在本文的最后插上memsetup.S的代码。 mov lr, ip mov pc, lr返回,并且回到上面,进行重定位 /* ************************************************************************* * * 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 add r8, sp, #S_PC ldr r2, _armboot_end add r2, r2, #CONFIG_STACKSIZE sub r2, r2, #8 ldmia r2, {r2 - r4} @ get pc, cpsr, old_r0 add r0, sp, #S_FRAME_SIZE @ restore sp_SVC add r5, sp, #S_SP mov r1, lr stmia r5, {r0 - r4} @ save sp_SVC, lr_SVC, pc, cpsr, old_r 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_end @ setup our mode stack add r13, r13, #CONFIG_STACKSIZE @ resides at top of normal stack sub r13, r13, #8 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 @ Processor clock values .align 4 mpll_50mhz: .long vMPLLCON_50 mpll_100mhz: .long vMPLLCON_100 mpll_200mhz: .long vMPLLCON_200 .align 5 .globl reset_cpu reset_cpu: #ifdef CONFIG_S3C2400 bl disable_interrupts ldr r1, _rWTCON ldr r2, _rWTCNT /* Disable watchdog */ mov r3, #0x0000 str r3, [r1] /* Initialize watchdog timer count register */ mov r3, #0x0001 str r3, [r2] /* Enable watchdog timer; assert reset at timer timeout */ mov r3, #0x0021 str r3, [r1] _loop_forever: b _loop_forever _rWTCON: .word 0x15300000 _rWTCNT: .word 0x15300008 #else /* ! CONFIG_S3C2400 */ mov ip, #0 mcr p15, 0, ip, c7, c7, 0 @ invalidate cache mcr p15, 0, ip, c8, c7, 0 @ flush TLB (v4) mrc p15, 0, ip, c1, c0, 0 @ get ctrl register bic ip, ip, #0x000f @ ............wcam bic ip, ip, #0x2100 @ ..v....s........ mcr p15, 0, ip, c1, c0, 0 @ ctrl register mov pc, r0 #endif /* CONFIG_S3C2400 */ ///////////////////////////////////////////////////////////////////// start.S到此结束 //下面就是初始化内存的代码 /board/smdk2410/memsetup.S memsetup: /* 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 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 0x31 .word 0x30 .word 0x30 下篇文章,http://www.linuxidc.com/Linux/2011-08/39959.htm 我们主要分析start_armboot()函数的主要作用,其实该函数就是进行一系列的硬件初始化,然后进入main_loop,等待用户的命令,或者直接默认加载Linux内核,启动Linux,好了今天就到这吧,该休息了:)
收藏该网址