学习笔记--- U-BOOT从启动到引导内核过程分析
2014-03-04 10:49
681 查看
U-BOOT是操作系统启动之前的一段引导程序,所以他的终极目的就是要启动内核;所以他有三个重要的任务:
1 初始化硬件信息与环境
2 从NANDFLASH内核分区读取内核到SDRAM
3 跳转到内核入口地址执行内核
这三个是核心,其他的什么烧内核,文件系统等功能都是为了在开发阶段协助开发使用的功能,但最后都是为了引导内核,下面按照上面三个重要的任务分析,先看启动:
第一阶段要做的事情:
1 设置中断向量表
2 切换到管理模式
3 关看门狗,屏蔽终端,刷新chach,静止MMU,初始化SDRAM,设置堆栈区,初始化时钟
4 复制代码到SDRAM
5 清BSS段,判断设置NORFLASH启动标志
6 调用start_armboot进入第二阶段
---------------------------------------------------------------------------------------------------------------------------------------------------------------
start_armboot 分析:
第二阶段初始化一些变量,信息,中断,串口,堆栈地址,nand,SDRAM,外设初始化,环境变量;主要目的就是为了设置下面这个结构体:(填充全局的一些参数)
这个结构体数据存放在:
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
这里在第一阶段subr0, r0, #CFG_GBL_DATA_SIZE 设置好了区域;
结构体内容:
填充的主要代码有:
gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
gd->bd->bi_boot_params = 0x30000100;
gd->bd->bi_baudrate = gd->baudrate = (i > 0)? (int) simple_strtoul (tmp, NULL, 10): CONFIG_BAUDRATE;
gd->have_console = 1;
gd->flags |= GD_FLG_SILENT;
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
gd->env_valid = 1;
gd->env_addr = (ulong)&(env_ptr->data);
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
gd->jt[XF_getc] = serial_getc;
这里面的一些值是环境变量来的,环境变量默认的设置在下面,首次加载环境变量是默认值,当进行一次改写后,就会把这些环境变量存在NANDFLASH uboot参数区,
之后启动就会通过:
来读取环境变量。默认值通过下面变量设置:
修改环境变量通过下面函数实现的:
这些东西准备好之后,再根据这些参数或者环境变量设置好串口,网卡等设备,所有初始化完成后,进入一个死循环:main_loop ();
这个函数核心就是:
如果开机延时时间(bootdelay环境变量)内按空格键,则执行:
run_command("menu", 0); //进入维护模式
否则:
printf("Booting Linux ...\n");
run_command (s, 0); //进入引导模式
run_command 函数就是个uboot自带的shell内核,输入指令执行函数的shell模型;
他的指令与执行的函数有一个约定:输入help指令,就会对应执行do_help函数,就是指令对应的执行函数名要定义为:
do_指令;
本文开始就说了启动内核的这三步:
1 初始化硬件信息与环境
2 从NANDFLASH内核分区读取内核到SDRAM
3 跳转到内核入口地址执行内核
第一步完成,剩下2是通过nand 指令从nandflash读内核到sdram实现的,3是由bootm来启动内核的。所以bootcmd=这个环境变量必须设置这两项:
这里假设引导模式传入的指令 s=”bootcmd=nand read 30007fc0 3c0000 300000;bootm 00007fc0“
这里执行nand指令和bootm指令,查看源码就知道:
nand read 00007fc0 3c0000 300000 从nandflash 地址0x3c0000 读取0x300000长度的代码到SDRAM的0x00007fc0 里面;
bootm 00007fc0 为启动SDRAM地址;
bootm 的时候,有这样一段代码(这里指考虑未压缩):
1 如果nand read 下载到sdram的下载地址和bootm地址相同,那么这个地址对应的uImage的头部起始;
这里如果是未压缩内核则判断bootm地址是否和装载地址一致,如果一致则不移动内核数据到装载地址,如果不一致则移动,也就是说在这个条件下:
如果bootm地址 = 装载地址 ,那么 入口地址=装载地址-0x40(64字节头部)
如果bootm地址!=装载地址,那么入口地址=装载地址
而上面分析Makefile可知道入口地址和装载地址是相等的,那么必须满足:
下载地址 = bootm地址 != 装载地址 = 入口地址
2 如果nand read 下载到sdram的地址和bootm地址不同;假设nand read 下载到sdram的地址为0x30007fc0,而bootm地址为0x30008000,那么这个bootm地址刚好对应的是内核起始;此时bootm地址
= 装载地址,那么内核数据不移动;那么必须满足:
下载地址 +0x40 = bootm地址 = 装载地址 = 入口地址
满足这样的关系,系统才能正常启动;
进入内核前的最后一步调用do_bootm_linux函数:
内部实现代码:
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
这三个参数分别存放在寄存器r0 r1 r2 ,内核启动代码里面读取这三个寄存器就表示从uboot传入参数到了内核了;这个函数调用成功之后就没uboot事了。到此uboot全部任务完成了。
这里再看下bd->bi_boot_params这个参数:
这个参数是在调用theKernel (0, bd->bi_arch_number, bd->bi_boot_params);之前设置好的,称为TAG设置:
所以params的地址就是0x30000100,接下来的参数都是从这个开始陆续存下去的;这个参数设置需要和内核设置的匹配:
填完一个之后,下一个:
1 初始化硬件信息与环境
2 从NANDFLASH内核分区读取内核到SDRAM
3 跳转到内核入口地址执行内核
这三个是核心,其他的什么烧内核,文件系统等功能都是为了在开发阶段协助开发使用的功能,但最后都是为了引导内核,下面按照上面三个重要的任务分析,先看启动:
#include <config.h> @实际上就是uboot在执行配置脚本的时候生成的"#include <configs/$BOARD.h>" #include <version.h> /* ************************************************************************* * * Jump vector table as in table 3.1 in [1] * ************************************************************************* */ /* _start是GNU汇编器的默认入口标签,.globl将_start声明为外部程序可访问的标签,.globl是GNU汇编的保留关键字,前面加点是GNU汇编的语法 */ .globl _start _start: b reset /*为什么只能用b指令,参考学习笔记----S3C2440 从NANDFLASH启动的设计原理与过程*/ 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 /* 上面列了一张32字节的向量表,相当于定义8个函数指针,占用32字节; 硬件根据相对应的情况自动跳转进来查询表内的跳转地址,上电复位跳进来从_start:开始执行,跳到reset函数执行 */ _undefined_instruction: .word undefined_instruction /*相当于C语言定义一个函数指针,再给其赋值;先从向量表跳转到_undefined_instruction,再通过这个跳转到ndefined_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 /* 理解为如果当前PC不是16的倍数,那么填充deadbeef,而此时PC为15X4=60,所以填进去一块"死牛肉",之后变为64字节,为16的倍数; 这个牛肉只是一个标签,只是告诉你前面的东西软件禁止访问,其实是由硬件去访问的 */ /* ************************************************************************* * * 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指针并赋值为TEXT_BASE,而TEXT_BASE的值在BOARD目录config.mk里面定义,代表启动代码在SDRAM中的加载地址 */ _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 .globl FREE_RAM_END FREE_RAM_END: .word 0x0badc0de .globl FREE_RAM_SIZE FREE_RAM_SIZE: .word 0x0badc0de .globl PreLoadedONRAM PreLoadedONRAM: .word 0 #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 r0,r0,#0x1f orr r0,r0,#0xd3 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 # define INTMOD 0X4A000004 # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */ # define INTSUBMSK 0x4A00001C # define CLKDIVN 0x4C000014 /* clock divisor register */ #endif #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) 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, =INTMSK str r1, [r0] # if defined(CONFIG_S3C2410) ldr r1, =0x3ff ldr r0, =INTSUBMSK str r1, [r0] # endif #if 0 /* FCLK:HCLK:PCLK = 1:2:4 */ /* default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN mov r1, #3 str r1, [r0] #endif #endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */ /* * we do sys-critical inits only at reboot, * not when booting from ram! */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT adr r0, _start /* r0 <- current position of code 如果是从NANDFLASH启动,_start的当前运行地址肯定是0*/ ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* don't reloc during debug */ blne cpu_init_crit #endif /* Set up the stack */ stack_setup: ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ sub r0, r0, #CFG_MALLOC_LEN /* malloc area */ sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQ sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif sub sp, r0, #12 /* leave 3 words for abort-stack */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl clock_init #endif #ifndef CONFIG_SKIP_RELOCATE_UBOOT relocate: /* relocate U-Boot to RAM */ adr r0, _start /* r0 <- current position of code */ ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* don't reloc during debug */ beq clear_bss ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 /* r2 <- size of armboot */ bl CopyCode2Ram /* r0: source, r1: dest, r2: size */ #endif /* CONFIG_SKIP_RELOCATE_UBOOT */ 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 SetLoadFlag: /* Set a global flag, PreLoadedONRAM */ adr r0, _start /* r0 <- current position of code */ ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* don't reloc during debug */ ldr r2, =PreLoadedONRAM mov r3, #1 streq r3, [r2] ldr pc, _start_armboot _start_armboot: .word start_armboot /*下面还有这么多代码,都是处理异常的,我们先不分析,以后再具体分析,现在只讲主干流程*/
第一阶段要做的事情:
1 设置中断向量表
2 切换到管理模式
3 关看门狗,屏蔽终端,刷新chach,静止MMU,初始化SDRAM,设置堆栈区,初始化时钟
4 复制代码到SDRAM
5 清BSS段,判断设置NORFLASH启动标志
6 调用start_armboot进入第二阶段
---------------------------------------------------------------------------------------------------------------------------------------------------------------
start_armboot 分析:
第二阶段初始化一些变量,信息,中断,串口,堆栈地址,nand,SDRAM,外设初始化,环境变量;主要目的就是为了设置下面这个结构体:(填充全局的一些参数)
这个结构体数据存放在:
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
这里在第一阶段subr0, r0, #CFG_GBL_DATA_SIZE 设置好了区域;
结构体内容:
typedef structglobal_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; 包括里面的结构体: typedef struct bd_info { int bi_baudrate;/* serial console baudrate */ unsigned long bi_ip_addr;/* IP Address */ unsigned char bi_enetaddr[6]; /* Ethernet adress */ struct environment_s *bi_env; ulong bi_arch_number;/* unique id for this board */ ulong bi_boot_params;/* where this board expects params */ struct /* RAM configuration */ { ulong start; ulong size; } bi_dram[CONFIG_NR_DRAM_BANKS]; #ifdef CONFIG_HAS_ETH1 /* second onboard ethernet port */ unsigned char bi_enet1addr[6]; #endif } bd_t;
填充的主要代码有:
gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
gd->bd->bi_boot_params = 0x30000100;
gd->bd->bi_baudrate = gd->baudrate = (i > 0)? (int) simple_strtoul (tmp, NULL, 10): CONFIG_BAUDRATE;
gd->have_console = 1;
gd->flags |= GD_FLG_SILENT;
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
gd->env_valid = 1;
gd->env_addr = (ulong)&(env_ptr->data);
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
gd->jt[XF_getc] = serial_getc;
这里面的一些值是环境变量来的,环境变量默认的设置在下面,首次加载环境变量是默认值,当进行一次改写后,就会把这些环境变量存在NANDFLASH uboot参数区,
之后启动就会通过:
ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);
来读取环境变量。默认值通过下面变量设置:
uchar default_environment[]
修改环境变量通过下面函数实现的:
int saveenv(void) { ulong total; int ret = 0; puts ("Erasing Nand..."); if (nand_erase(&nand_info[0], CFG_ENV_OFFSET, CFG_ENV_SIZE)) return 1; puts ("Writing to Nand... "); total = CFG_ENV_SIZE; ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr); if (ret || total != CFG_ENV_SIZE) return 1; puts ("done\n"); return ret; }
这些东西准备好之后,再根据这些参数或者环境变量设置好串口,网卡等设备,所有初始化完成后,进入一个死循环:main_loop ();
这个函数核心就是:
如果开机延时时间(bootdelay环境变量)内按空格键,则执行:
run_command("menu", 0); //进入维护模式
否则:
printf("Booting Linux ...\n");
run_command (s, 0); //进入引导模式
run_command 函数就是个uboot自带的shell内核,输入指令执行函数的shell模型;
他的指令与执行的函数有一个约定:输入help指令,就会对应执行do_help函数,就是指令对应的执行函数名要定义为:
do_指令;
本文开始就说了启动内核的这三步:
1 初始化硬件信息与环境
2 从NANDFLASH内核分区读取内核到SDRAM
3 跳转到内核入口地址执行内核
第一步完成,剩下2是通过nand 指令从nandflash读内核到sdram实现的,3是由bootm来启动内核的。所以bootcmd=这个环境变量必须设置这两项:
这里假设引导模式传入的指令 s=”bootcmd=nand read 30007fc0 3c0000 300000;bootm 00007fc0“
这里执行nand指令和bootm指令,查看源码就知道:
nand read 00007fc0 3c0000 300000 从nandflash 地址0x3c0000 读取0x300000长度的代码到SDRAM的0x00007fc0 里面;
bootm 00007fc0 为启动SDRAM地址;
bootm 的时候,有这样一段代码(这里指考虑未压缩):
if(ntohl(hdr->ih_load) == addr)// 相等则不需要移动 { printf (" XIP %s ... ", name); } else { memmove ((void *) ntohl(hdr->ih_load),(uchar *)data, len);//把真正的内核移动到加载地址 }
1 如果nand read 下载到sdram的下载地址和bootm地址相同,那么这个地址对应的uImage的头部起始;
这里如果是未压缩内核则判断bootm地址是否和装载地址一致,如果一致则不移动内核数据到装载地址,如果不一致则移动,也就是说在这个条件下:
如果bootm地址 = 装载地址 ,那么 入口地址=装载地址-0x40(64字节头部)
如果bootm地址!=装载地址,那么入口地址=装载地址
而上面分析Makefile可知道入口地址和装载地址是相等的,那么必须满足:
下载地址 = bootm地址 != 装载地址 = 入口地址
2 如果nand read 下载到sdram的地址和bootm地址不同;假设nand read 下载到sdram的地址为0x30007fc0,而bootm地址为0x30008000,那么这个bootm地址刚好对应的是内核起始;此时bootm地址
= 装载地址,那么内核数据不移动;那么必须满足:
下载地址 +0x40 = bootm地址 = 装载地址 = 入口地址
满足这样的关系,系统才能正常启动;
进入内核前的最后一步调用do_bootm_linux函数:
内部实现代码:
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
这三个参数分别存放在寄存器r0 r1 r2 ,内核启动代码里面读取这三个寄存器就表示从uboot传入参数到了内核了;这个函数调用成功之后就没uboot事了。到此uboot全部任务完成了。
这里再看下bd->bi_boot_params这个参数:
这个参数是在调用theKernel (0, bd->bi_arch_number, bd->bi_boot_params);之前设置好的,称为TAG设置:
#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 if (initrd_start && initrd_end) setup_initrd_tag (bd, initrd_start, initrd_end); #endif #if defined (CONFIG_VFD) || defined (CONFIG_LCD) setup_videolfb_tag ((gd_t *) gd); #endif setup_end_tag (bd); #endif /* we assume that the kernel is in place */ printf ("\nStarting kernel ...\n\n"); #ifdef CONFIG_USB_DEVICE { extern void udc_disconnect (void); //udc_disconnect (); // cancled by www.arm9.net } #endif先看下第一个设置函数:
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); }基本上没做什么事情,就是起个头,设置好params的实际操作地址,而之前在board_init里面设置好了
gd->bd->bi_boot_params = 0x30000100;
所以params的地址就是0x30000100,接下来的参数都是从这个开始陆续存下去的;这个参数设置需要和内核设置的匹配:
MACHINE_START(SMDK2410, "SMDK2410") .phys_ram = S3C2410_SDRAM_PA, .phys_io = S3C2410_PA_UART, .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc, .boot_params = S3C2410_SDRAM_PA + 0x100,//必须匹配gd->bd->bi_boot_params .map_io = smdk2410_map_io, .init_irq = smdk2410_init_irq, .timer = &s3c24xx_timer, MACHINE_END
填完一个之后,下一个:
void setup_serial_tag (struct tag **tmp) { struct tag *params = *tmp; struct tag_serialnr serialnr; void get_board_serial(struct tag_serialnr *serialnr); get_board_serial(&serialnr); params->hdr.tag = ATAG_SERIAL; params->hdr.size = tag_size (tag_serialnr); params->u.serialnr.low = serialnr.low; params->u.serialnr.high= serialnr.high; params = tag_next (params); *tmp = params; }这个基本上都不用,再往下面放,设置版本参数:
void setup_revision_tag(struct tag **in_params) { u32 rev = 0; u32 get_board_rev(void); rev = get_board_rev(); params->hdr.tag = ATAG_REVISION; params->hdr.size = tag_size (tag_revision); params->u.revision.rev = rev; params = tag_next (params); }再往下是填写SDRAM的地址和大小:
static void setup_memory_tags (bd_t *bd) { int i; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { params->hdr.tag = ATAG_MEM; params->hdr.size = tag_size (tag_mem32); params->u.mem.start = bd->bi_dram[i].start; params->u.mem.size = bd->bi_dram[i].size; params = tag_next (params); } }再下一个就是存放bootargs环境变量,这个参数在内核启动的时候非常重要:
static void setup_commandline_tag (bd_t *bd, char *commandline) { char *p; if (!commandline) return; /* eat leading white space */ for (p = commandline; *p == ' '; p++); /* skip non-existent command lines so the kernel will still * use its default command line. */ if (*p == '\0') return; params->hdr.tag = ATAG_CMDLINE; params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; strcpy (params->u.cmdline.cmdline, p); params = tag_next (params); }下面几个都不是常用的,反正就是按照这个params的格式写一些参数进去,然后地址累加下去,一直加到下面这个:
static void setup_end_tag (bd_t *bd) { params->hdr.tag = ATAG_NONE; params->hdr.size = 0; }表示参数段结束了,所以这些TAG参数就是uboot与内核的数据通道,通过把这个params的地址传递给内核,就表示这部分数据被内核共享了,共享地址为0x30000100
相关文章推荐
- TQ2440 学习笔记—— 30、移植U-Boot【U-Boot 的启动过程第一阶段源码分析】
- TQ2440 学习笔记—— 31、移植U-Boot【U-Boot 的启动过程第二阶段源码分析】
- 移植u-boot学习笔记1-----实验及分析启动过程之概述
- 移植u-boot学习笔记3-----分析启动过程之重定位
- 学习笔记:u-boot引导内核启动
- 学习笔记3-跟踪分析Linux内核的启动过程
- 移植u-boot学习笔记2-----分析启动过程之内存分布
- linux内核分析学习笔记:用gdb跟踪linux内核启动过程
- U-Boot启动引导内核分析
- Linux0.11内核--启动引导代码分析bootsect.s
- u-boot2013.01 smdk2410 启动第二阶段分析之引导内核
- Uboot学习笔记②---(bootloader的一些共同特性、uboot启动过程、编译环境、地址规划设计)
- Linux0.11内核--启动引导代码分析bootsect.s
- u-boot简单学习笔记(三)——AR9331 uboot启动分析
- u-boot-2009.08引导内核学习笔记(转)
- Spring Boot学习笔记03--深入了解SpringBoot的启动过程
- u-boot-2009.08引导内核学习笔记(转)
- Linux学习笔记:系统启动引导过程
- u-boot-2010.12引导内核启动过程记录
- 学习笔记 --- LINUX内核启动第二阶段分析(不考虑自解压过程)