• BeagleBone Black 从零到一 (2 MLO、U-Boot) 转


    文章原址:jexbat.com/categories/BeagleBone/

    什么是 U-Boot

    熟悉嵌入式开发的应该都听过它,U-boot 就是启动系统前的一段引导程序,虽然是引导程序,但是功能非常强大。

    这一篇主要讲解如何从无到有运行 U-Boot,关于 U-Boot 引导 Linux 的部分放在另外一篇文章讲解。

    U-Boot 之前的版本以版本号命名如:0.1.0, 0.2.0 这几年改为了以时间和日期命名:U-Boot 2016.03。

    使用 git 获得 U-Boot 的源码:

    1
    
    git clone git://git.denx.de/u-boot.git
    

    目前我使用的是 2016.02 的版本。

    MLO 及其启动过程

    上一篇文章,我们了解了 BeagleBone 有个 SPL 过程,就在这个时候读取 MLO 文件,MLO 文件其实是个精简版的 U-Boot,也是由 U-Boot 生成,但是功能有限,只初始化了部分资源如 DDR,然后启动 U-Boot。

    MLO 文件是如何编译出来的

    分析 MLO 的编译过程之前需要知道编译原理和 Makefile 等相关知识。
    我们先找找 Makefile 看看能不能找到什么。建议使用 Sublime 编辑器。用全局查找功能查找 MLO 关键字。

    找到 u-boot/scripts/Makefile.spl 文件 117行

    u-boot/scripts/Makefile.spl

    1
    2
    
    MLO MLO.byteswap: $(obj)/u-boot-spl.bin FORCE
    	$(call if_changed,mkimage)
    

    可以看到 MLO 文件是由 u-boot-spl.bin 文件通过 mkimage 命令生成的。
    再查到 u-boot/Makefile 文件 1310 行

    u-boot/Makefile

    1
    2
    3
    4
    
    spl/u-boot-spl.bin: spl/u-boot-spl
    	@:
    spl/u-boot-spl: tools prepare $(if $(CONFIG_OF_SEPARATE),dts/dt.dtb)
    	$(Q)$(MAKE) obj=spl -f $(srctree)/scripts/Makefile.spl all
    

    u-boot-spl.bin 文件是还是由 u-boot/scripts/Makefile.spl 文件生成。
    文件 u-boot/scripts/Makefile.spl 168 行 定义了 u-boot-spl.bin 的生成:

    u-boot/scripts/Makefile.spl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    ifeq ($(CONFIG_SPL_OF_CONTROL),y)
    $(obj)/$(SPL_BIN)-dtb.bin: $(obj)/$(SPL_BIN)-nodtb.bin $(obj)/$(SPL_BIN)-pad.bin 
    		$(obj)/$(SPL_BIN).dtb FORCE
    	$(call if_changed,cat)
    
    $(obj)/$(SPL_BIN).bin: $(obj)/$(SPL_BIN)-dtb.bin FORCE
    	$(call if_changed,copy)
    else
    $(obj)/$(SPL_BIN).bin: $(obj)/$(SPL_BIN)-nodtb.bin FORCE
    	$(call if_changed,copy)
    endif
    

    因为 SPL_BIN 在 第32行 定义为 u-boot-spl:

    u-boot/scripts/Makefile.spl

    1
    2
    3
    4
    5
    
    ifeq ($(CONFIG_TPL_BUILD),y)
    SPL_BIN := u-boot-tpl
    else
    SPL_BIN := u-boot-spl
    endif
    

    由 168 行 上面的定义可以知道 u-boot-spl.bin 和 u-boot-spl-nodtb.bin 有关系。

    接着查找到第223行:

    u-boot/scripts/Makefile.spl

    1
    2
    
    $(obj)/$(SPL_BIN)-nodtb.bin: $(obj)/$(SPL_BIN) FORCE
    	$(call if_changed,objcopy)
    

    u-boot-spl-nodtb.bin 是通过 objcopy 命令由 u-boot-spl 生成。

    再看第246行:

    u-boot/scripts/Makefile.spl

    1
    2
    
    $(obj)/$(SPL_BIN): $(u-boot-spl-init) $(u-boot-spl-main) $(obj)/u-boot-spl.lds FORCE
    	$(call if_changed,u-boot-spl)
    

    所以u-boot-spl 是由 u-boot-spl.lds 链接文件生成的 ,但是目录下面有几个u-boot-spl.lds文件,到底是哪个 lds 文件呢,上面是 $(obj)/u-boot-spl.lds, obj 在 1310 行 编译 u-boot-spl.bin 的时候赋值为 obj=spl,所以我们需要看 u-boot/spl/u-boot-spl.lds 这个文件,但是如果你之前没有编译过这个文件是没有的。这个文件是如何生成的呢?我们稍后再看,先看 lds 文件的内容:

    u-boot/spl/u-boot-spl.lds

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    MEMORY { .sram : ORIGIN = 0x402F0400, LENGTH = (0x4030B800 - 0x402F0400) }
    MEMORY { .sdram : ORIGIN = 0x80a00000, LENGTH = 0x80000 }
    OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
    OUTPUT_ARCH(arm)
    ENTRY(_start)
    SECTIONS
    {
     .text :
     {
      __start = .;
      *(.vectors)
      arch/arm/cpu/armv7/start.o (.text)
      *(.text*)
     } >.sram
     . = ALIGN(4);
     .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram
     . = ALIGN(4);
     .data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram
     .u_boot_list : {
      KEEP(*(SORT(.u_boot_list*)));
     } >.sram
     . = ALIGN(4);
     __image_copy_end = .;
     .end :
     {
      *(.__end)
     } >.sram
     .bss :
     {
      . = ALIGN(4);
      __bss_start = .;
      *(.bss*)
      . = ALIGN(4);
      __bss_end = .;
     } >.sdram
    }
    

    链接文件里面说明了内存布局,arch/arm/cpu/armv7/start.o 代码段都放在 SRAM 中,所以 arch/arm/cpu/armv7/start.S 就是我们要找的东西了。

    lds 链接文件的生成

    u-boot/spl/u-boot-spl.lds 这个文件的生成在 u-boot/scripts/Makefile.spl 有解释:

    u-boot/scripts/Makefile.spl

    1
    2
    
    $(obj)/u-boot-spl.lds: $(LDSCRIPT) FORCE
    	$(call if_changed_dep,cpp_lds)
    

    LDSCRIPT 的定义:

    u-boot/scripts/Makefile.spl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    # Linker Script
    ifdef CONFIG_SPL_LDSCRIPT
    # need to strip off double quotes
    LDSCRIPT := $(addprefix $(srctree)/,$(CONFIG_SPL_LDSCRIPT:"%"=%))
    endif
    
    ifeq ($(wildcard $(LDSCRIPT)),)
    	LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot-spl.lds
    endif
    ifeq ($(wildcard $(LDSCRIPT)),)
    	LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot-spl.lds
    endif
    ifeq ($(wildcard $(LDSCRIPT)),)
    	LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot-spl.lds
    endif
    ifeq ($(wildcard $(LDSCRIPT)),)
    $(error could not find linker script)
    endif
    

    可见 Makefile.spl 文件中先是判断有没有指定的 lds 文件,如果没有指定的,就查找 board 文件夹中目标板目录下面有没有 lds 文件,如果没有就查找相应的 cpu 目录,因为我们目标器件是 am335x,所以发现有 u-boot/arch/arm/cpu/armv7/am33xx/u-boot-spl.lds 再通过 cpp_lds 命令编译成,cpp_lds 是一组命令的集合,具体定义还是在 Makefile.spl 文件中,我们查看 u-boot/arch/arm/cpu/armv7/am33xx/u-boot-spl.lds 也发现 MLO 文件代码是在 start.S 文件中。

    MLO 程序分析

    查看 start.S 分析下 MLO 程序具体的执行流程,MLO 的 makefile 会根据 CONFIG_SPL_BUILD 编译不同的源文件,同样的在源码内也通过 CONFIG_SPL_BUILD 控制不同的代码执行,前面一部分 MLO 文件和 U-Boot 是类似的,进入到 _main 函数中两个程序的功能就开始出现差异了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    
    reset //(arch/arm/cpu/armv7/start.S)
    save_boot_params_ret //(arch/arm/cpu/armv7/start.S)
      |- disable interrupts 
      |- cpu_init_cp15 //(arch/arm/cpu/armv7/start.S)
      |   |- Invalidate L1 I/D
      |   |- disable MMU stuff and caches
      |- cpu_init_crit //(arch/arm/cpu/armv7/start.S)
      |   |- lowlevel_init //(arch/arm/cpu/armv7/lowlevel_init.S)
      |       |- Setup a temporary stack
      |       |- Set up global data 
      |       |- s_init //(arch/arm/cpu/armv7/am33xx/board.c)
      |           |- watchdog_disable
      |           |- set_uart_mux_conf
      |           |- setup_clocks_for_console
      |           |- uart_soft_reset
      |- _main //(arch/arm/lib/crt0.S)
      	  
          |(MLO)如果是 MLO 文件
          |- board_init_f //(arch/arm/cpu/armv7/am33xx/board.c)
          |   |- board_early_init_f //(arch/arm/cpu/armv7/am33xx/board.c)
          |   |   |- prcm_init
          |   |   |- set_mux_conf_regs
          |   |- sdram_init //(board/ti/am335x/board.c) 初始化 DDR
          |- spl_relocate_stack_gd
          |- board_init_r //(common/spl/spl.c)
              |- ...
              |- spl_load_image //根据不同的启动方式加载 u-boot 镜像,
              |- jump_to_image_no_args //进入u-boot代码运行
      	  
      
          |(U-Boot)如果是U-Boot 镜像
          |- board_init_f //(common/board_f.c)
          |   |- ...
          |   |- initcall_run_list(init_sequence_f)   
          |   |- ...   
          |   
          |- relocate_code //(arch/arm/lib/relocate.S) 代码重定位
          |- relocate_vectors //(arch/arm/lib/relocate.S) 向量表重定义
          |- Set up final (full) environment 
          |- board_init_r //(common/board_r.c)
              |- initcall_run_list(init_sequence_r)//初始化各种外设
                  |- main_loop()
    

    当 U-Boot 重定位好代码、向量表之后,运行 board_init_r 函数,此函数会调用 init_sequence_r 列表里面的函数初始化各种外设驱动,最后在 main_loop() 函数中运行,U-Boot 有个 bootdelay 延时启动,如果不手动停止 U-Boot 会自动运行 bootcmd 包含的命令。

    内核引导这部分放在另外一篇文章详细讲解。

    U-Boot 编译

    编译 U-Boot

    编译 U-Boot 前我们需要安装交叉编译器:

    1
    
    # sudo apt-get install gcc-arm-linux-gnueabihf
    

    下载 U-Boot 源码:

    1
    
    # git clone git://git.denx.de/u-boot.git
    

    因为 U-Boot 官方已经支持了 Beaglebone Black 所以配置文件也已经自带了,编译输入如下命令:

    1
    2
    3
    
    # make distclean
    # make am335x_boneblack_defconfig
    # ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make
    

    片刻后会生成 MLO 和 u-boot.img 文件。

    配置 U-Boot 参数

    有两种方式可以配置 U-Boot 的一些参数,分别是 uEnv.txt 和 boot.src 文件。
    U-Boot 启动的时候会在启动分区寻找这两个文件。

    boot.scr: This file is a U-Boot script. It contains instructions for U-Boot. Using these instruction, the kernel is loaded into memory, and (optionally) a ramdisk is loaded. boot.scr can also pass parameters to the kernel. This file is a compiled script, and cannot be edited directly. In some cases, boot.scr loads further instructions and configuration parameters from a text file.

    uEnv.txt: A file with additional boot parameters. This file can be read by boot.scr, or by the boot sequence if there is no script file. uEnv.txt is a regular text file that can be edited. This file should have Unix line ending, so a compatible program must be used when editing this file.

    U-Boot 启动的时候如果不打断会调用 bootcmd 包含的命令来执行,通常 bootcmd 会调用 bootscript 脚本也就是boot.scr 里面的命令进行执行, boot.scr 通常也会先读取 uEnv.txt 确定额外参数,因为 boot.src 文件必须通过 boot.cmd 文件编译而来, uEnv.txt 则是可以任意编辑,这样可配置性就大大提高了。如果没有 boot.src 文件,U-Boot 有默认配置的 bootcmd 命令。

    在 Beagelbone Black 中我们不需要额外的 boot.scr 文件,用默认的命令即可,默认的命令为:

    1
    2
    3
    
    #define CONFIG_BOOTCOMMAND 
    	"run findfdt; " 
    	"run distro_bootcmd"
    

    run distro_bootcmd 最终会调用 run mmcboot 命令加载 uEnv.txt 文件,并且会运行 uEnv.txt 文件里面 uenvcmd 指代的命令。

    uEnv.txt 从网络启动例子:

    1
    2
    3
    4
    5
    6
    7
    
    console=ttyO0,115200n8
    ipaddr=192.168.23.2
    serverip=192.168.23.1
    rootpath=/exports/rootfs
    netargs=setenv bootargs console=${console} ${optargs} root=/dev/nfs nfsroot=${serverip}:${rootpath},${nfsopts} rw ip=${ipaddr}:${serverip}:192.168.23.1:255.255.255.0:beaglebone:eth0:none:192.168.23.1
    netboot=echo Booting from network ...; tftp ${loadaddr} ${bootfile}; tftp ${fdtaddr} ${fdtfile}; run netargs; bootz ${loadaddr} - ${fdtaddr}
    uenvcmd=run netboot
    

    制作 U-Boot 的 SD 启动卡

    制作 SD 启动卡之前首先需要为 SD 卡分区, ROM Code 启动的时候如果是从 MMC 设备加载启动代码,ROM Code 会从第一个活动分区寻找名为 “MLO” 的文件,并且此分区必须为 FAT文件系统。所以制作 U-Boot 的启动卡只需要一个带有 MLO 和 U-Boot 镜像的 FAT 格式的 SD 卡,如果需要启动 Linux 内核还需要别的分区,我们以后再讲。

    有两种方式可以制作包含 U-Boot 的可启动的 SD 卡,一种是用 RAW Mode 的方式,还有一种是用 FTA 的方式。

    RAW Mode 和烧写方式在这篇文章里面有讲:解析 BeagleBone Black 官方镜像

    FTA 模式下只要建立一个 FTA 分区再把 MLO 和 uboot.img 文件拷贝进去即可。

    我是使用的 USB 读卡器,插入后 Linux /dev/ 目录会显示 /dev/sd* 设备,我这里多出两个设备分别显示 /dev/sdb 和 /dev/sdb1 ,其中 /dev/sdb 表示一整个物理磁盘, /dev/sdb1 表示的是具体的分区。

    使用命令 sudo fdisk /dev/sdb 管理磁盘:

    a : toggle a bootable flag(设置或取消启动表示)

    b : edit bsd disklabel(编辑 bsd disklabel)

    c : toggle the dos compatibility flag

    d : delete a partition (删除一个分区)

    l : list known partition types (列出已知的分区类型)

    m : print this menu (打印次列表)

    n : add a new partition (增加一个新分区)

    o : create a new empty DOS partition table (建立一个新的空 DOS 分区表)

    p : print the partition table (打印分区表)

    q : quit without saving changes (不保存退出)

    s : create a new empty Sun disklabel

    t : change a partition’s system id

    u : change display/entry units

    v : verify the partition table (验证分区表)

    w : write table to disk and exit (把分区表写入磁盘)

    x : extra functionality (experts only) (额外的功能)

    新建启动分区

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    
    Command (m for help): p
    
    Disk /dev/sdb: 7746 MB, 7746879488 bytes
    24 heads, 20 sectors/track, 31522 cylinders, total 15130624 sectors
    Units = sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disk identifier: 0x00000000
    
       Device Boot      Start         End      Blocks   Id  System
    /dev/sdb1   *        2048    15130623     7564288    c  W95 FAT32 (LBA)
    
    Command (m for help): m
    Command action
       a   toggle a bootable flag
       b   edit bsd disklabel
       c   toggle the dos compatibility flag
       d   delete a partition
       l   list known partition types
       m   print this menu
       n   add a new partition
       o   create a new empty DOS partition table
       p   print the partition table
       q   quit without saving changes
       s   create a new empty Sun disklabel
       t   change a partition's system id
       u   change display/entry units
       v   verify the partition table
       w   write table to disk and exit
       x   extra functionality (experts only)
    
    Command (m for help): d
    Selected partition 1
    
    Command (m for help): p
    
    Disk /dev/sdb: 7746 MB, 7746879488 bytes
    24 heads, 20 sectors/track, 31522 cylinders, total 15130624 sectors
    Units = sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disk identifier: 0x00000000
    
       Device Boot      Start         End      Blocks   Id  System
    
    Command (m for help): n
    Partition type:
       p   primary (0 primary, 0 extended, 4 free)
       e   extended
    Select (default p): p
    Partition number (1-4, default 1): 
    Using default value 1
    First sector (2048-15130623, default 2048): 
    Using default value 2048
    Last sector, +sectors or +size{K,M,G} (2048-15130623, default 15130623): 
    Using default value 15130623
    
    Command (m for help): p
    
    Disk /dev/sdb: 7746 MB, 7746879488 bytes
    24 heads, 20 sectors/track, 31522 cylinders, total 15130624 sectors
    Units = sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disk identifier: 0x00000000
    
       Device Boot      Start         End      Blocks   Id  System
    /dev/sdb1            2048    15130623     7564288   83  Linux
    
    Command (m for help): t
    Selected partition 1
    Hex code (type L to list codes): c
    Changed system type of partition 1 to c (W95 FAT32 (LBA))
    
    Command (m for help): a
    Partition number (1-4): 1
    
    Command (m for help): p
    
    Disk /dev/sdb: 7746 MB, 7746879488 bytes
    24 heads, 20 sectors/track, 31522 cylinders, total 15130624 sectors
    Units = sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disk identifier: 0x00000000
    
       Device Boot      Start         End      Blocks   Id  System
    /dev/sdb1   *        2048    15130623     7564288    c  W95 FAT32 (LBA)
    
    Command (m for help): w
    The partition table has been altered!
    
    Calling ioctl() to re-read partition table.
    
    WARNING: If you have created or modified any DOS 6.x
    partitions, please see the fdisk manual page for additional
    information.
    Syncing disks.
    

    建立好新的分区之后需要命名并格式化:

    1
    
    # sudo mkfs.vfat -F 32 -n boot /dev/sdb1
    

    格式化之后挂载磁盘并把 MLO 文件和 u-boot.img 文件拷贝进去:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    # sudo mount /dev/sdb1 /media/jg/boot 
    # sudo cp MLO /media/jg/boot/MLO
    # ls /media/jg/boot             
    MLO
    # sudo cp u-boot.img /media/jg/boot/u-boot.img
    # ls /media/jg/boot 
    MLO  u-boot.img
    # sync 
    # sudo umount /media/jg/boot
    

    接着把 SD 卡插入 Beaglebone Black 并且按着 S2 按钮上电,从串口打印出的信息我们可以看到 U-Boot 已经可以正常启动了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    
    U-Boot SPL 2016.03-rc2-00084-g595af9d (Feb 29 2016 - 22:21:20)
    Trying to boot from MMC
    Card doesn't support part_switch
    MMC partition switch failed
    *** Warning - MMC partition switch failed, using default environment
    
    reading u-boot.img
    reading u-boot.img
    
    U-Boot 2016.03-rc2-00084-g595af9d (Feb 29 2016 - 22:21:20 +0800)
    
           Watchdog enabled
    I2C:   ready
    DRAM:  512 MiB
    MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
    *** Warning - bad CRC, using default environment
    
    Net:   <ethaddr> not set. Validating first E-fuse MAC
    cpsw, usb_ether
    Press SPACE to abort autoboot in 2 seconds
    switch to partitions #0, OK
    mmc0 is current device
    Scanning mmc 0:1...
    switch to partitions #0, OK
    mmc0 is current device
    SD/MMC found on device 0
    reading boot.scr
    ** Unable to read file boot.scr **
    reading uEnv.txt
    ** Unable to read file uEnv.txt **
    ** File not found /boot/zImage **
    switch to partitions #0, OK
    mmc1(part 0) is current device
    Scanning mmc 1:1...
    switch to partitions #0, OK
    mmc1(part 0) is current device
    SD/MMC found on device 1
    reading boot.scr
    ** Unable to read file boot.scr **
    reading uEnv.txt
    ** Unable to read file uEnv.txt **
    ** File not found /boot/zImage **
    ## Error: "bootcmd_nand0" not defined
    cpsw Waiting for PHY auto negotiation to complete......... TIMEOUT !
    BOOTP broadcast 1
    BOOTP broadcast 2
    BOOTP broadcast 3
    BOOTP broadcast 4
    

    启动之后,前面一段打印信息是 MLO 程序打印出来的,读取 U-Boot 之后开始运行完整的 U-Boot,之后程序扫描各个设备读取 boot.scr 和 uEnv.txt 文件,接着再读取是否有 Linux 内核可以运行。

    参考资料

  • 相关阅读:
    mysqldump 命令的使用
    linux find 命令查找文件和文件夹
    linux定时任务
    find: `./folder': No such file or directory 错误处理
    利用mysqldump 与 nginx定时器 定时备份mysql库
    vue项目在nginx中不能刷新问题
    CodeReview规范
    VUE npm run build的项目出现跨域请求的问题npm run dev没有这个问题
    composer.json和composer.lock到底是什么以及区别?
    K近邻算法小结
  • 原文地址:https://www.cnblogs.com/lh03061238/p/10179486.html
Copyright © 2020-2023  润新知