U-Boot启动过程

转载地址:http://blog.csdn.net/yangzhiloveyou/article/details/8743987

(国嵌)

bootloader 除了依赖CPU,还依赖板级设备的配置,例如板卡的硬件地址分配,外设硬件芯片的类型。不同的板子需修改
bootloader。阶段1:硬件初始化,为加载bootloader的二阶段准备RAM空间,拷贝2阶段代码到内存,设置好堆栈,跳到2阶
段,初始化本阶段要的设备,将内核和根文件从flash中拷贝到RAM中,最后调用内核。


开发板上电后,执行U-Boot的第一条指令,然后顺序执行U-Boot启动函数。看一下board/smdk2410/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的是cpu/arm920t/start.o,那么U-Boot的入口指令一定位于这个程序中。下面分两阶段介绍启动流程:

       

第一阶段

.globl _start
_start: b reset  //复位向量,这里使用b指令转移到“子程序名”处开始执行
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
_pad:
.word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:
各种模式下使用的寄存器有所不同.


ldr pc, _undefined_instruction  将 _undefined_instruction地址load到pc中,即跳转

1.cpu/arm920t/start.S

这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。

_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      //中断向量

/* the actual reset code  */

reset:          //复位启动子程序

       /* 设置CPU为SVC32模式 */

       mrs  r0,cpsr

       bic  r0,r0,#0x1f

       orr  r0,r0,#0xd3

       msr  cpsr,r0

/* 关闭看门狗 */

……     ……

relocate:                  /* 把U-Boot重新定位到RAM*/

adr   r0, _start          /* r0是代码的当前位置 */

ldr   r1, _TEXT_BASE      /*_TEXT_BASE是RAM中的地址 */

cmp     r0, r1          /* 比较r0和r1,判断当前是从Flash启动,还是RAM */

beq     stack_setup  /* 如果r0等于r1,跳过重定位代码 */

/* 准备重新定位代码 */

ldr   r2, _armboot_start

ldr   r3, _bss_start

sub   r2, r3, r2       /* r2 得到armboot的大小   */

add   r2, r0, r2      /* r2 得到要复制代码的末尾地址 */

copy_loop:/* 重新定位代码 */

ldmiar0!, {r3-r10}   /*从源地址[r0]复制 */

stmiar1!, {r3-r10}   /* 复制到目的地址[r1] */

cmp   r0, r2   /* 复制数据块直到源数据末尾地址[r2] */

ble   copy_loop

/* 初始化堆栈等    */

stack_setup:

ldr   r0, _TEXT_BASE  /* 上面是128 KiB重定位的u-boot */

sub   r0, r0, #CFG_MALLOC_LEN  /* 向下是内存分配空间 */

sub   r0, r0, #CFG_GBL_DATA_SIZE /* 然后是bdinfo结构体地址空间  */

#ifdefCONFIG_USE_IRQ

subr0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

sub   sp, r0, #12     /* 为abort-stack预留3个字 */

clear_bss:

ldr   r0, _bss_start      /* 找到bss段起始地址 */

ldr   r1, _bss_end        /* bss段末尾地址   */

mov   r2, #0x00000000     /* 清零 */

clbss_l:strr2, [r0]  

/*bss段地址空间清零循环... */

       add  r0, r0, #4

       cmp  r0, r1

bne   clbss_l

/* 跳转到start_armboot函数入口,_start_armboot字保存函数入口指针 */

ldr   pc, _start_armboot

_start_armboot:.word start_armboot     //start_armboot函数在lib_arm/board.c中实现

第二阶段

2.lib_arm/board.c

start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。

3.init_sequence[]

init_sequence[]数组保存着基本的初始化函数指针。

init_fnc_t *init_sequence[] = {

cpu_init,   /* 基本的处理器相关配置 -- cpu/arm920t/cpu.c */

board_init,/* 基本的板级相关配置 -- board/smdk2410/smdk2410.c */

interrupt_init,  /* 初始化中断处理 -- cpu/arm920t/s3c24x0/interrupt.c */

env_init,      /* 初始化环境变量 -- common/cmd_flash.c */

init_baudrate,  /* 初始化波特率设置 -- lib_arm/board.c */

serial_init,  /* 串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */

console_init_f,       /* 控制台初始化阶段1 -- common/console.c */

display_banner,       /* 打印u-boot信息 -- lib_arm/board.c */

dram_init,     /* 配置可用的RAM -- board/smdk2410/smdk2410.c */

display_dram_config,  /* 显示RAM的配置大小 -- lib_arm/board.c */

NULL,

};

voidstart_armboot (void)

{

/* 顺序执行init_sequence数组中的初始化函数 */

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

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

                      hang ();

              }

       }

/*配置可用的Flash*/

       size = flash_init ();

       display_flash_config (size);

       /* _armboot_start 在u-boot.lds链接脚本中定义 */

       mem_malloc_init (_armboot_start -CFG_MALLOC_LEN);

/* 配置环境变量*/

env_relocate();

/* 从环境变量中获取IP地址 */

gd->bd->bi_ip_addr= getenv_IPaddr ("ipaddr");

/* 以太网接口MAC 地址 */

       ……

       devices_init ();      /* 获取列表中的设备 */

       jumptable_init ();

       console_init_r ();    /* 完整地初始化控制台设备 */

       enable_interrupts (); /* 使能中断处理 */

/* 通过环境变量初始化 */

       if ((s = getenv ("loadaddr"))!= NULL) {

               load_addr = simple_strtoul (s,NULL, 16);

       }

/*main_loop()循环不断执行 */

for(;;)

