BootLoader基本概念

2019-07-12 23:44发布

转自:http://blog.chinaunix.net/uid-25445243-id-3869348.html
一、BootLoader简介
1.1、嵌入式Linux软件结构与分布         在一般情况下嵌入式Linux系统中的软件主要分为以下几部分:         1) 引导加载程序:其中包括内部ROM中的固化启动代码和Boot Loader两部分。内部固化ROM是厂家在芯片生产时候固化的,作用基本上是引导Boot Loader。有的芯片比较复杂,比如Omap3,他在flash中没有代码的时候有许多启动方式:USB、UART或以太网等等。而S3C24x0则很简单,只有Norboot和Nandboot。         2) Linux kernel 和drivers。
        3) 文件系统。包括根文件系统和建立于Flash内存设备之上的文件系统(EXT4、UBI、CRAMFS等等)。它是提供管理系统的各种配置文件以及系统执行用户应用程序的良好运行环境的载体。         4) 应用程序。用户自定义的应用程序,存放于文件系统之中。         在Flash 存储器中,他们的一般分布如下:         但是以上只是大部分情况下的分布,也有一些可能根文件系统是initramfs,被一起压缩到了内核映像里,或者没有Bootloader参数区等等。
1.2、嵌入式Linux中BootLoader
        在linux内核的启动运行除了内核映像必须在主存的适当位置,CPU还必须具备一定的条件:
 1. CPU 寄存器的设置: R0=0;
R1=Machine ID(即Machine Type Number,定义在linux/arch/arm/tools/mach-types);
R2=内核启动参数在 RAM 中起始基地址;  2. CPU 模式: 必须禁止中断(IRQs和FIQs);
CPU 必须 SVC 模式;  3. Cache 和 MMU 的设置: MMU 必须关闭;
指令 Cache 可以打开也可以关闭;
数据 Cache 必须关闭;         但是在CPU刚上电启动的时候,一般连内存控制器都没有配置过,根本无法在内存中运行程序,更不可能处在Linux内核的启动环境中。为了初始化CPU及其他外设,使得Linux内核可以在系统主存中跑起来,并让系统符合Linux内核启动的必备条件,必须要有一个先于内核运行的程序,他就是所谓的引导加载程序(BootLoader)。
        而Boot Loader并不是Linux才需要,是几乎所有的运行操作系统的设备都具备的。我们的PC的BOIS就是BootLoader的一部分(只是前期引导,后面一般还有外存中的各种BootLoader),对于Linux PC来说,BootLoader = BIOS + GRUB/LILO。
二、Boot Loader的功能和选择
        通过上面的讲述,我们可以知道:BootLoader是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境,最后从别处(Flash、以太网、UART)载入内核映像并跳到入口地址。
        由于BootLoader直接操作硬件,所以她严重依赖于硬件,而且依据所引导操作系统的不同,也有不同的选择,对于嵌入式世界中更是如此。就S3C24x0而言,如果是引导Linux,一般选用韩国的mizi公司设计的vivi或者DENX软件工程中心的U-boot,如果是引导Win CE,就选用Eboot。如果是开发StrongARM 构架下的LART,就可选用由Jan-Derk Bakker 和 Erik Mouw发布的Blob(Boot Loader Object)。如果是要引导eCos系统,就可以选用同是Redhat公司开发的Redboot。
        所以在嵌入式世界中建立一个通用的 BootLoader 几乎是不可能的,而有可能的是让一个 Boot Loader代码支持多种不同的构架和操作系统,并让她方便移植。U-boot就是支持多平台多操作系统的一个杰出代表。如果在开发S3C2440时熟悉了U-boot,再转到别的平台的时候就可以很快地完成这个平台下的U-boot移植,而且U-boot的代码结构越来越合理,对于新功能的添加也十分容易。
*推荐阅读:嵌入式系统 Boot Loader 技术内幕
三、常用的U-boot命令详解 
        U-boot发展到现在,他的命令行模式已经非常接近Linux下的shell了,如果你输入的命令的前几个字符和别的命令不重复,那么你就只需要打这几个字符即可,比如我想看这个U-boot的版本号,命令就是“ version”,但是在所有的命令中没有其他任何一个的命令是由“v”开头的,所以只需要输入“v”即可。
1、获取帮助
        命令:help 或 ?
        功能:查看当前U-boot版本中支持的所有命令。
        如果你想获取某条命令的更详细的帮助,可以使用:
        help <你想要查的指令>
        或者? <你想要查的指令>,
        甚至 h <你想要查的指令缩写>。
