uboot启动kernel使用命令过程分析

本文从代码角度,分析uboot如何使用环境变量以及加载命令,启动kernel的。

本文以armV8 CPU为例子(例如RK1808或者7ev),其他架构类型基本一样。

1. uboot启动流程简介

        为了让读者知道本文介绍的知识点在uboot所在位置,在介绍之前,先简单回顾下uboot的启动流程。

        从uboot链接脚本(u-boot\arch\arm\cpu\armv8\u-boot.lds)可知,uboot的启动入口符号 _start:

.......
ENTRY(_start)
SECTIONS
{
#ifdef CONFIG_ARMV8_SECURE_BASE
	/DISCARD/ : { *(.rela._secure*) }
#endif
	. = 0x00000000;

	. = ALIGN(8);
	.text :
	{
		*(.__image_copy_start)
		CPUDIR/start.o (.text*)
		*(.text*)
	}
........

而_start符号定义在 u-boot\arch\arm\cpu\armv8\start.S 文件里:


.globl	_start
_start:
#ifdef CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK
/*
 * Various SoCs need something special and SoC-specific up front in
 * order to boot, allow them to set that in their boot0.h file and then
 * use it here.
 */
#include <asm/arch/boot0.h>
#else
	b	reset
#endif

start.S主要对CPU环境初始化,最后跳转到_main符号地址,_main定义在u-boot\arch\arm\lib\crt0_64.S:

ENTRY(_main)

分析该文件,做了一些列初始化,包括为C环境做的准备等,主要跳转了两个函数:board_init_f 和 board_init_r。

其中board_init_f定义在uboot/common/board_f.c里,该函数主要执行了init_sequence_f定义的函数结构体,至于具体做了什么,留着专题分析,本文重点不在这里。

void board_init_f(ulong boot_flags)
{
	gd->flags = boot_flags;
	gd->have_console = 0;

	if (initcall_run_list(init_sequence_f))
		hang();

#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
		!defined(CONFIG_EFI_APP) && !CONFIG_IS_ENABLED(X86_64)
	/* NOTREACHED - jump_to_copy() does not return */
	hang();
#endif
}

crt0_64.S最后跳转至board_init_r函数,该函数定义在u-boot\common\board_r.c,该函数也是执行了init_sequence_r定义的函数结构体,而init_sequence_r(u-boot\common\board_r.c里定义)定义的函数集里,最后执行的函数是run_main_loop,查看run_main_loop(u-boot\common\board_r.c里定义)函数定义知道最后跳转到main_loop。


void board_init_r(gd_t *new_gd, ulong dest_addr)
{
	/*
	 * Set up the new global data pointer. So far only x86 does this
	 * here.
	 * TODO(sjg@chromium.org): Consider doing this for all archs, or
	 * dropping the new_gd parameter.
	 */
.............

	if (initcall_run_list(init_sequence_r))
		hang();

	/* NOTREACHED - run_main_loop() does not return */
	hang();
}

static int run_main_loop(void)
{
.....
	for (;;)
		main_loop();
	return 0;
}

main_loop定义在u-boot\common\main.c,main_loop是uboot最后进入的一个函数,主要执行如下动作:初始化跳转kernel前环境,响应看客户按键,读取环境变量并执行命令跳转至kernel,跳转失败后续处理等。

本文主要分析uboot如何利用环境变量执行命令跳转至kernel,主要在main_loop函数里。

2. uboot使用环境变量跳转kernel

        在main_loop里,可以看到uboot在执行了bootdelay_process(u-boot\common\autoboot.c)函数,读取包括bootdelay、bootcmd等环境变量值,其中bootcmd是启动kernel的命令:

const char *bootdelay_process(void)
{
	char *s;
	int bootdelay;
........
	s = env_get("bootdelay");
	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
.......
	debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);  
.......
	s = env_get("bootcmd");

	process_fdt_options(gd->fdt_blob);
	stored_bootdelay = bootdelay;

	return s;
}

