uboot启动流程和架构

概述:
本文将从两个方面来阐述uboot:
1、启动流程
2、架构

一、uboot流程图:
这里写图片描述

从上图中看到红色1,2,3,4,5,7,8,9的标号,下面分别说明每个过程:
1、启动入口:

  • (1)确定链接脚本文件:

    在根目录下Makefile下LDSCRIPT宏值,就是指定链接脚本(如:arch/arm/cpu/u-boot.lsds)路径的

  • (2)从脚本文件找入口:
    在链接脚本中可以看到ENTRY()指定的入口,如:ENTRY(_start),_start就是入口

  • (3)链接脚本简要分析:

    ...
    ...
    ...
    ENTRY(_start)
    SECTIONS
    {
        .=0x00000000;
        .=ALIGN(4);
        .text:
        {
            *(.__image_copy_start)
            *(.vectors)
            CPUDIR/start.o (.text*)
            *(.text*)
        }
    }
    ...
    ...
    ...

像*(.__image_copy_start)这种,指定将标记为这个标记的代码段编译链接放入这个地方,上面的事例表示0x00000000地址(在编译完,生成的System.map文件中可以看到),在汇编中可以用如下方式将一段代码做标记(直到遇到下一个section为止,要是没有就到文件结束):

.section ".__image_copy_start", "ax"

在C文件中可以用如下方式,把一个变量或者函数标记到指定段:

char __image_copy_start[0] __attribute__((section(".__image_copy_start")));

在本人手中的例子,被编译的文件中没有__image_copy_start这样标记的代码段,只能找.vectors,在arch/arm/lib/vectors.S开头地方,找到被.vectors标记的代码段,并且_start也是位于有效代码第一行,最终启动入口位置确定了(假设_start没有位于vectors.S有效代码第一行,更或者,在start.o中,而ENTRY()又指定ENTRY(_start),会是什么结果?)。

2、初始化:
(1)启动过程中memory变化:
这里写图片描述

如上图:
a、上电,首先在CPU片上的bootrom空间里运行(CPU出厂就固化的code),检查外部flash(nand , emmc , nor , sdmmc等,看CPU支持情况):
——检测到,就会读取flash中前2K code到片上Sram,然后由这2K code负责将flash中全部code加载到外部RAM(EXT RAM),最终跳转到外部ram运行,然后继续初始化环境,加载系统等。
——检测不到外部flash,或者在外部flash检测不到有效bank,则会初始化USB(视具体板子),启动下载,等待img数据。
(验证:本人,验证结果是,在uboot前2k,里面取出的PC值,始终是外部RAM所在的地址空间,但是各种资料都描述有片上2K RAM)

(2)初始化过程,分board_init_f前,board_init_f,relocate, board_init_r这四个过程描述:

  • a、board_init_f前,做的都是关于CPU体系结构相关的初始化,对于做电子产品开发的,基本都不会去修改,就简单描述,首先,8个异常入口:
b   reset   
b   pc, _undefined_instuction
b   pc, _software_interrupt
b   pc, _prefetch_abort
b   pc, _data_abort
b   pc, _not_used
b   pc, _irq
b   pc, _fiq

从上面字面意思大概也知道,重点关注下reset(定位reset时,结合链接脚本文件很容易找到),reset首先做的是将FIQ,IRQ关闭,将CPU(arm)模式设置成SVC32(保护模式),然后配置CP15协处理器,CP15作用:

  • 第一,配置异常入口,以前都认为CPU发生reset异常后,都是PC为0x0地址,
    然而,通过配置CP15后,可以修改:
ldr     r0, =_start
mcr     p15,0,r0,c12,c0,0

如上,之后,再出现reset异常后,PC就是跳到_start处,不再是0x0地址

  • 第二,cpu_init_cp15在这个函数里面,配置(CP15配置)cache,MMU,TLBS,I-cache,L2 cache。

