u-boot详细解析

来源:互联网 发布:php 执行shell命令 编辑:程序博客网 时间:2024/04/30 06:37

对于Uboot移植工作,有一些技术细节可能不需要我们搞特别清楚,但是通过分析Uboot源码,可以让我们对uboot整个架构有一个清晰的认识,在进行uboot移植的过程中可以有一个清晰的思路。


u-boot 源码目录

平台相关

Arch

Board

Include

平台无关

Api

Common

Disk

Doc

Drivers

Dts

Examples

Fs

Lib

Net

Post

Spl

Test

tools

u-boot 的启动分为两个阶段:


u-boot第一阶段 :

u-boot 的入口是由链接脚本决定的, uboot 下 armv7 链接脚本默认目录为arch/arm/cpu/u-boot.lds ,从下图可以看出,第一个链接的是 CPUDIR/start.o ,其对应的代码为 arch/arm/cpu/armv7/start.S 

ENTRY() 和 ENDPROC() 分别为汇编函数的入口和出口


arch/arm/cpu/armv7/start.S :

设置异常向量表:

//ldr指令是将寄存器装载指令,将内存中的地址的内容装载到PC寄存器中。

看到上电后 u-boot 会进入 reset 函数:


可以看到进入 reset 函数后,会先进入 save_boot_params 函数


注意到 save_boot_params 函数中没有进行任何操作,直接返回了,注释告诉我们栈指针还没有初始化,不能向栈中保存

任何数据。

值得注意的是 .weak 伪操作: .weak save_boot_params

可以理解为如果定义了 save_boot_params 这个函数,那么久调用它,如果没有定义,就定义它,具体可以参考http://blog.chinaunix.net/uid-25358071-id-309285.html

 

然后 reset 函数执行下面的指令:

 

reset:

         bl      save_boot_params

         ldr   r0, =0x44e10884

         ldr   r1, =0x10

         str    r1,[r0]

         /* * set the cpu to SVC32 mode */

         mrs  r0, cpsr

         bic    r0, r0, #0x1f

         orr    r0, r0, #0xd3

         msr  cpsr,r0

设置了 CPSR 寄存器为 d3 ,使得 CPU 进入 SVC 模式,并禁止中断, CPSR 寄存器详情查看《 ARM 基础知识》

这个时候可以用汇编代码进行点灯,确定代码确实运行到这一步:

#if 1

ldr r0, =0x11000c40 @GPK2_7 led2

ldr r1, [r0]

bic r1, r1, #0xf0000000

orr r1, r1, #0x10000000

str r1, [r0]

ldr r0, =0x11000c44

mov r1,#0xff

str r1, [r0]

#endif


接着调用cpu_init_crit函数,禁止MMU,禁止caches等工作

bl      cpu_init_crit

cpu_init_crit:

         /*

          * Invalidate L1 I/D

          */

         mov r0, #0                          @ set up for MCR

         mcr  p15, 0, r0, c8, c7, 0 @ invalidate TLBs

         mcr  p15, 0, r0, c7, c5, 0 @ invalidate icache

         mcr  p15, 0, r0, c7, c5, 6 @ invalidate BP array

         mcr     p15, 0, r0, c7, c10, 4        @ DSB

         mcr     p15, 0, r0, c7, c5, 4 @ ISB

         /*

          * disable MMU stuff and caches

          */

         mrc  p15, 0, r0, c1, c0, 0

         bic    r0, r0, #0x00002000         @ clear bits 13 (--V-)

         bic    r0, r0, #0x00000007         @ clear bits 2:0 (-CAM)

         orr    r0, r0, #0x00000002         @ set bit 1 (--A-) Align

         orr    r0, r0, #0x00000800         @ set bit 11 (Z---) BTB

#ifdef CONFIG_SYS_ICACHE_OFF

         bic    r0, r0, #0x00001000         @ clear bit 12 (I) I-cache

#else

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

#endif

         mcr  p15, 0, r0, c1, c0, 0

 

         /*

          * Jump to board specific initialization...

          * The Mask ROM will have already initialized

          * basic memory. Go here to bump up clock rate and handle

          * wake up conditions.

          */

         mov ip, lr                    @ persevere link reg across call

         bl      lowlevel_init             @ go setup pll,mux,memory

         mov lr, ip                    @ restore link

         mov pc, lr                            @ back to my caller

#endif


 

lowlevel_init.S (mcu-uboot-wk\arch\arm\cpu\armv7\ti81xx)

这个函数就和具体的硬件有关系了,我所使用的单板是Nor flash


.globl lowlevel_init

lowlevel_init:

         /* The link register is saved in ip by start.S */

         mov r6, ip

         /* check if we are already running from RAM */

         ldr r2, _lowlevel_init

         ldr r3, _TEXT_BASE

         sub r4, r2, r3

         sub r0, pc, r4

         /* require dummy instr or subtract pc by 4 instead i'm doing stack init */

         ldr sp, SRAM_STACK

mark1:

         ldr r5, _mark1

         sub r5, r5, r2 /* bytes between mark1 and lowlevel_init */

         sub r0, r0, r5 /* r0 <- _start w.r.t current place of execution */

         mov r10, #0x0 /* r10 has in_ddr used by s_init() */

 