2、 内存/寄存器操作指令
        常用的Nand Flash指令如下: 指令 功能 nand info 显示可使用的Nand Flash nand device [dev] 显示或设定当前使用的Nand Flash nand read addr off size Nand Flash读取命令,从Nand的off偏移地址处读取size字节的数据到SDRAM的addr地址。 nand write  addr off size Nand Flash烧写命令,将SDRAM的addr地址处的size字节的数据烧写到Nand的off偏移地址。 nand write[.yaffs[1]]   addr off size 烧写yaffs映像专用的命令,.yaffs1 for 512+16 NAND nand erase [clean] [off size] Nand Flash檫除命令,擦除Nand Flash的off偏移地址处的size字节的数据 nand bad 显示Nand Flash的坏块 nand dump[.oob] off 显示Nand Flash中的数据(16进制) nand scrub 彻底擦除整块Nand Flash中的数据,包括OOB。可以擦除软件坏块标志。 nand markbad off 标示Nand的off偏移地址处的块为坏块         nm      修改内存值 (指定地址)   
        格式: nm [.b, .w, .l] address         mm     修改内存值(地址自动加一)
        格式:  mm [.b, .w, .l] address         md       显示内存值
        格式:  md [.b, .w, .l] address [# of objects]         mw     用指定的数据填充内存
        格式:  mw [.b, .w, .l] address value [count]         cp      内存的拷贝(包括内存与Nor Flash间的数据拷贝)
        格式:cp [.b, .w, .l] source target count         上面是查看和修改内存值的指令,可以查看和修改SDRAM和寄存器值。
        [.b, .w, .l]代表了查看和修改形式:Byte、word、long        
        你可以试着修改LED相连的GPIO寄存器的数据寄存器值,可以控制LED的点亮!
        先熄灭后点亮LED1的范例:(这个实验要结合芯片数据手册和mini2440的原理图来理解)
        [u-boot@MINI2440]#md 0x56000014 1
        [u-boot@MINI2440]#nm.w 0x56000014
3、Nor Flash指令         Nor Flash 的命令经常用于烧写数据到Nor Flash 。
        flinfo  打印Flash存储器的信息,并列出所有Sector。
        flinfo  N 单独打Flash存储器N Block的信息。(在有多块Nor Flash时使用)
        后面带有(RO)的说明这个Sector已经写保护了。
        因为Nor Flash的读取接口和SDRAM是一样的,所以Nor Flash的读取也是使用md命令。
        但由于Nor Flash的烧写时序和SDRAM的写入不同,烧写Nor  Flash 不能使用mm等命令,只能使用cp命令从内存拷贝到Nor Flash,而且烧写之前必须解除保护并擦除!命令如下:
        protect :对Flash 写保护的操作,可以使能和解除写保护。
        格式:
        protect on/off start end
        protect on/off start +end
        protect on/off N:SF[-SL]
        protect on/off bank N
        protect on/off all
        第1 个参数on 代表使能写保护;off 代表解除写保护。
        第2 、3 参数是指定Flash 写保护操作范围
        start end是照起始地址和结束地址定义范围,start是擦除块的起始地址;end 是擦除末尾块的结束地址。
        例如:擦除Sector 2和Sector 3区域命令为erase 20000 3ffff 。
        start +end是照起始地址和操作字节数定义范围,这种方式最常用。start是擦除块的起始地址;end 是擦除的字节数。
        例如:擦除Sector 2和Sector 3区域命令为erase 20000  +20000
        N:SF[-SL]是按照组和扇区,N 表示Flash 的Block号,SF 表示擦除起始Sector号,SL 表示擦除结束Sector号。
        例如:擦除Block1 的Sector 2和Sector 3区域命令为erase 1:2-3。
        bank N是擦除整个Block,擦除Block号为N 的整个Flash。
        all是擦除全部Flash。
        注意:Nor Flash擦除的最小单位是Sector,也就是0x10000字节,如果你定义的大小不满1 Sector或超过Sector的边界,那么被定义到的Sector会被全部擦除。         erase  :擦除Flash的命令
        格式:
        erase start end
        erase start +end
        erase N:SF[-SL]
        erase bank N
        erase all
        参数是指定Flash 擦除操作范围,跟写保护的方式相同。
4、USB 操作指令 指令 功能 usb reset 初始化USB控制器 usb stop [f] 关闭USB控制器 usb tree 已连接的USB设备树 usb info [dev] 显示USB设备[dev]的信息 usb storage 显示已连接的USB存储设备 usb dev [dev] 显示和设置当前USB存储设备 usb part [dev] 显示USB存储设备[dev]的分区信息 usb read addr blk# cnt 读取USB存储设备数据 5、SD卡(MMC)指令
        SD卡的使用命令比较简单,只有初始化和设备信息的显示,读写是通过文件系统命令实现的。
        mmc init [dev] - 初始化MMC子系统
        mmc device [dev] - 查看和设置当前设备         使用和USB类似,在所有的命令使用前,必须先插入SD卡,然后使用:mmc init,以初始化MMC 控制器,获取设备信息。 6、 系统引导指令         boot  和bootd  都是运行ENV”bootcmd”中指定的指令。
        bootm 指令是专门用于启动在SDRAM中的用U-boot的mkimage工具处理过的内核映像。
        格式:bootm [addr [arg ...]]
        addr 是内核映像所在的SDRAM中的地址
        当启动的是Linux内核时,'arg' 可以使 initrd 的地址。 7、EEPROM 读写指令         eeprom  - I2C 接口的EEPROM 读写指令
        格式:
        eeprom read  addr off cnt
        eeprom write addr off cnt
        第一个参数addr 是要写入或读出的数据在SDRAM中的存放地址;
        第二个参数off 是在EEPROM中的偏移;
        第三个参数cnt 是读写的数据字节数。 8、设置和读取RTC指令         date    - 设置和读取RTC
        格式:
        date [MMDDhhmm[[CC]YY][.ss]]
        MM:月份         DD:日期         hh:小时         mm 分钟         CC:年份的前两个数字         YY:年份的后两个数字         ss:秒数 9、脚本运行指令         run var [...]         var :ENV中的脚本名
10、系统重启指令         reset  -重启CPU 11、环境变量
环境变量 解释说明 bootdelay 执行自动启动(bootcmd中的命令)的等候秒数 baudrate 串口控制台的波特率 netmask 以太网的网络掩码 ethaddr 以太网的MAC地址 bootfile 默认的下载文件名 bootargs 传递给Linux内核的启动参数 bootcmd 自动启动时执行命令 serverip TFTP服务器端的IP地址 ipaddr 本地的IP地址 stdin 标准输入设备,一般是串口 stdout 标准输出,一般是串口,也可以是LCD(VGA) stderr 标准出错,一般是串口,也可以是LCD(VGA)         要看到你的板上的ENV值可使用printenv命令。
        你会发现有些有的ENV上面没有,原因是如果你没有设置这个环境变量就不会打印出,你也可以自己定义ENV,并在命令中使用${ENV}来调用它。同时你也可以删除这个ENV。设置ENV的命令是setenv,格式为: 
        setenv name value
        第1个参数是环境变量的名称。         第2个参数是要设置的值,如果没有第2个参数,表示删除这个环境变量。
        当你设置了ENV,它只保存在内存中,如果你要它保存在存放ENV的固态存储器中,请使用:saveenv。
        [u-boot@apple_guet]#saveenv
        如果在启动的时候会看到U-boot打印出:“Warning - bad CRC, using default environment”,说明U-boot没有在存放ENV的固态存储器中找到有效的ENV,只好使用你在编译的时候定义的默认ENV。如果U-boot存放ENV的固态存储器的驱动是OK的,那么只要运行 saveenv就可以把默认ENV写入固态存储器,下次启动就不会有这个警告了。
       ENV可以放在许多固体存储器中,对于mini2440来说Nor Flash、Nand Flash或EEPROM都可以,就看你如何配置了(include/configs下的配置文件)。例如:
        Nor Flash:
        #defineCONFIG_ENV_IS_IN_FLASH 1
        #defineCONFIG_ENV_OFFSET 0X40000
        #defineCONFIG_ENV_SIZE            0x20000    /* Total Size of Environment Sector */
        Nand Flash: 
        #defineCONFIG_ENV_IS_IN_NAND 1
        #defineCONFIG_ENV_OFFSET 0X40000
        #defineCONFIG_ENV_SIZE            0x20000    /* Total Size of Environment Sector */
        EEPROM:
        #defineCONFIG_ENV_IS_IN_EEPROM 1/* use EEPROM for environment vars */
        #defineCONFIG_ENV_OFFSET 0x000/* environment starts at offset 0 */
        #defineCONFIG_ENV_SIZE     0x400/* 1KB */
        CONFIG_ENV_OFFSET是在整个存储器中的偏移地址;
        CONFIG_ENV_SIZE是指其使用的大小。
        注意:CONFIG_ENV_OFFSET和 CONFIG_ENV_SIZE的设置,不要覆盖了其他分区。 四、下载与烧写
        使用U-boot将映像文件烧写到板上的Flash,一般步骤是:
        1) 通过网络、串口、U盘、SD卡等方式将文件传输到SDRAM;
        2) 使用Nand Flash或Nor Flash相关的读写命令将SDRAM中的数据烧入Flash。         下面是烧写范例:
        如果使用 SD卡或者U盘形式更新U-boot,那么首先SD卡或者U盘中必须有FAT32文件系统,并在里面存放了u-boot.bin 文件。
