I modified the arm's hardware (on gem5) to add two registers. I want them to be process independent, each process has its own corresponding value stored in these registers, just like ttbr0
.
I would like to modify the Linux Kernel (v5.15.36) to maintain these two registers. I have made the following changes, but there is a failure that the value of these two registers becomes 0 which is nosense.
First I added the variables corresponding to these two registers in pt_regs
at arch/arm64/include/asm/ptrace.h
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; // <================ my register
u64 inst_priv_base; // <================ my register
};
Also I added the corresponding macro definition at 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));
I'll init these two registers when in copying_thread
(a child function for fork
) at
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;
// <=============== init the register
childregs->inst_priv_base = isa_grid_get_inst_base_pa(user_id);
regs->isa_domain = 1;
// other code .....
}
}
Finally, I save and restore them on the stack in kernel_entry
and kernel_exit
at /arch/arm64/kernel/entry.S
.
.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
But it seems there are times when the registers will have a value of 0 (I wouldn't initialize these two registers to 0) Is there some maintenance step I'm missing?
Eventually I fixed the bug. The bug wasn't caused by my failure to maintain context restoration and saving, but by the fact that my initialization didn't take into account cases like the init
process and the User-mode Helper
, where a kernel thread is turned into a user process via kernel_execve
.
Also, my previous approach of maintaining registers in pt_regs
was inappropriate (but correct); for per-process-independent variables, they don't need to be maintained every time an exception or interrupt occurs, but only when the process switches. I have now modified the __switch_to()
function in the arch/arm64/kernel/process.c
file to maintain the variables. It works fine and efficiently now!