u-boot启动流程

u-boot启动流程

u-boot启动过程分为两个阶段(2440为例):

1. 第一阶段功能:

入口是start.S汇编文件:

  • 硬件设备初始化
//跳转到reset函数,设置cpu模式为SVC32管理模式
.globl _start
_start:	b       reset

reset:
	/*
	 * set the cpu to SVC32 mode
	 */
	mrs	r0,cpsr
	bic	r0,r0,#0x1f
	orr	r0,r0,#0xd3
	msr	cpsr,r0
	
	//关看门狗
	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]
	

  • 为加载Bootloader的第二阶段代码准备RAM空间
	//配置cpu,cpu_init_crit初始化SDRAM
	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         */
	blne	cpu_init_crit
	
	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 lowlevel_init.S in your board directory.
	 */
	mov	ip, lr
	bl	lowlevel_init
	mov	lr, ip
	mov	pc, lr
	
	lowlevel_init:
	/* 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 0xb1
    .word 0x30
    .word 0x30
  • 复制Bootloader的第二阶段代码到RAM空间中
/* 重定位,即将代码拷贝到RAM空间中*/
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            */
#if 1
	bl  CopyCode2Ram	/* r0: source, r1: dest, r2: size */
	/* CopyCode2Ram 为判断从nor启动还是nand启动,判断方法:
	  * nor启动时地址从0开始,nand启动时地址不是从0,所以0地址为nor时需要一
	  * 定序列才能写成功,所以写0地址时判断是否写成功则可判断是nor或nand启动。
	*/
#else
	add	r2, r0, r2		/* r2 <- source end address         */

    /* 清bss段,bss段为需要初始化为0的静态变量和全局变量
     * _bss_start和_bss_end值可以根据u-boot-1.1.6/board/100ask24x0/u-boot.lds链接文件的
     */
    clear_bss:
	ldr	r0, _bss_start		/* find start of bss segment        */
	ldr	r1, _bss_end		/* stop here                        */
	mov 	r2, #0x00000000		/* clear                            */

  • 设置好栈(第二阶段调用C函数需要用栈)
	//设置栈
	/* 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                        */

avatar

  • 跳转到第二阶段代码的C入口点
//start_armboot为第二阶段启动函数
_start_armboot:	.word start_armboot
2. 第二阶段功能:

第二阶段入口c语言函数start_armboot():

  • 初始化第二阶段要用到的硬件设备
/* Pointer is writable since we allocated a register for it */
	gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
	
	for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
		if ((*init_fnc_ptr)() != 0) {
			hang ();
		}
	}
	
	/* init_sequence */
	init_fnc_t *init_sequence[] = {
	cpu_init,		/* basic cpu dependent setup */
	board_init,		/* basic board dependent setup */
	interrupt_init,		/* set up exceptions */
	env_init,		/* initialize environment */
	init_baudrate,		/* initialze baudrate settings */
	serial_init,		/* serial communications setup */
	console_init_f,		/* stage 1 init of console */
	display_banner,		/* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
	print_cpuinfo,		/* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
	checkboard,		/* display board info */
#endif
	dram_init,		/* configure available RAM banks */
	display_dram_config,
	NULL,
    };
    /* 其中board_init里面可以知道2440开发板的机器码和内核参数存储地址等数据 */
    
    size = flash_init ();
    nand_init();		/* go init the NAND */
    /* 初始化norflash和nandflash,之后uboot有能力读写flash*/
    
    main_loop ();//这是检测命令输入事件,即uboot是命令的处理
  • 检测系统内存映射
	/* armboot_start is defined in the board-specific linker script */
	mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
  • 将内核映像和根文件系统映像从Flash上读取到RAM空间中
  • 为内核设置启动参数
  • 调用内核
   、* 下面是uboot倒数计时的实现 */
    s = getenv ("bootcmd");
	//bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0

	debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

	if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
		int prev = disable_ctrlc(1);	/* disable Control C checking */
# endif

# ifndef CFG_HUSH_PARSER
        {
            printf("Booting Linux ...\n"); //若倒数计时时没有按键输入则调用bootcmd命令    
    	    run_command (s, 0);
        }
# else

bootcmd的值:bootcmd=nand read.jffs2 0x30007FC0 kernel bootm 0x30007FC0;
从nandflash kernel分区的0x30007FC0读取内核,然后通过bootm 0x30007FC0命令启动内核。

下面分析读内核命令:nand read.jffs2 0x30007FC0 kernel

内核的镜像文件uImage由头部+镜像组成,头部有加载地址in_load和入口地址in_ep,这些信息是在代码(u-boot-1.1.6\include\configs\100ask24x0.h)里写死的。

2440分区配置信息:

#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," \
                            "128k(params)," \
                            "2m(kernel)," \
                            "-(root)"
/* 分区位于nanflash上面,其中bootloader占前256k地址,后面以此是params,kernel,root;*/

下面是2440的分区信息:

OpenJTAG> mtd

device nand0 <jz2440-0>, # parts = 4
 #: name                        size            offset          mask_flags
 0: u-boot              0x00040000      0x00000000      0
 1: params              0x00020000      0x00040000      0
 2: kernel              0x00200000      0x00060000      0
 3: rootfs              0x0fda0000      0x00260000      0

active partition: nand0,0 - (u-boot) 0x00040000 @ 0x00000000

defaults:
mtdids  : nand0=nandflash0
mtdparts: mtdparts=nandflash0:256k@0(bootloader),128k(params),2m(kernel),-(root)

所以从上可以得知kernel分区信息是offset:0x00060000, size:0x00200000
nand read.jffs2 0x30007FC0 kernel解析为nand read.jffs2 0x30007FC0 0x00060000 0x00200000
即从0x00060000读出0x00200000大小的的kernel放到地址0x30007FC0上。
avatar
调用完上面命令就完成了kernel的加载,具体实现可以查看nand的实现函数do_nand

接下来就是执行命令bootm 0x30007FC0

bootm命令功能:
  1. 先读取uImage的头部信息获得kernel的加载地址和入口地址
  2. 若kernel的当前地址并不位于加载地址,则将kernel移到加载地址,然后再跳到入口地址执行。
  3. 调用do_bootm_linux启动内核:
    3.1 告诉内核一些启动参数(设置启动参数)
    setup_start_tag、setup_memory_tags、setup_commandline_tag、setup_end_tag
    上面这四个函数是把参数排列到内存,地址可由u-boot-1.1.6\board\100ask24x0.c\board_init()函数gd->bd->bi_boot_params可知,按格式设置完参数存放地址后,内核启动的时候就会到该地址读取参数。
    3.2 跳到入口地址:
    theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
    theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
    启动的时候传入两个参数bd->bi_arch_numberbd->bi_boot_paramsbd->bi_arch_number是机器ID,用于给内核判断是否支持的机器id,bd->bi_boot_params是前面说的参数地址,这两个参数值可以查询u-boot-1.1.6\board\100ask24x0.c\board_init()函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值