uboot引导内核全过程

本文讲述uboot引导内核启动的全部过程,uboot版本为2010.06

1、arch/arm/cpu/hi3536/u-boot.lds

         连接文件,不同平台不一样。

OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm", "elf32-littlearm") //elf格式 32位,小端

OUTPUT_ARCH(arm)//arm架构

ENTRY(_start)//代码进入点arch/arm/cpu/hi3536/start.S中定义,一切从这里开始

SECTIONS

{

    .= 0x00000000; //可执行文件入口地址

    .= ALIGN(4);

   .text   ://代码段

    {

       __text_start = .;

       arch/arm/cpu/hi3536/start.o (.text)

       drivers/ddr/ddr_training_impl.o (.text)

       drivers/ddr/ddr_training_ctl.o (.text)

       drivers/ddr/ddr_training_boot.o (.text)

       drivers/ddr/ddr_training_custom.o (.text)

       __init_end = .;

       ASSERT(((__init_end - __text_start) < 0x16000), "init sectionstoo big!");

       *(.text)

}

    .= ALIGN(4);

   .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } //只读数据段

    .= ALIGN(4);

   .data : { *(.data) } //数据段

    .= ALIGN(4);

   .got : { *(.got) }

   __u_boot_cmd_start = .; //存放uboot命令

   .u_boot_cmd : { *(.u_boot_cmd) }

   __u_boot_cmd_end = .;

    .= ALIGN(4);

   __bss_start = .; //bss

   .bss : { *(.bss) }

   _end = .;

}

2、arch/arm/cpu/hi3536/start.S

         Arm在uboot下的入口文件,纯汇编,由于代码太多,这里描述基本过程:

(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第一步

A. set the cpu to SVC32 mode

B. Invalidate L1 I/D

C. Invalidate L1 D-cache

D. disable MMU stuff and caches

(3)第二步

判断boot方式,从bootrom / spi /nand中的一种启动,撤销地址空间映射。

假设启动方式为spi nor flash启动,这时起始地址空间映射到spi nor flash地址空间,但是spi nor flash不能直接执行代码,这里猜测是:映射后硬件自己从spi nor flash中将部分代码搬运到内部开始执行。

(4)第三步

进行pll /flash/ddr等的初始化

(5)第4步

拷贝flash数据到DDR空间。在这步之前只是搬运了初始化用的最基本的部分到内部,但是整个uboot并未搬运到DDR,这里则进行完整的搬运:r1-DDR地址0x40c00000(src/board/hi3536/config.mk中定义),r0-flash地址为spi地址,r2大小为_bss_start- _armboot_start(_start),memcpy(r1, r0, r2)。内存布局见第8

根据uboot.map可以看到连接收地址发生了变化从0x40c00000开始,修改这个的地方由底层的config.mk完成:

LDFLAGS += -Bstatic -T $(obj)u-boot.lds$(PLATFORM_LDFLAGS)

ifneq ($(TEXT_BASE),)

LDFLAGS += -Ttext $(TEXT_BASE)

endif

连接地址替换成TEXT_BASE(src/board/hi3536/config.mk定义)

(6)第5步

跳转到DDR,进入第一个C语言入口

clbss_l:                                                                                                                                         

   str r2, [r0]        @ clear BSSlocation                                                                                                      

   cmp r0, r1          @ are we atthe end yet                                                                                                  

   add r0, r0, #4      @ incrementclear index pointer                                                                                          

   bne clbss_l         @ keepclearing till at end                                                                                              

                                                                                                                                                 

   ldr pc, _start_armboot  @ jump to C code                                                                                                     

                                                                                                                                                 

_start_armboot: .word start_armboot0x40c04fdc  start_armboot连接地址)

3、src/arch/arm/lib/board.c

init_fnc_t *init_sequence[] = { //初始化函数列表

   timer_init,     /* initializetimer before usb init */

   board_init,     /* basic boarddependent setup */

   env_init,       /* initializeenvironment */

   init_baudrate,      /* initialzebaudrate settings */

   serial_init,        /* serialcommunications setup */

   console_init_f,     /* stage 1init of console */

   display_banner,     /* say that weare here */

   dram_init,      /* configureavailable RAM banks */

   NULL,

};

void start_armboot (void)

{

/* Pointer iswritable since we allocated a register for it */

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

         _armboot_start = 0x40c00000(内存起始地址0x40000000,预留了12M

         CONFIG_SYS_MALLOC_LEN = 4 * 1024 * 1024(自己修改)

         …

         //运行所有初始化函数

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

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

           hang ();

       }

}

#ifdefCONFIG_CMD_SF

    spi_flash_probe(0, 0, 0, 0); //初始化spi flash

#endif

env_relocate ();//初始化uboot环境变量

         …

         #ifdefined(CONFIG_CMD_NET)

   eth_initialize(gd->bd); //网络初始化

#endif

#ifdefSTART_UBOOT_NO_NET_OPERATION

    int ret = 0;

    ret = eth_init(gd->bd); //网络驱动初始化,该部分自己添加

    if(!ret)

        eth_halt();

#endif

         …

         for(;;) {

       main_loop (); //进入main主函数

    }

}

4、src/common/main.c

void main_loop (void)                                                                                                                            

{

         …

         #ifdefined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)

    s= getenv ("bootdelay"); //获取bootdelay次数(即秒数)

   bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

   debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);

#ifdef CONFIG_HI3536_A7

       //modify by hlb

       //s = getenv("slave_cmd");

       s = getenv("bootcmd");

