• U-Boot启动过程 分类: HI3531 arm-linux-Ubuntu 2013-12-18 09:36 587人阅读 评论(0) 收藏


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

                      

    第一阶段

    1cpu/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:          //复位启动子程序

           /* 设置CPUSVC32模式 */

           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_BASERAM中的地址 */

    cmp     r0, r1          /* 比较r0r1,判断当前是从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: /* 重新定位代码 */

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

    stmia r1!, {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结构体地址空间  */

    #ifdef CONFIG_USE_IRQ

    sub r0, 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:str r2, [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中实现

     

    第二阶段

    2lib_arm/board.c

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

     

    3init_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,

    };

    void start_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,具备多种引导内核启动的方式。常用的gobootm命令可以直接引导内核映像启动。U-Boot与内核的关系主要是内核启动过程中参数的传递。

     

    1go命令的实现

    /* common/cmd_boot.c  */

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

    {

           ulong addr, rc;

           int     rcode = 0;

           if (argc < 2) {

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

                  return 1;

           }

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

           printf ("## Starting application at 0x%08lX ... ", addr);

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

        

    if (rc != 0) rcode = 1;

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

           return rcode;

    }

     

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

     

    2bootm命令的实现

    /* common/cmd_bootm.c */

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

    {

    ……        ……

    /* 检查头部 */

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

           puts ("Bad Header Checksum ");

           SHOW_BOOT_PROGRESS (-2);

           return 1;

      }

    ……          ……

    /*解压缩*/

      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_t l = len;

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

                void *from = (void *)data;

     

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

     

                while (l > 0) {

                    size_t tail = (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 ");

                SHOW_BOOT_PROGRESS (-6);

                do_reset (cmdtp, flag, argc, argv);

           }

           break;

    #ifdef CONFIG_BZIP2

      case IH_COMP_BZIP2:

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

           /*

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

            * use slower decompression algorithm which requires

            * 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 ", 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 ", hdr->ih_comp);

           SHOW_BOOT_PROGRESS (-7);

           return 1;

      }

    }

    ……           ……             ……

     

    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函数。这个函数专门用来引导各种操作系统映像,可以支持引导LinuxvxWorksQNX等操作系统。引导Linux的时候,调用do_bootm_linux()函数。

     

    3do_bootm_linux函数的实现

    /* lib_arm/armlinux.c */

    void do_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 (" Starting kernel ... ");

    … …                 … …

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

    }

     

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

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    Axure RP使用攻略--动态面板滑动效果(10)
    Axure RP使用攻略--带遮罩层的弹出框(9)
    Axure RP使用攻略--动态面板的用途(8)
    Axure RP使用攻略--入门级(七)之axure元件使用思路的补充
    Axure RP使用攻略--入门级(六)关于Axure rp触发事件中IF和ELSE IF的使用说明
    Axure RP使用攻略--入门级(五)系统函数与变量
    Axure RP使用攻略--入门级(四)条件生成器
    Axure RP使用攻略--入门级(三)元件的触发事件
    Altium Designer 14安装破解
    小米手环暴力拆解
  • 原文地址:https://www.cnblogs.com/mao0504/p/4706692.html
Copyright © 2020-2023  润新知