您的位置:首页 > 编程语言

u-boot启动代码分析

2012-02-26 17:31 447 查看
u-boot版本为u-boot-2009.08,平台smdk2410

一、第一阶段启动代码分析

在学习ARM时就知道,ARM在上电复位时将PC指针修改为0,即ARM是从0地址开始读取指令执行的。在cpu/arm920t/目录下有个u-boot.lds链接脚本,首先看代码段定义:

. =0x00000000;

. =ALIGN(4);

.text :

{

cpu/arm920t/start.o (.text)

*(.text)

}

我们看start.o就是放在0地址处的,所以它就是我们要找的启动代码,再看程序的入口点在哪:

ENTRY(_start)

程序入口点是由ENTRY伪指令指定的,所以程序的入口点就是_start,最终我们找到了程序的入口点,cpu/arm920t/start.S中的_start。

1.异常向量表定义

.globl_start

_start: b start_code

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

_undefined_instruction: .word undefined_instruction

_software_interrupt: .word software_interrupt

_prefetch_abort: .word prefetch_abort

_data_abort: .word data_abort

_not_used: .word not_used

_irq: .word irq

_fiq: .word fiq

上电复位后,一条跳转指令跳转到start_code

2.设置ARM工作模式

start_code:

/*

*set the cpu to SVC32 mode

*/

mrs r0,cpsr

bic r0,r0,#0x1f

orr r0,r0,#0xd3

msr cpsr,r0

设置ARM工作模式为管理模式,并禁止所有中断。

3.关闭看门狗

ldr r0,=pWTCON

mov r1,#0x0

str r1,[r0]

4.屏蔽所有中断

/*

*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

5.设置ARM时钟频率分频比

/* FCLK:HCLK:PCLK = 1:2:4 */

/* default FCLK is 120 MHz ! */

ldr r0,=CLKDIVN

mov r1,#3

str r1,[r0]

6.清除cache和禁止MMU

bl cpu_init_crit

一条跳转指令跳转到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

7.初始化ARM存储控制器

bl lowlevel_init

跳转到lowlevel_init,这个符号定义在board/samsung/smdk2410/lowlevel_init.S中

8.拷贝代码到SDRAM中

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

relocate: /* relocate U-Boot to RAM */

adr r0,_start /* r0 <- current positionof code */

ldr r1,_TEXT_BASE /* test if we run fromflash or RAM */

cmp r0, r1 /* don'treloc during debug */

beq stack_setup

ldr r2,_armboot_start

ldr r3,_bss_start

sub r2,r3, r2 /* r2 <- size ofarmboot */

add r2,r0, r2 /* r2 <- source endaddress */

copy_loop:

ldmia r0!,{r3-r10} /* copy from sourceaddress [r0] */

stmia r1!,{r3-r10} /* copy to target address [r1] */

cmp r0,r2 /* until source endaddreee [r2] */

ble copy_loop

#endif /* CONFIG_SKIP_RELOCATE_UBOOT */

先是比较_start和_TEXT_BASE这两个符号的地址是否相等,_TEXT_BASE这个符号取值为TEXT_BASE,它的值为0x33F80000。其实就是判断u-boot是否已经在SDRAM中,如果u-boot已经在SDRAM中,那么也就不必拷贝了,直接跳到堆栈设置。如果u-boot没有在SDRAM中,那么就将u-boot拷贝到SDRAM中,CPU是可以从nor flash中取指执行的,但是在SDRAM中执行速度更快,所以将代码拷贝到SDRAM中。

u-boot代码段起始地址是放在寄存器r0中的,然后计算代码段的结束地址,结束地址放在寄存器r2中,然后使用多寄存器加载指令将代码复制到SDRAM中,代码放在SDRAM中的什么地方的呢,就是放在TEXT_BASE这个地方的,也就是0x33F80000这个地方。

9.设置栈

/* Set up the stack */

stack_setup:

