Linux-arm64中断现场保护详解
零、源码及详细注释
.macro kernel_ventry, el, label, regsize = 64sub sp, sp, #S_FRAME_SIZE //在堆栈中预留出S_FRAME_SIZE大小的空间.macro kernel_entry, el, regsize = 64.if \regsize == 32mov w0, w0 // zero upper 32 bits of x0.endifstp x0, x1, [sp, #16 * 0] //入栈stp x2, x3, [sp, #16 * 1]stp x4, x5, [sp, #16 * 2]stp x6, x7, [sp, #16 * 3]stp x8, x9, [sp, #16 * 4]stp x10, x11, [sp, #16 * 5]stp x12, x13, [sp, #16 * 6]stp x14, x15, [sp, #16 * 7]stp x16, x17, [sp, #16 * 8]stp x18, x19, [sp, #16 * 9]stp x20, x21, [sp, #16 * 10]stp x22, x23, [sp, #16 * 11]stp x24, x25, [sp, #16 * 12]stp x26, x27, [sp, #16 * 13]stp x28, x29, [sp, #16 * 14].if \el == 0 // 异常等级0 clear_gp_regs // 清空寄存器mrs x21, sp_el0 // x21 = sp_el0 如果异常发生在用户态(EL0)则pt_regs将保存用户态堆栈指针sp_el0 */ldr_this_cpu tsk, __entry_task, x20 // 设置tsk为当前进程的task_struct, x20 = tpidr_el1/tpidr_el2ldr x19, [tsk, #TSK_TI_FLAGS] // x19 = tsk->thread_info.flagsdisable_step_tsk x19, x20 // 关闭mdscr_el1寄存器的SS位apply_ssbd 1, x22, x23 .else //异常等级不为0add x21, sp, #S_FRAME_SIZE //X21保存压入pt_regs数据之前的栈地址(栈框的位置) S_FRAME_SIZE:栈帧的大小get_thread_info tsk //tsk = sp_el0/* Save the task's original addr_limit and set USER_DS */ldr x20, [tsk, #TSK_TI_ADDR_LIMIT] //x20 = tsk->thread_info.addr_limitstr x20, [sp, #S_ORIG_ADDR_LIMIT] //pt_regs->orig_addr_limit = x20mov x20, #USER_DS //X2O = USER_DS = 1<<48str x20, [tsk, #TSK_TI_ADDR_LIMIT] //tsk->thread_info.addr_limit = 1<<48/* No need to reset PSTATE.UAO, hardware's already set it to 0 for us */.endif /* \el == 0 */mrs x22, elr_el1 //x22 = elr_el1mrs x23, spsr_el1 //x23 = spsr_el1stp lr, x21, [sp, #S_LR] //pt_regs.lr = lr,pt_regs.fp = x21 保存lr和fp到栈帧中/** In order to be able to dump the contents of struct pt_regs at the* time the exception was taken (in case we attempt to walk the call* stack later), chain it together with the stack frames.*/.if \el == 0 //异常等级0stp xzr, xzr, [sp, #S_STACKFRAME] //清0 pt_regs.stackframe[0]与[1](详见十一).else //异常等级不为0stp x29, x22, [sp, #S_STACKFRAME] //pt_regs.stackframe[0] = x29 , pt_regs.stackframe[1] = x22 = elr_el1.endifadd x29, sp, #S_STACKFRAME //x29 = pt_regs.stackframe#ifdef CONFIG_ARM64_SW_TTBR0_PANalternative_if ARM64_HAS_PANb 1f // skip TTBR0 PANalternative_else_nop_endif.if \el != 0 //异常等级不为0mrs x21, ttbr0_el1 //x21 = ttbr0_el1tst x21, #TTBR_ASID_MASK //检测ttbr0_el1的48-63位的ASIDorr x23, x23, #PSR_PAN_BIT //设置spsr_el1的PAN位b.eq 1f // TTBR0 access already disabledand x23, x23, #~PSR_PAN_BIT // Clear the emulated PAN in the saved SPSR.endif__uaccess_ttbr0_disable x211:#endifstp x22, x23, [sp, #S_PC] //pt_regs.pc = x22 = elr_el1 pt_regs.pstate = x23 = spsr_el1/* Not in a syscall by default (el0_svc overwrites for real syscall) */.if \el == 0 //异常等级为0mov w21, #NO_SYSCALL //w21 = NO_SYSCALLstr w21, [sp, #S_SYSCALLNO] //pt_regs.syscallno = w21 = NO_SYSCALL.endif/** Set sp_el0 to current thread_info.*/.if \el == 0 //异常等级为0msr sp_el0, tsk //sp_el0 = tsk.endif/** Registers that may be useful after this macro is invoked:** x21 - aborted SP* x22 - aborted PC* x23 - aborted PSTATE*/.endm
注解:
一、ldr_this_cpu
ldr_this_cpu tsk, __entry_task, x20</arch/arm64/include/asm/assembler.h>macro ldr_this_cpu dst, sym, tmpadr_l \dst, \sym //tsk = __entry_taskalternative_if_not ARM64_HAS_VIRT_HOST_EXTNmrs \tmp, tpidr_el1 //x20 = tpidr_el1 读取per_cpu变量时,计算偏移alternative_elsemrs \tmp, tpidr_el2 //x20 = tpidr_el2 TPIDR_EL1, EL1 Software Thread ID Registeralternative_endifldr \dst, [\dst, \tmp] //ldr tsk, [tsk,x20] task += TID 获取__entry_task变量.endm
二、__entry_task
__entry_task为内核静态定义的percpu变量,在进程切换时,会将next进程的进程描述符保存到该变量中
/** We store our current task in sp_el0, which is clobbered by userspace. Keep a* shadow copy so that we can restore this upon entry from userspace.** This is *only* for exception entry from EL0, and is not valid until we* __switch_to() a user task.*/DEFINE_PER_CPU(struct task_struct *, __entry_task);static void entry_task_switch(struct task_struct *next){__this_cpu_write(__entry_task, next);}
三、per_cpu变量获取offset
static inline unsigned long __my_cpu_offset(void){unsigned long off;/** We want to allow caching the value, so avoid using volatile and* instead use a fake stack read to hazard against barrier().*/asm(ALTERNATIVE("mrs %0, tpidr_el1","mrs %0, tpidr_el2",ARM64_HAS_VIRT_HOST_EXTN): "=r" (off) :"Q" (*(const unsigned long *)current_stack_pointer));return off;}
四、偏移
</arch/arm64/kernel/asm-offsets.c>DEFINE(TSK_TI_FLAGS, offsetof(struct task_struct, thread_info.flags));DEFINE(TSK_TI_ADDR_LIMIT, offsetof(struct task_struct, thread_info.addr_limit));DEFINE(S_ORIG_ADDR_LIMIT, offsetof(struct pt_regs, orig_addr_limit));
五、disable_step_tsk
disable_step_tsk x19, x20.macro disable_step_tsk, flgs, tmptbz \flgs, #TIF_SINGLESTEP, 9990f //判断x19的#TIF_SINGLESTEP位是否为0,若为0,跳转到9990fmrs \tmp, mdscr_el1 //x20 = mdscr_el1bic \tmp, \tmp, #1 //清除x20的第0位,SS:Software step disabledmsr mdscr_el1, \tmp //mdscr_el1 = x20isb // Synchronise with enable_dbg9990:.endm
六、get_thread_info
.macro get_thread_info, rdmrs \rd, sp_el0 // \rd = sp_el0.endm
七、S_FRAME_SIZE
DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
八、struct pt_regs
/** This struct defines the way the registers are stored on the stack during an* exception. Note that sizeof(struct pt_regs) has to be a multiple of 16 (for* stack alignment). struct user_pt_regs must form a prefix of struct pt_regs.*/struct pt_regs {union {struct user_pt_regs user_regs;struct {u64 regs[31];u64 sp;u64 pc;u64 pstate;};};u64 orig_x0;#ifdef __AARCH64EB__u32 unused2;s32 syscallno;#elses32 syscallno;u32 unused2;#endifu64 orig_addr_limit;u64 unused; // maintain 16 byte alignmentu64 stackframe[2];};
九、thread_info
struct thread_info {unsigned long flags; /* low level flags */mm_segment_t addr_limit; /* address limit */#ifdef CONFIG_ARM64_SW_TTBR0_PANu64 ttbr0; /* saved TTBR0_EL1 */#endifunion {u64 preempt_count; /* 0 => preemptible, <0 => bug */struct {#ifdef CONFIG_CPU_BIG_ENDIANu32 need_resched;u32 count;#elseu32 count;u32 need_resched;#endif} preempt;};};
十、USER_DS
#define USER_DS TASK_SIZE_64#define TASK_SIZE_64 (UL(1) << VA_BITS)
十一、S_LR与S_STACKFRAME与S_STACKFRAME
DEFINE(S_STACKFRAME, offsetof(struct pt_regs, stackframe));DEFINE(S_LR, offsetof(struct pt_regs, regs[30]));DEFINE(S_STACKFRAME, offsetof(struct pt_regs, stackframe));#define S_PC 256 /* offsetof(struct pt_regs, pc) */