setup_arch()函数分析4--cpu_init

本文详细解析了ARM处理器在不同模式下堆栈的初始化过程,特别是IRQ、ABT、UND模式下的堆栈配置及如何在有限的空间内保存大量寄存器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

arch/arm/kernel/setup.c
struct stack {
    u32 irq[3];
    u32 abt[3];
    u32 und[3];
} ____cacheline_aligned;

/*
 * cpu_init - initialise one CPU.
 *
 * cpu_init sets up the per-CPU stacks.
 */
arm的R13是bank register,arm的七种处理器模式有各自的R13(user模式和system模式共用一个,总共六个不同的R13),也就是arm汇编默认的 SP(堆栈指针),就是说一旦产生了模式切换(IRQ,FIQ,异常等), 接着的代码就使用模式对应的SP.cpu_init就是对用到的IRQ, ABT, UND模式的SP都作了初始化
void cpu_init(void) //设置cpu堆栈
{
    unsigned int cpu = smp_processor_id(); //获取cpu的id
    struct stack *stk = &stacks[cpu];

    if (cpu >= NR_CPUS) {  //对于ARM NR_CPUS为1
        printk(KERN_CRIT "CPU%u: bad primary CPU number\n", cpu);
        BUG();
    }

    /*
     * Define the placement constraint for the inline asm directive below.
     * In Thumb-2, msr with an immediate value is not allowed.
     */
#ifdef CONFIG_THUMB2_KERNEL
#define PLC    "r"
#else
#define PLC    "I"
#endif

    /*
     * setup stacks for re-entrant exception handlers
     */
    __asm__ (
    "msr    cpsr_c, %1\n\t"   //切换到IRQ_MODE
    "add    r14, %0, %2\n\t"  
    "mov    sp, r14\n\t"     //设置r13_irq,此时sp是r13_irq
    "msr    cpsr_c, %3\n\t"  //切换到ABT_MODE
    "add    r14, %0, %4\n\t"
    "mov    sp, r14\n\t"     //设置r13_abt,此时sp是r13_abt
    "msr    cpsr_c, %5\n\t"  //切换到UND_MODE
    "add    r14, %0, %6\n\t"
    "mov    sp, r14\n\t"   //设置r13_und,此时sp是r13_und
    "msr    cpsr_c, %7"    //切换回SVC_MODE
        :
        : "r" (stk),
          PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
          "I" (offsetof(struct stack, irq[0])),
          PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
          "I" (offsetof(struct stack, abt[0])),
          PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
          "I" (offsetof(struct stack, und[0])),
          PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
        : "r14");
}
可以看到,IRQ, ABT, UND模式的堆栈大小都只有3个int,
你可能会问,进入这三个模式后, 怎么保存大量的寄存器?
呵呵,进入这三种模式后,linux会把模式切换到SVC_MODE,使用的是SVC_MODE的堆栈.我们看一下在arch/arm/kernel/entry-armv.S中的汇编代码
1040 /*
1041  * Vector stubs.
1042  *
1043  * This code is copied to 0xffff0200 so we can use branches in the
1044  * vectors, rather than ldr's.  Note that this code must not
1045  * exceed 0x300 bytes.
1046  *
1047  * Common stub entry macro:
1048  *   Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
1049  *
1050  * SP points to a minimal amount of processor-private memory, the address
1051  * of which is copied into r0 for the mode specific abort handler.
1052  */
1053         .macro  vector_stub, name, mode, correction=0
1054         .align  5
1055
1056 vector_\name:
1057         .if \correction
1058         sub     lr, lr, #\correction
1059         .endif
1060
1061         @
1062         @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
1063         @ (parent CPSR)
1064         @
1065         stmia   sp, {r0, lr}            @ save r0, lr //R13_<mode>保存两个int
1066         mrs     lr, spsr
1067         str     lr, [sp, #8]            @ save spsr   //R13_<mode>再保存一个int
1068
1069         @
1070         @ Prepare for SVC32 mode.  IRQs remain disabled.
1071         @
1072         mrs     r0, cpsr
1073         eor     r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE) //切换到SVC,之后的堆栈操作都用R13_svc,sp都是R13_svc的值
1074         msr     spsr_cxsf, r0
1075
1076         @
1077         @ the branch table must immediately follow this code
1078         @
1079         and     lr, lr, #0x0f
1080  THUMB( adr     r0, 1f                  )
1081  THUMB( ldr     lr, [r0, lr, lsl #2]    )
1082         mov     r0, sp
1083  ARM(   ldr     lr, [pc, lr, lsl #2]    )
1084         movs    pc, lr                  @ branch to handler in SVC mode
1085 ENDPROC(vector_\name)

/*
 * PSR bits
 */
#define USR26_MODE    0x00000000
#define FIQ26_MODE    0x00000001
#define IRQ26_MODE    0x00000002
#define SVC26_MODE    0x00000003
#define USR_MODE    0x00000010
#define FIQ_MODE    0x00000011
#define IRQ_MODE    0x00000012
#define SVC_MODE    0x00000013
#define ABT_MODE    0x00000017
#define UND_MODE    0x0000001b
#define SYSTEM_MODE    0x0000001f
#define MODE32_BIT    0x00000010
#define MODE_MASK    0x0000001f
#define PSR_T_BIT    0x00000020
#define PSR_F_BIT    0x00000040
#define PSR_I_BIT    0x00000080
#define PSR_A_BIT    0x00000100
#define PSR_E_BIT    0x00000200
#define PSR_J_BIT    0x01000000
#define PSR_Q_BIT    0x08000000
#define PSR_V_BIT    0x10000000
#define PSR_C_BIT    0x20000000
#define PSR_Z_BIT    0x40000000
#define PSR_N_BIT    0x80000000


<think>我们正在处理一个关于`arch_misc_init`函数返回错误代码-19的问题。错误代码-19通常对应Linux内核中的错误码`ENODEV`,即"No such device"(没有这样的设备)。这意味着在初始化过程中,某个必要的设备没有被找到或者初始化失败。 ### 步骤分析 1. **理解`arch_misc_init`函数**- `arch_misc_init`是U-Boot中的一个函数,通常在板级初始化过程中被调用。它负责执行一些特定于架构的杂项初始化任务。 - 这个函数通常定义在`arch/arm/mach-<soc>/<board>.c`或类似路径的文件中。 2. **错误代码-19的含义**- 在Linux内核错误码中,-19对应`ENODEV`,表示设备不存在或不可用。在U-Boot中,错误码通常沿用相同的含义。 3. **可能的原因**- **硬件设备不存在**函数尝试初始化某个硬件设备,但该设备在系统中不存在(例如,设备树中未正确配置或硬件未连接)。 - **依赖的驱动未加载**:设备依赖的驱动未正确初始化或加载。 - **设备树配置错误**:设备树中相关节点的配置有误,导致设备无法被正确识别或初始化。 - **时钟或电源未正确配置**:设备所需的时钟或电源域未正确设置,导致设备无法工作。 - **早期初始化失败**:在`arch_misc_init`之前调用的某个初始化函数失败,导致后续初始化无法进行。 4. **调试方法**- **查看代码**:检查`arch_misc_init`函数的实现,确定它尝试初始化哪些设备。 - **日志分析**:查看U-Boot启动日志,寻找在`arch_misc_init`之前或期间的错误信息。 - **设备树检查**:检查设备树源文件(dts/dtsi),确认相关设备节点的状态和配置。 - **硬件检查**:确认硬件上相关设备是否存在且连接正确。 ### 解决方案 1. **定位`arch_misc_init`函数**: 找到你的板级代码中`arch_misc_init`函数的定义。例如,在Rockchip平台,该函数可能位于`arch/arm/mach-rockchip/misc.c`。 2. **检查函数实现**: 查看函数中每一步操作,特别是那些可能返回错误码的地方。例如: ```c int arch_misc_init(void) { int ret; ret = setup_macaddr(); if (ret) return ret; #ifdef CONFIG_DM_KEY ret = key_init(); if (ret) return ret; #endif return 0; } ``` 如果函数中有类似上述的步骤,那么错误码-19可能来自`setup_macaddr()`或`key_init()`等子函数。 3. **检查子函数**: 如果`arch_misc_init`调用了其他函数,并且其中一个返回了-19,则需要进一步分析该子函数。例如,`setup_macaddr()`可能尝试从EEPROM或熔丝中读取MAC地址,但未能找到相应的设备。 4. **检查设备树**: 确保设备树中包含了必要的设备节点,并且状态设置为`okay`。例如,如果`setup_macaddr()`依赖于I2C EEPROM,则设备树中应有相应的I2C节点和EEPROM节点。 5. **硬件支持**: 确认硬件上是否存在该设备。例如,如果代码期望从EEPROM读取MAC地址,但硬件上没有焊接EEPROM,那么初始化就会失败。 6. **修复或绕过**- 如果设备不存在且不是必需的,可以修改代码跳过该设备的初始化。 - 如果设备是必需的,则需要检查硬件连接或更换硬件。 - 如果是设备树配置问题,修正设备树并重新编译。 ### 示例:修复MAC地址读取失败 假设错误发生在`setup_macaddr()`函数,并且是因为无法读取EEPROM。我们可以考虑以下两种修复方式: 1. **提供备用的MAC地址设置方式**: 如果EEPROM不存在,可以使用其他方式设置MAC地址(如环境变量)。 2. **跳过错误**(如果允许): 如果该设备不是必需的,可以忽略错误: ```c int arch_misc_init(void) { // 忽略setup_macaddr的错误 setup_macaddr(); // ... 其他初始化 return 0; } ``` ### 总结 错误代码-19(ENODEV)在`arch_misc_init`中通常表示某个设备初始化失败。解决步骤包括: 1. 定位`arch_misc_init`函数及其调用的子函数。 2. 通过日志和代码分析确定具体是哪个设备初始化失败。 3. 检查设备树配置和硬件连接。 4. 根据具体情况修改代码或硬件配置。 §§相关问题§§ 1. U-Boot启动过程中常见的错误代码有哪些? 2. 如何调试U-Boot的板级初始化过程? 3. 设备树配置错误可能导致哪些U-Boot启动问题?</think>### `arch_misc_init` 错误码 -19 的原因与解决方案 错误码 **-19** 在 Linux 内核中对应标准错误号 `ENODEV`(Error No Device),表示 **设备不存在或初始化失败**。在 U-Boot 的 `arch_misc_init` 函数中,此错误通常与硬件设备初始化或依赖项缺失相关。 --- ### **常见原因分析** #### 1. **硬件设备未正确配置** - **设备树(DTS)配置错误**:设备树中缺少关键节点(如时钟、电源管理、外设控制器),或节点状态未启用(`status = "okay"`)。 - **硬件依赖缺失**函数尝试访问未物理存在或未初始化的硬件(如 EEPROM、加密模块、传感器)。 #### 2. **驱动初始化失败** - 依赖的驱动程序未正确加载(如 I2C/SPI 控制器驱动未就绪)。 - 资源冲突(如内存地址、中断号被占用)。 #### 3. **SoC 特定初始化问题** - 芯片特定功能(如安全启动、熔丝配置)未启用或配置错误。 - 早期硬件初始化(如时钟、电源)未完成,导致后续设备无法访问。 #### 4. **代码逻辑错误** - `arch_misc_init` 中的自定义代码尝试访问未初始化的硬件。 - 条件编译(`#ifdef`)错误,导致关键代码未启用。 --- ### **解决方案** #### 1. **检查设备树配置** - 确认相关硬件节点在设备树中已启用(例如 I2C 控制器): ```dts &i2c0 { status = "okay"; // 必须为 "okay" eeprom@50 { compatible = "atmel,24c02"; reg = <0x50>; }; }; ``` - 使用 `fdtdump` 工具验证编译后的设备树(`*.dtb`)是否包含所需节点。 #### 2. **调试 U-Boot 启动日志** - 启用 U-Boot 详细日志: ```bash setenv bootargs "earlycon console=ttyS0,115200 debug"; # 添加 debug 参数 ``` - 检查 `arch_misc_init` 前后的错误日志,定位失败的具体函数(如 I2C 读写、时钟初始化)。 #### 3. **验证硬件依赖** - 确认硬件上是否存在 `arch_misc_init` 尝试访问的设备(如通过原理图检查 EEPROM 是否焊接)。 - 测试硬件访问基础功能(如通过 U-Boot CLI 手动读写 I2C)。 #### 4. **审查板级初始化代码** - 在 U-Boot 源码中检查 `arch_misc_init` 实现(路径通常为 `arch/arm/mach-<soc>/<board>.c`),例如: ```c int arch_misc_init(void) { int ret; ret = setup_crypto_device(); // 可能返回 -ENODEV if (ret) return ret; ... } ``` - 若问题在自定义代码中,需确保所有依赖已初始化(如先初始化 I2C 控制器再访问设备)。 #### 5. **修复驱动初始化顺序** - 确保依赖的驱动在 `arch_misc_init` 前已初始化(如调整 U-Boot 初始化序列中的函数调用顺序)。 - 检查资源冲突(如通过 `dm tree` 命令查看设备模型状态)。 --- ### **典型案例** > **问题描述**: > Rockchip RK3399 平台在 `arch_misc_init` 返回 -19,日志显示 `rk808_read: i2c write failed`。 > **原因**: > I2C 控制器未在设备树中启用,或 PMIC(RK808)供电未配置。 > **修复**: > 1. 在设备树中启用 I2C1 节点。 > 2. 确保 PMIC 的电源使能引脚(GPIO)在 U-Boot 中正确配置。 --- ### **总结步骤** 1. 通过启动日志定位失败的具体操作。 2. 检查设备树中相关硬件节点的状态和配置。 3. 验证硬件是否存在且可访问。 4. 审查 `arch_misc_init` 源码,确认依赖项初始化顺序。 5. 修复设备树或代码后重新编译 U-Boot 并烧录测试。 > 提示:若问题涉及厂商私有代码,需参考 SoC 厂商的 SDK 文档或联系技术支持获取板级初始化规范。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值