ldr r0,_TEXT_BASE /* upper 128 KiB:relocated uboot */

sub r0,r0, #CONFIG_SYS_MALLOC_LEN /* mallocarea */

sub r0,r0, #CONFIG_SYS_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 forabort-stack */

0x33F80000是代码段的起始地址,将这个地址减去堆空间大小192K,其中堆空间包括64K的环境变量空间。再减去全局数据区的128字节,将栈指针指向这里。其实就是将栈指针指向一段空闲的内存区。

10.bss段清零

clear_bss:

ldr r0,_bss_start /* find start of bsssegment */

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

11.跳转到第二阶段执行

ldr pc, _start_armboot

二、第二阶段启动代码分析

第二阶段启动代码是从lib_arm/board.c的start_armboot函数开始的

/* Pointer is writable since we allocateda register for it */

gd = (gd_t*)(_armboot_start -CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));

/* compiler optimization barrier neededfor 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));

首先将全局数据指针指向全局数据区,清零这个内存区,全局数据区后为板级数据区,将板级数据指针指向这里。

SDRAM空间分配图如下:



for (init_fnc_ptr = init_sequence;*init_fnc_ptr; ++init_fnc_ptr) {

if ((*init_fnc_ptr)() != 0) {

hang ();

}

}

然后一个for循环执行init_sequence函数指针数组中的函数。

1.板级初始化

int board_init (void)

{

S3C24X0_CLOCK_POWER * const clk_power =S3C24X0_GetBase_CLOCK_POWER();

S3C24X0_GPIO * const gpio =S3C24X0_GetBase_GPIO();

/* to reduce PLL lock time, adjust theLOCKTIME register */

clk_power->LOCKTIME = 0xFFFFFF;

/*设置FCLK和UCLK时钟频率*/

/* configure MPLL */

clk_power->MPLLCON = ((M_MDIV <<12) + (M_PDIV << 4) + M_SDIV);

/* some delay between MPLL and UPLL */

delay (4000);

/* configure UPLL */

clk_power->UPLLCON = ((U_M_MDIV<< 12) + (U_M_PDIV << 4) + U_M_SDIV);

/* some delay between MPLL and UPLL */

delay (8000);

/* 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;

/*设置板子机器码,要和Linux中机器码对应*/

/* arch number of SMDK2410-Board */

gd->bd->bi_arch_number =MACH_TYPE_SMDK2410;

/*设置启动参数地址*/

/* adress of boot parameters */

gd->bd->bi_boot_params =0x30000100;

icache_enable();

dcache_enable();

return 0;

}

2.时钟初始化

int timer_init (void)

{

S3C24X0_TIMERS * const timers =S3C24X0_GetBase_TIMERS();

/* use PWM Timer 4 because it has nooutput */

/* prescaler for Timer 4 is 16 */

timers->TCFG0 = 0x0f00;

if (timer_load_val == 0)

{

/*

* for 10 ms clock period @ PCLK with 4 bitdivider = 1/2

* (default) and prescaler = 16. Should be10390

* @33.25MHz and 15625 @ 50 MHz

*/

timer_load_val = get_PCLK()/(2 *16 * 100);

}

/* load value for 10 ms timeout */

lastdec = timers->TCNTB4 =timer_load_val;

/* auto load, manual update of Timer 4 */

timers->TCON = (timers->TCON &~0x0700000) | 0x600000;

/* auto load, start Timer 4 */

timers->TCON = (timers->TCON &~0x0700000) | 0x500000;

timestamp = 0;

return (0);

}

这个函数做时钟的初始化,在移植到S3C2440的时候要注意,S3C2440和S3C2410的时钟频率设置是不一样的,所以这个地方要针对S3C2440做特别的修改。

3.第三个函数是env_init,环境变量初始化,这要看我们是将环境变量放在什么地方了,如果是放在nor flash中,那么执行的是common/env_flash.c中的函数,如果是放在nand flash中,那么执行的是common/env_nand.c中的函数。我们只看common/env_flash.c中的env_init函数。

