从单片机到系统之--uboot启动arm linux
UBOOT官网下载地址:http://ftp.denx.de/pub/u-boot/
很详细的UBOOT解释: https://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/html/uboot_starts_analysis.html
U-BOOT主要作用和执行流程
①一句话描述
U-BOOT对硬件进行前期的初始化并准备堆栈,之后载入内核并向内核传递必要的参数,便于内核启动。
②执行流程概况
u-boot载入芯片后nor flash从0地址开始执行,nand flash会将前4K考入芯片的内存然后从0地址开始执行,程序最还是从_start标签开始(lds文件定义,start.s有具体实现)。
u-boot一般启动分为两阶段:1.硬件相关的用汇编实现,初始化及重定位代码完成后交给第二阶段 2.第二阶段较为通用,使用c代码编写。
1. UBoot内存划分, lds文件
内存划分存在于对应芯片架构中的u-boot.lds文件中,这个文件是用于连接器连接时对程序各个段空间进行划分之用。
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start) //程序开始标号,既程序从标号为_start的位置开始执行,这个标号在对应CPU(如arm920t)的start.s文件中,接下来程序分析将分析这个文件
SECTIONS
{
. = 0x00000000; //程序最开始的地址为0 . = ALIGN(); //4字节地址对齐
.text : //文本段开始位置
{
__image_copy_start = .; //拷贝image开始的地方(其实也是0)
CPUDIR/start.o (.text) //保证start.s的text段放在最前面
*(.text) //u-boot中所有程序的文本段都会放到这个位置
} . = ALIGN(); //4字节对齐
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } //只读数据从这里开始存放 . = ALIGN();
.data : {
*(.data) //所有文件的数据段
} . = ALIGN(); . = .; //不i知道,可能重定位一下定位器的位置比较好玩儿吧
__u_boot_cmd_start = .; //同上,定义了一个宏,保存当前位置信息
.u_boot_cmd : { *(.u_boot_cmd) } //存储u_boot_cmd
__u_boot_cmd_end = .; . = ALIGN(); __image_copy_end = .; .rel.dyn : {
__rel_dyn_start = .;
*(.rel*)
__rel_dyn_end = .;
} .dynsym : {
__dynsym_start = .;
*(.dynsym)
} _end = .; /*
* Deprecated: this MMU section is used by pxa at present but
* should not be used by new boards/CPUs.
*/
. = ALIGN();
.mmutable : {
*(.mmutable)
} .bss __rel_dyn_start (OVERLAY) : {
__bss_start = .;
*(.bss)
. = ALIGN();
__bss_end__ = .;
} /DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
}
2. 按代码执行流程分析各个函数
2.1 主要功能
- 设置CPU模式
- 关闭看门狗
- 关闭中断
- 设置堆栈sp指针
- 清除bss段
- 异常中断处理
2.2 主要函数
函数入口:
.globl _start //全局声明,在lds中定义为entry
_start: b start_code //主要执行函数,进入代码后直接跳转到start_code
ldr pc, _undefined_instruction //ldr,将地址导入寄存器,pc为程序指针。导入pc后会进行调用跳转
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
初始化函数:start_code
start_code:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr //cspr值存入寄存器,方便修改
bic r0, r0, #0x1f //将低5位【4:0】清零
orr r0, r0, #0xd3 //或操作,进行置位
msr cpsr, r0 //修改后的cspr放入寄存器
/*以上为设置cspr值的代码段,使系统进入SVC方式*/
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
/*
* relocate exception table
*/
ldr r0, =_start //标号地址放入r0,重载时确定起始位置
ldr r1, =0x0 //地址0x00放入r1, 重载后为0地址
mov r2, # //循环次数计数, 16*4 存前64字节?
copyex:
subs r2, r2, # //减数做循环
ldr r3, [r0], # //r0地址存入r3,并且r9加4
str r3, [r1], # //r3地址数据(start开始的地方),放入r1的地址(0地址),r1加4
bne copyex //r2不为0则循环写数据,因为r2做了减法,cspr状态值改变只会因为r2的操作产生
#endif #ifdef CONFIG_S3C24X0
/* turn off the watchdog */ # if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interrupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#else
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interrupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
# endif ldr r0, =pWTCON //看门狗地址存入r0,查datasheet
mov r1, #0x0 //0存入r1
str r1, [r0] //看门狗地址存入0,既关看门狗 /*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0] //中断地址存入全1,禁止中断
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif /* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #
str r1, [r0] //修改时钟参数
#endif /* CONFIG_S3C24X0 */ /*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit //非从ram启动时会调用这个函数
#endif /* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f: //设置了lowlevel初始化从这里初始化板子
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) //设置栈顶指针,宏在config.mk
bic sp, sp, # /* 8-byte alignment for ABI compliance */ //清除后四位,8字节对齐
ldr r0,=0x00000000
bl board_init_f //调用函数,bl相对于b调用区别:会填充R4(lr)寄存器,调用结束会返回,函数在board.c中。返回后重新执行下面的函数,正好对应c中的relocate_code
//cpu_init_crit最终也会调用这个函数
非从RAM启动的情况会执行这个函数:cpu_init_crit
cpu_init_crit:
/*
* flush v4 I/D caches
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 //关闭ICaches(指令缓存,关闭是为了降低MMU查表带来的开销)和DCaches(数据缓存,DCaches使用的是虚拟地址,开启MMU之前必须关闭)
mcr p15, 0, r0, c8, c7, 0 //使无效整个数据TLB和指令TLB(TLB就是负责将虚拟内存地址翻译成实际的物理内存地址
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) //bit8:系统不保护,bit9:ROM不保护,bit13:设置中断向量表的位置为0x0~0x1c,即异常模式基地址为0X0
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) //bit0~2:禁止MMU,禁止地址对齐检查,禁止数据Cache.bit7:设为小端模式
orr r0, r0, #0x00000002 @ set bit 2 (A) Align //bit2:开启数据Cache
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache //bit12:开启指令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.
*/
mov ip, lr //保存lr的值,用于返回调用cpu_init_crit的函数处,用于调用board_init_f
bl lowlevel_init //函数在对应的board文件夹中,第二阶段启动代码,主要作用是初始化两个重要数据结构,对SDRAM的内存分配设置,对各种需要用到的外设进行初始化,最后循环跳入main_loop()函数
mov lr, ip
mov pc, lr
board_init_f函数
涉及到两个重要的数据结构:1)bd_t结构体,关于开发板信息(波特率,ip, 平台号,启动参数)。2)gd_t结构体成员主要是一些全局的系统初始化参数。需要用到时,用宏定义DECLARD_GLOBAL_DATA_PTT,指定占用寄存器r8,具体定义如下: typedef struct bd_info {
int bi_baudrate; /* serial console baudrate串口波特率 */
unsigned long bi_ip_addr; /* IP Address IP 地址*/
ulong bi_arch_number; /* unique id for this board 板子的id */
ulong bi_boot_params; /* where this board expects params 启动参数*/
struct /* RAM configuration RAM 配置*/
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
Gd_t结构体定义,如下:
typedef struct global_data {
bd_t *bd;
unsigned long flags; //指示标志,如设备已经初始化标志等
unsigned long baudrate; //串行口通信速率
unsigned long have_console; /* serial_init() was called */
#ifdef CONFIG_PRE_CONSOLE_BUFFER
unsigned long precon_buf_idx; /* Pre-Console buffer index */
#endif
unsigned long env_addr; /* Address of Environment struct 环境参数地址 */
unsigned long env_valid; /* Checksum of Environment valid? 环境参数CRC检验有效标志*/
unsigned long fb_base; /* base address of frame buffer 帧缓冲区基地址*/
……
} gd_t;
c函数,计算重定位后地址分配,填充bd和gd数据:board_init_f
void board_init_f(ulong bootflag)
{
bd_t *bd;
init_fnc_t **init_fnc_ptr;
gd_t *id;
ulong addr, addr_sp;
#ifdef CONFIG_PRAM
ulong reg;
#endif bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f"); /* Pointer is writable since we allocated a register for it */
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory"); /*内存屏障,防止编译器优化赋值顺序*/ memset((void *)gd, , sizeof(gd_t)); gd->mon_len = _bss_end_ofs; /*_bss_end_ofs的定义在start.S中: .globl _bss_end_ofs,这里计算出来了U-BOOT的大小,用于后面做减法计算重定位的起始地址*/
#ifdef CONFIG_OF_EMBED
/* Get a pointer to the FDT */
gd->fdt_blob = _binary_dt_dtb_start;
#elif defined CONFIG_OF_SEPARATE
/* FDT is at end of image */
gd->fdt_blob = (void *)(_end_ofs + _TEXT_BASE);
#endif
/* Allow the early environment to override the fdt address */
gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", ,
(uintptr_t)gd->fdt_blob); for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { /*初始化函数的数组,会逐个调用数组中的初始化函数*/
if ((*init_fnc_ptr)() != ) {
hang ();
}
} #ifdef CONFIG_OF_CONTROL
/* For now, put this check after the console is ready */
if (fdtdec_prepare_fdt()) {
panic("** CONFIG_OF_CONTROL defined but no FDT - please see "
"doc/README.fdt-control");
}
#endif debug("monitor len: %08lX\n", gd->mon_len);
/*
* Ram is setup, size stored in gd !!
*/
debug("ramsize: %08lX\n", gd->ram_size);
#if defined(CONFIG_SYS_MEM_TOP_HIDE)
/*
* Subtract specified amount of memory to hide so that it won't
* get "touched" at all by U-Boot. By fixing up gd->ram_size
* the Linux kernel should now get passed the now "corrected"
* memory size and won't touch it either. This should work
* for arch/ppc and arch/powerpc. Only Linux board ports in
* arch/powerpc with bootwrapper support, that recalculate the
* memory size from the SDRAM controller setup will have to
* get fixed.
*/
gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; /*这样addr是内存的高地址,0x30000000+256M, 关于gd->ram_size,在初始化函数dram_init中,已经给过值了*/ #ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/* reserve kernel log buffer */
addr -= (LOGBUFF_RESERVE);
debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN,
addr);
#endif
#endif #ifdef CONFIG_PRAM
/*
* reserve protected RAM
*/
reg = getenv_ulong("pram", , CONFIG_PRAM); /*从环境变量获取代码地址,pram*/
addr -= (reg << ); /* size is in kB */
debug("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
#endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
/* reserve TLB table */
addr -= ( * ); /* round down to next 64 kB limit */
addr &= ~(0x10000 - ); //64K对齐 gd->tlb_addr = addr;
debug("TLB table at: %08lx\n", addr);
#endif /* round down to next 4 kB limit */
addr &= ~( - ); // K对齐,此处前面已经64K对齐了,就不需改动
debug("Top of RAM usable for U-Boot at: %08lx\n", addr); #ifdef CONFIG_LCD
#ifdef CONFIG_FB_ADDR
gd->fb_base = CONFIG_FB_ADDR;
#else
/* reserve memory for LCD display (always full pages) */
addr = lcd_setmem(addr);
gd->fb_base = addr;
#endif /* CONFIG_FB_ADDR */
#endif /* CONFIG_LCD */ /*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len; /*重定位地址再减去U-BOOT的大小,此时就是重定位时代码要拷贝到的目的位置*/
addr &= ~( - ); debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> , addr); #ifndef CONFIG_SPL_BUILD
/*
* reserve memory for malloc() arena
*/
addr_sp = addr - TOTAL_MALLOC_LEN; /*当前地址减去预留给堆的空间后,作为栈的初始地址*/
debug("Reserving %dk for malloc() at: %08lx\n",
TOTAL_MALLOC_LEN >> , addr_sp);
/*
* (permanently) allocate a Board Info struct
* and a permanent copy of the "global" data
*/
addr_sp -= sizeof (bd_t); /*栈中用于放bd_t的数据,预留出足够的空间*/
bd = (bd_t *) addr_sp;
gd->bd = bd; /*bd指向新分配的地址,代码重定位把数据copy过去后可以直接使用地址指针*/
debug("Reserving %zu Bytes for Board Info at: %08lx\n",
sizeof (bd_t), addr_sp); #ifdef CONFIG_MACH_TYPE
gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
#endif addr_sp -= sizeof (gd_t); /*栈顶指针减法,预留出gd_t的空间,参考bd_t*/
id = (gd_t *) addr_sp;
debug("Reserving %zu Bytes for Global Data at: %08lx\n",
sizeof (gd_t), addr_sp); /* setup stackpointer for exeptions */
gd->irq_sp = addr_sp; /*此时栈顶的地址放入gd结构体,作为ird中断使用的栈顶地址*/
#ifdef CONFIG_USE_IRQ
addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
#endif
/* leave 3 words for abort-stack */
addr_sp -= ; /*为中断预留的栈空间*/ /* 8-byte alignment for ABI compliance */
addr_sp &= ~0x07;
#else
addr_sp += ; /* leave 32 words for abort-stack */
gd->irq_sp = addr_sp;
#endif debug("New Stack Pointer is: %08lx\n", addr_sp); #ifdef CONFIG_POST
post_bootmode_init();
post_run(NULL, POST_ROM | post_bootmode_get());
#endif gd->bd->bi_baudrate = gd->baudrate;
/* Ram ist board specific, so move it to board code ... */
dram_init_banksize();
display_dram_config(); /* and display it */ gd->relocaddr = addr; /*u-boot重新搬运后的起始地址。*/
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE;
debug("relocation Offset is: %08lx\n", gd->reloc_off);
memcpy(id, (void *)gd, sizeof(gd_t)); relocate_code(addr_sp, id, addr); /*relocate_code(addr_sp, id, addr);在start.S中定义,C又回到了汇编*/ /* NOTREACHED - relocate_code() does not return */
}
被c函数调用,用于重定位代码,存在于start.s中:relocate_code
.globl relocate_code
relocate_code:
mov r4, r0 /* save addr_sp */
mov r5, r1 /* save addr of gd */
mov r6, r2 /* save addr of destination */
/*以上为保存传入的3个参数*/
/* Set up the stack */
stack_setup:
mov sp, r4 adr r0, _start
cmp r0, r6
beq clear_bss /* skip relocation */
mov r1, r6 /* r1 <- scratch for copy_loop */
ldr r3, _bss_start_ofs
add r2, r0, r3 /* r2 <- source end address */ copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */
stmia r1!, {r9-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end address [r2] */
blo copy_loop
/*上述代码将U-BOOT拷贝到了新的重定位后的位置,即上面片段计算出来的addr的地址*/
#ifndef CONFIG_SPL_BUILD
/*
* fix .rel.dyn relocations
*/
/*使用 .rel.dyn字段进行重定位*/
ldr r0, _TEXT_BASE /* r0 <- Text base */
sub r9, r6, r0 /* r9 <- relocation offset */
ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */
add r10, r10, r0 /* r10 <- sym table in FLASH */
ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */
add r2, r2, r0 /* r2 <- rel dyn start in FLASH */
ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */
add r3, r3, r0 /* r3 <- rel dyn end in FLASH */
fixloop:
ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */
add r0, r0, r9 /* r0 <- location to fix up in RAM */
ldr r1, [r2, #]
and r7, r1, #0xff
cmp r7, # /* relative fixup? */
beq fixrel
cmp r7, # /* absolute fixup? */
beq fixabs
/* ignore unknown type of fixup */
b fixnext
fixabs:
/* absolute fix: set location to (offset) symbol value */
mov r1, r1, LSR # /* r1 <- symbol index in .dynsym */
add r1, r10, r1 /* r1 <- address of symbol in table */
ldr r1, [r1, #] /* r1 <- symbol value */
add r1, r1, r9 /* r1 <- relocated sym addr */
b fixnext
fixrel:
/* relative fix: increase location by offset */
ldr r1, [r0]
add r1, r1, r9
fixnext:
str r1, [r0]
add r2, r2, # /* each rel.dyn entry is 8 bytes */
cmp r2, r3
blo fixloop
#endif clear_bss:
#ifndef CONFIG_SPL_BUILD
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
mov r4, r6 /* reloc addr */
add r0, r0, r4
add r1, r1, r4
mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #
cmp r0, r1
bne clbss_l bl coloured_LED_init
bl red_led_on
#endif /*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
#ifdef CONFIG_NAND_SPL
ldr r0, _nand_boot_ofs
mov pc, r0 _nand_boot_ofs:
.word nand_boot //nand启动
#else
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
add lr, lr, r9
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r6 /* dest_addr */
/* jump to it ... */
mov pc, lr //这里lr为board_init_r地址 _board_init_r_ofs:
.word board_init_r - _start
#endif _rel_dyn_start_ofs:
.word __rel_dyn_start - _start
_rel_dyn_end_ofs:
.word __rel_dyn_end - _start
_dynsym_start_ofs:
.word __dynsym_start - _start
第二阶段:board_init_r最终调用main_loop解析参数启动内核
void board_init_r(gd_t *id, ulong dest_addr)
{
ulong malloc_start;
#if !defined(CONFIG_SYS_NO_FLASH)
ulong flash_size;
#endif gd = id; gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */
bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r"); monitor_flash_len = _end_ofs; /* Enable caches */
enable_caches(); debug("monitor flash len: %08lX\n", monitor_flash_len);
board_init(); /* Setup chipselects */ /*设置板级的初始化*/
/*
* TODO: printing of the clock inforamtion of the board is now
* implemented as part of bdinfo command. Currently only support for
* davinci SOC's is added. Remove this check once all the board
* implement this.
*/
#ifdef CONFIG_CLOCKS
set_cpu_clk_info(); /* Setup clock information */
#endif
#ifdef CONFIG_SERIAL_MULTI
serial_initialize();
#endif debug("Now running in RAM - U-Boot at: %08lx\n", dest_addr); #ifdef CONFIG_LOGBUFFER
logbuff_init_ptrs();
#endif
#ifdef CONFIG_POST
post_output_backlog();
#endif /* The Malloc area is immediately below the monitor copy in DRAM */
malloc_start = dest_addr - TOTAL_MALLOC_LEN; /*剩余出堆内存的空间*/
mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN); #if !defined(CONFIG_SYS_NO_FLASH)
puts("Flash: "); flash_size = flash_init();
if (flash_size > ) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
char *s = getenv("flashchecksum"); print_size(flash_size, "");
/*
* Compute and print flash CRC if flashchecksum is set to 'y'
*
* NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
*/
if (s && (*s == 'y')) {
printf(" CRC: %08X", crc32(,
(const unsigned char *) CONFIG_SYS_FLASH_BASE,
flash_size));
}
putc('\n');
# else /* !CONFIG_SYS_FLASH_CHECKSUM */
print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
} else {
puts(failed);
hang();
}
#endif #if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init(); /* go init the NAND */
#endif #if defined(CONFIG_CMD_ONENAND)
onenand_init();
#endif #ifdef CONFIG_GENERIC_MMC
puts("MMC: ");
mmc_initialize(gd->bd);
#endif #ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif /* initialize environment */
env_relocate(); #if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
arm_pci_init();
#endif /* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr"); stdio_init(); /* get the devices list going. */ jumptable_init(); #if defined(CONFIG_API)
/* Initialize API */
api_init();
#endif console_init_r(); /* fully init console as a device */ #if defined(CONFIG_ARCH_MISC_INIT)
/* miscellaneous arch dependent initialisations */
arch_misc_init();
#endif
#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r();
#endif /* set up exceptions */
interrupt_init();
/* enable exceptions */
enable_interrupts(); /* Perform network card initialisation if necessary */
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
/* XXX: this needs to be moved to board init */
if (getenv("ethaddr")) {
uchar enetaddr[];
eth_getenv_enetaddr("ethaddr", enetaddr);
smc_set_mac_addr(enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */ /* Initialize from environment */
load_addr = getenv_ulong("loadaddr", , load_addr);
#if defined(CONFIG_CMD_NET)
{
char *s = getenv("bootfile"); if (s != NULL)
copy_filename(BootFile, s, sizeof(BootFile));
}
#endif #ifdef CONFIG_BOARD_LATE_INIT
board_late_init();
#endif #ifdef CONFIG_BITBANGMII
bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET)
puts("Net: ");
eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
debug("Reset Ethernet PHY\n");
reset_phy();
#endif
#endif #ifdef CONFIG_POST
post_run(NULL, POST_RAM | post_bootmode_get());
#endif #if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
/*
* Export available size of memory for Linux,
* taking into account the protected RAM at top of memory
*/
{
ulong pram = ;
uchar memsz[]; #ifdef CONFIG_PRAM
pram = getenv_ulong("pram", , CONFIG_PRAM);
#endif
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/* Also take the logbuffer into account (pram is in kB) */
pram += (LOGBUFF_LEN + LOGBUFF_OVERHEAD) / ;
#endif
#endif
sprintf((char *)memsz, "%ldk", (gd->ram_size / ) - pram);
setenv("mem", (char *)memsz);
}
#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 */
}
main.c中的main_loop函数
main_loop函数分析参考这里: https://blog.csdn.net/andy_wsj/article/details/8614905
执行到启动内核的调用流程:parse_file_outer->parse_stream_outer->run_list->run_list_real->run_pipe_real->cmd_process->(通过注册到u_boot_cmd结构的的do_bootm函数后向下继续调用)->do_bootm->do_bootm_linux->【boot_prep_linux(填充a_tag),boot_jump_linux(控制权移交内核,内核入口从image的laodaddr获取)】,到此u_boot寿终正寝,权限交给内核。
void main_loop (void)
{
#ifndef CONFIG_SYS_HUSH_PARSER
static char lastcommand[CONFIG_SYS_CBSIZE] = { , };
int len;
int rc = ;
int flag;
#endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s;
int bootdelay;
#endif
#ifdef CONFIG_PREBOOT
char *p;
#endif
#ifdef CONFIG_BOOTCOUNT_LIMIT
unsigned long bootcount = ;
unsigned long bootlimit = ;
char *bcs;
char bcs_set[];
#endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_BOOTCOUNT_LIMIT
bootcount = bootcount_load();
bootcount++;
bootcount_store (bootcount);
sprintf (bcs_set, "%lu", bootcount);
setenv ("bootcount", bcs_set);
bcs = getenv ("bootlimit");
bootlimit = bcs ? simple_strtoul (bcs, NULL, ) : ;
#endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_MODEM_SUPPORT
debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init);
if (do_mdm_init) {
char *str = strdup(getenv("mdm_cmd"));
setenv ("preboot", str); /* set or delete definition */
if (str != NULL)
free (str);
mdm_init(); /* wait for modem connection */
}
#endif /* CONFIG_MODEM_SUPPORT */ #ifdef CONFIG_VERSION_VARIABLE
{
setenv ("ver", version_string); /* set version variable */
}
#endif /* CONFIG_VERSION_VARIABLE */ #ifdef CONFIG_SYS_HUSH_PARSER
u_boot_hush_start ();
#endif #if defined(CONFIG_HUSH_INIT_VAR)
hush_init_var ();
#endif #ifdef CONFIG_PREBOOT
if ((p = getenv ("preboot")) != NULL) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(); /* disable Control C checking */
# endif run_command(p, ); # ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
}
#endif /* CONFIG_PREBOOT */ #if defined(CONFIG_UPDATE_TFTP)
update_tftp (0UL);
#endif /* CONFIG_UPDATE_TFTP */ #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, ) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); #if defined(CONFIG_MENU_SHOW)
bootdelay = menu_show(bootdelay);
#endif
# ifdef CONFIG_BOOT_RETRY_TIME
init_cmd_timeout ();
# endif /* CONFIG_BOOT_RETRY_TIME */ #ifdef CONFIG_POST
if (gd->flags & GD_FLG_POSTFAIL) {
s = getenv("failbootcmd");
}
else
#endif /* CONFIG_POST */
#ifdef CONFIG_BOOTCOUNT_LIMIT
if (bootlimit && (bootcount > bootlimit)) {
printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
(unsigned)bootlimit);
s = getenv ("altbootcmd");
}
else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
s = getenv ("bootcmd"); debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); if (bootdelay >= && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(); /* disable Control C checking */
# endif run_command(s, ); # ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
} # ifdef CONFIG_MENUKEY
if (menukey == CONFIG_MENUKEY) {
s = getenv("menucmd");
if (s)
run_command(s, );
}
#endif /* CONFIG_MENUKEY */
#endif /* CONFIG_BOOTDELAY */ /*
* Main Loop for Monitor Command Processing
*/
#ifdef CONFIG_SYS_HUSH_PARSER
parse_file_outer(); /*2410走这里,最终通过cmd_process根据命令行的入参调用内核启动,启动函数定义在U_BOOT_CMD结构体定义的结构中*/
/* This point is never reached */
for (;;);
#else
for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
if (rc >= ) {
/* Saw enough of a valid command to
* restart the timeout.
*/
reset_cmd_timeout();
}
#endif
len = readline (CONFIG_SYS_PROMPT); flag = ; /* assume no special flags for now */
if (len > )
strcpy (lastcommand, console_buffer);
else if (len == )
flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
else if (len == -) {
/* -2 means timed out, retry autoboot
*/
puts ("\nTimed out waiting for command\n");
# ifdef CONFIG_RESET_TO_RETRY
/* Reinit board to run initialization code again */
do_reset (NULL, , , NULL);
# else
return; /* retry autoboot */
# endif
}
#endif if (len == -)
puts ("<INTERRUPT>\n");
else
rc = run_command(lastcommand, flag); if (rc <= ) {
/* invalid command or not repeatable, forget it */
lastcommand[] = ;
}
}
#endif /*CONFIG_SYS_HUSH_PARSER*/
}
从单片机到系统之--uboot启动arm linux的更多相关文章
- golang 2行代码在基于arm linux的树莓派、orangepi上运行http web服务
go语言(golang)简化了跨平台交叉编译步骤,支持在windows系统下交叉编译基于arm+linux平台的应用,运行时无需其它依赖库.以下以一个简单的http server为例,先上源码: ** ...
- tiny4412学习(一)之从零搭建linux系统(烧写uboot、内核进emmc+uboot启动内核)【转】
本文转载自:http://blog.csdn.net/fengyuwuzu0519/article/details/74080109 版权声明:本文为博主原创文章,转载请注明http://blog.c ...
- Qemu搭建ARM vexpress开发环境(二)----通过u-boot启动Linux内核
Qemu搭建ARM vexpress开发环境(二)----通过u-boot启动Linux内核 标签(空格分隔): Qemu ARM Linux 在上文<Qemu搭建ARM vexpress开发环 ...
- u-boot烧写Linux及系统整个启动过程
一.烧写文件 u-boot: u-boot.bin linux kernel: uImage Filesystem: root.bin(yaffs) 二.烧写步骤 1.烧写u-boot tftp 0 ...
- 嵌入式Linux驱动学习之路(五)u-boot启动流程分析
这里说的u-boot启动流程,值得是从上电开机执行u-boot,到u-boot,到u-boot加载操作系统的过程.这一过程可以分为两个过程,各个阶段的功能如下. 第一阶段的功能: 硬件设备初始化. 加 ...
- (转载)U-boot启动完全分析
1.1 U-Boot工作过程 U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶段的功能 Ø 硬件设备初始化 Ø 加载U-Boot第二阶段代码到RAM空间 Ø 设置好栈 Ø ...
- u-boot启动流程分析(2)_板级(board)部分
转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...
- u-boot启动流程分析(1)_平台相关部分
转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的“board—>machine—>arch—> ...
- 在Linux运行期间升级Linux系统(Uboot+kernel+Rootfs)
版本:v1.2 Crifan Li 摘要 本文主要介绍了如何在嵌入式Linux系统运行的时候,进行升级整个Linux系统,包括uboot,kernel和rootfs.以及简介Linux中的已有的通 ...
随机推荐
- 网络流强化-HDU4280
数组没开够居然显示TLE而不是RE,自己觉得好的优化的方法没什么用…… //http://www.renfei.org/blog/isap.html 带解释的 //https://www.cnblog ...
- [LeetCode] 461. Hamming Distance(位操作)
传送门 Description The Hamming distance between two integers is the number of positions at which the co ...
- (vue.js)axios interceptors 拦截器中添加headers 属性
(vue.js)axios interceptors 拦截器中添加headers 属性:http://www.codes51.com/itwd/4282111.html 问题: (vue.js)axi ...
- 转 router-view 的理解
主要是构建 SPA (单页应用) 时,方便渲染你指定路由对应的组件.你可以 router-view 当做是一个容器,它渲染的组件是你使用 vue-router 指定的.比如: 视图层: <div ...
- C# json格式的序列化与反序列化
使用C#,来序列化对象成为Json格式的数据,以及如何反序列化Json数据到对象 Json[javascript对象表示方法],它是一个轻量级的数据交换格式,我们可以很简单的来读取和写它,并且它很容易 ...
- php中__call与__callstatic()使用
php 5.3 后新增了 __call 与__callStatic 魔法方法. __call 当要调用的方法不存在或权限不足时,会自动调用__call 方法. __callStatic 当调用的静态方 ...
- Centos上Docker的安装及加速
#环境 :内核的版本必须大于3.10 #安装docker yum install epel-release -y yum install docker-ce ##安装docker-ce #配置文件 d ...
- javaweb各种框架组合案例(五):springboot+mybatis+generator
一.介绍 1.springboot是spring项目的总结+整合 当我们搭smm,ssh,ssjdbc等组合框架时,各种配置不胜其烦,不仅是配置问题,在添加各种依赖时也是让人头疼,关键有些jar包之间 ...
- Sass-颜色运算
所有算数运算都支持颜色值,并且是分段运算的.也就是说,红.绿和蓝各颜色分段单独进行运算.如: p { color: #010203 + #040506; } 计算公式为 01 + 04 = 05.02 ...
- 分布式理论 BASE、CAP、ACID
CAP原理: 在理论计算机科学中,CAP定理(CAP theorem),又被称作布鲁尔定理(Brewer's theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点: 一致性(Co ...