一、uboot启动原理
1、第一阶段初始化(汇编)
(1)关闭看门狗
(2)初始化时钟
(3)初始化SDRAM
(4)设置堆栈
(5)拷贝第二阶段代码
2、第二阶段初始化(c语言)
(1)实现读写flash
(2)串口通信
(3)网络通信
(4)读取内核,引导内核
二、源码分析
1、第一阶段,硬件相关初始化(arch/arm/cpu/arm920t/start.S)
1、向量表
.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
2、上电跳转reset启动
(1)设置CPU为SVC32模式
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
(2)关闭看门狗
/* turn off the watchdog */
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMOD 0X4A000004
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
(3)屏蔽所有中断
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
(4)设置CPU相关设置(cpu_init_crit: 清除I/O caches,初始化存储管理器)
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
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
#endif
(5)设置栈
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
(6)初始化时钟
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl clock_init
#endif
(7)重定位,将代码拷贝至内存(SDRAM)
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq clear_bss
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
#if 1
bl CopyCode2Ram /* r0: source, r1: dest, r2: size */
#else
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
(8)清除bss段
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
(9)跳转至第二阶段
ldr pc, _start_armboot
_start_armboot: .word start_armboot
2、第二阶段,c语言程序(lib_arm/board.c)
(1)lib_arm/board.c
1、设置运行环境
/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned long board_type;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
unsigned long ram_size; /* RAM size */
unsigned long reloc_off; /* Relocation Offset */
unsigned long env_addr; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
void **jt; /* jump table */
} gd_t;
(2)进一步初始化硬件
monitor_flash_len = _bss_start - _armboot_start;
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
dram_init, /* configure available RAM banks */
mem_malloc_init, /* dependant on dram_init */
interrupt_init, /* set up exceptions */
timer_init,
serial_init,
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
display_banner,
display_dram_config,
NULL,
};
int board_init (void) // board/mini2440/mini2440.c
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
/* set up the I/O ports */
gpio->GPACON = 0x007FFFFF;
gpio->GPBCON = 0x00044555;
gpio->GPBUP = 0x000007FF;
gpio->GPCCON = 0xAAAAAAAA;
gpio->GPCUP = 0x0000FFFF;
gpio->GPDCON = 0xAAAAAAAA;
gpio->GPDUP = 0x0000FFFF;
gpio->GPECON = 0xAAAAAAAA;
gpio->GPEUP = 0x0000FFFF;
gpio->GPFCON = 0x000055AA;
gpio->GPFUP = 0x000000FF;
gpio->GPGCON = 0xFF95FFBA;
gpio->GPGUP = 0x0000FFFF;
gpio->GPHCON = 0x002AFAAA;
gpio->GPHUP = 0x000007FF;
/* support both of S3C2410 and S3C2440, by www.arm9.net */
if ((gpio->GSTATUS1 == 0x32410000) || (gpio->GSTATUS1 == 0x32410002))
{
/* arch number of SMDK2410-Board */
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
}
else
{
/* arch number of SMDK2440-Board */
gd->bd->bi_arch_number = MACH_TYPE_S3C2440; //机器ID,内核启动时会使用
}
/* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100;
icache_enable();
#if 0
dcache_enable();
#endif
return 0;
}
(3)环境变量初始化(uboot命令行的printenv)
/* initialize environment */
env_relocate ();
(4)uboot主循环
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
(2)common/main.c
1、获取bootcmd,如果没有命令则启动内核
s = getenv ("bootcmd");
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
#ifdef CONFIG_SURPORT_WINCE
if (!TOC_Read())
{
/* Launch wince */
char cmd_buf[16];
printf("Booting wince ...\n");
strcpy(cmd_buf, "wince");
run_command(cmd_buf, 0);
}
else
#endif
{
printf("Booting Linux ...\n");
run_command (s, 0);
}
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
(2)获取命令行命令,执行命令
for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
if (rc >= 0) {
/* Saw enough of a valid command to
* restart the timeout.
*/
reset_cmd_timeout();
}
#endif
len = readline (CFG_PROMPT);
flag = 0; /* assume no special flags for now */
if (len > 0)
strcpy (lastcommand, console_buffer);
else if (len == 0)
flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
else if (len == -2) {
/* -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, 0, 0, NULL);
# else
return; /* retry autoboot */
# endif
}
#endif
if (len == -1)
puts ("<INTERRUPT>\n");
else
rc = run_command (lastcommand, flag);
if (rc <= 0) {
/* invalid command or not repeatable, forget it */
lastcommand[0] = 0;
}
}