加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

u-boot启动代码分析

发布时间:2020-12-15 06:22:04 所属栏目:百科 来源:网络整理
导读:u-boot版本为u-boot-2009.08,平台smdk2410 ? 一、第一阶段启动代码分析 在学习ARM时就知道,ARM在上电复位时将PC指针修改为0,即ARM是从0地址开始读取指令执行的。在cpu/arm920t/目录下有个u-boot.lds链接脚本,首先看代码段定义: . =0x00000000; ? . =ALI

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,#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,c7,0????? /* flush v3/v4 cache*/

?????? mcr p15,c8,0????? /* flush v4 TLB */

?

?????? /*

?????? ?*disable MMU stuff and caches

?????? ?*/

?????? mrc p15,c1,c0,0

?????? bic?? r0,#0x00002300????? @ clear bits 13,9:8(--V- --RS)

?????? bic?? r0,#0x00000087????? @ clear bits 7,2:0(B--- -CAM)

?????? orr?? r0,#0x00000002????? @ set bit 2 (A) Align

?????? orr?? r0,#0x00001000????? @ set bit 12 (I)I-Cache

?????? mcr p15,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,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,#CONFIG_SYS_MALLOC_LEN????? /* mallocarea????????????????????? */

?????? sub? r0,#CONFIG_SYS_GBL_DATA_SIZE /* bdinfo??????????????????????? */

#ifdef CONFIG_USE_IRQ

?????? sub? r0,#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

?????? sub? sp,#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,#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,sizeof (gd_t));

?????? gd->bd = (bd_t*)((char*)gd -sizeof(bd_t));

?????? memset (gd->bd,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 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 ("nn%snn",version_string);

?????? debug ("U-Boot code: %08lX ->%08lX? BSS: -> %08lXn",

?????? ??????_armboot_start,_bss_start,_bss_end);

#ifdef CONFIG_MODEM_SUPPORT

?????? debug ("Modem Supportenabledn");

#endif

#ifdef CONFIG_USE_IRQ

?????? debug ("IRQ Stack: %08lxn",IRQ_STACK_START);

?????? debug ("FIQ Stack: %08lxn",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 MiBn",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%lxn",__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%pn",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%pn",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 defaultenvironmentnn");

#else

????????????? puts ("*** Warning - bad CRC,using default environmentnn");

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读