U-Boot系统基础入门

项目2:实现双系统启动 - 通过U-Boot选择加载Linux或RTOS

项目概述

本项目将指导您如何配置U-Boot,使其能够在启动时提供选择菜单,让用户决定加载Linux操作系统或实时操作系统(RTOS)。这种双系统启动配置在嵌入式开发中非常有用,特别是当您需要在同一硬件平台上测试不同操作系统时。

应用场景: 这种配置常用于开发板调试、产品原型验证、多系统比较测试等场景。例如,您可能需要在Linux上开发应用程序,同时在RTOS上测试实时性能。

预备知识

在开始本项目前,您需要了解以下内容:

项目实现步骤

  1. 准备系统镜像

    确保您已经准备好以下文件:

    • Linux内核镜像 (如zImage或uImage)
    • Linux设备树文件 (.dtb)
    • Linux根文件系统 (如initramfs或存储在分区中的文件系统)
    • RTOS镜像 (根据您选择的RTOS而定)
    注意: 确保RTOS镜像格式与您的硬件平台兼容。不同RTOS可能有不同的加载要求。
  2. 配置U-Boot环境变量

    我们需要设置不同的环境变量来加载不同的系统:

    # Linux启动命令
    setenv bootcmd_linux 'fatload mmc 0:1 ${loadaddr} zImage; fatload mmc 0:1 ${fdt_addr} board.dtb; bootz ${loadaddr} - ${fdt_addr}'
    
    # RTOS启动命令 (以FreeRTOS为例)
    setenv bootcmd_rtos 'fatload mmc 0:1 ${loadaddr} rtos.bin; go ${loadaddr}'
    
    # 默认启动Linux
    setenv bootcmd 'run bootcmd_linux'
                    
  3. 创建启动菜单

    在U-Boot中添加一个交互式菜单,让用户可以选择启动的系统:

    # 菜单定义
    setenv menu_title 'Select Operating System'
    setenv menu_1 'Boot Linux'
    setenv menu_2 'Boot RTOS'
    setenv menu_timeout '5'
    
    # 菜单命令
    setenv menu_cmd_1 'run bootcmd_linux'
    setenv menu_cmd_2 'run bootcmd_rtos'
    
    # 菜单脚本
    setenv bootmenu "
        setenv bootdelay 3
        setenv menu_timeout ${menu_timeout}
        while true; do
            echo
            echo '==========================================='
            echo '         ${menu_title}'
            echo '==========================================='
            echo '1: ${menu_1}'
            echo '2: ${menu_2}'
            echo
            echo 'Press 1 or 2 to select, or wait ${menu_timeout}s for default'
            echo '==========================================='
            
            setenv bootchoice
            for i in 0 1 2 3 4 5 6 7 8 9; do
                if test -n \"${bootchoice}\"; then
                    break
                fi
                if test ${i} -lt ${menu_timeout}; then
                    setenv oldtime ${time}
                    setenv time
                    if test \"${oldtime}\" != \"${time}\"; then
                        setenv time ${oldtime}
                        setexpr i ${i} + 1
                    fi
                fi
                
                if test ${i} -eq ${menu_timeout}; then
                    setenv bootchoice 1
                    break
                fi
                
                if test -n \"${getc}\"; then
                    setenv c ${getc}
                else
                    if mmc dev 0; then
                        mw.b 0x80000000 0x1 1
                        mmc read 0x80000000 0x0 0x1
                        setenv c *0x80000000
                    fi
                fi
                
                if test \"${c}\" = \"1\"; then
                    setenv bootchoice 1
                elif test \"${c}\" = \"2\"; then
                    setenv bootchoice 2
                fi
            done
            
            if test \"${bootchoice}\" = \"1\"; then
                echo 'Selected: ${menu_1}'
                run menu_cmd_1
            elif test \"${bootchoice}\" = \"2\"; then
                echo 'Selected: ${menu_2}'
                run menu_cmd_2
            else
                echo 'Timeout, booting default (${menu_1})'
                run menu_cmd_1
            fi
        done
    "
    
    # 设置默认启动命令为菜单
    setenv bootcmd 'run bootmenu'
    saveenv
                    
    说明: 这个菜单脚本会在启动时显示一个5秒倒计时的选择菜单。如果用户不选择,将默认启动Linux系统。
  4. 测试双系统启动

    保存环境变量并重启开发板进行测试:

    # 保存环境变量
    saveenv
    
    # 重启开发板
    reset
                    

    您应该看到类似以下的启动菜单:

    ===========================================
             Select Operating System
    ===========================================
    1: Boot Linux
    2: Boot RTOS
    
    Press 1 or 2 to select, or wait 5s for default
    ===========================================
                    