int env_init(void)

{

if (crc32(0, env_ptr->data, ENV_SIZE)== env_ptr->crc) {

gd->env_addr = (ulong)&(env_ptr->data);

gd->env_valid = 1;

return(0);

}

gd->env_addr = (ulong)&default_environment[0];

gd->env_valid = 0;

return (0);

}

首先是判断flash是否存在环境变量,如果flash存在环境变量,那就将全局数据结构中环境变量起始地址设置为include/configs/smdk2410.h由宏CONFIG_ENV_ADDR定义的值。如果flash不存在环境变量,那就将这个地址设置为default_environment数组地址值,表示采用默认环境变量。

4.串口波特率初始化

static int init_baudrate (void)

{

char tmp[64]; /* long enough for environment variables */

int i = getenv_r ("baudrate",tmp, sizeof (tmp));

gd->bd->bi_baudrate = gd->baudrate= (i > 0)

? (int) simple_strtoul(tmp, NULL, 10)

: CONFIG_BAUDRATE;

return (0);

}

首先是从环境变量中去获取,如果环境变量中没有定义,就使用配置文件中include/configs/smdk2410.h中定义的波特率值。

5.串口初始化

/*Initialise the serial port. The settings are always 8 data bits, no parity,

* 1 stop bit, no start bits.

*/

static int serial_init_dev(const int dev_index)

{

S3C24X0_UART * const uart =S3C24X0_GetBase_UART(dev_index);

/* FIFO enable, Tx/Rx FIFO clear */

uart->UFCON = 0x07;

uart->UMCON = 0x0;

/* Normal,No parity,1 stop,8 bit */

uart->ULCON = 0x3;

/*

*tx=level,rx=edge,disable timeout int.,enable rx error int.,

*normal,interrupt or polling

*/

uart->UCON = 0x245;

#ifdef CONFIG_HWFLOW

uart->UMCON = 0x1; /* RTS up */

#endif

/* FIXME: This is sooooooooooooooooooougly */

#if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2)

/* we need auto hw flow control on thegsm and gps port */

if (dev_index == 0 || dev_index == 1)

uart->UMCON = 0x10;

#endif

_serial_setbrg(dev_index);

return (0);

}

#if !defined(CONFIG_SERIAL_MULTI)

/*Initialise the serial port. The settings are always 8 data bits, no parity,

* 1 stop bit, no start bits.

*/

int serial_init (void)

{

return serial_init_dev(UART_NR);

}

#endif

6.第一阶段控制台初始化

/* Calledbefore relocation - use serial functions */

int console_init_f(void)

{

gd->have_console = 1;

#ifdef CONFIG_SILENT_CONSOLE

if (getenv("silent") != NULL)

gd->flags |= GD_FLG_SILENT;

#endif

return 0;

}

7.显示u-boot版本信息

static int display_banner (void)

{

printf ("\n\n%s\n\n", version_string);

debug ("U-Boot code: %08lX ->%08lX BSS: -> %08lX\n",

_armboot_start, _bss_start, _bss_end);

#ifdef CONFIG_MODEM_SUPPORT

debug ("Modem Supportenabled\n");

#endif

#ifdef CONFIG_USE_IRQ

debug ("IRQ Stack: %08lx\n",IRQ_STACK_START);

debug ("FIQ Stack: %08lx\n",FIQ_STACK_START);

#endif

return (0);

}

在u-boot启动起来后,显示的第一条信息

8.内存初始化

int dram_init (void)

{

/*SDRAM起始地址和SDRAM大小*/

gd->bd->bi_dram[0].start =PHYS_SDRAM_1;

gd->bd->bi_dram[0].size =PHYS_SDRAM_1_SIZE;

return 0;

}

9.显示SDRAM相关信息

static int display_dram_config (void)