4.1、 通过SD卡烧入Nand Flash
        [u-boot@apple_guet]#mmc init         [u-boot@apple_guet]#fatload mmc 1 0x30008000 u-boot.bin         [u-boot@apple_guet]#nand erase 0 0x40000         [u-boot@apple_guet]#nand write 0x30008000 0 0x40000
4.2、通过U盘烧入Nor Flash         [u-boot@apple_guet]#usb start         [u-boot@apple_guet]#usb part0         [u-boot@apple_guet]#fatload usb 0:4 0x30008000 u-boot.bin         [u-boot@apple_guet]#protect off all         [u-boot@apple_guet]#erase 0x0 0x3ffff         [u-boot@MINI2440]#cp.b 0x30008000 0x0 0x3ffff
4.3、通过TFTP服务烧入Nand Flash         [u-boot@apple_guet]#tftpboot 30008000 192.168.1.100:u-boot.bin         [u-boot@MINI2440]#nand erase 0 0x40000         [u-boot@apple_guet]#nand write 0x30008000 0 0x40000
4.4、通过NFS 服务烧入Nand Flash         [u-boot@apple_guet]#nfs 30008000 192.168.1.100:/home/tekkaman/development/share/u-boot.bin         [u-boot@apple_guet]#nand erase 0 0x40000         [u-boot@apple_guet]#nand write 0x30008000 0 0x40000
五、内核引导
        内核的引导步骤如下:         1) 用U-boot的mkimage工具处理内核映像zImage。
        2) 通过网络、串口、U盘、SD卡等方式将处理过的内核映像传输到SDRAM的一定位置(一般使用0x30008000)
        3) 然后使用”bootm"等内核引导命令来启动内核。