高级配置选项

1. 使用U-Boot的菜单系统

如果您使用的是较新版本的U-Boot,可以利用内置的菜单系统:

# 创建menu.cfg文件
menu title Select Operating System
menu timeout 20
menu margin 1

menu label Boot Linux
    linux /boot/zImage
    fdt /boot/board.dtb
    append console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait

menu label Boot RTOS
    kernel /boot/rtos.bin
    bootm
        

然后将此文件编译为U-Boot可识别的格式:

mkimage -T script -C none -n 'Boot Menu' -d menu.cfg boot.scr
        

最后在U-Boot中加载这个菜单:

setenv bootcmd 'fatload mmc 0:1 ${loadaddr} boot.scr; source ${loadaddr}'
saveenv
        

2. 使用GPIO选择启动系统

您可以通过读取GPIO状态来自动选择启动的系统:

# 检查GPIO状态
gpio input 23
if test ${?} -eq 0; then
    echo "Booting Linux (GPIO23 LOW)"
    run bootcmd_linux
else
    echo "Booting RTOS (GPIO23 HIGH)"
    run bootcmd_rtos
fi
        

3. 使用EEPROM或Flash存储选择

将启动选择存储在非易失性存储器中:

# 读取EEPROM中的启动选择
i2c dev 0
i2c read 0x50 0x00 0x1 0x80000000
setenv boot_choice *0x80000000

if test "${boot_choice}" = "1"; then
    run bootcmd_linux
elif test "${boot_choice}" = "2"; then
    run bootcmd_rtos
else
    # 默认启动
    run bootcmd_linux
fi
        

常见问题与解决方案

问题 可能原因 解决方案
菜单不显示 U-Boot版本不支持getc命令 使用更简单的菜单实现或升级U-Boot
RTOS无法启动 镜像加载地址不正确 检查RTOS的入口地址和加载地址是否匹配
Linux启动失败 设备树或内核参数错误 验证设备树兼容性和内核命令行参数
环境变量不保存 存储设备写保护或损坏 检查环境存储区域并尝试重新保存

项目扩展

1. 添加更多系统选项

您可以扩展此项目以支持更多系统选项,如:

2. 实现网络启动

添加通过网络(TFTP)加载系统的选项:

setenv bootcmd_net 'tftp ${loadaddr} zImage; tftp ${fdt_addr} board.dtb; bootz ${loadaddr} - ${fdt_addr}'
        

3. 添加安全启动验证

在加载镜像前验证其完整性和真实性:

setenv bootcmd_linux '
    fatload mmc 0:1 ${loadaddr} zImage;
    fatload mmc 0:1 ${fdt_addr} board.dtb;
    fatload mmc 0:1 ${sig_addr} zImage.sig;
    if check_sig ${loadaddr} ${filesize} ${sig_addr}; then
        bootz ${loadaddr} - ${fdt_addr};
    else
        echo "Invalid signature!";
    fi
'
        
项目完成! 您现在应该能够在U-Boot中选择启动Linux或RTOS了。这个功能为您的嵌入式系统开发提供了极大的灵活性。