1.<br />u-boot简介1.1.<br />概述U-Boot,全称Universal Boot Loader,是遵循GPL条款的开放源码项目,从FADSROM、8xxROM、PPCBOOT逐步发展演化而来,其源码目录、编译形式与Linux内核很相似。事实上,不少U-Boot源码就是相应Linux内核源程序的简化,尤其是一些设备的驱动程序,从UBoot源码的注释中能体现这一点。但是U-Boot不仅仅支持嵌入式Linux系统的引导,当前,它还支持NetBSD、VxWorks、QNX、RTEMS、ARTOS、LynxOS嵌入式操作系统。其目前要支持的目标操作系统包括OpenBSD、NetBSD、FreeBSD、4.4BSD、Linux、SVR4、Esix、Solaris、Irix、SCO、Dell、NCR、VxWorks、LynxOS、pSOS、QNX、RTEMS和ARTOS。这是U-Boot中Universal的一层含义。另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、x86、ARM、Nios、xScale等诸多常用系列的处理器。这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。就目前来看,U-Boot对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。其它系列的处理器和操作系统基本是在2002年11月PPCBOOT改名为U-Boot后逐步扩充的。从PPCBOOT向U-Boot的顺利过渡,很大程度上归功于U-Boot的维护人,德国DENX软件工程中心的Wolfgang Denk(以下简称W.D)本人精湛的专业水平和持着不懈的努力。当前,U-Boot项目在他的领军下,众多有志于开放源码Bootloader移植工作的嵌入式开发人员,正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入,以支持更多嵌入式操作系统的装载与引导。<br />1.2.<br />U-Boot主要目录结构<br />board——目标板相关文件,主要包含SDRAM、Flash驱动; <br />common——独立于处理器体系结构的通用代码,如 内存大小探测与故障检测; <br />cpu——与处理器相关的文件,如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件; <br />driver——通用设备驱动,如CFI Flash驱动(目前对Intel Flash支持较好) <br />doc——U-Boot的说明文档; <br />examples——可在U-Boot下运行的示例程序;如 helloworld.c,timer.c; <br />include——U-Boot头文件,configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件; <br />lib_xxx——处理器体系相关的文件,如lib—ppc,lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件; <br />net——与网络功能相关的文件目录,如bootp、nfs、tftp; <br />post——上电自检文件目录,尚有待于进一步完善; <br />rtc——RTC驱动程序; <br />tools——用于创建U-Boot S-RECORD和BIN镜像文件的工具。 <br />1.3.<br />U-Boot支持的主要功能U-Boot可支持的主要功能如表1所列。<br />表格 1<br />系统引导<br /> 支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统 <br /> <br />支持NTS挂载,从Flash中引导压缩或非压缩系统内核<br /> <br />基本辅助功能<br /> 强大的操作系统接口功能,可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤其对Linux支持最为强劲 <br /> <br />支持目标板环境参数的多种存储方式,如Flash、NVRAIvl、EEPROM <br /> <br />CRC32校验,可校验Flash中内核、RAMDISK镜像文件是否完好<br /> <br />设备驱动<br /> 串口、SDRAM、Flash、以太网、LCD、NVRA/vl、EEPROM、键盘、JSB、PCMCIA、PCI、RTC等驱动支持 <br /> <br />上电自检功能<br /> SDRAM、Flash大小自动检测;SDRAM故障检测:CPLy型号<br /> <br />特殊功能<br /> xⅢ内核引导 <br /> <br /><br />2.<br />u-boot启动过程2.1.<br />启动模式Boot loader启动模式通常有两种:启动加载模式和下载模式。u-boot作为boot loader的一种也不例外。<br />启动加载模式:整个系统启动过程是自主的,完全没有人的干预。<br />下载模式:linux内核在加载时有开发人员控制,通过串口或者网络进行加载的方式。<br />对于普通用户来说,一般都用在启动加载模式,但是对于开发人员来数,则需要运行在下载模式下,因为开发过程中需要经常更新镜像。为了达到两者兼顾的目的,通常,采用两种模式相结合的方式。复位后,boot loader 首先进行初始化工作,结束后不是立刻加载linux镜像,而是等待一段时间,如果用户有键盘输入,就进入下载模式,否则进入启动加载模式。<br />2.2.<br />启动的阶段u-boot启动分为两个阶段。第一阶段主要针对与CPU体系结构和存储设备密切相关部分作一些必要的初始化工作,是一些依赖于CPU结构的代码,为了增加效率以及涉及到处理器的设置,只能采用汇编语言编写。第二阶段主要针对目标板一级的一些初始化工作并且提供一些驱动的支持,用c语言实现。<br /><br /><br />2.3.<br />启动代码分析首先我们整体上浏览下u-boot启动代码的流程图。<br /><br /><br /><br /><br />relocate<br /><br /> <br /><br />Stack_setup<br /><br /> <br />Start_armboot()<br /><br /> <br />Init_sequence[]<br /><br />……<br />Getenv()<br /><br /> <br />Cpu_init_crit<br /><br /> <br />Main_loop()<br /><br /> <br />Lowelevel_init.S<br /><br /> <br />Reset<br /><br /><br /> <br /><br />下面详细分析各个阶段中代码的具体工作。<br />1. Start.S<br />源码位置:cpu/arm920t/start.S<br />运行位置:Flash<br />描述:一个可执行的映像有且只有一个全局入口。这个入口点为处理器复位取第一条指令的位置。U-boot映像的入口就是start.S。这个可以通过 board/smdk2410/u-boot.lds连接脚本文件决定。<br /><br />.globl _start<br />_start:<br />b<br />reset<br />//复位向量<br /><br />ldr<br />pc, _undefined_instruction<br /><br />ldr<br />pc, _software_interrupt<br /><br />ldr<br />pc, _prefetch_abort<br /><br />ldr<br />pc, _data_abort<br /><br />ldr<br />pc, _not_used<br /><br />ldr<br />pc, _irq<br /><br />ldr<br />pc, _fiq<br /><br />_undefined_instruction:<br />.word undefined_instruction<br />_software_interrupt:<br />.word software_interrupt<br />_prefetch_abort:<br />.word prefetch_abort<br />_data_abort:<br />.word data_abort<br />_not_used:<br /><br />.word not_used<br />_irq:<br />.word irq<br />//中断向量<br />_fiq:<br />.word fiq<br /><br />//快速中断向量<br /><br /><br /><br />_TEXT_BASE:<br /><br />.word<br />TEXT_BASE<br /><br />.globl _armboot_start<br />_armboot_start:<br /><br />.word _start<br /><br />/*<br /><br />* These are defined in the board-specific linker script.<br /><br />*/<br />.globl _bss_start<br />_bss_start:<br /><br />.word __bss_start<br /><br />.globl _bss_end<br />_bss_end:<br /><br />.word _end<br /><br />#ifdef CONFIG_USE_IRQ<br />/* IRQ stack memory (calculated at run-time) */<br />.globl IRQ_STACK_START<br />IRQ_STACK_START:<br /><br />.word<br />0x0badc0de<br /><br />/* IRQ stack memory (calculated at run-time) */<br />.globl FIQ_STACK_START<br />FIQ_STACK_START:<br /><br />.word 0x0badc0de<br />#endif<br /><br /><br />/*<br /><br />* the actual reset code<br /><br />*/<br /><br />reset:<br />//复位启动子程序<br /><br />/* 设置CPU<br />为SVC32模式<br />*/<br /><br />mrs<br />r0,cpsr<br /><br />bic<br />r0,r0,#0x1f<br /><br />orr<br />r0,r0,#0xd3<br /><br />msr<br />cpsr,r0<br /><br />/* 关闭看门狗 */<br />#if defined(CONFIG_S3C2400)<br /># define pWTCON<br />0x15300000<br /># define INTMSK<br />0x14400008<br />/* Interupt-Controller base addresses */<br /># define CLKDIVN<br />0x14800014<br />/* clock divisor register */<br />#elif defined(CONFIG_S3C2410)<br /># define pWTCON<br />0x53000000<br /># define INTMSK<br />0x4A000008<br />/* Interupt-Controller base addresses */<br /># define INTSUBMSK<br />0x4A00001C<br /># define CLKDIVN<br />0x4C000014<br />/* clock divisor register */<br />#endif<br /><br />#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)<br /><br />ldr<br />r0, =pWTCON<br /><br />mov<br />r1, #0x0<br /><br />str<br />r1, [r0]<br /><br /><br />/*<br /><br /><br />* mask all IRQs by setting all bits in the INTMR - default<br /><br /><br />*/<br /><br />mov<br />r1, #0xffffffff<br /><br />ldr<br />r0, =INTMSK<br /><br />str<br />r1, [r0]<br /># if defined(CONFIG_S3C2410)<br /><br />ldr<br />r1, =0x3ff<br /><br />ldr<br />r0, =INTSUBMSK<br /><br />str<br />r1, [r0]<br /># endif<br /><br /><br />/* FCLK:HCLKCLK = 1:2:4 */<br /><br />/* default FCLK is 120 MHz ! */<br /><br />ldr<br />r0, =CLKDIVN<br /><br />mov<br />r1, #3<br /><br />str<br />r1, [r0]<br />#endif<br />/* CONFIG_S3C2400 || CONFIG_S3C2410 */<br /><br /><br />/*<br />关键的初始化代码在系统重启是执行,而在热复位也就是直接从RAM中启动不执行<br /><br />*/<br />#ifndef CONFIG_SKIP_LOWLEVEL_INIT<br /><br />bl<br />cpu_init_crit<br />#endif<br /><br />#ifndef CONFIG_SKIP_RELOCATE_UBOOT<br />relocate:<br />/* 把u-boot重新定位到RAM<br /><br />*/<br /><br />adr<br />r0, _start<br /><br />/* 把代码的当前位置也就是需要复制代码的起始地址放到r0 */<br /><br />ldr<br />r1, _TEXT_BASE<br />/* 测试启动从Flash还是RAM */<br /><br />cmp<br />r0, r1<br />/* 比较r0和r1,调试的时候也就是都是从RAM启动*/<br />beq<br />stack_setup<br />/*的情况下,不需要重定位,跳过重定位代码 */<br /> <br /><br />ldr<br />r2, _armboot_start<br />/*准备重新定位代码,得到_armboot_start的位置*/<br /><br />ldr<br />r3, _bss_start<br />/*得到_bss_start的位置*/<br /><br />sub<br />r2, r3, r2<br /><br />/*r2为arm_boot的大小<br />*/<br /><br />add<br />r2, r0, r2<br /><br />/* r2得到需要复制代码的末尾地址 */<br /><br />copy_loop:<br />/* 重新定位代码,也就是执行从代码从flash到RAM的拷贝工作*/<br /><br />ldmia<br />r0!, {r3-r10}<br />/* copy from source address [r0]<br />*/<br /><br />stmia<br />r1!, {r3-r10}<br />/* copy to<br />target address [r1]<br />*/<br /><br />cmp<br />r0, r2<br />/* until source end addreee [r2]<br />*/<br /><br />ble<br />copy_loop<br />#endif<br />/* CONFIG_SKIP_RELOCATE_UBOOT */<br /><br /><br />/* 初始化堆栈<br />*/<br />stack_setup:<br /><br />ldr<br />r0, _TEXT_BASE<br />/* 上面128KiB是重定位后的u-boot<br />*/<br /><br />sub<br />r0, r0, #CFG_MALLOC_LEN<br /><br />/*向下是内存分配空间<br />*/<br /><br />sub<br />r0, r0, #CFG_GBL_DATA_SIZE <br />/*然后是bdinfo结构体地址空间<br />*/<br />#ifdef CONFIG_USE_IRQ<br /><br />sub<br />r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)<br />#endif<br /><br />sub<br />sp, r0, #12<br />/* 为abort-stack预留3个字<br />*/<br /><br />clear_bss:<br /><br />ldr<br />r0, _bss_start<br />/* 找到bss 段起始地址<br />*/<br /><br />ldr<br />r1, _bss_end<br />/* bss段的末尾地址<br />*/<br /><br />mov <br />r2, #0x00000000<br />/* 清零<br />*/<br /><br />clbss_l:str<br />r2, [r0]<br />/* bss段地址空间循环清零<br />*/<br /><br />add<br />r0, r0, #4<br /><br />cmp<br />r0, r1<br /><br />ble<br />clbss_l<br /><br />/* 跳转到start_armboot函数入口, _start_armboot字保存函数的入口地址 */<br /><br />ldr<br />pc, _start_armboot<br /><br />_start_armboot:<br />.word start_armboot<br /><br /><br />#ifndef CONFIG_SKIP_LOWLEVEL_INIT<br />cpu_init_crit:<br />/*关键的初始化程序 */<br /><br />/*<br /><br /><br />初始化CACHE<br /><br /><br />*/<br /><br />mov<br />r0, #0<br /><br />mcr<br />p15, 0, r0, c7, c7, 0<br />/* flush v3/v4 cache */<br /><br />mcr<br />p15, 0, r0, c8, c7, 0<br />/* flush v4 TLB */<br /><br /><br />/*<br /><br /><br />* 关闭MMU 和 CACHE<br /><br /><br />*/<br /><br />mrc<br />p15, 0, r0, c1, c0, 0<br /><br />bic<br />r0, r0, #0x00002300<br />@ clear bits 13, 9:8 (--V- --RS)<br /><br />bic<br />r0, r0, #0x00000087<br />@ clear bits 7, 2:0 (B--- -CAM)<br /><br />orr<br />r0, r0, #0x00000002<br />@ set bit 2 (A) Align<br /><br />orr<br />r0, r0, #0x00001000<br />@ set bit 12 (I) I-Cache<br /><br />mcr<br />p15, 0, r0, c1, c0, 0<br /><br /><br />/*<br /><br /><br />* before relocating, we have to setup RAM timing<br /><br /><br />* because memory timing is board-dependend, you will<br /><br /><br />* find a lowlevel_init.S in your board directory.<br /><br /><br />*/<br /><br />mov<br />ip, lr<br /><br />bl<br />lowlevel_init<br /><br />mov<br />lr, ip<br /><br />mov<br />pc, lr<br />#endif /* CONFIG_SKIP_LOWLEVEL_INIT */<br /><br />/*<br /><br />*************************************************************************<br /><br />*<br /><br />* 中断向量<br /><br />*<br /><br />*************************************************************************<br /><br />*/<br /><br />@<br />@ IRQ stack frame.<br />@<br />#define S_FRAME_SIZE<br />72<br /><br />#define S_OLD_R0<br />68<br />#define S_PSR<br />64<br />#define S_PC<br />60<br />#define S_LR<br />56<br />#define S_SP<br />52<br /><br />#define S_IP<br />48<br />#define S_FP<br />44<br />#define S_R10<br />40<br />#define S_R9<br />36<br />#define S_R8<br />32<br />#define S_R7<br />28<br />#define S_R6<br />24<br />#define S_R5<br />20<br />#define S_R4<br />16<br />#define S_R3<br />12<br />#define S_R2<br />8<br />#define S_R1<br />4<br />#define S_R0<br />0<br /><br />#define MODE_SVC 0x13<br />#define I_BIT<br />0x80<br /><br />/*<br /><br />* use bad_save_user_regs for abort/prefetch/undef/swi ...<br /><br />* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling<br /><br />*/<br /><br /><br />.macro<br />bad_save_user_regs<br /><br />sub<br />sp, sp, #S_FRAME_SIZE<br /><br />stmia<br />sp, {r0 - r12}<br />@ Calling r0-r12<br /><br />ldr<br />r2, _armboot_start<br /><br />sub<br />r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)<br /><br />sub<br />r2, r2, #(CFG_GBL_DATA_SIZE+8)<br />@ set base 2 words into abort stack<br /><br />ldmia<br />r2, {r2 - r3}<br />@ get pc, cpsr<br /><br />add<br />r0, sp, #S_FRAME_SIZE<br />@ restore sp_SVC<br /><br /><br />add<br />r5, sp, #S_SP<br /><br />mov<br />r1, lr<br /><br />stmia<br />r5, {r0 - r3}<br />@ save sp_SVC, lr_SVC, pc, cpsr<br /><br />mov<br />r0, sp<br /><br />.endm<br /><br /><br />.macro<br />irq_save_user_regs<br /><br />sub<br />sp, sp, #S_FRAME_SIZE<br /><br />stmia<br />sp, {r0 - r12}<br />@ Calling r0-r12<br /><br />add<br />r8, sp, #S_PC<br /><br />stmdb<br />r8, {sp, lr}^<br />@ Calling SP, LR<br /><br />str<br />lr, [r8, #0]<br />@ Save calling PC<br /><br />mrs<br />r6, spsr<br /><br />str<br />r6, [r8, #4]<br />@ Save CPSR<br /><br />str<br />r0, [r8, #8]<br />@ Save OLD_R0<br /><br />mov<br />r0, sp<br /><br />.endm<br /><br /><br />.macro<br />irq_restore_user_regs<br /><br />ldmia<br />sp, {r0 - lr}^<br />@ Calling r0 - lr<br /><br />mov<br />r0, r0<br /><br />ldr<br />lr, [sp, #S_PC]<br />@ Get PC<br /><br />add<br />sp, sp, #S_FRAME_SIZE<br /><br />subs<br />pc, lr, #4<br />@ return & move spsr_svc into cpsr<br /><br />.endm<br /><br /><br />.macro get_bad_stack<br /><br />ldr<br />r13, _armboot_start<br />@ setup our mode stack<br /><br />sub<br />r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)<br /><br />sub<br />r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack<br /><br /><br />str<br />lr, [r13]<br />@ save caller lr / spsr<br /><br />mrs<br />lr, spsr<br /><br />str<br />lr, [r13, #4]<br /><br /><br />mov<br />r13, #MODE_SVC<br />@ prepare SVC-Mode<br /><br />@ msr<br />spsr_c, r13<br /><br />msr<br />spsr, r13<br /><br />mov<br />lr, pc<br /><br />movs<br />pc, lr<br /><br />.endm<br /><br /><br />.macro get_irq_stack<br />@ setup IRQ stack<br /><br />ldr<br />sp, IRQ_STACK_START<br /><br />.endm<br /><br /><br />.macro get_fiq_stack<br />@ setup FIQ stack<br /><br />ldr<br />sp, FIQ_STACK_START<br /><br />.endm<br /><br />/*<br /><br />*异常向量<br /><br />*/<br /><br />.align<br />5<br />undefined_instruction:<br /><br />get_bad_stack<br /><br />bad_save_user_regs<br /><br />bl <br />do_undefined_instruction<br /><br /><br />.align<br />5<br />software_interrupt:<br /><br />get_bad_stack<br /><br />bad_save_user_regs<br /><br />bl <br />do_software_interrupt<br /><br /><br />.align<br />5<br />prefetch_abort:<br /><br />get_bad_stack<br /><br />bad_save_user_regs<br /><br />bl <br />do_prefetch_abort<br /><br /><br />.align<br />5<br />data_abort:<br /><br />get_bad_stack<br /><br />bad_save_user_regs<br /><br />bl <br />do_data_abort<br /><br /><br />.align<br />5<br />not_used:<br /><br />get_bad_stack<br /><br />bad_save_user_regs<br /><br />bl <br />do_not_used<br /><br />#ifdef CONFIG_USE_IRQ<br /><br /><br />.align<br />5<br />irq:<br /><br />get_irq_stack<br /><br />irq_save_user_regs<br /><br />bl <br />do_irq<br /><br />irq_restore_user_regs<br /><br /><br />.align<br />5<br />fiq:<br /><br />get_fiq_stack<br /><br />/* someone ought to write a more effiction fiq_save_user_regs */<br /><br />irq_save_user_regs<br /><br />bl <br />do_fiq<br /><br />irq_restore_user_regs<br /><br />#else<br /><br /><br />.align<br />5<br />irq:<br /><br />get_bad_stack<br /><br />bad_save_user_regs<br /><br />bl <br />do_irq<br /><br /><br />.align<br />5<br />fiq:<br /><br />get_bad_stack<br /><br />bad_save_user_regs<br /><br />bl <br />do_fiq<br /><br />#endif<br /> |
|