Linux内核中维护进程独立寄存器失败问题排查
我在gem5中修改了ARM硬件,添加了两个寄存器,希望它们像ttbr0一样具备进程独立性,每个进程拥有各自对应的寄存器值。我对Linux 5.15.36内核进行了如下修改,但出现了无意义的寄存器值变为0的故障:
- 在
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; // <================ 我的寄存器 };
- 在
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));
- 在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 ..... } }
- 在
/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