#else

       s = getenv("bootcmd"); //获取bootcmd命令内容

                   /* bootcmd=usbupdate;tftpupdate; fsload; bootm

                     usbupdate: 扫描U盘,升级U盘文件,自己添加不进行说明

                     tftpupdate: tftp自动化升级,自己添加不进行说明

                     fsload:加载内核文件, 自己修改,后续章节说明

                     bootm:引导内核,后续章节说明

                   */

#endif

   debug ("### main_loop: bootcmd=\"%s\"\n", s ? s :"<UNDEFINED>");

         //bootdelay时间内连续输入3个字符*(自己修改为*),则不执行bootcmd命令,进入uboot命令行,否则执行bootcmd命令

   if (bootdelay >= 0 && s && !abortboot (bootdelay)) {

# ifndef CONFIG_SYS_HUSH_PARSER

       run_command (s, 0); //执行bootcmd命令

# else

       parse_string_outer(s, FLAG_PARSE_SEMICOLON |

                    FLAG_EXIT_FROM_LOOP);

# endif

}

//进入uboot命令行,响应用户输入的命令

for (;;) {

len = readline (CONFIG_SYS_PROMPT);

    flag = 0;  /* assume no special flags for now */

    if (len > 0)

        strcpy (lastcommand, console_buffer);

   else if (len == 0)

        flag |= CMD_FLAG_REPEAT;

   if (len == -1)

        puts ("<INTERRUPT>\n");

   else

        run_command(lastcommand, flag);

     /* invalid command or not repeatable,forget it */

     lastcommand[0] = 0;

}

}

5、src/common/cmd_fsload.c

bootcmd环境变量中fsload命令,实际运行函数就为该文件的do_fsload函数。

int do_fsload(cmd_tbl_t *cmdtp, int flag,int argc, char *argv[])                                                                                

{                                                                                                                                                

   char *fsname;                                                                                                                                

   char *filename = "/boot/uImage";         //读取内核完整路径名                                                                                                             

   int size = 0;                                                                                                                                

                                                                                                                                                  

   struct part_info *part;                                                                                                                      

   ulong offset = CONFIG_SYS_LOAD_ADDR; //=0x42000000,内核加载的内存地址                                                                                                        

                                                                                                                                                 

   part = spi_cramfs_init(); //读取内核所在根文件系统信息                                                                                                                  

   if (part ==0)                                                                                                                                

   {                                                                                                                                            

       return 1;                                                                                                                                

    }

   printf("### squashfs loading '%s' to 0x%lx\n", filename, offset);

   if(squashfs_check(part)) //这里使用自己添加的squashfs文件系统,默认是cramfs

    {

       size = squashfs_fload((char *) offset, part, filename); //加载内核到上述内存地址

    }

   if (size > 0) { //加载成功

       char buf[10];

       printf("### %s load complete: %d bytes loaded to 0x%lx\n",

       fsname, size, offset);

       sprintf(buf, "%x", size);

       setenv("filesize", buf);

    }else { //加载失败

       printf("### %s LOAD ERROR<%x> for %s!\n", fsname, size,filename);

    }

   return !(size > 0);

}

6、src/common/cmd_bootm.c

bootcmd环境变量中bootm命令,实际运行函数就为该文件的do_bootm函数。

int do_bootm (cmd_tbl_t *cmdtp, int flag,int argc, char *argv[])

{

         …

         if(bootm_start(cmdtp, flag, argc, argv)) //该函数解析内核image头,获取内核信息

       return 1;

         /*

         images.os.image_start= 0x42000040 (0x40 64字节头大小)

         images.os.image_len= 0x25fa88

         images.os.type= IH_TYPE_KERNEL       

         images.os.comp= IH_COMP_NONE

         images.os.os= IH_OS_LINUX

         images.os.start= 0x42000000

images.os.end = 0x4225fac8

         images.os.load= 0x40008000 真正的内核会加载到该地方

*/

ret = bootm_load_os(images.os,&load_end, 1);

/*

         将内核从os.image_start搬运到os.load地址

         load_end = 0x40267a88(0x40008000+0x25fa88)

*/

                                                                                                           

         boot_fn= boot_os[images.os.os]; //根据操作系统类型获取对应操作系统入口函数,这里为do_bootm_linux

    …

boot_fn(0, argc, argv, &images); //运行do_bootm_linux函数,成功则会进入内核

do_reset (cmdtp, flag, argc, argv); //如果失败则重启uboot

}

7、src/arch/arm/lib/bootm.c

int do_bootm_linux(int flag, int argc, char*argv[], bootm_headers_t *images)

{

         intmachid = bd->bi_arch_number; //传递给内核的机器码hi3536 0x8000

   void    (*theKernel)(int zero, intarch, uint params); //进入内核函数指针

        

         theKernel= (void (*)(int, int, uint))images->ep; //ep =0x40008000

         …

         #ifdefined (CONFIG_SETUP_MEMORY_TAGS) || \

   defined (CONFIG_CMDLINE_TAG) || \

   defined (CONFIG_INITRD_TAG) || \

   defined (CONFIG_SERIAL_TAG) || \

   defined (CONFIG_REVISION_TAG)

             setup_start_tag (bd);

#ifdefCONFIG_SETUP_MEMORY_TAGS

             setup_memory_tags (bd);

#endif

#ifdefCONFIG_CMDLINE_TAG

             setup_commandline_tag (bd,commandline);

#endif

….

             setup_end_tag (bd);

#endif

/* 需要传递给内核的各个TAG,放入参数列表 */

theKernel (0, machid, bd->bi_boot_params); //进入内核,无返回

/* 2个参数为机器码,第二个参数为传递给内核参数列表的首地址=0x40000100 */

}

8、uboot阶段的内存布局

猜你喜欢

转载自blog.csdn.net/heliangbin87/article/details/79957967