Android 4.0启动流程分析

在分析SPRD启动流程之前,第一步:先来分析一下嵌入式系统一般的启动流程,也就是从uboot开始在到启动kernel模块。第二部:再来分析一下SPRD android系统的启动与一般启动有和不同,启动过程分析道android init进程起来为止。至于init后的启动过程应该是地球人都知道的。

 

第一部分:关于uboot分析有些摘自网络,

http://blog.chinaunix.net/uid-24951403-id-2212589.html

bootloader 除了依赖CPU,还依赖板级设备的配置,例如板卡的硬件地址分配,外设硬件芯片的类型。不同的板子需修改bootloader。阶段1:硬件初始化,为加载bootloader的二阶段准备RAM空间,拷贝2阶段代码到内存,设置好堆栈,跳到2阶段,初始化本阶段要的设备,将内核和根文件从flash中拷贝到RAM中,最后调用内核。





1、Stage1 start.S代码结构 u-boot的stage1代码通常放在start.S文件中,他用汇编语言写成,其主要代码部分如下

(1)定义入口。:

该工作通过修改连接器脚本来完成。

(2)设置异常向量(Exception Vector)。

(3)设置CPU的速度、时钟频率及终端控制寄存器。

(4)初始化内存控制器。

(5)将ROM中的程序复制到RAM中。

(6)初始化堆栈。

(7)转到RAM中执行,该工作可使用指令ldr pc来完成。

2、Stage2

 C语言代码部分lib_arm/board.c中的start arm boot是C语言开始的函数也是整个启动代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,该函数只要完成如下操作:

(1)调用一系列的初始化函数。

(2)初始化Flash设备。

(3)初始化系统内存分配函数。

(4)如果目标系统拥有NAND设备,则初始化NAND设备。

(5)如果目标系统有显示设备,则初始化该类设备。

(6)初始化相关网络设备,填写IP、MAC地址等。

(7)进去命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。


3、U-Boot的启动顺序

发板上电后,执行U-Boot的第一条指令,然后顺序执行U-Boot启动函数。看一下u-boot\board\spreadtrum\sp6825ga/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的是arch/arm/cpu/arm926ejs/start.o,那么U-Boot的入口指令一定位于这个程序中。下面分两阶段介绍启动流程:

如下:u-boot\board\spreadtrum\sp6825ga\start.s

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
	. = 0x00000000;

	. = ALIGN(4);
	.text :
	{
		arch/arm/cpu/arm926ejs/start.o	(.text)
		*(.text)
	}

	. = ALIGN(4);
	.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }

	. = ALIGN(4);
	.data : { *(.data) }

	. = ALIGN(4);
	.got : { *(.got) }

	. = .;
	__u_boot_cmd_start = .;
	.u_boot_cmd : { *(.u_boot_cmd) }
	__u_boot_cmd_end = .;

	. = ALIGN(4);
	__bss_start = .;
	.bss (NOLOAD) : { *(.bss) . = ALIGN(4); }
	_end = .;
}

第一阶段

1.  start.S  这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。

.globl _start
_start: b	reset //复位向量
… …
	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   //

… …

/*
 * the actual reset code
 */

reset://复位启动子程序
	/*
	 * set the cpu to SVC32 mode
	 */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
#ifdef SPRD_EVM_TAG_ON
	ldr r0,=SPRD_EVM_ADDR_START
	ldr r1,=0x87003004
	ldr r2,[r1]
	str r2,[r0]
#endif   /*?设置CPU为SVC32模式?*/
	mrs	r0, cpsr
	bic	r0, r0, #0x1f
	orr	r0, r0, #0xd3
	msr	cpsr,r0
#endif

… …

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
	bl	cpu_init_crit
	mov    r10, #0
	/*set up temp stack*///设置堆栈
	LDR    sp, =SVC_STACK_TEMP

	STMDB sp!,{lr} 
	bl lowlevel_init//初始化CPU
	@bl MMU_Init     //初始化MMU内存管理单元
	/*Re-set up stack
	  The sp here must be in the reserved region
	  *///重新设置堆栈  sp必须指向此区域
	LDMIA sp!, {lr}
	LDR     sp, =SPL_STACK 
#endif

/* Set stackpointer in internal RAM to call board_init_f */
//设置堆栈指针 指向 RAM 调用board_init_f
call_board_init_f:
	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
	bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */
#ifndef CONFIG_NAND_SPL
	ldr	r0,=0x00000000
	bl	board_init_f
#else
	ldr r0, =(CONFIG_SYS_INIT_SP_ADDR)
	ldr r1, =0x00000000
	ldr r2, =(CONFIG_SYS_TEXT_BASE)
	bl relocate_code
#endif
… …
/*
 * We are done. Do not return, instead branch to second part of board
 * initialization, now running from RAM.
 */
jump_2_ram:
#ifdef CONFIG_NAND_SPL
	ldr     r0, _nand_boot_ofs
	mov	pc, r0

_nand_boot_ofs:
	.word nand_boot
#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

_board_init_r_ofs:
	.word board_init_r - _start