CP15完成配置,cpu_init_crit会调用lowlevel_init,这个一般定义板级初始化,需要很早初始化的,主要初始化pll,mux,memory(ddr)。

cpu_init_crit返回后,进入_main,这个_main入口有点难找,搜索会出来一大堆,我的是arm体系的,在arch/arm/lib/crt0.S里面找到的,从_main开始到board_init_f都是对ram空间的划分,有个重点宏CONFIG_SYS_INIT_SP_ADDR需要根据具体硬件RAM空间划分配置,例如,我的板子2G RAM空间映射到0x0地址开始2G地址空间,厂商将128M空间到0x0地址作为系统用,因此CONFIG_SYS_INIT_SP_ADD=0x0+SZ128M。

在_main第一条指令:

ldr     sp, =(CONFIG_SYS_INIT_SP_ADD)

然后,对sp按照8字节对齐处理后,将SP-sizeof(gd_t):

sub     sp, sp, #GD_SIZE

GD_SIZE定义在include/generated/generic-asm-offsets.h里面,从这里面看:

#define     GD_SIZE     232

gd_t结构体size与结构体系有关,这里写死,估计是厂商根据自己板子cpu体系算过。

这里sp是按照向下生长的,这条指令后sp,再通过:

mov     r9, sp

这样r9就指向gd全局变量起始地址了(空间就是从gd到CONFIG_SYS_INI_SP_ADD),至于为何这么说,且看下面:
gd_t(通用)定义在include/asm-generic/global_data.h,然后在gd_t结构体重嵌入了与体系有关的struct arch_global_data,我的是arm的,所以在arch/arm/include/asm/global_data.h中定义了arch_global_data,在这个头文件中还发现了:

#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm("r9")

结合上面r9的值,可以确定gd=CONFIG_SYS_INIT_SP_ADD-sizeof(gd_t)。

然后_main完成r9赋值后,要对gd这段空间清零处理,清零完成后就进入board_init_f函数。

  • b、board_init_f中,主要是做了gd成员值的初始化,这些成员值,最后会为copy code提供地址和size。

    最终划分的空间大致情况(根据firefly-rk3288,2G的ram空间,图子格子大小不代表ram空间大小),整理如下图:
    这里写图片描述

更详细的分析图(relocaddr表示的是gd->relocaddr,基于firefly-rk3288开发板分析的):
这里写图片描述
图看不清楚的可以百度盘下载看。

各分区说明:
tlb: 一种cach,百度吧,我没很明白
globa buff:firefly-rk3288用作flash管理buff(nand/emmc坏块等)
boot /fastboot buff
fastboot log buff:
uboot code data&bss:relocate后uboot code会copy到此处运行
malloc: malloc()分配的内存空间
bd_t: DDR信息,即几个bank,每个bank多大(bank信息的获取,就要看具体的板子 了,例如,我的firefly-rk3288,做得够隐秘了,代码(函数dram_init_banksize())分析看,将信息放在了0x0+32M的地方,然后用这个函数解析存入gd->bd->rk_dram里面,bd_t是一个与CPU体系结构相关的,也是可以根据在基础上增加成员)
gd_t:new gd所在空间,原来在r9指定的gd空间,会copy到此处后,在board_init_f调用完成后由汇编把r9修改指向这块空间

在board_init_f中也有对serial,console初始化:
serial_init()在这里的初始化,做两件事,
首先,初始化gd->flags|=GD_FLG_SERIAL_READY表示serial以就绪
然后,调用具体serial初始化函数,将所用的serial端口和波特率等设置到就绪状态。
console_init_f()之中了一件事情,就是将gd->have_console=1,表示有控制台。
board_init_f最后调用setup_reloc()两个很重要的动作:

gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
memcpy(gd->newd_gd,(char *)gd,sizeof(gd_t))

