第一部分 bootm.c 整体呈现
/* * * * - 添加了prep子命令支持 * SPDX-License-Identifier: GPL-2.0+ * 授权标识符: GPL-2.0+ */ /* 包含必要的头文件 */ #include <common.h> /* U-Boot通用头文件,包含全局数据结构和基本函数声明 */ #include <command.h> /* 命令处理相关头文件,定义命令结构体和注册函数 */ #include <image.h> /* 镜像处理头文件,包含各种镜像格式的定义和操作函数 */ #include <u-boot/zlib.h> /* zlib压缩库头文件,用于内核解压 */ #include <asm/byteorder.h> /* 字节序处理头文件,定义大小端转换函数 */ #include <libfdt.h> /* Flattened Device Tree库头文件,用于设备树操作 */ #include <mapmem.h> /* 内存映射头文件,提供物理地址到虚拟地址的转换函数 */ #include <fdt_support.h> /* 设备树支持头文件,包含U-Boot特有的设备树辅助函数 */ #include <asm/bootm.h> /* ARM架构的bootm相关头文件,包含启动特定定义 */ #include <asm/secure.h> /* ARM安全模式相关头文件,用于TrustZone等安全功能 */ #include <linux/compiler.h> /* Linux编译器属性头文件,包含__weak等编译器指令 */ #include <bootm.h> /* 通用bootm头文件,包含bootm的核心数据结构 */ #include <vxworks.h> /* VxWorks RTOS支持头文件,用于VxWorks启动 */ #ifdef CONFIG_ARMV7_NONSEC /* 如果配置了ARMv7非安全模式支持 */ #include <asm/armv7.h> /* ARMv7架构特定头文件,包含协处理器操作函数 */ #endif /* * 声明全局数据指针 * DECLARE_GLOBAL_DATA_PTR: 声明一个指向全局数据结构的指针gd * 设计模式: 单例模式 - 全局数据指针提供对U-Boot全局状态的唯一访问点 */ DECLARE_GLOBAL_DATA_PTR; /* * 静态变量定义 * params: ATAG参数列表指针,用于向Linux内核传递启动参数 * 设计模式: 享元模式 - ATAG参数结构被多个函数共享使用 */ static struct tag *params; /** * @brief 获取当前堆栈指针 * * 通过内联汇编获取当前堆栈指针的值 * * @return ulong 当前堆栈指针值 * * 性能分析: * - 使用内联汇编直接读取SP寄存器,开销极小 * - 单条指令完成,无函数调用开销 */ static ulong get_sp(void) { ulong ret; /* 返回值变量 */ asm("mov %0, sp" : "=r"(ret) : ); /* 内联汇编:将堆栈指针SP的值移动到ret变量 */ return ret; /* 返回堆栈指针值 */ } /** * @brief 为Linux内核预留内存区域 * * 预留内核启动所需的内存空间,特别是命令行和板级信息区域 * 该区域应尽可能高(在CONFIG_SYS_BOOTMAPSZ范围内) * 但位于未使用的内存中,即远低于当前堆栈指针 * * @param lmb 内存块管理器指针 * * 设计模式: 策略模式 - 根据不同的内存布局策略预留空间 * 性能分析: * - 调用get_sp()获取当前堆栈指针,开销小 * - 计算预留区域,进行边界检查 * - lmb_reserve()操作通常较快,但可能涉及内存管理数据结构更新 */ void arch_lmb_reserve(struct lmb *lmb) { ulong sp; /* 堆栈指针变量 */ /* * 启动(Linux)内核镜像 * * 为命令行和板级信息分配空间 - * 地址应尽可能高,在kernel可达范围内(参考CONFIG_SYS_BOOTMAPSZ设置) * 但位于未使用的内存中,这意味着要远低于当前堆栈指针 */ sp = get_sp(); /* 获取当前堆栈指针 */ debug("## Current stack ends at 0x%08lx ", sp); /* 调试输出当前堆栈结束地址 */ /* 为安全起见,将sp向下调整4KB */ sp -= 4096; /* * 预留内存区域:从sp到DRAM区域结束 * 参数1: lmb管理器 * 参数2: 起始地址sp * 参数3: 大小 = DRAM结束地址 - sp */ lmb_reserve(lmb, sp, gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp); } /** * @brief 打印启动消息并执行清理操作 * * 在跳转到内核前打印启动消息,执行必要的清理操作 * 如断开USB连接、清理缓存等 * * @param fake 非0值表示模拟运行(只执行除实际启动外的所有操作) * * 设计模式: 模板方法模式 - 定义启动前准备的标准流程 * 性能分析: * - 字符串输出有I/O开销 * - bootstage标记和报告有日志记录开销 * - 清理操作可能涉及缓存刷新等耗时操作 */ static void announce_and_cleanup(int fake) { /* 打印启动信息,如果是模拟运行则特别标注 */ printf("\nStarting kernel ...%s\n\n", fake ? "(fake run for tracing)" : ""); /* 标记启动阶段:启动移交阶段 */ bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel"); #ifdef CONFIG_BOOTSTAGE_FDT /* 如果配置了bootstage设备树支持 */ bootstage_fdt_add_report(); /* 将bootstage报告添加到设备树 */ #endif #ifdef CONFIG_BOOTSTAGE_REPORT /* 如果配置了bootstage报告 */ bootstage_report(); /* 生成bootstage报告 */ #endif #ifdef CONFIG_USB_DEVICE /* 如果配置了USB设备支持 */ udc_disconnect(); /* 断开USB设备连接 */ #endif cleanup_before_linux(); /* Linux启动前清理操作(如缓存刷新) */ } /** * @brief 设置起始标签(ATAG_CORE) * * 初始化ATAG参数列表,设置核心标签 * * @param bd 板级数据指针 * * 设计模式: 建造者模式 - 逐步构建ATAG参数列表 * 性能分析: * - 直接内存赋值,开销极小 * - 标签指针更新操作快速 */ static void setup_start_tag (bd_t *bd) { /* 从板级数据中获取ATAG参数存储地址 */ params = (struct tag *)bd->bi_boot_params; /* 设置标签头:ATAG_CORE表示核心标签 */ params->hdr.tag = ATAG_CORE; /* 设置标签大小,使用tag_size宏计算 */ params->hdr.size = tag_size (tag_core); /* 设置核心标签的具体字段 */ params->u.core.flags = 0; /* 标志位清零 */ params->u.core.pagesize = 0; /* 页面大小设为0(由内核决定) */ params->u.core.rootdev = 0; /* 根设备设为0 */ /* 移动到下一个标签位置 */ params = tag_next (params); } /** * @brief 设置内存标签(ATAG_MEM) * * 为每个DRAM bank设置内存标签 * * @param bd 板级数据指针 * * 设计模式: 迭代器模式 - 遍历所有DRAM banks * 性能分析: * - 循环次数由CONFIG_NR_DRAM_BANKS决定,通常较小(1-4) * - 每次循环执行固定次数的赋值操作,开销可预测 */ static void setup_memory_tags(bd_t *bd) { int i; /* 循环计数器 */ /* 遍历所有配置的DRAM banks */ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { /* 设置标签头:ATAG_MEM表示内存标签 */ params->hdr.tag = ATAG_MEM; /* 设置标签大小 */ params->hdr.size = tag_size (tag_mem32); /* 设置内存起始地址和大小 */ params->u.mem.start = bd->bi_dram[i].start; /* DRAM起始地址 */ params->u.mem.size = bd->bi_dram[i].size; /* DRAM大小 */ /* 移动到下一个标签位置 */ params = tag_next (params); } } /* 外部变量声明:启动类型 */ extern int starttype; /** * @brief 设置命令行标签(ATAG_CMDLINE) * * 将内核命令行参数打包到ATAG_CMDLINE标签中 * * @param bd 板级数据指针 * @param commandline 命令行字符串 * * 设计模式: 装饰器模式 - 在基础命令行上添加启动类型信息 * 性能分析: * - 字符串操作(strlen, strcpy, strcat)有O(n)复杂度 * - 命令行长度通常有限,开销可控 */ static void setup_commandline_tag(bd_t *bd, char *commandline) { char *p; /* 命令行处理指针 */ char *s = NULL; /* 启动类型字符串 */ if (!commandline) /* 如果命令行为空则直接返回 */ return; /* 跳过前导空白字符 */ for (p = commandline; *p == ' '; p++); /* * 跳过不存在的命令行,这样内核将仍然使用其默认命令行 * 同时检查starttype是否小于0(特殊条件) */ if ((*p == '\0') && (starttype < 0)) return; /* 根据starttype添加相应的启动类型参数 */ if(starttype == 0) s = " starttype=main"; /* 主启动类型 */ else s = " starttype=backup"; /* 备份启动类型 */ /* 设置标签头:ATAG_CMDLINE表示命令行标签 */ params->hdr.tag = ATAG_CMDLINE; /* 计算标签大小:头部大小 + 命令行长度 + 启动类型字符串长度 + 1(终止符) + 4字节对齐 */ params->hdr.size = (sizeof (struct tag_header) + strlen (p) + strlen (s) + 1 + 4) >> 2; /* 复制命令行字符串 */ strcpy (params->u.cmdline.cmdline, p); /* 追加启动类型字符串 */ strcat (params->u.cmdline.cmdline, s); /* 移动到下一个标签位置 */ params = tag_next (params); } /** * @brief 设置initrd标签(ATAG_INITRD2) * * 设置初始RAM磁盘的位置和大小 * * @param bd 板级数据指针 * @param initrd_start initrd起始地址 * @param initrd_end initrd结束地址 * * 性能分析: * - 简单的算术计算和赋值操作,开销极小 * - initrd_end - initrd_start计算大小,快速 */ static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end) { /* * ATAG_INITRD节点告诉内核压缩的ramdisk的位置 * 实际上ATAG_RDIMG是更好的名字 */ params->hdr.tag = ATAG_INITRD2; /* 设置标签类型为INITRD2 */ params->hdr.size = tag_size (tag_initrd); /* 设置标签大小 */ /* 设置initrd的起始地址和大小 */ params->u.initrd.start = initrd_start; /* 起始地址 */ params->u.initrd.size = initrd_end - initrd_start; /* 大小计算 */ params = tag_next (params); /* 移动到下一个标签 */ } /** * @brief 设置序列号标签(ATAG_SERIAL) * * 将板级序列号传递给内核 * * @param tmp 标签指针的指针(用于更新指针位置) * * 设计模式: 适配器模式 - 将板级序列号适配到ATAG格式 */ static void setup_serial_tag(struct tag **tmp) { struct tag *params = *tmp; /* 获取当前标签指针 */ struct tag_serialnr serialnr; /* 序列号结构体 */ get_board_serial(&serialnr); /* 获取板级序列号 */ params->hdr.tag = ATAG_SERIAL; /* 设置标签类型为序列号 */ params->hdr.size = tag_size (tag_serialnr); /* 设置标签大小 */ params->u.serialnr.low = serialnr.low; /* 设置序列号低部分 */ params->u.serialnr.high= serialnr.high; /* 设置序列号高部分 */ params = tag_next (params); /* 移动到下一个标签 */ *tmp = params; /* 更新外部指针 */ } /** * @brief 设置版本标签(ATAG_REVISION) * * 将板级版本号传递给内核 * * @param in_params 标签指针的指针 * * 设计模式: 工厂方法模式 - 创建特定类型的ATAG标签 */ static void setup_revision_tag(struct tag **in_params) { u32 rev = 0; /* 版本号变量 */ rev = get_board_rev(); /* 获取板级版本号 */ params->hdr.tag = ATAG_REVISION; /* 设置标签类型为版本 */ params->hdr.size = tag_size (tag_revision); /* 设置标签大小 */ params->u.revision.rev = rev; /* 设置版本号 */ params = tag_next (params); /* 移动到下一个标签 */ } /** * @brief 设置结束标签(ATAG_NONE) * * 标记ATAG参数列表的结束 * * @param bd 板级数据指针 * * 设计模式: 终止符模式 - 标记数据结构结束 */ static void setup_end_tag(bd_t *bd) { params->hdr.tag = ATAG_NONE; /* 结束标签类型 */ params->hdr.size = 0; /* 大小为0表示结束 */ } /** * @brief 弱定义的板级标签设置函数 * * 允许板级代码覆盖此函数以添加自定义ATAG标签 * * @param in_params 标签指针的指针 * * 设计模式: 钩子模式 - 提供扩展点供板级代码定制 */ __weak void setup_board_tags(struct tag **in_params) {} #ifdef CONFIG_ARM64 /* ARM64架构特定代码 */ /** * @brief 执行非安全虚拟模式切换(ARM64) * * 在ARM64架构上切换到EL2(或EL1)异常级别 * * 性能分析: * - smp_kick_all_cpus()可能涉及核间通信,开销较大 * - dcache_disable()刷新缓存,耗时操作 * - 异常级别切换需要多个寄存器操作 */ static void do_nonsec_virt_switch(void) { smp_kick_all_cpus(); /* 唤醒所有CPU核心 */ dcache_disable(); /* 切换到EL2前刷新缓存 */ armv8_switch_to_el2(); /* 切换到EL2异常级别 */ #ifdef CONFIG_ARMV8_SWITCH_TO_EL1 /* 如果配置了切换到EL1 */ armv8_switch_to_el1(); /* 进一步切换到EL1异常级别 */ #endif } #endif /** * @brief 子命令: PREP - 准备Linux内核启动 * * 根据配置准备Linux内核启动环境,支持FDT和ATAGS两种方式 * * @param images bootm镜像头信息 * * 设计模式: 策略模式 - 根据配置选择FDT或ATAGS策略 * 性能分析: * - FDT路径涉及设备树处理,复杂度较高 * - ATAGS路径相对简单,但需要设置多个标签 * - 条件编译减少不必要的代码路径 */ static void boot_prep_linux(bootm_headers_t *images) { /* 从环境变量获取内核命令行参数 */ char *commandline = getenv("bootargs"); /* 如果启用设备树且设备树长度不为0,使用FDT方式 */ if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) { #ifdef CONFIG_OF_LIBFDT debug("using: FDT\n"); /* 调试输出:使用FDT */ /* 设置Linux设备树,失败则挂起系统 */ if (image_setup_linux(images)) { printf("FDT creation failed! hanging..."); hang(); /* 挂起系统 */ } #endif } /* 如果启用ATAGS支持,使用ATAGS方式 */ else if (BOOTM_ENABLE_TAGS) { debug("using: ATAGS\n"); /* 调试输出:使用ATAGS */ /* 设置起始标签 */ setup_start_tag(gd->bd); /* 根据配置添加各种标签 */ if (BOOTM_ENABLE_SERIAL_TAG) setup_serial_tag(¶ms); /* 序列号标签 */ if (BOOTM_ENABLE_CMDLINE_TAG) setup_commandline_tag(gd->bd, commandline); /* 命令行标签 */ if (BOOTM_ENABLE_REVISION_TAG) setup_revision_tag(¶ms); /* 版本标签 */ if (BOOTM_ENABLE_MEMORY_TAGS) setup_memory_tags(gd->bd); /* 内存标签 */ /* 设置initrd标签 */ if (BOOTM_ENABLE_INITRD_TAG) { /* * 在boot_ramdisk_high()中,可能将ramdisk重定位到指定位置 * 并设置images->initrd_start和images->initrd_end为重定位后的地址 * 因此尽可能使用这些值而不是images->rd_start和images->rd_end */ if (images->initrd_start && images->initrd_end) { setup_initrd_tag(gd->bd, images->initrd_start, images->initrd_end); } else if (images->rd_start && images->rd_end) { setup_initrd_tag(gd->bd, images->rd_start, images->rd_end); } } /* 调用板级特定的标签设置 */ setup_board_tags(¶ms); /* 设置结束标签 */ setup_end_tag(gd->bd); } /* 既不支持FDT也不支持ATAGS,报错 */ else { printf("FDT and ATAGS support not compiled in - hanging\n"); hang(); /* 挂起系统 */ } } /** * @brief 弱定义的ARMv7非安全启动默认值 * * 返回ARMv7非安全启动的默认配置 * * @return bool true表示默认非安全启动,false表示默认安全启动 * * 设计模式: 模板方法模式 - 定义默认行为,可被覆盖 */ __weak bool armv7_boot_nonsec_default(void) { #ifdef CONFIG_ARMV7_BOOT_SEC_DEFAULT /* 如果配置了默认安全启动 */ return false; /* 返回false,表示安全启动 */ #else return true; /* 默认返回true,表示非安全启动 */ #endif } #ifdef CONFIG_ARMV7_NONSEC /* ARMv7非安全模式支持 */ /** * @brief 确定ARMv7是否以非安全模式启动 * * 检查环境变量bootm_boot_mode,决定启动模式 * * @return bool true表示非安全启动,false表示安全启动 * * 设计模式: 策略模式 - 根据环境变量选择启动策略 * 性能分析: * - getenv()调用有字符串查找开销 * - strcmp()比较操作,但字符串长度固定,开销小 */ bool armv7_boot_nonsec(void) { /* 从环境变量获取启动模式设置 */ char *s = getenv("bootm_boot_mode"); /* 获取默认的非安全启动设置 */ bool nonsec = armv7_boot_nonsec_default(); /* 如果环境变量设置为"sec",则使用安全启动 */ if (s && !strcmp(s, "sec")) nonsec = false; /* 如果环境变量设置为"nonsec",则使用非安全启动 */ if (s && !strcmp(s, "nonsec")) nonsec = true; return nonsec; /* 返回确定的启动模式 */ } #endif /** * @brief 子命令: GO - 跳转到Linux内核 * * 实际跳转到Linux内核入口点执行 * * @param images bootm镜像头信息 * @param flag 启动标志位 * * 设计模式: 桥接模式 - 连接U-Boot和Linux内核 * 性能分析: * - ARM64和ARM32有不同实现路径 * - 环境变量解析有额外开销 * - 内核跳转是单次操作,但准备阶段可能耗时 */ static void boot_jump_linux(bootm_headers_t *images, int flag) { #ifdef CONFIG_ARM64 /* ARM64实现 */ /* 定义内核入口函数指针 */ void (*kernel_entry)(void *fdt_addr, void *res0, void *res1, void *res2); /* 检查是否为模拟运行 */ int fake = (flag & BOOTM_STATE_OS_FAKE_GO); /* 将镜像入口点转换为函数指针 */ kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1, void *res2))images->ep; /* 调试输出:即将跳转到内核 */ debug("## Transferring control to Linux (at address %lx)...\n", (ulong) kernel_entry); /* 标记运行OS阶段 */ bootstage_mark(BOOTSTAGE_ID_RUN_OS); /* 执行启动前清理和通知 */ announce_and_cleanup(fake); /* 如果不是模拟运行,则实际跳转 */ if (!fake) { /* 执行非安全虚拟模式切换 */ do_nonsec_virt_switch(); /* 调用内核入口函数,传递设备树地址 */ kernel_entry(images->ft_addr, NULL, NULL, NULL); } #else /* ARM32实现 */ /* 获取机器ID */ unsigned long machid = gd->bd->bi_arch_number; char *s; /* 环境变量字符串指针 */ /* 定义ARM32内核入口函数指针 */ void (*kernel_entry)(int zero, int arch, uint params); unsigned long r2; /* 参数寄存器2的值 */ int fake = (flag & BOOTM_STATE_OS_FAKE_GO); /* 检查是否为模拟运行 */ /* 将镜像入口点转换为函数指针 */ kernel_entry = (void (*)(int, int, uint))images->ep; /* 从环境变量获取机器ID(如果有的话) */ s = getenv("machid"); if (s) { /* 严格转换字符串为长整型 */ if (strict_strtoul(s, 16, &machid) < 0) { debug("strict_strtoul failed!\n"); return; } /* 打印从环境变量获取的机器ID */ printf("Using machid 0x%lx from environment\n", machid); } /* 调试输出:即将跳转到内核 */ debug("## Transferring control to Linux (at address %08lx)" \ "...\n", (ulong) kernel_entry); /* 标记运行OS阶段 */ bootstage_mark(BOOTSTAGE_ID_RUN_OS); /* 执行启动前清理和通知 */ announce_and_cleanup(fake); /* 确定r2参数的值:设备树地址或ATAGS参数地址 */ if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) r2 = (unsigned long)images->ft_addr; /* 使用设备树地址 */ else r2 = gd->bd->bi_boot_params; /* 使用ATAGS参数地址 */ /* 如果不是模拟运行,则实际跳转 */ if (!fake) { #ifdef CONFIG_ARMV7_NONSEC /* 如果支持ARMv7非安全模式 */ /* 检查是否以非安全模式启动 */ if (armv7_boot_nonsec()) { /* 初始化非安全模式 */ armv7_init_nonsec(); /* 安全RAM地址执行非安全入口 */ secure_ram_addr(_do_nonsec_entry)(kernel_entry, 0, machid, r2); } else #endif /* 直接调用内核入口函数 */ kernel_entry(0, machid, r2); } #endif } /** * @brief ARM bootm实现的主要入口点 * * 模仿powerpc实现建模 * 不同之处:如果子命令等于0,则在最后调用prep和go * * @param flag 启动标志位 * @param argc 参数个数 * @param argv 参数数组 * @param images bootm镜像头信息 * @return int 执行结果,0成功,-1失败 * * 设计模式: 状态模式 - 根据flag的不同状态执行不同操作 * 性能分析: * - 快速的状态检查(位操作) * - 按需执行prep和go,避免不必要的工作 * - 代码路径清晰,执行效率高 */ int do_bootm_linux(int flag, int argc, char * const argv[], bootm_headers_t *images) { /* ARM上不需要这些状态 */ if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE) return -1; /* 如果是PREP状态,执行准备操作 */ if (flag & BOOTM_STATE_OS_PREP) { boot_prep_linux(images); return 0; } /* 如果是GO或FAKE_GO状态,执行跳转操作 */ if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) { boot_jump_linux(images, flag); return 0; } /* 默认情况:先执行准备操作,再执行跳转操作 */ boot_prep_linux(images); boot_jump_linux(images, flag); return 0; } #if defined(CONFIG_BOOTM_VXWORKS) /* VxWorks启动支持 */ /** * @brief 准备VxWorks启动 * * VxWorks启动前的准备工作,主要是设备树处理 * * @param images bootm镜像头信息 * * 设计模式: 适配器模式 - 适配VxWorks启动的特殊需求 */ void boot_prep_vxworks(bootm_headers_t *images) { #if defined(CONFIG_OF_LIBFDT) /* 如果支持设备树 */ int off; /* 设备树节点偏移量 */ /* 如果存在设备树地址 */ if (images->ft_addr) { /* 查找内存节点 */ off = fdt_path_offset(images->ft_addr, "/memory"); if (off < 0) { /* 如果找不到内存节点 */ #ifdef CONFIG_ARCH_FIXUP_FDT /* 如果支持架构特定的设备树修复 */ /* 尝试修复设备树 */ if (arch_fixup_fdt(images->ft_addr)) puts("## WARNING: fixup memory failed!\n"); #endif } } #endif /* Linux启动前清理操作(也适用于VxWorks) */ cleanup_before_linux(); } /** * @brief 跳转到VxWorks * * 实际跳转到VxWorks入口点 * * @param images bootm镜像头信息 * * 设计模式: 简单工厂模式 - 创建并执行VxWorks启动 */ void boot_jump_vxworks(bootm_headers_t *images) { /* * ARM VxWorks要求传递设备树物理地址 * 将镜像入口点转换为函数指针并调用,传递设备树地址 */ ((void (*)(void *))images->ep)(images->ft_addr); } #endif设计模式综合分析
1.建造者模式 (Builder Pattern)
应用场景:
setup_*_tag系列函数分析: 这些函数逐步构建ATAG参数列表,类似于建造者模式中逐步构建复杂对象的过程
2.模板方法模式 (Template Method Pattern)
应用场景:
announce_and_cleanup()函数分析: 定义了启动前准备的标准流程,具体步骤可以通过条件编译定制
3.策略模式 (Strategy Pattern)
应用场景: FDT vs ATAGS 选择,安全 vs 非安全启动选择
分析: 根据配置和环境变量选择不同的启动策略
4.状态模式 (State Pattern)
应用场景:
do_bootm_linux()函数中的flag处理分析: 根据不同的状态标志执行不同的操作序列
5.钩子模式 (Hook Pattern)
应用场景:
__weak函数定义分析: 提供默认实现,允许板级代码覆盖特定功能
性能分析总结
1.优点
条件编译优化: 通过宏定义移除未使用代码路径
内存访问优化: 直接操作内存,减少中间拷贝
流程控制优化: 清晰的状态检查,避免不必要的操作
2.潜在瓶颈
字符串操作:
strlen(),strcpy(),strcat()在长命令行时可能成为瓶颈环境变量访问:
getenv()可能涉及字符串查找,建议缓存结果设备树处理: FDT路径可能涉及较复杂的树遍历操作
3.建议优化
缓存环境变量查询结果
预计算字符串长度避免重复计算
对于频繁使用的标签操作,可以考虑批处理
设备树操作可以延迟到真正需要时执行
这个实现展示了U-Boot启动Linux内核的完整流程,平衡了灵活性和性能,为不同硬件平台提供了良好的支持基础。
第二部分 修改分析
主要修改点分析
1.版权信息修改
/* * Copyright (C) 2011 * * - 添加了prep子命令支持 * - 重新组织源代码 - 基于powerpc版本建模 */
修改原因:
记录了Corscience公司的贡献
明确说明了两项主要修改:prep子命令支持和代码重构
基于PowerPC版本进行重构,利用了PowerPC架构上成熟的bootm实现
2.源代码重新组织
原始文件通常是arch/arm/lib/bootm.c,而这个版本明显进行了重构。
修改内容:
将启动流程清晰地分为
boot_prep_linux()和boot_jump_linux()两个主要函数增加了
do_bootm_linux()作为统一的入口点模仿PowerPC的实现方式,使ARM和PowerPC的bootm实现保持一致性
修改原因:
代码复用:PowerPC的bootm实现相对成熟,借鉴其设计可以提高代码质量
维护性:统一的代码结构便于跨架构维护
清晰度:分离"准备"和"跳转"两个阶段,逻辑更清晰
3.添加prep子命令支持
/* 如果是PREP状态,执行准备操作 */ if (flag & BOOTM_STATE_OS_PREP) { boot_prep_linux(images); return 0; }修改原因:
调试需求:允许单独执行启动准备阶段,便于调试
模块化:将复杂的启动过程分解为可独立测试的模块
灵活性:支持如
bootm prep这样的命令,可以只准备不跳转
4.VxWorks启动支持
#if defined(CONFIG_BOOTM_VXWORKS) void boot_prep_vxworks(bootm_headers_t *images) void boot_jump_vxworks(bootm_headers_t *images) #endif
修改原因:
多OS支持:原始U-Boot主要支持Linux,这个版本增加了VxWorks RTOS支持
工业应用:VxWorks在工业控制领域广泛使用
统一接口:通过相同的bootm框架支持不同操作系统
5.ARMv7非安全模式支持
#ifdef CONFIG_ARMV7_NONSEC bool armv7_boot_nonsec(void) #endif
修改原因:
安全扩展:支持ARM TrustZone技术
虚拟化:为虚拟化环境提供支持
现代ARM特性:适应新一代ARM处理器的安全需求
6.ARM64架构支持
#ifdef CONFIG_ARM64 static void do_nonsec_virt_switch(void) #endif
修改原因:
架构演进:支持64位ARM架构
异常级别:ARM64使用不同的异常级别模型(EL1/EL2/EL3)
兼容性:需要为ARM64提供特定的启动切换代码
7.改进的ATAG处理
/* 根据starttype添加相应的启动类型参数 */ if(starttype == 0) s = " starttype=main"; /* 主启动类型 */ else s = " starttype=backup"; /* 备份启动类型 */
修改原因:
冗余启动:支持主/备份双系统启动
可靠性:在主系统损坏时可以切换到备份系统
工业标准:符合工业控制系统的高可靠性要求
8.内存布局优化
/* 为安全起见,将sp向下调整4KB */ sp -= 4096;
修改原因:
安全边界:为堆栈保留额外的安全空间
防止溢出:避免内核启动时堆栈溢出
对齐要求:确保内存边界对齐
9.增强的调试支持
debug("## Current stack ends at 0x%08lx ", sp); bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel"); #ifdef CONFIG_BOOTSTAGE_REPORT bootstage_report(); #endif修改原因:
调试信息:提供更详细的启动过程信息
性能分析:bootstage支持启动性能分析
问题诊断:便于定位启动过程中的问题
10.灵活的启动模式选择
#ifdef CONFIG_ARMV7_NONSEC bool armv7_boot_nonsec(void) { char *s = getenv("bootm_boot_mode"); bool nonsec = armv7_boot_nonsec_default(); if (s && !strcmp(s, "sec")) nonsec = false; if (s && !strcmp(s, "nonsec")) nonsec = true; return nonsec; } #endif修改原因:
运行时配置:可以通过环境变量动态选择启动模式
灵活性:不需要重新编译即可切换启动模式
测试便利:便于测试不同的安全配置
设计理念的变化
1.从单片代码到模块化设计
原始:所有启动代码在一个大函数中
修改后:分解为多个职责清晰的函数
2.从ARM特定到跨架构统一
原始:ARM特有的实现方式
修改后:借鉴PowerPC的设计,向跨架构统一迈进
3.从单一OS到多OS支持
原始:主要支持Linux
修改后:支持Linux、VxWorks等多个操作系统
4.从固定配置到灵活配置
原始:编译时确定的配置
修改后:支持运行时通过环境变量配置
具体使用场景分析
这些修改特别适合以下场景:
1.工业控制系统
VxWorks支持
冗余启动(主/备份)
高可靠性要求
2.安全敏感应用
ARM TrustZone支持
安全/非安全模式选择
内存保护增强
3.虚拟化环境
ARM64支持
异常级别切换
多OS共存
4.调试和开发
详细的调试输出
bootstage性能分析
独立的prep子命令
潜在问题与考量
1.兼容性问题
修改可能破坏与原始U-Boot的兼容性
需要确保所有板级代码适配新的接口
2.代码复杂度增加
更多的条件编译分支
更复杂的函数调用链
需要更多的测试用例
3.性能影响
额外的函数调用开销
环境变量查找开销
更多的调试代码可能影响性能
总结
这个修改版本的U-Boot启动代码主要体现了以下几个设计思想:
模块化:将复杂功能分解为独立的模块
可配置性:提供多种配置选项和运行时选择
可扩展性:易于添加对新架构和OS的支持
可调试性:丰富的调试信息和性能分析工具
可靠性:增强的错误处理和冗余机制
这些修改使得U-Boot更加适合现代嵌入式系统,特别是那些需要高可靠性、安全性和灵活性的工业应用场景。不过,这些改进也带来了一定的复杂性和维护成本,需要在具体项目中权衡利弊。