U-Boot启动过程

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

                  

第一阶段

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: /* 重新定位代码 */

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中实现

 

第二阶段

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,

};

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

 

1.go命令的实现

/* 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:\n%s\n", cmdtp->usage);

              return 1;

       }

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

       printf ("## Starting application at 0x%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 */

int do_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);

       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\n");

            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\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);

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

 

3.do_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 ("\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可以识别的符号。

时间: 2024-10-28 11:56:18

U-Boot启动过程的相关文章

Spring boot 启动过程解析 logback

使用 Spring Boot 默认的日志框架 Logback. 所有这些 POM 依赖的好处在于为开发 Spring 应用提供了一个良好的基础.Spring Boot 所选择的第三方库是经过考虑的,是比较适合产品开发的选择.但是 Spring Boot 也提供了不同的选项,比如日志框架可以用 Logback 或 Log4j,应用服务器可以用 Tomcat 或 Jetty. http://www.ibm.com/developerworks/cn/java/j-lo-spring-boot/ sp

Linux的开机启动过程简介

开机过程指的是从打开计算机电源直到LINUX显示用户登录画面的全过程.分析LINUX开机过程也是深入了解LINUX核心工作原理的一个很好的途径. 启动第一步--加载BIOS 当 你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关 信息.设备启动顺序信息.硬盘信息.内存信息.时钟信息.PnP特性等等.在此之后,计算机心里就有谱了,知道应该去读取哪个硬件设备了.在BIOS将系 统的控制权交给硬盘第一个扇区之

WinPE的启动过程是怎样的

  WinPE的启动过程是这样的:grub4dos--setupldr.bin--ntdetect.com--winnt.sif--winpe.is_. setupldr.bin中定义了ntdetect.com和winnt.sif的位置.名称等. 在定义的位置中,任意一个位置能找到ntdetect.com就可以正常执行.所以,如果直接使用maotao的setupldr.bin,可以把NTDETECT.COM放在根目录,也可以放在wxpe目录,当然,如果你想放在别的位置,自己改一下就可以.同样,w

WinPE的启动过程

  WinPE的启动过程是这样的:grub4dos--setupldr.bin--ntdetect.com--winnt.sif--winpe.is_. setupldr.bin中定义了ntdetect.com和winnt.sif的位置.名称等. 在定义的位置中,任意一个位置能找到ntdetect.com就可以正常执行.所以,如果直接使用maotao的setupldr.bin,可以把NTDETECT.COM放在根目录,也可以放在wxpe目录,当然,如果你想放在别的位置,自己改一下就可以.同样,w

WinPE在4种介质上的启动过程

  WinPE是经常用到系统维护工具了,现在为大家介绍一下WinPE在各种介质上的启动过程. 一.光盘(WinPE)的启动过程: BIOS->PEBOOT.BIF(光盘引导信息)->定位启动映像区...->SETUPLDR.BIN(PELDR或其它相关名字)->NTDETECT.COM->WINNT.SIF(WINNT.XPE)->WINPE.ISO(WINPE.IMG)->TXTSETUP.SIF->WINPE系统桌面 二.硬盘(WinPE)的启动过程:

小议WinPE的启动过程

  一.光盘(WinPE)的启动过程: BIOS->PEBOOT.BIF(光盘引导信息)->定位启动映像区...->SETUPLDR.BIN(PELDR或其它相关名字)->NTDETECT.COM->WINNT.SIF(WINNT.XPE)->WINPE.ISO(WINPE.IMG)->TXTSETUP.SIF->WINPE系统桌面 二.硬盘(WinPE)的启动过程: BIOS->MBR(主引导记录)->PBR(分区引导)->NTLDR-&

WinPE启动过程大全

  1.U盘HDD(WinPE)的启动过程: BIOS->NTLDR->SETUPLDR.BIN(PELDR或其它相关名字)->NTDETECT.COM->WINNT.SIF(WINNT.XPE)->WINPE.ISO(WINPE.IMG)->TXTSETUP.SIF->WINPE系统桌面 2.U盘ZIP(WinPE)的启动过程: BIOS->IO.SYS->COMMAND.COM->AUTOEXEC.BAT->SETUPLDR.BIN(P

WinPE启动过程介绍

  WinPE是经常用到系统维护工具了,现在就为大家介绍一下WinPE在各种介质上的启动过程. 一.光盘(WinPE)的启动过程: BIOS->PEBOOT.BIF(光盘引导信息)->定位启动映像区...->SETUPLDR.BIN(PELDR或其它相关名字)->NTDETECT.COM->WINNT.SIF(WINNT.XPE)->WINPE.ISO(WINPE.IMG)->TXTSETUP.SIF->WINPE系统桌面 二.硬盘(WinPE)的启动过程:

linux启动过程

本文转载于阮一峰的博客,写的非常清晰,转载收藏: 原文网址:http://www.ruanyifeng.com/blog/2013/08/linux_boot_process.html 这个部分比较有意思.因为在BIOS阶段,计算机的行为基本上被写死了,程序员可以做的事情并不多:但是,一旦进入操作系统,程序员几乎可以定制所有方面.所以,这个部分与程序员的关系更密切. 我主要关心的是Linux操作系统,它是目前服务器端的主流操作系统.下面的内容针对的是Debian发行版,因为我对其他发行版不够熟悉

VxWorks启动过程详解(下)

上一节主要是从映像的分类和各种映像的大致加载流程上看VxWorks的启动过程,这一节让我们从函数级看一下VxWorks的启动过程: 1. Boot Image + Loadable Images:   下面是具体的流程图:  其中第一阶段的执行流程使用的是上图的左边的源文件中的那些函数(romInit->romStart->usrInit->sysHwinit->usrKernelinit->usrRoot);第二阶段执行流程使用的是上图中右边源文件中的那些函数(sysIni