重要的宏CONFIG_SYS_TEXT_BASE,可以说虚拟地址空间,编译的时候,uboot会根据这值来链接。实际真实的硬件RAM起始地址和CONFIG_SYS_TEXT_BASE差,就是虚拟地址和物理地址偏移量。在这里,relocate后uboot在实际的物理地址gd->relocaddr(起始地址),CONFIG_SYS_TEXT_BASE是虚拟地址的起始,差值自然就是relocate后虚拟地址和物理地址的偏移量。

然后将重要的gd数据从(CONFIG_SYS_INIT_SP_ADD-SZ128M,CONFIG_SYS_INIT_SP_ADD)区间,copy到新的new gd区间

c、relocate:
这部分就是将,当前uboot运行空间的code,copy到上面“uboot code data&bss”空间里,即gd->relocaddr地址处,接着上面的运行。具体可以看这个博客地址:uboot的relocation原理详细分析

d、board_init_r做的事情主要概括如下:

  • 标记uboot启动已经到了board_init_r阶段
  • 设置machine id & 启动参数存放位置
  • 最后硬件的初始化
  • Flash接口初始化
  • 环境变量最后的初始化
  • console最后初始化
  • 检查key是否进入下载模式
  • 进入main loop或者直接启动系统

    检查key是否进入下载模式、进入main loop或者直接启动系统放到流程图中3、4、5、6、7、8、9降

标记uboot启动已经到了board_init_r阶段
看下面代码很好理解:

static int initr_reloc(void)
{
    gd->flags |= GD_FLG_RELOC;
    bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R,"board_init_r");
}

设置machine id & 启动参数存放位置
如下就是对gd两个重要成员的初始化:

int board_init(void)
{
    gd->bd->bi_arch_number = MACH_TYPE_XX;
    gd->bd->bi_boot_params = PHYS_SDRAM;
}

gd->bd->bi_arch_number作为machine id通过R1传递到linux kernel,也就是linux kernel会将这个值与宏MACHINE_START定义的nr比较,要相同,才能通过linux kernel的检查,否则会导致系统不能启动,这是旧版linux方式,新版的用宏DT_MACHINE_START,从宏定义也可以看出nr已无作用,这两个宏都定义在linux根目录下arch/arm/include/asm/mach/arch.h:

#define MACHINE_START(_type,_name)          \
static const struct machine_desc __mach_desc_##_type    \
 __used                         \
 __attribute__((__section__(".arch.info.init"))) = {    \
    .nr     = MACH_TYPE_##_type,        \
    .name       = _name,

#define MACHINE_END             \
};

#define DT_MACHINE_START(_name, _namestr)       \
static const struct machine_desc __mach_desc_##_name    \
 __used                         \
 __attribute__((__section__(".arch.info.init"))) = {    \
    .nr     = ~0,              \
    .name       = _namestr,

现在来看下linux是怎么来兼容新旧两种方式的,在linux根目录下init/main.c中start_kernel()调用setup_arch():

void __init setup_arch(char **cmdline_p)
{
    const struct machine_desc *mdesc;

    setup_processor();
    mdesc = setup_machine_fdt(__atags_pointer);
    if (!mdesc)
        mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
    machine_desc = mdesc;
    .......
    .......
    .......
}

setup_machine_fdt()就是新版,参数__atags_pointer就是r2传过来的地址(下面会讲到uboot对r2的处理),匹配的方式就是r2地址开始的内存中,前4byte必须是0xd00dfeed:

#define FDT_MAGIC   0xd00dfeed

头部匹配通过后,开始匹配compatible属性,r2是dtb存放的内存起始地址,在dtb中有这么样值:

compatible = "rockchip,rk3288"

在r2中搜索这个属性,然后将属性值与linux中DT_MACHINE_START定义的dt_compat值对比,相同才最终匹配成功。DT_MACHINE_START定义变量都存储在.arch.info.init段中,搜索这段中的dt_compat就可以了。匹配成功后,会搜索dtb中如下节点:

chosen{
    bootargs = "console=ttyS2 init=/init initrd=0x62000000,0x00800000";
};

搜索到后,将bootargs的值copy到boot_command_line中,到此linux新版获取启动参数方式成功。

当linux新版获取启动参数方式失败,调用setup_machine_tags(__atags_pointer, __machine_arch_type)用旧版获取启动参数的方式,形参__atags_pointer当然就是r2的值,__machine_arch_type就是machine id,在哪里获取?

在linux根目录arch/arm/boot/compressed/misc.c中:

decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p,
        unsigned long free_mem_ptr_end_p,
        int arch_id)
{
    ...
    ...
    ...
    __machine_arch_type = arch_id;
    ...
    ...
    ...
}