#endif

第二阶段:跳转到u-boot\arch\arm\lib

start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。

在board_init_f函数中有init_sequence[]

init_sequence[]数组保存着基本的初始化函数指针。

 

init_fnc_t *init_sequence[] = {

cpu_init,  /* 基本的处理器相关配置 -- arch\arm\cpu\armv7 */

board_init,/* 基本的板级相关配置 -- board\spreadtrum\sp6825ga*/

interrupt_init,  /* 初始化中断处理 -- arch\arm\lib/interrupt.c */

env_init,      /* 初始化环境变量 --common/cmd_flash.c */

init_baudrate,  /* 初始化波特率设置 --lib_arm/board.c */

serial_init,  /* 串口通讯设置 -- common /serial.c*/

console_init_f,       /* 控制台初始化阶段1 --common/console.c */

display_banner,       /* 打印u-boot信息 --lib_arm/board.c */

dram_init,     /* 配置可用的RAM -- board/ spreadtrum/ openphone.c */

display_dram_config,  /* 显示RAM的配置大小 --lib_arm/board.c */

NULL,

};


void board_init_f (ulong bootflag)
{
	bd_t *bd;
	init_fnc_t **init_fnc_ptr;
	gd_t *id;
	ulong addr, addr_sp;

	/* 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, 0, sizeof (gd_t));

	gd->mon_len = _bss_end_ofs;
  /* 顺序执行init_sequence数组中的初始化函数 */ 
	for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
		if ((*init_fnc_ptr)() != 0) {
			hang ();
		}
	}

	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;

