在分析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: breset //复位向量
… …
ldrpc, _undefined_instruction
ldrpc, _software_interrupt
ldrpc, _prefetch_abort
ldrpc, _data_abort
ldrpc, _not_used
ldrpc, _irq //中断向量
ldrpc, _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模式?*/
mrsr0, cpsr
bicr0, r0, #0x1f
orrr0, r0, #0xd3
msrcpsr,r0
#endif
… …
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
blcpu_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:
ldrsp, =(CONFIG_SYS_INIT_SP_ADDR)
bicsp, sp, #7 /* 8-byte alignment for ABI compliance */
#ifndef CONFIG_NAND_SPL
ldrr0,=0x00000000
blboard_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
movpc, r0
_nand_boot_ofs:
.word nand_boot
#else
ldrr0, _board_init_r_ofs
adrr1, _start
addlr, r0, r1
addlr, lr, r9
/* setup parameters for board_init_r */
movr0, r5/* gd_t */
movr1, r6/* dest_addr */
/* jump to it ... */
movpc, 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
movpc, r0
_nand_boot_ofs:
.word nand_boot
#else
ldrr0, _board_init_r_ofs
adrr1, _start
addlr, r0, r1
addlr, lr, r9
/* setup parameters for board_init_r */
movr0, r5/* gd_t */
movr1, r6/* dest_addr */
/* jump to it ... */
movpc, 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、 总结回顾。