u-boot启动代码分析

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、串口和以太网等等。

posted @ 2012-02-26 17:31  移动应用开发  阅读(633)  评论(0编辑  收藏  举报