在读取到bootcmd命令内容后,main_loop又执行了autoboot_command(u-boot\common\autoboot.c)函数,来执行bootcmd的命令内容。所以接下来看下bootcmd内容从来哪里来,内容是什么,以及过程中执行了哪些命令才能加载kernel并跳转的。

3. bootcmd的命令内容分析

        bootcmd命令定义在u-boot\include\env_default.h文件,放到default_environment这个大的结构体,该结构体会在uboot启动的时候,根据内容被加载成可识别环境变量内容。从中可可看到bootcmd内容来自CONFIG_BOOTCOMMAND,说明用户可自定义bootcmd的内容。

env_t environment __UBOOT_ENV_SECTION__ = {
.......
	{
#elif defined(DEFAULT_ENV_INSTANCE_STATIC)
static char default_environment[] = {
#else
const uchar default_environment[] = {
<think> 根据用户查询,问题聚焦于“uboot启动Kernel时CPU0 STATUS=10000002错误的解决方案”。结合引用内容,分析可能原因和解决方向: 1. **引用[1]和[2]** 提到uboot启动内核的关键步骤:设置`bootargs`、加载内核镜像和设备树、执行`booti`/`bootm`命令。其中隔离CPU的配置(`isolcpus=2,3`)可能影响CPU状态。 2. **引用[3]** 指出内核中操作寄存器需通过虚拟地址映射(`ioremap`),若uboot阶段未正确初始化相关寄存器,可能导致内核启动时CPU状态异常。 3. **引用[4]** 描述uboot重定位过程,涉及地址对齐和全局数据结构(GD)迁移,若重定位出错可能引发后续CPU状态异常。 ### 可能原因及解决方案: 1. **CPU隔离配置冲突** `isolcpus`参数(如引用[1])可能使内核尝试隔离CPU0,但实际硬件不支持或配置冲突,导致状态异常。 **解决**:检查`bootargs`中的`isolcpus`参数,暂时移除并测试是否正常。 2. **设备树或内核镜像加载地址错误** 引用[1]中加载内核镜像(`ext4load mmc ...`)和设备树的地址必须与硬件内存映射匹配。若地址冲突或越界,可能引发CPU异常状态。 **解决**: - 确认内核镜像和设备树加载地址(如`0x80080000`、`0x83E00000`)是否与硬件设计一致。 - 检查uboot环境变量:`loadaddr`、`fdt_addr`、`image`、`fdt_file`的值。 3. **寄存器初始化问题** 引用[3]强调操作寄存器需虚拟地址映射。若uboot未正确初始化关键寄存器(如时钟、中断控制器),内核启动时CPU可能进入错误状态。 **解决**: - 在uboot阶段通过`mw`命令手动设置相关寄存器: ```bash mw.l <寄存器物理地址> <值> # 示例:设置CPU0状态寄存器 ``` - 确认设备树中CPU节点配置正确。 4. **uboot重定位异常** 引用[4]描述uboot重定位过程(`relocate_code`),若地址计算错误(如`GD_RELOC_OFF`或`GD_RELOCADDR`错误),可能导致后续执行异常。 **解决**: - 在uboot启动早期添加调试输出(如`print_gd`函数),检查`gd->relocaddr`等值。 - 确保uboot链接脚本(`u-boot.lds`)中内存布局与实际硬件一致。 ### 操作步骤建议: 1. **简化启动参数** 临时修改`bootargs`,仅保留必要参数(如`root=/dev/mmcblk0p1 rw`),移除`isolcpus`等调试参数。 2. **验证内存地址** 使用uboot命令检查关键地址: ```bash # 打印内核镜像和设备树地址 echo loadaddr=$loadaddr, fdt_addr=$fdt_addr # 手动加载并校验数据完整性 ext4load mmc 0:5 $loadaddr Image-ETH md $loadaddr # 查看前16字节是否为有效的内核头 ``` 3. **寄存器状态检查** 在uboot中读取CPU0状态寄存器(需查芯片手册获取地址): ```bash md.l <STATUS寄存器地址> 1 # 读取当前值 ``` 4. **更新固件** 若问题持续,尝试升级uboot或内核版本,修复潜在缺陷。 ### 相关问题扩展 §§相关问题§§ 1. 如何调试uboot启动内核时的CPU异常状态? 2. 设备树配置错误会导致哪些内核启动问题? 3. uboot环境变量设置对内核启动的影响有哪些? </think> ### U-Boot启动Kernel时CPU0 STATUS=10000002错误解决方案 根据引用内容和U-Boot启动流程分析,该错误通常与以下原因相关: #### 1. **CPU隔离配置冲突** 引用[1]提到`isolcpus`参数用于隔离CPU核心,若配置冲突会导致状态异常: ```bash # 检查bootargs中的isolcpus设置 setenv bootargs root=/dev/mmcblk0p1 rootwait rw earlycon isolcpus=2,3 ``` **解决方案**: - 临时移除隔离参数测试:`setenv bootargs root=/dev/mmcblk0p1 rw` - 确认硬件支持的CPU核心数(如S32V234为4核) - 避免隔离CPU0(系统必需的核心) #### 2. **内存地址映射错误** 引用[3][4]指出物理/虚拟地址转换问题可能导致寄存器访问异常: ```c // 错误示例(直接操作物理地址) __raw_writel(reg, IOMUXC_BASE_ADDR + REG); // 正确做法(需ioremap转换) void __iomem *base = ioremap(IOMUXC_BASE_ADDR, SIZE); __raw_writel(reg, base + REG); ``` **解决方案**: 1. 检查U-Boot设备树地址映射: ```bash md 0x83E00000 10 # 查看设备树加载是否正确 ``` 2. 确认内核`memreserve`区域与U-Boot无冲突 3. 核对芯片手册中`STATUS`寄存器(0x10000002)的定义 #### 3. **镜像加载异常** 引用[1][2]显示内核/设备树加载错误会引发CPU状态异常: ```bash # 验证镜像加载完整性 ext4load mmc 0:5 0x80080000 Image-ETH iminfo 0x80080000 # 检查镜像头信息 # 对比烧写到eMMC的原始文件 mmc dev 0 mmc read 0x82000000 0x1000 0x2000 # 读取eMMC分区 cmp.b 0x80080000 0x82000000 0x100000 ``` **解决方案**: - 重新烧写内核镜像和设备树到eMMC - 检查`ext4load`地址是否对齐(ARMv7需64B对齐) - 更换存储介质测试(如TFTP加载) #### 4. **启动流程调试** 引用[4]表明重定位错误可能引发异常: ```bash # 启用U-Boot调试信息 setenv boot_mtd 'booti 0x80080000 - 0x83E00000; echo DEBUG: $?' ``` **调试步骤**: 1. 在`board_init_r`阶段添加寄存器打印 2. 检查重定位偏移量: ```c gd->reloc_off = (ulong)_new_loc - _start; ``` 3. 确认`CONFIG_SKIP_RELOCATE_UBOOT`配置 #### 5. **硬件相关检查** 1. **电源管理**:确认所有CPU核心供电稳定 2. **时钟信号**:使用示波器检测主频时钟 3. **温度监控**:排除过热保护触发 4. **异常代码解析**:`10000002`通常表示: - Bit 1: Undefined Instruction - Bit 25: Data Abort > **紧急恢复方案**: > 若无法进入系统,通过U-Boot重置CPU状态: > ```bash > mw.l 0xFFFF0000 0 0x100 # 清除异常状态区域 > cpu reset 0 # 复位CPU0 > ``` ### 验证流程 ```mermaid graph TD A[启动失败] --> B{检查bootargs} B -->|isolcpus冲突| C[移除隔离参数] B -->|正常| D{验证镜像完整性} D -->|损坏| E[重新烧写镜像] D -->|正常| F{检查地址映射} F -->|错误| G[修正ioremap] F -->|正确| H{调试重定位} H -->|异常| I[检查gd->relocaddr] H -->|正常| J[硬件诊断] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值