5.1、mkimage工具作用         因为在用bootm命令引导内核的时候,bootm需要读取一个64字节的文件头,来获取这个内核映象所针对的CPU体系结构、OS、加载到内存中的位置、在内存中入口点的位置以及映象名等等信息。这样bootm才能为OS设置好启动环境,并跳入内核映象的入口点。而mkimage就是添加这个文件头的专用工具。具体的实现请看U-boot中bootm的源码和mkimage的源码。 mkimage工具的使用:         参数说明:         -A指定CPU的体系结构,可用值有:alpha、arm、x86、ia64、mips、mips64、ppc、s390、sh、sparc、sparc64、m68k等         -O指定操作系统类型,可用值有:openbsd、netbsd、freebsd、4_4bsd、linux、svr4、esix、solaris、irix、sco、dell、ncr、lynxos、vxworks、psos、qnx、u-boot、rtems、artos         -T指定映象类型,可用值有:standalone、kernel、ramdisk、multi、firmware、script、filesystem         -C指定映象压缩方式,可用值有:             none不压缩(一般使用这个,因为zImage是已经被bzip2压缩过的自解压内核)             gzip用gzip的压缩方式             bzip2用bzip2的压缩方式         -a指定映象在内存中的加载地址,映象下载到内存中时,要按照用mkimage制作映象时,这个参数所指定的地址值来下载         -e指定映象运行的入口点地址,这个地址就是-a参数指定的值加上0x40(因为前面有个mkimage添加的0x40个字节的头)         -n指定映象名         -d指定制作映象的源文件
5.2、使用范例:         1、通过SD卡引导内核:
        首先SD卡中必须有FAT32文件系统,并在里面存放了处理过的内核映像文件。
        [u-boot@apple_guet]#mmc init                         [u-boot@apple_guet]# fatload mmc 1 30008000 zImage.img         [u-boot@apple_guet]#bootm 30008000
        2、通过TFTP服务引导内核         [u-boot@apple_guet]# tftpboot 0x30008000 192.168.1.122:zImage.img         [u-boot@apple_guet]# bootm 30008000
        3、通过NFS服务引导内核         [u-boot@apple_guet]# nfs 30008000 192.168.1.100:/home/tekkaman/development/share/zImage.img         [u-boot@apple_guet]# bootm 30008000
        4、通过Nand Flash引导内核:         首先要将处理过的内核映像文件烧入Nand Flash的一定位置(由内核分区表决定)。以后每次启动时用Nand Flash的读取命令先将这个内核映像文件读到内存的一定位置(由制作内核映像时的-a参数决定),再使用bootm命令引导内核。         内核映像文件的烧入:
        [u-boot@apple_guet]# nfs 30008000 192.168.1.100:/home/tekkaman/development/share/zImage.img         Bytes transferred = 2277540 (22c0a4 hex)         [u-boot@apple_guet]# nand erase 0x80000 0x300000         [u-boot@apple_guet]# nand write 30008000 0x80000 300000
        内核引导:         [u-boot@apple_guet]# nand read 30008000 0x80000 300000         [u-boot@apple_guet]# bootm 30008000