You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux内核中维护进程独立寄存器失败问题排查

问题描述

我在gem5中修改了ARM硬件,添加了两个寄存器,希望它们像ttbr0一样具备进程独立性,每个进程拥有各自对应的寄存器值。我对Linux 5.15.36内核进行了如下修改,但出现了无意义的寄存器值变为0的故障:

  1. 在arch/arm64/include/asm/ptrace.h的pt_regs中添加对应变量:
struct pt_regs {
    union {
        struct user_pt_regs user_regs;
        struct {
            u64 regs[31];
            u64 sp;
            u64 pc;
            u64 pstate;
        };
    };

    // other original code .......

    u64 isa_domain;     // <================ 我的寄存器
    u64 inst_priv_base; // <================ 我的寄存器
};
  1. 在arch/arm/kernel/asm-offsets.h中添加对应宏定义:
DEFINE(S_ISA_DOMAIN,          offsetof(struct pt_regs, isa_domain));
  DEFINE(S_INST_PRIV_BASE,          offsetof(struct pt_regs, inst_priv_base));
  1. 在fork的子函数copy_thread中初始化寄存器:
int copy_thread(unsigned long clone_flags, unsigned long stack_start,
        unsigned long stk_sz, struct task_struct *p, unsigned long tls)
{
    struct pt_regs *childregs = task_pt_regs(p);
    memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context));

    fpsimd_flush_task_state(p);

    ptrauth_thread_init_kernel(p);

    if (likely(!(p->flags & (PF_KTHREAD | PF_IO_WORKER)))) {
        *childregs = *current_pt_regs();
        childregs->regs[0] = 0;
        // <=============== 初始化寄存器
        childregs->inst_priv_base = isa_grid_get_inst_base_pa(user_id);
        regs->isa_domain = 1;
        // other code .....
    }
}
  1. 在/arch/arm64/kernel/entry.S的kernel_entry和kernel_exit中完成寄存器的栈上保存与恢复:
.macro  kernel_entry, el, regsize = 64
    // some original code
    stp x24, x25, [sp, #16 * 12]
    stp x26, x27, [sp, #16 * 13]
    stp x28, x29, [sp, #16 * 14]

    // ISA-Grid
    .if \el == 0
    mrs x20, S3_7_C7_C1_0
    str x20, [sp, #S_ISA_DOMAIN]
    mrs x20, S3_7_C7_C1_1
    str x20, [sp, #S_INST_PRIV_BASE]
    .endif
    // some original code
.endm

.macro  kernel_exit, el
    // some original code
    apply_ssbd 0, x0, x1
    .endif

    msr elr_el1, x21            // set up the return data
    msr spsr_el1, x22

    // ISA-Grid
    .if \el == 0
    ldr x20, [sp, #S_ISA_DOMAIN]
    msr S3_7_C7_C1_0, x20
    ldr x20, [sp, #S_INST_PRIV_BASE]
    msr S3_7_C7_C1_1, x20
    .endif

    ldp x0, x1, [sp, #16 * 0]
    ldp x2, x3, [sp, #16 * 1]
    ldp x4, x5, [sp, #16 * 2]
    ldp x6, x7, [sp, #16 * 3]
    ldp x8, x9, [sp, #16 * 4]
    // some original code
.endm

但仍出现寄存器值变为0的情况(我并未将其初始化为0),请问我遗漏了哪些维护步骤?


遗漏的维护步骤

1. 进程切换时的上下文同步

你仅处理了用户态进出内核时的寄存器保存/恢复,但**进程切换(context switch)**时,内核需要将当前进程的寄存器值持久化到task_struct,并加载下一个进程的寄存器值。

在ARM64中,进程切换逻辑在arch/arm64/kernel/process.c的cpu_switch_to函数(或对应汇编实现)中,需添加以下操作:

  • 切换出当前进程时,将S3_7_C7_C1_0和S3_7_C7_C1_1的值保存到当前进程的thread_struct;
  • 切换到新进程时,从新进程的thread_struct中取出对应值,写入硬件寄存器。

2. 扩展struct thread_struct存储进程上下文

pt_regs仅用于中断/异常的临时上下文保存,进程的持久化上下文需存在struct thread_struct(定义在arch/arm64/include/asm/processor.h)中:

struct thread_struct {
    // 原有字段...
    u64 isa_domain;
    u64 inst_priv_base;
};

同时在copy_thread中,除了初始化pt_regs,还要同步初始化p->thread.isa_domain和p->thread.inst_priv_base,保证上下文一致性。

3. 修复copy_thread中的笔误

代码中regs->isa_domain = 1;的regs未定义,应改为childregs->isa_domain = 1;,否则子进程的isa_domain未被正确初始化。

4. 内核线程的上下文初始化

当前仅对非内核线程做了寄存器初始化,但内核线程创建时也会触发进程切换,若未初始化其寄存器上下文,切换到内核线程时可能将寄存器置0,后续切回用户态时就会读到错误值。需在copy_thread的内核线程分支中,初始化这两个寄存器的默认值(如0或合适的内核态值)。

5. 覆盖所有异常返回路径

除kernel_exit外,ARM64还有其他返回用户态的路径(如eret_to_user宏),需检查所有返回路径,确保都正确恢复这两个寄存器的值。

6. gem5硬件模拟的寄存器状态同步

由于你修改了gem5的硬件寄存器,需在gem5的ArmISA CPU模型中,将这两个寄存器添加到进程上下文的状态列表中,否则模拟时寄存器值可能未被正确保存。


内容的提问来源于stack exchange,提问作者Thysrael

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 16:33:20