#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
	 */
	i = getenv_r ("pram", (char *)tmp, sizeof (tmp));
	reg = (i > 0) ? simple_strtoul ((const char *)tmp, NULL, 10) : CONFIG_PRAM;
	addr -= (reg << 10);		/* size is in kB */
	debug ("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
#endif /* CONFIG_PRAM */

#if !(defined(CONFIG_SYS_NO_ICACHE) && defined(CONFIG_SYS_NO_DCACHE))
	/* reserve TLB table */
	addr -= (4096 * 4);

	/* round down to next 64 kB limit */
	addr &= ~(0x10000 - 1);

	gd->tlb_addr = addr;
	debug ("TLB table at: %08lx\n", addr);
#endif

	/* round down to next 4 kB limit */
	addr &= ~(4096 - 1);
	debug ("Top of RAM usable for U-Boot at: %08lx\n", addr);

#ifdef CONFIG_VFD
#	ifndef PAGE_SIZE
#	  define PAGE_SIZE 4096
#	endif
	/*
	 * reserve memory for VFD display (always full pages)
	 */
	addr -= vfd_setmem (addr);
	gd->fb_base = addr;
#endif /* CONFIG_VFD */

#ifdef CONFIG_LCD
	/* reserve memory for LCD display (always full pages) */
	addr = lcd_setmem (addr);
	gd->fb_base = addr;
#endif /* CONFIG_LCD */

	/*
	 * reserve memory for U-Boot code, data & bss
	 * round down to next 4 kB limit
	 */
	addr -= gd->mon_len;
	addr &= ~(4096 - 1);

	debug ("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);

#ifndef CONFIG_PRELOADER
	/*
	 * reserve memory for malloc() arena
	 */
	addr_sp = addr - TOTAL_MALLOC_LEN;
	debug ("Reserving %dk for malloc() at: %08lx\n",
			TOTAL_MALLOC_LEN >> 10, addr_sp);
	/*
	 * (permanently) allocate a Board Info struct
	 * and a permanent copy of the "global" data
	 */
	addr_sp -= sizeof (bd_t);
	bd = (bd_t *) addr_sp;
	gd->bd = bd;
	debug ("Reserving %zu Bytes for Board Info at: %08lx\n",
			sizeof (bd_t), addr_sp);
	addr_sp -= sizeof (gd_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;
#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 -= 3;

	/* 8-byte alignment for ABI compliance */
	addr_sp &= ~0x07;
#else
	addr_sp += 128;	/* 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(0));
#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;
	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);

	/* NOTREACHED - relocate_code() does not return */
}

然后还会调用此函数

在start.s中

/*
 * We are done. Do not return, instead branch to second part of board
 * initialization, now running from RAM.
 */
jump_2_ram:
#ifdef CONFIG_NAND_SPL
	ldr     r0, _nand_boot_ofs
	mov	pc, r0

_nand_boot_ofs:
	.word nand_boot
#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

_board_init_r_ofs:
	.word board_init_r - _start
#endif

将调用board.c中的board_init_r 

/************************************************************************
 *
 * This is the next part if the initialization sequence: we are now
 * running from RAM and have a "normal" C environment, i. e. global
 * data can be written, BSS has been cleared, the stack size in not
 * that critical any more, etc.
 *
 ************************************************************************
 */

void board_init_r (gd_t *id, ulong dest_addr)
{
	char *s;
	bd_t *bd;
	ulong malloc_start;
#if !defined(CONFIG_SYS_NO_FLASH)
	ulong flash_size;
#endif

	gd = id;
	bd = gd->bd;

	gd->flags |= GD_FLG_RELOC;	/* tell others: relocation done */

	monitor_flash_len = _bss_start_ofs;
	debug ("monitor flash len: %08lX\n", monitor_flash_len);
	board_init();	/* Setup chipselects */

    boot_pwr_check();

#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;
#ifdef SPRD_EVM_TAG_ON
		SPRD_EVM_TAG(4);
#endif
    /* _armboot_start在u-boot.lds链接脚本中定义*/
	mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);
#ifdef SPRD_EVM_TAG_ON
		SPRD_EVM_TAG(5);
#endif
    boot_pwr_check();

#if !defined(CONFIG_SYS_NO_FLASH)
	puts ("FLASH: ");
    /*配置可用的Flash */ 
	if ((flash_size = flash_init ()) > 0) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
		print_size (flash_size, "");
		/*
		 * Compute and print flash CRC if flashchecksum is set to 'y'
		 *
		 * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
		 */
		s = getenv ("flashchecksum");
		if (s && (*s == 'y')) {
			printf ("  CRC: %08X",
				crc32 (0, (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
    boot_pwr_check();

#if !defined(CONFIG_EMMC_BOOT)
#if defined(CONFIG_CMD_NAND)
	puts ("NAND:  ");
	nand_init();		/* go init the NAND */
#endif
#endif

    boot_pwr_check();
#ifdef SPRD_EVM_TAG_ON
		SPRD_EVM_TAG(6);
#endif

#if defined(CONFIG_CMD_ONENAND)
#if !(defined CONFIG_TIGER && defined CONFIG_EMMC_BOOT)
	onenand_init();
#endif
#endif

#ifdef CONFIG_GENERIC_MMC
       puts("MMC:   ");
       mmc_initialize(bd);
#endif

#ifdef CONFIG_HAS_DATAFLASH
	AT91F_DataflashInit();
	dataflash_print_info();
#endif

#ifdef CONFIG_EMMC_BOOT
	mmc_legacy_init(1);
#endif
	/* initialize environment */
	env_relocate ();
    boot_pwr_check();

#ifdef CONFIG_VFD
	/* must do this after the framebuffer is allocated */
	drv_vfd_init();
#endif /* CONFIG_VFD */

/*tempaily use for tiger to avoid died as refreshing LCD*/

	/* IP Address */ /* 从环境变量中获取IP地址 */
	gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
  
	stdio_init ();	/* get the devices list going. */
    boot_pwr_check();

	jumptable_init ();
    boot_pwr_check();

#if defined(CONFIG_API)
	/* Initialize API */
	api_init ();
#endif
    char fake[4]="fak";
    setenv("splashimage", fake);
    /* 完整地初始化控制台设备 */
	console_init_r ();	/* fully init console as a device */
    boot_pwr_check();

#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 ();/* 使能中断处理 */
    boot_pwr_check();

	/* 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[6];
		eth_getenv_enetaddr("ethaddr", enetaddr);
		smc_set_mac_addr(enetaddr);
	}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */

	/* Initialize from environment */ /* 通过环境变量初始化 */
	if ((s = getenv ("loadaddr")) != NULL) {
		load_addr = simple_strtoul (s, NULL, 16);
	}
#if defined(CONFIG_CMD_NET)
	if ((s = getenv ("bootfile")) != NULL) {
		copy_filename (BootFile, s, sizeof (BootFile));
	}
#endif
    boot_pwr_check();
	//usb_eth_initialize(NULL);
#ifdef BOARD_LATE_INIT
	board_late_init ();
#endif

#ifdef CONFIG_BITBANGMII
	bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
	puts ("Net:   ");
#endif
	//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(0));
#endif
    boot_pwr_check();

#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[32];
#ifdef CONFIG_PRAM
		char *s;

		if ((s = getenv ("pram")) != NULL) {
			pram = simple_strtoul (s, NULL, 10);
		} else {
			pram = CONFIG_PRAM;
		}
#else
		pram=0;
#endif
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
		/* Also take the logbuffer into account (pram is in kB) */
		pram += (LOGBUFF_LEN+LOGBUFF_OVERHEAD)/1024;
#endif
#endif
		sprintf ((char *)memsz, "%ldk", (bd->bi_memsize / 1024) - pram);
		setenv ("mem", (char *)memsz);
	}
#endif

#ifdef SPRD_EVM_TAG_ON
		SPRD_EVM_TAG(11);
#endif
    extern int do_cboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);
    boot_pwr_check();

    do_cboot(NULL, 0, 1, NULL);
	/* main_loop() can return to retry autoboot, if so just run it again. */
	for (;;) {
		main_loop ();/* 主循环函数处理执行用户命令 -- common/main.c */
	}

	/* NOTREACHED - no way out of command loop except booting */
}

命令实现

U-Boot作为Bootloader,具备多种引导内核启动的方式。常用的do命令可以直接引导内核映像启动。U-Boot与内核的关系主要是内核启动过程中参数的传递。

1.  do命令的实现:

查看参数:do_cboot(NULL, 0, 1, NULL); 

u-boot\property
U_BOOT_CMD(
            cboot, CONFIG_SYS_MAXARGS, 1, do_cboot,
            "choose boot mode",
            "mode: \nrecovery, fastboot, dloader, charge, normal, vlx, caliberation.\n"
            "cboot could enter a mode specified by the mode descriptor.\n"
            "it also could enter a proper mode automatically depending on "
            "the environment\n"
          );


int do_cboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{

  …  …
boot_pwr_check();//检查是否有power按键
… …
	CHG_Init();//充电模块初始化
	… …
    boot_pwr_check();//再次检查是否有power按键
    board_keypad_init();//初始化按键选项
    boot_pwr_check();//再次检查是否有power按键

    int recovery_init(void);//恢复出厂设置初始化
    int ret =0;
    ret = recovery_init();
    if(ret == 1){
        DBG("func: %s line: %d\n", __func__, __LINE__);
        recovery_mode_without_update();
    }else if(ret == 2){
	    try_update_modem(); //update img from mmc
	    normal_mode();
    }
 … …
    //检查重启模式
    unsigned check_reboot_mode(void);
    unsigned rst_mode= check_reboot_mode();
    if(rst_mode == RECOVERY_MODE){
        DBG("func: %s line: %d\n", __func__, __LINE__);
        recovery_mode();
    }
    else if(rst_mode == FASTBOOT_MODE){
        DBG("func: %s line: %d\n", __func__, __LINE__);
        fastboot_mode();
    }else if(rst_mode == NORMAL_MODE){
        normal_mode();
    }else if(rst_mode == WATCHDOG_REBOOT){
        watchdog_mode();
    }else if(rst_mode == UNKNOW_REBOOT_MODE){
        unknow_reboot_mode();
    }else if(rst_mode == PANIC_REBOOT){
        panic_reboot_mode();
    }else if(rst_mode == ALARM_MODE){
              int flag =alarm_flag_check();
              if(flag == 1)
			alarm_mode();
              else if(flag == 2)
			normal_mode();
    }else if(rst_mode == SLEEP_MODE){
		sleep_mode();
    }else if(rst_mode == SPECIAL_MODE){
		special_mode();
	}
 … …
    //充电连接,充电模式
   if(charger_connected()){
        DBG("%s: charger connected\n", __FUNCTION__);
        charge_mode();
}
    //find the power up trigger //检查是否有power按键,是否是组合按键
    else if(boot_pwr_check() >= get_pwr_key_cnt()){
        DBG("%s: power button press\n", __FUNCTION__);
	DBG("boot_pwr_check=%d,get_pwr_key_cnt=%d\n",boot_pwr_check(),get_pwr_key_cnt());
        //go on to check other keys
        mdelay(50);
        for(i=0; i<10;i++){
            key_code = board_key_scan();
            if(key_code != KEY_RESERVED)
              break;
        }
		DBG("key_code %d\n", key_code);
        key_mode = check_key_boot(key_code);

        switch(key_mode){
            case BOOT_FASTBOOT:
                fastboot_mode();
                break;
            case BOOT_RECOVERY:
                recovery_mode();
                break;
            case BOOT_CALIBRATE:
                engtest_mode();
                return 0; //back to normal boot
                break;
            case BOOT_DLOADER:
                dloader_mode();
                break;
            default:
                break;
        }
    }
    else if(alarm_triggered() && alarm_flag_check()){//alarm模式
        DBG("%s: alarm triggered\n", __FUNCTION__);
        int flag =alarm_flag_check();
        if(flag == 1)
		alarm_mode();
        else if(flag == 2)
              normal_mode();
    }else{
#if BOOT_NATIVE_LINUX_MODEM
        *(volatile u32*)CALIBRATION_FLAG = 0xca;
#endif
        calibration_detect(0);
        //if calibrate success, it will here
        DBG("%s: power done again\n", __FUNCTION__);
        power_down_devices();
        while(1)
          ;
    }
	//主要为1个参数,正常模式启动
    if(argc == 1){
	DBG("func: %s line: %d\n", __func__, __LINE__);
        normal_mode();
        return 1;
    }
… …
}

将会进入此函数normal_mode(normal_mode.c) 

void normal_mode(void)
{
#if defined (CONFIG_SC8810) || defined (CONFIG_SC8825)
    //MMU_Init(CONFIG_MMU_TABLE_ADDR);
	vibrator_hw_init();
#endif
    set_vibrator(1);//按开机按键震动设置

#if BOOT_NATIVE_LINUX
    vlx_nand_boot(BOOT_PART, CONFIG_BOOTARGS, BACKLIGHT_ON);
#else
    vlx_nand_boot(BOOT_PART, NULL, BACKLIGHT_ON);//从这里启动
#endif

}

将进入vlx_nand_boot(normal_nand_mode.c)

void vlx_nand_boot(char * kernel_pname, char * cmdline, int backlight_set)
{
    boot_img_hdr *hdr = (void *)raw_header;
	struct mtd_info *nand;
	struct mtd_device *dev;
	struct part_info *part;
	u8 pnum;
	int ret;
	size_t size;
	loff_t off = 0;
	char *fixnvpoint = "/fixnv";
	char *fixnvfilename = "/fixnv/fixnv.bin";
	char *fixnvfilename2 = "/fixnv/fixnvchange.bin";
	char *backupfixnvpoint = "/backupfixnv";
	char *backupfixnvfilename = "/backupfixnv/fixnv.bin";
	char *runtimenvpoint = "/runtimenv";
	char *runtimenvfilename = "/runtimenv/runtimenv.bin";
	char *runtimenvfilename2 = "/runtimenv/runtimenvbkup.bin";
	char *productinfopoint = "/productinfo";
	char *productinfofilename = "/productinfo/productinfo.bin";
	char *productinfofilename2 = "/productinfo/productinfobkup.bin";
	int orginal_right, backupfile_right;
	unsigned long orginal_index, backupfile_index;
	nand_erase_options_t opts;
	char * mtdpart_def = NULL;
	#if (defined CONFIG_SC8810) || (defined CONFIG_SC8825)
	MMU_Init(CONFIG_MMU_TABLE_ADDR);//初始化MMU内存管理单元
	#endif
	ret = mtdparts_init();//flash 区域初始化
	if (ret != 0){
		printf("mtdparts init error %d\n", ret);
		return;
	}

#ifdef CONFIG_SPLASH_SCREEN
#define SPLASH_PART "boot_logo"
	ret = find_dev_and_part(SPLASH_PART, &dev, &pnum, &part);//查找开机logo
	if(ret){
		printf("No partition named %s\n", SPLASH_PART);
		return;
	}else if(dev->id->type != MTD_DEV_TYPE_NAND){
		printf("Partition %s not a NAND device\n", SPLASH_PART);
		return;
	}

	off=part->offset;
	nand = &nand_info[dev->id->num];
	//read boot image header
	size = 1<<19;//where the size come from????
	char * bmp_img = malloc(size);
	if(!bmp_img){
	    printf("not enough memory for splash image\n");
	    return;
	}
	ret = nand_read_offset_ret(nand, off, &size, (void *)bmp_img, &off);
	if(ret != 0){
		printf("function: %s nand read error %d\n", __FUNCTION__, ret);
		return;
	}
	//显示开机logo
   lcd_display_logo(backlight_set,(ulong)bmp_img,size);
#endif

#if !(BOOT_NATIVE_LINUX)
	/*int good_blknum, bad_blknum;
	nand_block_info(nand, &good_blknum, &bad_blknum);
	printf("good is %d  bad is %d\n", good_blknum, bad_blknum);*/
	///
	/* recovery damaged fixnv or backupfixnv */
	/* FIXNV_PART */
	printf("Reading fixnv to 0x%08x\n", FIXNV_ADR);
	//try "/fixnv/fixnvchange.bin" first,if fail,
	//try /fixnv/fixnv.bin instead
	ret = load_sector_to_memory(fixnvpoint,
							fixnvfilename2,
							fixnvfilename,
							(unsigned char *)FIXNV_ADR,
							(unsigned char *)MODEM_ADR,
							FIXNV_SIZE + 4);
	if(ret == -1){
		//fixnvpoint's files are not correct
		//the "/backupfixnv/fixnv.bin" must be correct!
		ret = load_sector_to_memory(backupfixnvpoint,
						backupfixnvfilename,
						0,
						(unsigned char *)FIXNV_ADR,
						(unsigned char *)MODEM_ADR,//we just test if it's correct.
						FIXNV_SIZE + 4);
		if(ret ==1){
			//we got a right file in backupfixnvpoint,
			//use it to recovery fixnvpoint's files.
			recovery_sector(fixnvpoint,
				fixnvfilename,
				fixnvfilename2,
				(unsigned char *)FIXNV_ADR,
				FIXNV_SIZE + 4);
		}else{
			//backupfixnvpoint's files are still uncorrect.
			//then we can do nothing to get it right!!!!
			//there is an fatal error has occured.
			printf("\n\nfixnv and backupfixnv are all wrong!\n\n");
			return -1;
			//clear memory
			//memset(FIXNV_ADR, 0xff,FIXNV_SIZE + 4);
		}
	}else{
		//everything is right!!
		//we can chose to do it or not.
		ret = load_sector_to_memory(backupfixnvpoint,
						backupfixnvfilename,
						0,
						(unsigned char *)RUNTIMENV_ADR,//we just test if it's correct.
						(unsigned char *)MODEM_ADR,//we just test if it's correct.
						FIXNV_SIZE + 4);
		if(ret == -1){
			recovery_sector(backupfixnvpoint,
				backupfixnvfilename,
				0,
				(unsigned char *)FIXNV_ADR,
				FIXNV_SIZE + 4);
		}
	}

	//finally we check the fixnv structure,if fail,then u-boot will hung up!!!
	if(check_fixnv_struct(FIXNV_ADR,FIXNV_SIZE) == -1){
		printf("check fixnv structer error ............\r\n");
		return -1;
	}

	///
	/* PRODUCTINFO_PART */
	ret = load_sector_to_memory(productinfopoint,
							productinfofilename2,
							productinfofilename,
							(unsigned char *)PRODUCTINFO_ADR,
							(unsigned char *)MODEM_ADR,//we just test if it's correct.
							PRODUCTINFO_SIZE + 4);
	if(ret == -1){
		printf("don't need read productinfo  to 0x%08x!\n", PRODUCTINFO_ADR);
	}
	eng_phasechecktest((unsigned char *)PRODUCTINFO_ADR, SP09_MAX_PHASE_BUFF_SIZE);
	///

	///
	/* RUNTIMEVN_PART */
	ret = load_sector_to_memory(runtimenvpoint,
							runtimenvfilename2,
							runtimenvfilename,
							(unsigned char *)RUNTIMENV_ADR,
							(unsigned char *)MODEM_ADR,//we just test if it's correct.
							RUNTIMENV_SIZE + 4);
	if(ret == -1){
		//clear memory
		memset(RUNTIMENV_ADR, 0xff,RUNTIMENV_SIZE + 4);
	}
	//array_value((unsigned char *)RUNTIMENV_ADR, RUNTIMENV_SIZE);

	
	/* DSP_PART */
	printf("Reading dsp to 0x%08x\n", DSP_ADR);
	ret = find_dev_and_part(DSP_PART, &dev, &pnum, &part);
	if (ret) {
		printf("No partition named %s\n", DSP_PART);
		return;
	} else if (dev->id->type != MTD_DEV_TYPE_NAND) {
		printf("Partition %s not a NAND device\n", DSP_PART);
		return;
	}

	off = part->offset;
	nand = &nand_info[dev->id->num];
	flash_page_size = nand->writesize;
	size = (DSP_SIZE + (flash_page_size - 1)) & (~(flash_page_size - 1));
	if(size <= 0) {
		printf("dsp image should not be zero\n");
		return;
	}
	ret = nand_read_offset_ret(nand, off, &size, (void*)DSP_ADR, &off);
	if(ret != 0) {
		printf("dsp nand read error %d\n", ret);
		return;
	}
	secure_check(DSP_ADR, 0, DSP_ADR + DSP_SIZE - VLR_INFO_OFF, CONFIG_SYS_NAND_U_BOOT_DST + CONFIG_SYS_NAND_U_BOOT_SIZE - KEY_INFO_SIZ - VLR_INFO_OFF);
#elif defined(CONFIG_CALIBRATION_MODE_NEW)
#if defined(CONFIG_SP7702) || defined(CONFIG_SP8810W)
		/*
			force dsp sleep in native 8810 verson to reduce power consumption
		*/ 
		extern void DSP_ForceSleep(void);
		DSP_ForceSleep();
		printf("dsp nand read ok1 %d\n", ret);
#endif	
    //开机校准
	if(poweron_by_calibration())
	{
		/* recovery damaged fixnv or backupfixnv */
		orginal_right = 0;
		memset((unsigned char *)FIXNV_ADR, 0xff, 0x20000);
		cmd_yaffs_mount(fixnvpoint);
		ret = cmd_yaffs_ls_chk(fixnvfilename);
		if (ret == (FIXNV_SIZE + 4)) {
			cmd_yaffs_mread_file(fixnvfilename, (unsigned char *)FIXNV_ADR);
			//should do something here
		}
		cmd_yaffs_umount(fixnvpoint);
		
		printf("Reading fixnv to 0x%08x \n", FIXNV_ADR);
		/* DSP_PART */
		printf("Reading dsp to 0x%08x\n", DSP_ADR);
		ret = find_dev_and_part(DSP_PART, &dev, &pnum, &part);
		if (ret) {
		        printf("No partition named %s\n", DSP_PART);
		        return;
		} else if (dev->id->type != MTD_DEV_TYPE_NAND) {
		        printf("Partition %s not a NAND device\n", DSP_PART);
		        return;
		}
		off = part->offset;
		nand = &nand_info[dev->id->num];
		flash_page_size = nand->writesize;
		size = (DSP_SIZE + (flash_page_size - 1)) & (~(flash_page_size - 1));
		if(size <= 0) {
		        printf("dsp image should not be zero\n");
		        return;
		}
		ret = nand_read_offset_ret(nand, off, &size, (void*)DSP_ADR, &off);
		if(ret != 0) {
		        printf("dsp nand read error %d\n", ret);
		        return;
		}

		printf("Reading firmware to 0x%08x\n", FIRMWARE_ADR);
		ret = find_dev_and_part(FIRMWARE_PART, &dev, &pnum, &part);
		if (ret) {
		        printf("No partition named %s\n", FIRMWARE_PART);
		        return;
		} else if (dev->id->type != MTD_DEV_TYPE_NAND) {
		        printf("Partition %s not a NAND device\n", FIRMWARE_PART);
		        return;
		}
		off = part->offset;
		nand = &nand_info[dev->id->num];
		size = (FIRMWARE_SIZE +(flash_page_size - 1)) & (~(flash_page_size - 1));
		if(size <= 0) {
		        printf("firmware image should not be zero\n");
		        return;
		}
		ret = nand_read_offset_ret(nand, off, &size, (void*)FIRMWARE_ADR, &off);
		if(ret != 0) {
		        printf("firmware nand read error %d\n", ret);
		        return;
		}

		printf("Reading vmjaluna to 0x%08x\n", VMJALUNA_ADR);
		ret = find_dev_and_part(VMJALUNA_PART, &dev, &pnum, &part);
		if (ret) {
		        printf("No partition named %s\n", VMJALUNA_PART);
		        return;
		} else if (dev->id->type != MTD_DEV_TYPE_NAND) {
		        printf("Partition %s not a NAND device\n", VMJALUNA_PART);
		        return;
		}
		off = part->offset;
		nand = &nand_info[dev->id->num];
		size = (VMJALUNA_SIZE +(flash_page_size - 1)) & (~(flash_page_size - 1));
		if(size <= 0) {
		        printf("modem image should not be zero\n");
		        return;
		}
		ret = nand_read_offset_ret(nand, off, &size, (void*)VMJALUNA_ADR, &off);
		if(ret != 0) {
		        printf("modem nand read error %d\n", ret);
		        return;
		}
		printf("call bootup modem in vlx_nand_boot,0x%x 0x%x\n",FIXNV_ADR, FIXNV_SIZE);

		bootup_modem((char *)VMJALUNA_ADR,0x3000);
		calibration_mode(cmdline, 10);	
		memset(VMJALUNA_ADR,0,VMJALUNA_SIZE);
		memset(FIXNV_ADR,0,FIXNV_SIZE+4);
		memset(MODEM_ADR,0,MODEM_SIZE);
		memset(DSP_ADR,0,DSP_SIZE);
		memset(RUNTIMENV_ADR,0,RUNTIMENV_SIZE+4);
	}

#endif

	set_vibrator(0);

	
	/* KERNEL_PART */
	printf("Reading kernel to 0x%08x\n", KERNEL_ADR);

	ret = find_dev_and_part(kernel_pname, &dev, &pnum, &part);
	if(ret){
		printf("No partition named %s\n", kernel_pname);
        return;
	}else if(dev->id->type != MTD_DEV_TYPE_NAND){
		printf("Partition %s not a NAND device\n", kernel_pname);
        return;
	}

	off=part->offset;
	nand = &nand_info[dev->id->num];
	//read boot image header
    //加载内核
	ret = load_kernel_and_layout(nand,
							(unsigned int)off,
							(char *)raw_header,
							(char *) KERNEL_ADR,
							(char *) RAMDISK_ADR,
							2048,
							nand->writesize);

	if (ret != 0) {
		printf("ramdisk nand read error %d\n", ret);
		return;
	}


#if !(BOOT_NATIVE_LINUX)
	
	/* MODEM_PART */
	printf("Reading modem to 0x%08x\n", MODEM_ADR);
	ret = find_dev_and_part(MODEM_PART, &dev, &pnum, &part);
	if (ret) {
		printf("No partition named %s\n", MODEM_PART);
		return;
	} else if (dev->id->type != MTD_DEV_TYPE_NAND) {
		printf("Partition %s not a NAND device\n", MODEM_PART);
		return;
	}

	off = part->offset;
	nand = &nand_info[dev->id->num];
	flash_page_size = nand->writesize;
	size = (MODEM_SIZE +(flash_page_size - 1)) & (~(flash_page_size - 1));
	if(size <= 0) {
		printf("modem image should not be zero\n");
		return;
	}
	ret = nand_read_offset_ret(nand, off, &size, (void*)MODEM_ADR, &off);
	if(ret != 0) {
		printf("modem nand read error %d\n", ret);
		return;
	}

	secure_check(MODEM_ADR, 0, MODEM_ADR + MODEM_SIZE - VLR_INFO_OFF, CONFIG_SYS_NAND_U_BOOT_DST + CONFIG_SYS_NAND_U_BOOT_SIZE - KEY_INFO_SIZ - VLR_INFO_OFF);
	//array_value((unsigned char *)MODEM_ADR, MODEM_SIZE);

	
	/* VMJALUNA_PART */
	printf("Reading vmjaluna to 0x%08x\n", VMJALUNA_ADR);
	ret = find_dev_and_part(VMJALUNA_PART, &dev, &pnum, &part);
	if (ret) {
		printf("No partition named %s\n", VMJALUNA_PART);
		return;
	} else if (dev->id->type != MTD_DEV_TYPE_NAND) {
		printf("Partition %s not a NAND device\n", VMJALUNA_PART);
		return;
	}

	off = part->offset;
	nand = &nand_info[dev->id->num];
	size = (VMJALUNA_SIZE +(flash_page_size - 1)) & (~(flash_page_size - 1));
	if(size <= 0) {
		printf("VMJALUNA image should not be zero\n");
		return;
	}
	ret = nand_read_offset_ret(nand, off, &size, (void*)VMJALUNA_ADR, &off);
	if(ret != 0) {
		printf("VMJALUNA nand read error %d\n", ret);
		return;
	}
	secure_check(VMJALUNA_ADR, 0, VMJALUNA_ADR + VMJALUNA_SIZE - VLR_INFO_OFF, CONFIG_SYS_NAND_U_BOOT_DST + CONFIG_SYS_NAND_U_BOOT_SIZE - KEY_INFO_SIZ - VLR_INFO_OFF);
#endif
	creat_cmdline(cmdline,hdr);//
	vlx_entry();//启动内核模块
}

加载linux kernel函数

static int load_kernel_and_layout(struct mtd_info *nand,
						unsigned int phystart,
						char *header,
						char *kernel,
						char *ramdisk,
						unsigned int virtual_page_size,
						unsigned int real_page_size
						) {
	int ret = -1;
	boot_img_hdr *hdr = (boot_img_hdr*)header;
	unsigned int off = phystart;
	int size = real_page_size;

	printf("virtual_page_size : %x\n",virtual_page_size);
	printf("real_page_size : %x\n",real_page_size);
	//read boot image header
	ret = nand_read_offset_ret(nand, off, &size, (void *)hdr, &off);
	if(ret != 0){
		printf("function: %s nand read error %d\n", __FUNCTION__, ret);
        return -1;
	}
	if(memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)){
		printf("bad boot image header, give up read!!!!\n");
        return -1;
	}
	else
	{

		char* prev_page_addr = header;
		//we asume that the header takes only one page.
		//read kernel image prepare
		unsigned int used_size = 1*virtual_page_size;
		unsigned int spare_size = 0;
		unsigned int next_file_size = hdr->kernel_size;

		if(used_size > 0){
			spare_size = real_page_size - used_size;
		}else{
			spare_size = 0;
		}
		//read kernel image
		printf("file size: %x\n",hdr->kernel_size);
		printf("use size: %x\n",used_size);
		printf("spare size: %x\n",spare_size);

		if(spare_size) {
			memcpy(kernel,&prev_page_addr[used_size],spare_size);
			next_file_size -= spare_size;
		}
		size = (next_file_size+(real_page_size - 1)) & (~(real_page_size - 1));
		ret = nand_read_offset_ret(nand, off, &size, (void *)(kernel+spare_size), &off);
		if(ret != 0){
			printf("reading kernel error!\n");
			printf("try reading to %x\n",kernel+spare_size);
		}
		//read ramdisk image prepare
		prev_page_addr =  (char*)(kernel+spare_size+size-real_page_size);
		used_size = (next_file_size%real_page_size+virtual_page_size-1)&(~(virtual_page_size-1));
		if(used_size > 0){
			spare_size = real_page_size - used_size;
		}else{
			spare_size = 0;
		}
		next_file_size = hdr->ramdisk_size;
		printf("file size: %x\n",hdr->ramdisk_size);
		printf("use size: %x\n",used_size);
		printf("spare size: %x\n",spare_size);
		//read ramdisk image
		if(spare_size){
			memcpy(ramdisk,&prev_page_addr[used_size],spare_size);
			next_file_size -= spare_size;
		}
		size = (next_file_size+(real_page_size - 1)) & (~(real_page_size - 1));
		ret = nand_read_offset_ret(nand, off, &size, (void *)(ramdisk+spare_size), &off);
		if(ret != 0){
			printf("reading ramdisk error!\n");
			printf("try reading to %x\n",ramdisk+spare_size);
		}
	}

	return ret;
}

void vlx_entry()
{
	void (*entry)(void) = (void*) VMJALUNA_ADR;
#ifndef CONFIG_SC8810
#ifndef CONFIG_TIGER
	MMU_InvalideICACHEALL();
#endif
#endif
#if (defined CONFIG_SC8810) || (defined CONFIG_SC8825)
	MMU_DisableIDCM();
#endif

#ifdef REBOOT_FUNCTION_INUBOOT
	reboot_func();
#endif

#if BOOT_NATIVE_LINUX
	start_linux();
#else
	entry();
#endif
}

static int start_linux()
{
	void (*theKernel)(int zero, int arch, u32 params);
	u32 exec_at = (u32)-1;
	u32 parm_at = (u32)-1;
	u32 machine_type;

	machine_type = machine_arch_type;         /* get machine type */

	theKernel = (void (*)(int, int, u32))KERNEL_ADR; /* set the kernel address */

	*(volatile u32*)0x84001000 = 'j';
	*(volatile u32*)0x84001000 = 'm';
	*(volatile u32*)0x84001000 = 'p';

	theKernel(0, machine_type, VLX_TAG_ADDR);    /* jump to kernel with register set */
	while(1);
	return 0;
}

引导linux内核并启动。

遗留问题:

1、  传递参数到linux内核。

2、  Uboot到底做了什么?分别问那些部分?如:背光如何亮的,如何显示开机logo的。

3、  Nand flash如何启动并且初始化的?

4、  其他涉及到的模块初始化问题?

5、  Watchdog如何开始工作?

6、  总结回顾。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值