参数arch_id又是哪里传入的:

在linux根目录下arch/arm/boot/compressed/head.S中:

/*
 * The C runtime environment should now be setup sufficiently.
 * Set up some pointers, and start decompressing.
 *   r4  = kernel execution address
 *   r7  = architecture ID
 *   r8  = atags pointer
 */
        mov r0, r4
        mov r1, sp          @ malloc space above stack
        add r2, sp, #0x10000    @ 64k max
        mov r3, r7
        bl  decompress_kernel
        bl  cache_clean_flush
        bl  cache_off
        mov r1, r7          @ restore architecture number
        mov r2, r8          @ restore atags pointer

根据汇编和C互相调用时,传参数的规定,arch_id就等于r3的值,r3==r7,根据注释r7是architecture ID,至于r7什么时候得到,我就不继续追下去了,有兴趣的可以跟踪下代码。接下来就是用__machine_arch_type与MACHINE_START定义的nr比较,相等就匹配成功,获取linux中struct machine_desc结构体变量(描述cpu相关的):

    /*
     * locate machine in the list of supported machines.
     */
    for_each_machine_desc(p)
        if (machine_nr == p->nr) {
            pr_info("Machine: %s\n", p->name);
            mdesc = p;
            break;
        }

到此新旧两种获取启动参数的方式结束。我另外一篇博客:linux之early_param()和__setup对跟踪启动参数作用或许在理解上有一定帮助

回到uboot:
在arch/arm/lib/bootm.c中函数boot_jump_linux()如下代码,将bi_arch_number传到r1,然后传到linux:

static void boot_jump_linux(bootm_headers_t *images, int flag)
{
    unsigned long machid = gd->bd->bi_arch_number;
    unsigned long r2;
    void (*kernel_entry)(int zero, int arch, uint params);
    kernel_entry = (void (*)(int,int,uint))images->ep;
    if(IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
        r2 = (unsigned long)images->ft_addr;
    else
        r2 = gd->bd->bi_boot_params;
    kernel_entry(0,machid,r2);
}

images->ep就是linux在ram里面起始地址,地址直接赋值给一个函数指针,然后就当一个函数调用。uboot和linux传参,规定函数的第一个参数为0(r0 = 0),第2个参数machine id(r1= machine id),第三个参数为命令行起始地址(r2 = params addr)。
(c语言中具体地址当作函数调用,参数一般都是第一个对应r0,第2个对应r1…依次类推,这也是c调用汇编的规定)

关于MACH_TYPE_XX生成,由linux kernel一个awk脚本arch/arm/tools/gen-mach-types在编译的时候,会根据在同目录下的mach-types(修改这个文件,就可以在mach-types.h下生成形如MACH_TYPE_XX宏)文件生成linux根目录下include/generated/mach-types.h。与uboot根目录下arch/arm/include/asm/mach-types.h很相似,但在uboot下面没找到生成的脚本(个人感觉从linux下面copy过去,哈哈)。

gd->bd->bi_arch_number,gd->bd->bi_boot_params这个启动参数的方式是linux老式传参方式了,但新版本linux都是兼容的,bi_boot_params地址一般都设置在靠近ram起始地址(fireflye-rk3288设置在0x0+0x88000)。

uboot对新版命令行传参方式的支持:
在arch/arm/lib/bootm.c中boot_prep_linux()中有调用到image_setup_linux(bootm_headers_t *images) ,函数参数struct boot_headers_t结构体中成员ft_addr,ft_len就是新版参数的关键。ft_addr是dtb装载到ram中的地址,dtb可以从两个地方resource.img和boot.img装载,

boot.img是android中用到的,在android中有mkbootimg工具,将zImage(Linux),ramdisk,second stage(可选,可以将dtb放到这段)。这样,uboot就可以从boot.img中解析出sencond stage加载到内存,并将地址赋给fd_addr。关于boot.img和工具mkbootimg理解,可以参考这篇博客:通过分析mkbootimg源代码了解boot.img文件结构

dtb是linux根目录下scripts/dts/dtc(dtc工具编译的时候会产生)工具,将dts生成dtb二进制文件。dtc的工具使用如下:
dts编译成二进制文件dtb:
scripts/dtc/dtc -I dtb -O dts ./product1.dtb -o ./my.dts

dtb反编译成dts:
scripts/dtc/dtc -I dts -O dtb ./a.dts -o ./b.dtb

dts中命令行参数放在如下的节点中:

chosen{
    bootargs = "console=ttyS2 init=/init initrd=0x62000000,0x00800000";
};

从这个获取resource.img启动参数,在uboot根目录下common/resource.c,include/resource.h来看:
struct resource_content:这个结构体,是解析时的内存结构
struct resource_ptn_header:这个结构体,是存在于resource.img开始地方,即文件头
struct index_tbl_entry:存在于resource.img每个数据块的头
如下resource.img在nand/emmc结构示意图:
resource.img结构

捣事的优快云,写完整的博客,却不见了,这是我写到一半时保存在自己电脑上的先放这,心好累,以后有时间在看着补全吧,心累啊

<think>好的,我现在需要详细说明U-Boot及其启动流程。首先,我应该先了解U-Boot是什么,它的主要功能和用途。U-Boot,全称是Das U-Boot,是一个开源的引导加载程序,主要用于嵌入式系统。它支持多种架构,如ARM、MIPS、PowerPC等,常用于引导Linux内核。 接下来,我需要理解U-Boot启动流程。用户可能想知道从设备上电到加载内核的整个过程。启动流程通常分为几个阶段,每个阶段的具体任务可能有所不同,但大致可以分为以下几个步骤: 1. **ROM Bootloader**:设备上电后,首先执行的是芯片内部ROM中的引导代码。这部分代码通常是固化在硬件中的,负责初始化最基本的硬件,如时钟、内存控制器,并加载下一阶段的引导程序(如SPL)。 2. **SPL (Secondary Program Loader)**:由于内部ROM空间有限,SPL作为第二阶段引导加载程序,负责进一步初始化硬件(如DRAM),并加载完整的U-Boot到内存中。SPL通常是U-Boot的一部分,经过编译后生成的小型镜像。 3. **U-Boot主程序**:SPL加载完成后,跳转到U-Boot的主程序。U-Boot会进行更全面的硬件初始化,包括串口、网卡、存储设备等,然后读取环境变量,根据配置加载内核镜像、设备树文件(DTB)和根文件系统。 4. **加载内核**:U-Boot将内核镜像、设备树和可能的initramfs加载到内存中的指定地址,然后通过bootm或bootz命令启动内核,传递必要的参数。 在这个过程中,需要考虑到不同硬件平台的差异,比如启动介质(NOR Flash、eMMC、SD卡等)、内存布局、环境变量的存储位置(如EEPROM、Flash中的特定分区)等。 另外,U-Boot还支持交互式命令,用户可以在启动过程中打断自动流程,进入命令行界面,手动执行命令进行调试或配置。环境变量在U-Boot中非常重要,它们决定了启动参数和流程,比如bootcmd定义了自动执行的命令序列。 可能还需要解释U-Boot的编译和配置过程,比如通过make menuconfig配置目标板,选择所需的驱动和功能,然后编译生成u-boot.bin等文件。此外,不同开发板的配置文件位于configs目录下,如mx6ullevk_defconfig对应特定的开发板。 在启动流程中,设备树(Device Tree)的作用也很重要,它描述了硬件配置,使得内核无需硬编码硬件信息。U-Boot负责将设备树二进制文件(DTB)加载到内存,并传递给内核。 还要注意U-Boot的启动阶段可能涉及多个文件,比如SPL、U-Boot proper,以及如何将它们烧写到存储设备的正确位置。例如,在SD卡启动时,SPL可能需要存放在特定的偏移量位置,以便ROM Bootloader能够找到并加载它。 可能存在的疑问点包括:SPL和U-Boot的关系,如何配置环境变量,如何处理不同的启动介质,U-Boot如何与内核交互,以及如何调试启动问题。需要确保这些点在回答中都有所覆盖,并且结构清晰,步骤明确。 最后,需要验证这些信息是否正确,比如确认U-Boot的启动阶段划分是否准确,SPL的作用是否描述正确,以及加载内核的具体命令和流程是否符合实际。如果有不确定的地方,可能需要查阅U-Boot的官方文档或源码进行确认,确保回答的准确性。</think>--- ### **U-Boot详解与启动流程分析** --- #### **1. U-Boot概述** **定义**: U-Boot(Universal Boot Loader)是嵌入式系统领域使用最广泛的开源引导加载程序,支持**ARM、MIPS、PowerPC、RISC-V**等30+处理器架构,兼容**NOR Flash、eMMC、NAND、SD卡**等多种存储介质。 **核心功能**: - 硬件初始化(时钟/DDR/外设) - 多阶段引导加载 - 固件升级(USB DFU/TFTP) - 环境变量管理 - 操作系统映像加载(Linux/FreeRTOS/VxWorks) - 安全启动(Verified Boot) --- #### **2. 典型启动流程** ```mermaid sequenceDiagram participant ROM Code participant SPL participant U-Boot participant Kernel ROM Code->>SPL: 加载SPL到SRAM SPL->>U-Boot: 初始化DRAM,加载U-Boot到内存 U-Boot->>U-Boot: 外设初始化(网卡/USB/存储) U-Boot->>Kernel: 加载内核镜像、设备树、initramfs Kernel->>Kernel: 解压并启动操作系统 ``` --- #### **3. 关键阶段详解** **3.1 ROM Bootloader** - **硬件行为**:芯片上电后自动执行固化在ROM中的代码 - **主要任务**: - 初始化基础时钟(CPU/总线) - 检测启动介质(通过GPIO或OTP配置) - 加载SPL到内部SRAM(典型大小:64-256KB) **示例代码(i.MX6ULL)**: ```c /* arch/arm/mach-imx/imx6ull/lowlevel_init.S */ bl setup_pll // 配置PLL时钟 bl setup_dcdr // DDR控制器初始化 ldr r0, =SPL_LOAD_ADDR bl load_image // 从SD卡加载SPL ``` --- **3.2 SPL阶段** - **二进制文件**:`u-boot-spl.bin` - **内存限制**:通常在SRAM中运行(无DRAM支持) - **核心任务**: 1. 初始化DRAM控制器 2. 设置内存映射表 3. 加载完整U-Boot到DRAM 4. 验证U-Boot镜像签名(安全启动场景) **关键配置选项**: ```makefile # configs/mx6ullevk_defconfig CONFIG_SPL=y CONFIG_SPL_DM=y CONFIG_SPL_LOAD_FIT=y ``` --- **3.3 U-Boot主程序** - **入口函数**:`lib/init/board_init.c`中的`board_init_f()` - **启动阶段划分**: ```c board_init_f() // 初始化全局数据结构(gd_t) ↓ relocate_code() // 重定位到DRAM高端地址 ↓ board_init_r() // 完整外设初始化 ``` **硬件初始化顺序**: 1. 串口调试输出(`DEBUG_PORT`) 2. 内存控制器配置 3. 存储设备初始化(MMC/SATA) 4. 网络接口(PHY初始化) 5. 环境变量加载(`env_relocate()`) --- **3.4 环境变量管理** - **存储位置**:Flash专用分区(如eMMC的ENV分区) - **关键变量**: ```bash bootcmd=mmc dev 0; ext4load mmc 0:1 0x80800000 zImage; \ ext4load mmc 0:1 0x83000000 imx6ull.dtb; \ bootz 0x80800000 - 0x83000000 bootargs=console=ttymxc0,115200 root=/dev/mmcblk0p2 ro ``` **操作命令**: ```bash # 查看环境变量 printenv # 修改并保存 setenv ipaddr 192.168.1.100 saveenv ``` --- #### **4. 内核加载过程** **典型bootm命令流程**: ```c do_bootm() ↓ bootm_start() // 解析镜像头信息 ↓ bootm_load_os() // 加载内核到指定地址 ↓ boot_fn = boot_os[os] // 选择启动方法(Linux/NetBSD等) ↓ do_bootm_linux() // 设置ATAGs/FDT,跳转到内核入口 ``` **ARM平台内存布局示例**: | 地址范围 | 用途 | |------------------|-----------------------| | 0x80000000 | 内核加载地址 | | 0x81000000 | 设备树地址 | | 0x82000000 | initramfs地址 | --- #### **5. 安全启动实现** **数字签名验证流程**: ```mermaid graph LR A[SPL] --> B{验证U-Boot签名} B -->|有效| C[加载U-Boot] B -->|无效| D[进入恢复模式] C --> E[U-Boot验证内核签名] E -->|有效| F[启动内核] E -->|无效| G[终止启动] ``` **关键配置**: ```makefile CONFIG_SPL_FIT_SIGNATURE=y # 启用FIT镜像签名 CONFIG_SPL_CRYPTO_SUPPORT=y # 加密算法支持 CONFIG_SPL_RSA_VERIFY=y # RSA验证 ``` --- #### **6. 调试与优化** **6.1 常用调试手段**: - 串口日志输出(`CONFIG_DEBUG_UART`) - LED指示灯控制(`led_set_state()`) - 内存检测(`mtest`命令) - 启动暂停(`CONFIG_AUTOBOOT_KEYED`) **6.2 性能优化技巧**: ```c /* 加速存储设备访问 */ #define CONFIG_SYS_MMC_ENV_DEV 0 #define CONFIG_SYS_MMC_MAX_BLK_COUNT 1024 // 增大块传输数量 /* 压缩镜像尺寸 */ #define CONFIG_LZMA // 使用LZMA压缩算法 ``` --- #### **7. 典型移植步骤** 1. **创建板级目录**: ```bash board/<vendor>/<board>/ ├── Kconfig ├── Makefile └── <board>.c ``` 2. **配置设备树**: ```dts /dts-v1/; / { model = "Custom Board"; memory@80000000 { reg = <0x80000000 0x20000000>; }; }; ``` 3. **实现关键驱动**: - 串口(`drivers/serial/serial_xxx.c`) - 存储设备(`drivers/mmc/mmc_xxx.c`) - GPIO控制 --- #### **8. 启动时间分析** **典型时间分布(ARM Cortex-A9@1GHz)**: | 阶段 | 耗时(ms) | |----------------|-----------| | ROM Code | 12 | | SPL执行 | 58 | | U-Boot初始化 | 320 | | 内核加载 | 92 | | **总计** | **482** | **优化方向**: - 启用SPL框架(减少初始化步骤) - 预计算CRC32(替代运行时计算) - 并行初始化外设 --- 通过这种层次化的启动架构U-Boot实现了从硬件初始化到操作系统引导的完整控制流程,其模块化设计和可移植性使其成为嵌入式系统开发的核心组件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值