您的位置:首页 > 运维架构 > Linux

u-boot工作角色与启动过程

2012-02-23 15:30 260 查看
 
u-boot担任初始化硬件和引导操作系统的作用。从操作系统的角度看,Bootloader的总目标就是正确地调用内核来执行。

将bootload下载到开发板的方法有两种:1、通过片内固化的loader加载bootloader;通常某些CPU内部ROM中固化了一段程序可以用于最初的下载;2、通过JTAG或者仿真器下载:这些工具可以直接操作FLASH,对其进行编程烧录;

GRUB: GNU GRUB 是GRUB是GRand Unified Bootloader的缩写,它是一个多重操作系统启动管理器。用来引导不同系统,如windows,linux。

为什么装双系统的时候要先装windows后装linux原因:后装的操作系统引导程序会破坏之前安装的系统的引导程序,windows的引导程序NTLOADER不能识别linux而linux的引导程序能够识别windows,所以即使后面安装的linux引导程序grub破坏了windows的引导程序 windows也能够靠grub启动起来,故要先装windows再装linux这样两个系统都能启动。

Boot Loader的操作模式(Operation Mode)
主机和目标机之间一般通过串口建立连接,BootLoader软件在执行时通常会通过串口来进行数据传输,如输出打印信息到串口,从串口读取用户控制字符。
 
大多数Boot Loader都包含两种不同的操作模式:启动加载模式和下载模式,这种区别仅对于开发人员才有意义。从最终用户的角度看,BootLoader的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。
²      启动加载(Boot loading)模式:这种模式也称为自主模式bootstrap。也即Boot Loader将存储在目标板Flash中的内核和文件系统的镜像装载到SDRAM中,整个过程无需用户的介入。这种模式是BootLoader的正常工作模式,因此在嵌入式产品发布的时候,BootLoader显然必须工作在这种模式下。
²      下载Downloading模式:在这种模式下,目标机上的BootLoader将通过串口连接或网络连接等通信手段从宿主机Host下载文件,比如下载内核映像和根文件系统映像等。从主机下载的文件通常首先被BootLoader保存到目标机的RAM中,然后再被BootLoader写到目标机上的FLASH类固态存储设备中。BootLoader的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新(bootloader自身也可以这样更新)也会使用Boot Loader的这种工作模式。工作于这种模式下的BootLoader通常都会向它的终端用户提供一些简单的命令行接口。
 
像U-BOOT等这样功能强大的BootLoader通常同时支持这两种工作模式,而且允许用户在这两种工作模式之间进行切换。比如,U-BOOT在启动时处于正常的启动加载模式,但是它会延时几秒(在配置文件中可以设定)等待终端用户按下任意键而将其切换到下载模式(相当于bios下按del键可进入系统配置界面一样,设置从光盘启动可进行重装),如果在给定时间内没有用户按键,则U-BOOT继续启动,进行正常的启动加载。
Bootloader执行流程:大多数Bootloader都分为stagel和stage2两大部分。依赖于CPU体系结构的代码,比如设备初始化代码等,通常都放在stagel中,而且通常都用汇编语言来实现,以达到短小精悍和高效的目的。而stage2则通常用C语言来实现,这样可以实现更复杂的功能,而且代码会具有更好的可读性和可移植性。

Bootloader的stagel(主要在start.S代码文件)为位置无关代码,通常在FLASH中运行。所有的指令为相对寻址,可以在任何位置运行。通常包括以下步骤(以执行的先后顺序):
Ø       硬件设备初始化(配置SDRAM存储控制器及IO),中断初始化;
Ø       为加载Bootloader的stage2准备RAM空间(这个地址由链接脚本指定,为运行域地址,通常为RAM的高端地址),测试内存空间是否有效;
Ø       拷贝Bootloader的stage2到RAM空间中;
Ø       设置好堆栈;
Ø       跳转到stage2的C入口点。
 