{

int i;

#ifdef DEBUG

puts ("RAM Configuration:\n");

for(i=0; i<CONFIG_NR_DRAM_BANKS; i++){

printf ("Bank #%d: %08lx", i, gd->bd->bi_dram[i].start);

print_size(gd->bd->bi_dram[i].size, "\n");

}

#else

ulong size = 0;

for (i=0; i<CONFIG_NR_DRAM_BANKS; i++){

size +=gd->bd->bi_dram[i].size;

}

puts("DRAM: ");

print_size(size, "\n");

#endif

return (0);

}

init_sequence数组中的函数也就执行完了。

/* armboot_start is defined in theboard-specific linker script */

mem_malloc_init (_armboot_start -CONFIG_SYS_MALLOC_LEN);

接下来是将堆的内存空间清零。

10.显示flash相关信息

static void display_flash_config (ulong size)

{

puts ("Flash: ");

print_size (size, "\n");

}

11.nand flash初始化

void nand_init(void)

{

int i;

unsigned int size = 0;

for (i = 0; i <CONFIG_SYS_MAX_NAND_DEVICE; i++) {

nand_init_chip(&nand_info[i],&nand_chip[i], base_address[i]);

size += nand_info[i].size / 1024;

if (nand_curr_device == -1)

nand_curr_device = i;

}

printf("%u MiB\n", size /1024);

#ifdef CONFIG_SYS_NAND_SELECT_DEVICE

/*

*Select the chip in the board/cpu specific driver

*/

board_nand_select_device(nand_info[nand_curr_device].priv,nand_curr_device);

#endif

}

12.加载环境变量

void env_relocate (void)

{

DEBUGF ("%s[%d] offset = 0x%lx\n",__FUNCTION__,__LINE__,

gd->reloc_off);

#ifdef CONFIG_AMIGAONEG3SE

enable_nvram();

#endif

#ifdef ENV_IS_EMBEDDED

/*

*The environment buffer is embedded with the text segment,

*just relocate the environment pointer

*/

env_ptr = (env_t *)((ulong)env_ptr +gd->reloc_off);

DEBUGF ("%s[%d] embedded ENV at%p\n", __FUNCTION__,__LINE__,env_ptr);

#else

/*在堆上为环境变量申请内存空间*/

/*

*We must allocate a buffer for the environment

*/

env_ptr = (env_t *)malloc(CONFIG_ENV_SIZE);

DEBUGF ("%s[%d] malloced ENV at%p\n", __FUNCTION__,__LINE__,env_ptr);

#endif

/*gd->env_valid== 0表示flash没有环境变量,从而采用默认的环境变量*/

if (gd->env_valid == 0) {

#if defined(CONFIG_GTH) || defined(CONFIG_ENV_IS_NOWHERE) /*Environment not changable */

puts ("Using defaultenvironment\n\n");

#else

puts ("*** Warning - bad CRC,using default environment\n\n");

show_boot_progress (-60);

#endif

/*拷贝默认的环境变量到相关内存中*/

set_default_env();

}

else {

/*将环境变量拷贝到内存中*/

env_relocate_spec ();

}

/*将全局数据结构中环境变量指针指向环境变量内存空间处*/

gd->env_addr =(ulong)&(env_ptr->data);

#ifdef CONFIG_AMIGAONEG3SE

disable_nvram();

#endif

}

如果flash中有环境变量,则将flash中环境变量拷贝到SDRAM中环境变量空间中。但是如果flash没有环境变量,就采用默认的环境变量,也要将默认的环境变量拷贝到环境变量空间中,所以说在新烧写的u-boot启动的时候,会显示一条警告信息,表示采用默认的环境变量。

13.以太网的初始化

eth_initialize(gd->bd);

14.最后死循环,处理用户命令

/* main_loop() can return to retryautoboot, if so just run it again. */

for (;;) {

main_loop ();

}

第二阶段启动代码主要是外围设备的初始化,比如nor flash、nand flash、串口和以太网等等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: