uboot 启动流程
2018-02-23 14:41
267 查看
经过了上一篇的配置,我们已经执行make就可以编译出一个uboot.bin,但这还不够,首先,此时的uboot并不符合三星芯片对bootloader的格式要求,同时,此时的uboot.bin也没有结合我们的开发板进行配置,还无法使用。而要进行这样的个性化配置,前提条件就是对uboot开机流程和编译系统有所了解,本文主要讨论前者。在三星的SoC中, 启动流程可以分为三个阶段BL0, BL1, BL2, BL3, 三星自己的手册对BL1的解释也不尽相同, 一种是将在iRAM中运行的程序都归结为BL1; 一种是将iRAM中三星加密的代码bl1.bin作为BL1, iRAM中剩余的部分作为BL2, 本文采用后者, 他们的主要分工如下:BL0: ARM的起始地址都是0地址, 三星的芯片一般将0地址映射到iROM中, BL0就是指iROM中固化的启动代码, 主要负责加载BL1
BL1: 三星对于bootloader的加密代码bl1.bin, 要放在外设中uboot.bin的头上, 和一部分uboot.bin一起加载到iRAM中运行.
BL2: 从(nand/sd/usb)中拷贝的uboot.bin头最大14K到iRAM中代码中除去bl1.bin后剩余的部分, 负责设置CPU为SVC模式, 关闭MMU, 关闭中断, 关闭iCache, 关闭看门狗, 初始化DRAM,初始化时钟, 初始化串口, 设置栈, 校验BL2并将其搬移到DRAM高位地址, 重定位到DRAM中执行BL3
BL3:是指在代码重定向后在内存中执行的uboot的完整代码, 负责初始化外设,更新向量表, 清BSS, 准备内核启动参数, 加载并运行OS内核
可以借助下图理解这个流程
我们常说的uboot是一个两阶段bootloader,就是指上述的BL2和BL3. BL2主要做硬件直接相关的初始化,使用汇编编写;BL3主要为操作系统的运行准备环境,主要用C编写,这里以ARM平台为例分析其启动流程。下面是启动过程中主要涉及的文件arch/arm/cpu/armv7/start.S
board/samsung/myboard/lowlevel_init.S
arch/arm/lib/crt0.S
arch/arm/lib/board.c
arch/samsung/myboard/myboard.c
1. 设置CPU为SVC模式
2. 关闭MMU
3. 关闭Cache
4. 跳转到lowlevel_init.S low_level_init
board/samsung/origen/lowlevel_init.S
5. 初始化时钟
6. 初始化内存
7. 初始化串口
8. 关闭看门狗
9. 跳转到crt0.S _main
arch/arm/lib/crt0.S
10. 设置栈
11. 初始化C运行环境
12. 调用board_init_f()
arch/arm/lib/board.c
13. board_init_f对全局信息GD结构体进行填充
arch/arm/lib/crt0.S
14. 代码重定位------------BL2的最后的工作, 执行完就进入DRAM执行BL2
--155-->跳转执行cpu_init_crit,
--158-->跳转执行_main
--301-->关闭MMU
--85-->初始化系统内存
--89-->初始化UART串口,即跳转到329行
--90-->初始化TrustZoneProtectorController,即跳转到357行执行完lowlevel_init.S,依据上面那三行代码,执行流程就该回到start.S执行156行跳转到_main
--104-->初始化SP,为C语言做准备
--110-->保存128B放GD结构体来存放全局信息,
--111-->GD的地址放在r8中,
--115-->跳转到board_init_f(),这个整个初始化过程中第一次执行的C代码
--243--> 全局的函数指针数组,每个指针都是int (*ptr)(void)型的。
--291-->mon_len 通过链接脚本可以知道存放的是 uboot 代码大小;
--294-->fdt_blob 存放设备数地址;
--303--遍历函数指针数组init_sequence中的每一个成员,就是将数组中的每一个初始化函数都执行一次,这种写法可以借鉴
arch/arm/lib/board.c
1. board_init_r()是进入定制板目录的入口
common/main.c
2. main_loop()中关闭中断,执行命令以及加载引导内核
--532-->很多紧急工作都做完了,可以打开cache了
--535-->关键!!!这个就是我们苦苦寻找的板级定制文件的xxx.c的入口函数!!!
--651-->中断初始化
--653-->使能中断
--667-->网卡初始化,函数的实现在net/eth.c,会回调板级xxx.c中的board_eth_init()
--703-->**执行main_loop(),实现在common/main.c,它的主要功能就是循环检测输入的命令并执行,其中一个环境变量bootdelay(自启动)的设置决定了是否启动内核,如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核
├──getenv
│ └──getenv_f
│ ├──env_get_char
│ └──envmatch
└──run_command_list
└──builtin_run_command_list
└──builtin_run_command
├──process_macros
├──parse_line
└──cmd_process
├──find_cmd
├──cmd_call
│ └──cmdtp->cmd//找到命令do_bootm
│ └──bootm_start
│ └──boot_start_lmb
│ └──arch_lmb_reserve(struct lmb *lmb)
│ └──cleanup_before_linux
│ └──disable_interrupts //关闭中断
└──cmd_usage
└──bootm_load_os上面就是uboot启动Linux前大体上做的最后的工作流程, uboot做了这么多, 其实都是为了能引导OS内核,针对ARM Linux, 它对启动前的环境要求在内核文档"/Documentation/kernel-parameters.txt"和"Documentation/arm/Booting"" 有叙述,其中就可以解释uboot为什么做了上面这些工作. 文档中对于在ARM平台中启动Linux内核, 作了如下6条要求, 我们逐条解释:Setup and initialise the RAM.这里的RAM指的是DRAM, 因为Linux内核需要在DRAM中运行, 而DRAM必须初始化, 这部分工作在BL2中完成了.Initialise one serial port.初始化一个串口, bootloader应该初始化一个串口, 这样内核串口驱动才可以自动探测到给控制台用的串口是哪个
bootloader可以在taglist中使用console=来指定一个串口, 这部分工作在BL2中也做了Detect the machine type.探测板子类型, Linux内核需要知道自己运行的板子类型, 这部分工作也交给bootloader来完成, bootloader通过一些方式获得了板子的类型后, 按照内核源码的"arch/arm/tools/mach-types"中的描述, 将板子的type编号存储在r1寄存器
Setup the kernel tagged list.建立tag列表, 也就是内核参数, 在内核源码和uboot源码中都使用一个struct tag来描述. 数据结构 tag 和 tag_header 定义在 Linux 内核源码的"include/asm/setup.h" 头文件中:有效的tagged list 必须以ATAG_CORE开始并且以ATAG_NONE结束, 空的ATAG_CORE的size域是0x00000002, ATAG_NONE的size域是0。
list中可以放置任意数目的tag, 名称相同的tag的后果未定义, 最终的取值可能是之前的, 也可能是之后的
bootloader至少要将系统内存的位置, 内存的大小以及根文件系统的位置传递给内核
taggedlist放置的位置不能和内核自解压区域或initrd的bootp程序相冲突, 防止被重写, 建议的地址是RAM的头16KB
bootloader必须把dtb放置在64bit对齐的已经初始化过的内存中, dtb的格式在"Documentation/devicetree/booting-without-of.txt"中, 其中定义了设备树的大小
Load initramfs.加载ramfs,ramfs推荐正好放在设备树上面Calling the kernel image启动内核镜像,如果使用的flash中的zImage,bootloader可以直接将zImage加载到内存并执行,Linux内核对非zImage内核镜像的地址有更严格的要求————镜像必须加载到PAGE_OFFSET + TEXT_OFFSET处。PAGE_OFFSET定义了虚拟地址空间中内核空间的起始地址,32bit系统中就是3GB处。TEXT_OFFSET表示内核空间中开始的用来保存内核的页表(也就是进程0的PGD)、bootload和kernel传递参数的一块空间的大小,对于arm,TEXT_OFFSET是32kB,由于内核空间的前896MB(3GB~3GB+896MB)是一一映射的,所以将内核加载到物理地址0x4000 0000其实就是加载到虚拟地址空间的3GB处,考虑到上述的32KB用来保存页表、内核参数等,我们需要将内核解压到0x40008000处运行,即虚拟地址的(3GB+32KB),如果使用uImage,这个参数在制作uImage的时候被写入到了文件头中。我们在uboot的启动参数通常不指定加载到这个地址,因为内核自解压之后会自搬移到0x40008000处开始执行,如果我们指定的就是这个地址,那么内核首先会自搬移到别处,解压之后再搬回来执行,防止解压过程中将未解压的部分覆盖造成错误。无论是哪种启动方式,内核启动的时候都必须满足下面的条件:r0=0;r1=板子类型号;r2=内核中tagged list或设备树地址
所有的IRQ FIQ必须关闭
必须是ARM状态, SVC模式
MMU必须关闭
iCache可以关闭也可以不管
dCache必须关闭
DMA设备必须关闭
我们已经分析了整个Uboot的启动框架,在细节上,Uboot必须完成上面这些工作以满足Linux的启动要求。
BL1: 三星对于bootloader的加密代码bl1.bin, 要放在外设中uboot.bin的头上, 和一部分uboot.bin一起加载到iRAM中运行.
BL2: 从(nand/sd/usb)中拷贝的uboot.bin头最大14K到iRAM中代码中除去bl1.bin后剩余的部分, 负责设置CPU为SVC模式, 关闭MMU, 关闭中断, 关闭iCache, 关闭看门狗, 初始化DRAM,初始化时钟, 初始化串口, 设置栈, 校验BL2并将其搬移到DRAM高位地址, 重定位到DRAM中执行BL3
BL3:是指在代码重定向后在内存中执行的uboot的完整代码, 负责初始化外设,更新向量表, 清BSS, 准备内核启动参数, 加载并运行OS内核
可以借助下图理解这个流程
我们常说的uboot是一个两阶段bootloader,就是指上述的BL2和BL3. BL2主要做硬件直接相关的初始化,使用汇编编写;BL3主要为操作系统的运行准备环境,主要用C编写,这里以ARM平台为例分析其启动流程。下面是启动过程中主要涉及的文件arch/arm/cpu/armv7/start.S
board/samsung/myboard/lowlevel_init.S
arch/arm/lib/crt0.S
arch/arm/lib/board.c
arch/samsung/myboard/myboard.c
BL2
BL2的主要文件和任务流程如下arch/arm/cpu/armv7/start.S1. 设置CPU为SVC模式
2. 关闭MMU
3. 关闭Cache
4. 跳转到lowlevel_init.S low_level_init
board/samsung/origen/lowlevel_init.S
5. 初始化时钟
6. 初始化内存
7. 初始化串口
8. 关闭看门狗
9. 跳转到crt0.S _main
arch/arm/lib/crt0.S
10. 设置栈
11. 初始化C运行环境
12. 调用board_init_f()
arch/arm/lib/board.c
13. board_init_f对全局信息GD结构体进行填充
arch/arm/lib/crt0.S
14. 代码重定位------------BL2的最后的工作, 执行完就进入DRAM执行BL2
start.S
39 .globl _start 40 _start: b reset 41 ldr pc, _undefined_instruction 42 ldr pc, _software_interrupt 43 ldr pc, _prefetch_abort 44 ldr pc, _data_abort 45 ldr pc, _not_used 46 ldr pc, _irq 47 ldr pc, _fiq--40--> 异常向量表设置
126 reset: 127 bl save_boot_params 131 mrs r0, cpsr 132 bic r0, r0, #0x1f 133 orr r0, r0, #0xd3 134 msr cpsr,r0--126-->设置CPU为SVC模式下面这三行代码非常重要,是BL2启动过程的交叉点
154 bl cpu_init_cp15 155 bl cpu_init_crit 158 bl _main--154-->跳转执行cpu_init_cp15,即初始化CP15协处理器
--155-->跳转执行cpu_init_crit,
--158-->跳转执行_main
287 ENTRY(cpu_init_cp15) 291 mov r0, #0 @ set up for MCR 292 mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs 293 mcr p15, 0, r0, c7, c5, 0 @ invalidate icache 294 mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array 295 mcr p15, 0, r0, c7, c10, 4 @ DSB 296 mcr p15, 0, r0, c7, c5, 4 @ ISB 297 301 mrc p15, 0, r0, c1, c0, 0 302 bic r0, r0, #0x00002000 @ clear bits 13 (--V-) 303 bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM) 304 orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align 305 orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB 307 bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache 311 mcr p15, 0, r0, c1, c0, 0 312 mov pc, lr @ back to my caller 313 ENDPROC(cpu_init_cp15)--291-->关闭Cache
--301-->关闭MMU
324 ENTRY(cpu_init_crit) 331 b lowlevel_init @ go setup pll,mux,memory 332 ENDPROC(cpu_init_crit)--331-->跳转到lowlevel_init,位于"board/samsung/origen/lowlevel_init.S",进行板级相关的设置。
lowlevel_init.S
这是位于目录的初始化文件,主要完成特定开发板的初始化工作,包括时钟、内存和串口等。82 bl system_clock_init 85 bl mem_ctrl_asm_init 87 1: 88 /* for UART */ 89 bl uart_asm_init 90 bl tzpc_init 91 pop {pc} 114 system_clock_init: 329 uart_asm_init: 357 tzpc_init:--82-->初始化系统时钟,即跳转到114行
--85-->初始化系统内存
--89-->初始化UART串口,即跳转到329行
--90-->初始化TrustZoneProtectorController,即跳转到357行执行完lowlevel_init.S,依据上面那三行代码,执行流程就该回到start.S执行156行跳转到_main
crt0.S
首要任务就是设置栈, 准备C语言运行的环境:96 _main: 102 #if defined(CONFIG_NAND_SPL) 103 /* deprecated, use instead CONFIG_SPL_BUILD */ 104 ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) 105 #elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) 106 ldr sp, =(CONFIG_SPL_STACK) 107 #else 108 ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) 109 #endif 110 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ 111 sub sp, #GD_SIZE /* allocate one GD above SP */ 112 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ 113 mov r8, sp /* GD is above SP */ 114 mov r0, #0 115 bl board_init_f_main
--104-->初始化SP,为C语言做准备
--110-->保存128B放GD结构体来存放全局信息,
--111-->GD的地址放在r8中,
--115-->跳转到board_init_f(),这个整个初始化过程中第一次执行的C代码
board.c
下面这个函数就是uboot初始化过程中执行的第一个C函数,可以看作这个文件的入口函数。其中最重要的就是准备全局信息GD结构体, 内核启动参数就是来自于这个结构体209 typedef int (init_fnc_t) (void); 243 init_fnc_t *init_sequence[] = { 244 arch_cpu_init, /* basic arch cpu dependent setup */ 245 mark_bootstage, 246 #ifdef CONFIG_OF_CONTROL 247 fdtdec_check_fdt, ... 277 void board_init_f(ulong bootflag) 278 { ... 291 gd->mon_len = _bss_end_ofs; 292 #ifdef CONFIG_OF_EMBED 293 /* Get a pointer to the FDT */ 294 gd->fdt_blob = _binary_dt_dtb_start; 295 #elif defined CONFIG_OF_SEPARATE 296 /* FDT is at end of image */ 297 gd->fdt_blob = (void *)(_end_ofs + _TEXT_BASE); 298 #endif 299 /* Allow the early environment to override the fdt address */ 300 gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16, 301 (uintptr_t)gd->fdt_blob); 302 303 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { 304 if ((*init_fnc_ptr)() != 0) { 305 hang (); 306 } 307 } ...board_init_f()
--243--> 全局的函数指针数组,每个指针都是int (*ptr)(void)型的。
--291-->mon_len 通过链接脚本可以知道存放的是 uboot 代码大小;
--294-->fdt_blob 存放设备数地址;
--303--遍历函数指针数组init_sequence中的每一个成员,就是将数组中的每一个初始化函数都执行一次,这种写法可以借鉴
crt0.S
函数board_init_f()返回后,继续执行crt0.S中115行之后的部分,主要的工作是执行代码重定位,执行完这些之后,我们找到了最感兴趣的下面这几句163 /* call board_init_r(gd_t *id, ulong dest_addr) */ 164 mov r0, r8 /* gd_t */ 165 ldr r1, [r8, #GD_RELOCADDR] /* dest_addr */ 166 /* call board_init_r */ 167 ldr pc, =board_init_r /* this is auto-relocated! */--167-->跳转到board_init_r函数执行,这次跳出去这个文件的语句就执行完毕了,不会再回来了, 开始执行BL3.
BL3
这一阶段涉及的文件及任务如下arch/arm/lib/crt0.Sarch/arm/lib/board.c
1. board_init_r()是进入定制板目录的入口
common/main.c
2. main_loop()中关闭中断,执行命令以及加载引导内核
board.c
这也是最后一次跳转到这个文件了,执行额函数如下519 void board_init_r(gd_t *id, ulong dest_addr) 520 { 521 ulong malloc_start; 522 #if !defined(CONFIG_SYS_NO_FLASH) 523 ulong flash_size; 524 #endif 525 526 gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */ 527 bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r"); 528 529 monitor_flash_len = _end_ofs; 530 531 /* Enable caches */ 532 enable_caches(); 533 534 debug("monitor flash len: %08lX\n", monitor_flash_len); 535 board_init(); /* Setup chipselects */ ... 650 /* set up exceptions */ 651 interrupt_init(); 652 /* enable exceptions */ 653 enable_interrupts(); 667 eth_initialize(gd->bd); ... 701 /* main_loop() can return to retry autoboot, if so just run it again. */ 702 for (;;) { 703 main_loop(); 704 } 705board_init_r()
--532-->很多紧急工作都做完了,可以打开cache了
--535-->关键!!!这个就是我们苦苦寻找的板级定制文件的xxx.c的入口函数!!!
--651-->中断初始化
--653-->使能中断
--667-->网卡初始化,函数的实现在net/eth.c,会回调板级xxx.c中的board_eth_init()
--703-->**执行main_loop(),实现在common/main.c,它的主要功能就是循环检测输入的命令并执行,其中一个环境变量bootdelay(自启动)的设置决定了是否启动内核,如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核
main.c
这个文件就是main_loop()所在的文件, 不论是启动内核, 打印信息还是输入命令, 都在这个函数中执行, 我们这里主要关心这个函数是如何启动内核的, 函数的调用关系如下:main_loop()├──getenv
│ └──getenv_f
│ ├──env_get_char
│ └──envmatch
└──run_command_list
└──builtin_run_command_list
└──builtin_run_command
├──process_macros
├──parse_line
└──cmd_process
├──find_cmd
├──cmd_call
│ └──cmdtp->cmd//找到命令do_bootm
│ └──bootm_start
│ └──boot_start_lmb
│ └──arch_lmb_reserve(struct lmb *lmb)
│ └──cleanup_before_linux
│ └──disable_interrupts //关闭中断
└──cmd_usage
└──bootm_load_os上面就是uboot启动Linux前大体上做的最后的工作流程, uboot做了这么多, 其实都是为了能引导OS内核,针对ARM Linux, 它对启动前的环境要求在内核文档"/Documentation/kernel-parameters.txt"和"Documentation/arm/Booting"" 有叙述,其中就可以解释uboot为什么做了上面这些工作. 文档中对于在ARM平台中启动Linux内核, 作了如下6条要求, 我们逐条解释:Setup and initialise the RAM.这里的RAM指的是DRAM, 因为Linux内核需要在DRAM中运行, 而DRAM必须初始化, 这部分工作在BL2中完成了.Initialise one serial port.初始化一个串口, bootloader应该初始化一个串口, 这样内核串口驱动才可以自动探测到给控制台用的串口是哪个
bootloader可以在taglist中使用console=来指定一个串口, 这部分工作在BL2中也做了Detect the machine type.探测板子类型, Linux内核需要知道自己运行的板子类型, 这部分工作也交给bootloader来完成, bootloader通过一些方式获得了板子的类型后, 按照内核源码的"arch/arm/tools/mach-types"中的描述, 将板子的type编号存储在r1寄存器
Setup the kernel tagged list.建立tag列表, 也就是内核参数, 在内核源码和uboot源码中都使用一个struct tag来描述. 数据结构 tag 和 tag_header 定义在 Linux 内核源码的"include/asm/setup.h" 头文件中:有效的tagged list 必须以ATAG_CORE开始并且以ATAG_NONE结束, 空的ATAG_CORE的size域是0x00000002, ATAG_NONE的size域是0。
list中可以放置任意数目的tag, 名称相同的tag的后果未定义, 最终的取值可能是之前的, 也可能是之后的
bootloader至少要将系统内存的位置, 内存的大小以及根文件系统的位置传递给内核
taggedlist放置的位置不能和内核自解压区域或initrd的bootp程序相冲突, 防止被重写, 建议的地址是RAM的头16KB
bootloader必须把dtb放置在64bit对齐的已经初始化过的内存中, dtb的格式在"Documentation/devicetree/booting-without-of.txt"中, 其中定义了设备树的大小
struct boot_param_header { __be32 magic; //设备树魔数,固定为0xd00dfeed __be32 totalsize; //整个设备树的大小 __be32 off_dt_struct; //保存结构块在整个设备树中的偏移 __be32 off_dt_strings; //保存的字符串块在设备树中的偏移 __be32 off_mem_rsvmap; //保留内存区,该区保留了不能被内核动态分配的内存空间 __be32 version; //设备树版本 __be32 last_comp_version; //向下兼容版本号 __be32 boot_cpuid_phys; //为在多核处理器中用于启动的主cpu的物理id __be32 dt_strings_size; //字符串块大小 __be32 dt_struct_size; //结构块大小 };在内核关于启动参数的约定中, 它认为r2中的地址可能是设备树的地址, 也可能是tagged list的地址, 所以, 拿到这个地址后,内核首要的工作就是判断到底是什么,判断的依据就是判断其第一个32bit上存储的到底是设备树魔数0xd00dfeed还是ATAGS_CORE。参见内核"arch/arm/kernel/head-common.h", 这里我有一个疑问, 既然r2可能会存放dtb的地址, 那此时内核是如何找到tagged list的呢???。实际开发中一般都是将tagged list的地址放到r2中。内核推荐将dtb放置在RAM开始的128MB处
Load initramfs.加载ramfs,ramfs推荐正好放在设备树上面Calling the kernel image启动内核镜像,如果使用的flash中的zImage,bootloader可以直接将zImage加载到内存并执行,Linux内核对非zImage内核镜像的地址有更严格的要求————镜像必须加载到PAGE_OFFSET + TEXT_OFFSET处。PAGE_OFFSET定义了虚拟地址空间中内核空间的起始地址,32bit系统中就是3GB处。TEXT_OFFSET表示内核空间中开始的用来保存内核的页表(也就是进程0的PGD)、bootload和kernel传递参数的一块空间的大小,对于arm,TEXT_OFFSET是32kB,由于内核空间的前896MB(3GB~3GB+896MB)是一一映射的,所以将内核加载到物理地址0x4000 0000其实就是加载到虚拟地址空间的3GB处,考虑到上述的32KB用来保存页表、内核参数等,我们需要将内核解压到0x40008000处运行,即虚拟地址的(3GB+32KB),如果使用uImage,这个参数在制作uImage的时候被写入到了文件头中。我们在uboot的启动参数通常不指定加载到这个地址,因为内核自解压之后会自搬移到0x40008000处开始执行,如果我们指定的就是这个地址,那么内核首先会自搬移到别处,解压之后再搬回来执行,防止解压过程中将未解压的部分覆盖造成错误。无论是哪种启动方式,内核启动的时候都必须满足下面的条件:r0=0;r1=板子类型号;r2=内核中tagged list或设备树地址
所有的IRQ FIQ必须关闭
必须是ARM状态, SVC模式
MMU必须关闭
iCache可以关闭也可以不管
dCache必须关闭
DMA设备必须关闭
我们已经分析了整个Uboot的启动框架,在细节上,Uboot必须完成上面这些工作以满足Linux的启动要求。
相关文章推荐
- uboot启动流程详解(5)-_main
- Uboot启动流程分析
- Uboot详细启动流程
- UBoot启动代码第一阶段流程
- uboot-2012.04.01移植到mini2440(一)启动流程、内存分布及重定位初步分析
- uboot 2015-01版本启动linux简易流程
- uboot 启动流程
- uboot启动流程及内核参数传递
- Uboot 2017.01 启动流程分析
- uboot的启动流程
- Uboot启动流程(图+代码)
- uboot启动流程分析之四
- uboot启动流程分析
- uboot启动流程分析和uboot移植(粗略分析)
- uboot2015 启动流程
- 从0移植uboot (二) _uboot启动流程分析
- uboot启动流程
- IMX6Solo启动流程 外传-新建一条Uboot命令
- uboot流程分析--修改android启动模式按键
- 【转】P4080上电启动及uboot流程