{

      main_loop ();      /* 主循环函数处理执行用户命令 -- common/main.c */

}

命令实现

U-Boot作为Bootloader,具备多种引导内核启动的方式。常用的go和bootm命令可以直接引导内核映像启动。U-Boot与内核的关系主要是内核启动过程中参数的传递。

1.go命令的实现

/*common/cmd_boot.c  */

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

{

       ulong addr, rc;

       int    rcode = 0;

       if (argc < 2) {

              printf ("Usage:\n%s\n",cmdtp->usage);

              return 1;

       }

       addr = simple_strtoul(argv[1], NULL,16);

       printf ("## Starting application at0x%08lX ...\n", addr);

       rc = ((ulong(*)(int, char []))addr) (--argc, &argv[1]);    /*运行程序 */

    

if (rc != 0) rcode = 1;

       printf ("## Application terminated,rc = 0x%lX\n", rc);  /*如果是运行linux,这条指令是否能运行?*/

       return rcode;

}

go命令调用do_go()函数,跳转到某个地址执行的。如果在这个地址准备好了自引导的内核映像,就可以启动了。尽管go命令可以带变参,实际使用时不用来传递参数。

2.bootm命令的实现

/*common/cmd_bootm.c */

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

{

……       ……

/* 检查头部 */

if (crc32 (0, (uchar *)data, len) !=checksum) {

       puts("Bad Header Checksum\n");

       SHOW_BOOT_PROGRESS(-2);

       return1;

  }

……         ……

/*解压缩*/

  switch(hdr->ih_comp) {

  case IH_COMP_NONE:

       if(ntohl(hdr->ih_load)== addr) {

            printf("   XIP %s ... ", name);

       }else {

#if defined(CONFIG_HW_WATCHDOG) ||defined(CONFIG_WATCHDOG)

            size_tl = len;

            void*to = (void *)ntohl(hdr->ih_load);

            void*from = (void *)data;

            printf("   Loading %s ... ", name);

            while(l > 0) {

                size_ttail = (l > CHUNKSZ) ? CHUNKSZ : l;

                WATCHDOG_RESET();

                memmove(to, from, tail);

                to+= tail;

                from+= tail;

                l-= tail;

            }

#else   /*!(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */

            memmove((void *) ntohl(hdr->ih_load), (uchar *)data, len);

#endif  /*CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */

       }

       break;

  case IH_COMP_GZIP:

       printf("   Uncompressing %s ... ",name);

       if(gunzip ((void *)ntohl(hdr->ih_load), unc_len,

                (uchar *)data, &len) != 0) {

            puts("GUNZIP ERROR - must RESET board to recover\n");

            SHOW_BOOT_PROGRESS(-6);

            do_reset(cmdtp, flag, argc, argv);

       }

       break;

#ifdef CONFIG_BZIP2

  caseIH_COMP_BZIP2:

       printf("   Uncompressing %s ... ",name);

       /*

        * If we've got less than 4 MB of malloc()space,

        * use slower decompression algorithm whichrequires

        * at most 2300 KB of memory.

        */

       i= BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),

                          &unc_len,(char *)data, len,

                          CFG_MALLOC_LEN< (4096 * 1024), 0);

       if(i != BZ_OK) {

            printf("BUNZIP2 ERROR %d - must RESET board to recover\n", i);

            SHOW_BOOT_PROGRESS(-6);

            udelay(100000);

            do_reset(cmdtp, flag, argc, argv);

       }

       break;

#endif /* CONFIG_BZIP2 */

  default:

       if(iflag)

            enable_interrupts();

       printf("Unimplemented compression type %d\n", hdr->ih_comp);

       SHOW_BOOT_PROGRESS(-7);

       return1;

  }

}

……           ……             ……

switch(hdr->ih_os) {

     default:           /*handled by (original) Linux case */

     case IH_OS_LINUX:

        do_bootm_linux  (cmdtp, flag,argc, argv,

                   addr, len_ptr, verify);

        break;

     case IH_OS_NETBSD:

        do_bootm_netbsd (cmdtp, flag, argc, argv,

                   addr, len_ptr, verify);

        break;

     case IH_OS_RTEMS:

        do_bootm_rtems (cmdtp, flag, argc, argv,

                   addr, len_ptr, verify);

        break;

     case IH_OS_VXWORKS:

        do_bootm_vxworks (cmdtp, flag, argc, argv,

                    addr, len_ptr, verify);

        break;

     case IH_OS_QNX:

        do_bootm_qnxelf (cmdtp, flag, argc, argv,

                    addr, len_ptr, verify);

        break;

     }

bootm命令调用do_bootm函数。这个函数专门用来引导各种操作系统映像,可以支持引导Linux、vxWorks、QNX等操作系统。引导Linux的时候,调用do_bootm_linux()函数。

3.do_bootm_linux函数的实现

/*lib_arm/armlinux.c */

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

                   ulong addr, ulong *len_ptr,int verify)

{

theKernel = (void (*)(int, int,uint))ntohl(hdr->ih_ep);

… …                 … …

      /* we assume that the kernel is in place*/

      printf ("\nStarting kernel ...\n\n");

… …                 … …

      theKernel (0, bd->bi_arch_number,bd->bi_boot_params);  /*启动内核,传递启动参数*/

}

do_bootm_linux()函数是专门引导Linux映像的函数,它还可以处理ramdisk文件系统的映像。这里引导的内核映像和ramdisk映像,必须是U-Boot格式的。U-Boot格式的映像可以通过mkimage工具来转换,其中包含了U-Boot可以识别的符号。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值