Bootloader的stage2(主要在lib_arm/board.c代码文件)通常被拷贝到RAM中运行,这样可以提高运行速度。通常包括以下步骤(以执行的先后顺序):
Ø       初始化本阶段要使用到的硬件设备;
Ø       检测系统内存映射(memory map);
Ø       没有用户干预时将kernel映像和根文件系统映像从flash读到RAM空间中;
Ø       为内核设置启动参数;
Ø       调用内核。
     由于一个可执行的Image必须要有一个入口点,并且只能有一个全局入口,通常这个入口就在ROM (flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本u-boot.lds来完成,该阶段需要依次完成的工作一般包括:

²      CPU自身的初始化,它包括:CPU运行模式的设置(管理模式)、设置异常的入口地址和异常处理函数、运行时钟频率的设置等工作。
²      初始化GPIO和内存控制器。
²      为拷贝Stage2准备RAM空间。
²      进行自拷贝,将U-BOOT的Stage2拷贝到RAM中。
²      设置好堆栈。
²      跳转到Stage2的入口,从而转到RAM中执行,该工作是调用指令ldr pc, start armboot来完成的。
 
u-boot.lds中代码功能:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start) // 程序的入口在/cpu/××××/start.s中定义
SECTIONS
{
    . = 0x00000000; // 程序链接的地址
 
    . = ALIGN(4);
    .text      :
    {
      cpu/at91rm9200/start.o (.text)
      *(.text)
    }
 
    . = ALIGN(4);
    .rodata : { *(.rodata) }
 
    . = ALIGN(4);
    .data : { *(.data) }
 
    . = ALIGN(4);
    .got : { *(.got) }
 
  __u_boot_cmd_start = .;
  .u_boot_cmd : { *(.u_boot_cmd) }
  __u_boot_cmd_end = .;
 
    armboot_end_data = .; //代码段结束地址
 
    . = ALIGN(4);
    .bss : { *(.bss) }
 
    armboot_end = .;  //整个U-boot印象的结束地址
}
××××××××××××××××××××××××××××
board/mini2440中config.mk文件(TEXT_BASE = 0x21f00000,32M RAM的设置,为第二阶段程序在RAM中的运行地址)用于设置程序编译连接的起始地址,在程序中要特别注意与地址相关指令的使用。
// 比较运行地址和链接地址,如果当前已经在RAM中运行了,则无需拷贝到RAM中
relocate:                               /* relocate U-Boot to RAM           */
        adr     r0, _start              /* r0 <- current position of code   */
        ldr     r1, _TEXT_BASE          /* test if we run from flash or RAM */
        cmp     r0, r1                  /* don't reloc during debug         */
        beq     stack_setup
 
        ldr     r2, _armboot_start
        ldr     r3, _bss_start
        sub     r2, r3, r2              /* r2 <- size of armboot            */
        add     r2, r0, r2              /* r2 <- source end address         */
 
copy_loop:
        ldmia   r0!, {r3-r10}           /* copy from source address [r0]    */
        stmia   r1!, {r3-r10}           /* copy to   target address [r1]    */
        cmp     r0, r2                  /* until source end addreee [r2]    */
        ble     copy_loop
在上述操作运行完成后,就进入到/lib_arm/board.c 中的start_armboot()函数运行,并建立起了一个基本的环境,此时的物理内存空间的分布就变成了如图所示的情况。
第二阶段:运行U-BOOT的主体部分
该阶段以程序跳转到lib_arm/board.c中的start_armboot函数为标志,该函数同时也是C语言的开始函数,是整个启动代码的主体函数,同时还是整个U-BOOT的主体函数,该函数主要完成以下工作:
²      调用一系列的初始化函数,初始化本阶段使用到的硬件,如:
×××××××××××××××××××
init_fnc_t *init_sequence[] = {
    cpu_init,     /* basic cpu dependent setup */
    board_init,       /* basic board dependent setup */
    interrupt_init,      /* set up exceptions */
    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 */
    dram_init,    /* configure available RAM banks */
    display_dram_config,
#if defined(CONFIG_VCMA9)
    checkboard,
#endif
    NULL,
};
 
env_init:设置环境变量,初始化环境;
init_baudrate:设置串口的波特率;
serial_init:设置串口的工作方式;
dram_init:设置SDRAM的起始地址和大小;
     
²      检查存储器分配和使用情况:获取flash的bank分区情况、是否擦除、是否上锁等信息为以后flash相关命令使用;初始化系统内存分配函数,供后面的代码使用malloc等函数,U-BOOT没有使用其他现成的库所有函数的实现均在文件中),如果系统有液晶等显示设备,一并在此分配显示内存。
²      打印内存,flash、环境变量设置等信息。
²      等待几秒时间,如果有键盘输入,则进入命令模式,接收用户输入的命令并解释执行(如启动操作系统,更新flash内容)。
²      如果在给定的时间内没有用户输入或者在执行命令操作时收到了用户要求启动内核的命令(boot),则将把操作系统内核和根文件系统映像文件从flash中拷贝到RAM中相应位置,并在设置内核启动参数后跳转到内核映像的首地址处执行。
×××××××××××××××××××××××××××××
U-BOOT调用 Linux 内核的方法是直接跳转到内核的第一条指令处,也即直接跳转到 MEM_START+0x8000地址处。在跳转时,要满足下列条件:
a) CPU寄存器的设置:R0=0;R1=机器类型 ID,本系统的机器类型ID=193。R2=启动参数标记列表在RAM中的起始基地址; 
b) CPU模式:必须禁止中断(IRQs和FIQs);CPU必须工作在SVC模式;
c) Cache和MMU的设置:MMU 必须关闭;指令Cache可以打开也可以关闭;数据Cache必须关闭。
系统采用下列代码来进入内核函数:
void (*theKernel)(int zero, int arch);
theKernel = (void (*)(int, int))ntohl(hdr->ih_ep);
theKernel(0, bd->bi_arch_number);其中,hdr是image_header_t类型的结构体,hdr->ih_ep为entry point,指向内核的第一条指令地址,即Linux操作系统下的/kernel/arch/arm/boot/compressed/head.S汇编程序。theKernel()函数调用应该不会返回,如果该调用返回,则说明出错。
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息