#ifdef CONFIG_NOR_BOOT

         cmp r0, #0x08000000 /* check for running from NOR */

         beq ocmc_init_start /* if == then running from NOR */

 

在这个函数中进行了总线的初始化以及一些GPIO的初始化操作,我们可以根据具体的硬件需求添加一些自定义的代码

 

 

 

#ifdef CONFIG1_SPL_BUILD

         ands r0, r0, #0xC0000000 /* MSB 2 bits <> 0 then we are in ocmc or DDR */

         cmp r0, #0x40000000 /* if running from ocmc */

         beq nor_init_start /* if == skip ocmc init and jump to nor init */

将Nor flash中的代码拷贝到Ram中继续执行

nor_init_start:

         /* gpmc init */

         bl  cpy_nor_gpmc_code /* copy nor gpmc init code to sram */

         mov r0, pc

         add r0, r0, #12  /* 12 is for next three instructions */

         mov lr, r0 /* gpmc init code in sram should return to s_init_start */

         ldr r0, sram_pc_start

         mov pc, r0        /* transfer ctrl to nor_gpmc_init() in sram */

#endif

 

#endif

         mov r10, #0x01 /* if <> we are running from DDR hence skip ddr init */

                                        /* by setting in_ddr to 1 */

 

         b s_init_start /* and jump to s_init */

 

s_init_start:

         mov r0, r10 /* passing in_ddr in r0 */

         bl s_init

         /* back to arch calling code */

         mov pc, r6

         /* the literal pools origin */

         .ltorg

然后跳转到中执行

/* Set stackpointer in internal RAM to call board_init_f */

call_board_init_f:

         ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)

         bic    sp, sp, #7 /* 8-byte alignment for ABI compliance */

         ldr    r0,=0x00000000

         bl      board_init_f

 


Board.c (mcu-uboot-wk\arch\arm\lib)

函数作用是:对 gd 结构体( include/asm/global_data.h )进行设置,进行各种初始化

gd->mon_len = (ulong)&__bss_end - (ulong)_start;

初始化 mon_len ,代表 uboot code 的大小。

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

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

hang ();

}

}

 

 

 

这些函数中有很多与硬件有关,比如串口

init_fnc_t *init_sequence[] = {

#if defined(CONFIG_ARCH_CPU_INIT)

         arch_cpu_init,          /* basic arch cpu dependent setup */

#endif

#if defined(CONFIG_BOARD_EARLY_INIT_F)

         board_early_init_f,

#endif

         timer_init,                 /* initialize timer */

#ifdef CONFIG_FSL_ESDHC

         get_clocks,

#endif

         env_init,            /* initialize environment */

         init_baudrate,          /* initialze baudrate settings */

         serial_init,                 /* serial communications setup */

         console_init_f,          /* stage 1 init of console */

         display_banner,                 /* say that we are here */

#if defined(CONFIG_DISPLAY_CPUINFO)

         print_cpuinfo,           /* display cpu info (and speed) */

#endif

#if defined(CONFIG_DISPLAY_BOARDINFO)

         checkboard,              /* display board info */

#endif

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)

         init_func_i2c,

#endif

         dram_init,                  /* configure available RAM banks */

         NULL,

};

 

接着会执行

relocate_code(addr_sp, id, addr);  代码自搬移

stack_setup         //设置堆栈

copy_loop           //

clear_bss           //清BSS段

jump_2_ram

_board_init_r_ofs

     .word board_init_r - _start  //最终跳转到board_init_r执行后续程序,也就是第二阶段

 

 

U-boot第二阶段   Board.c (mcu-uboot-wk\arch\arm\lib),这阶段的代码与硬件关系很大,uboot的移植工作主要在此处,包括网卡初始化,串口初始化,中断初始化,中断使能,环境变量的初始化等工作。

void board_init_r(gd_t *id, ulong dest_addr)

{

enable_caches();

board_init();     /* Setup chipselects */

flash_size = flash_init();

env_relocate();   /* initialize environment ,初始化环境变量*/

gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr"); /* IP Address */

stdio_init();      /* get the devices list going. */

console_init_r();       /* fully init console as a device */

interrupt_init();   /* set up exceptions */

enable_interrupts()/* enable exceptions */

eth_init(gd->bd);   /*初始化网卡*/

/*最终进入死循环,进入uboot的主函数*/

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

         for (;;) {

                   main_loop();

         }

}

 


Main.c (mcu-uboot-wk\common)   

board_init_r进行完板级初始化后最后进入死循环,打印命令行,等待命令输入和解析。到这里uboot的启动过程就全部结束了!

void main_loop (void)

{

bootcount = bootcount_load();   /*uboot的倒计时*/

         bootcount++;

         bootcount_store (bootcount);

         sprintf (bcs_set, "%lu", bootcount);

         setenv ("bootcount", bcs_set);

         bcs = getenv ("bootlimit");

         bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;

 

update_tftp (0UL);  //tftp

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;

}

}

 




0 0
原创粉丝点击