当前位置: 首页 > article >正文

Linux-arm64中断现场保护详解

零、源码及详细注释

.macro kernel_ventry, el, label, regsize = 64

    sub sp, sp, #S_FRAME_SIZE     //在堆栈中预留出S_FRAME_SIZE大小的空间

.macro  kernel_entry, el, regsize = 64

    .if \regsize == 32

    mov w0, w0              // zero upper 32 bits of x0

    .endif

    stp 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_el2

    ldr x19, [tsk, #TSK_TI_FLAGS]           // x19 = tsk->thread_info.flags

    disable_step_tsk x19, x20               // 关闭mdscr_el1寄存器的SS位

    apply_ssbd 1, x22, x23          

    .else                                   //异常等级不为0

    add 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_limit

    str x20, [sp, #S_ORIG_ADDR_LIMIT]       //pt_regs->orig_addr_limit = x20

    mov x20, #USER_DS                       //X2O = USER_DS = 1<<48

    str 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_el1

    mrs x23, spsr_el1                      //x23 = spsr_el1

    stp 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                        //异常等级0

    stp xzr, xzr, [sp, #S_STACKFRAME]   //清0 pt_regs.stackframe[0]与[1](详见十一)

    .else                               //异常等级不为0

    stp x29, x22, [sp, #S_STACKFRAME]   //pt_regs.stackframe[0] = x29 , pt_regs.stackframe[1] = x22 = elr_el1

    .endif

    add x29, sp, #S_STACKFRAME          //x29 = pt_regs.stackframe

#ifdef CONFIG_ARM64_SW_TTBR0_PAN

alternative_if ARM64_HAS_PAN

    b   1f              // skip TTBR0 PAN

alternative_else_nop_endif

    .if \el != 0                        //异常等级不为0

    mrs x21, ttbr0_el1                  //x21 = ttbr0_el1

    tst x21, #TTBR_ASID_MASK            //检测ttbr0_el1的48-63位的ASID

    orr x23, x23, #PSR_PAN_BIT          //设置spsr_el1的PAN位

    b.eq    1f              // TTBR0 access already disabled

    and x23, x23, #~PSR_PAN_BIT     // Clear the emulated PAN in the saved SPSR

    .endif

    __uaccess_ttbr0_disable x21

1:

#endif

    stp 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                                //异常等级为0

    mov w21, #NO_SYSCALL                        //w21 = NO_SYSCALL

    str w21, [sp, #S_SYSCALLNO]                 //pt_regs.syscallno = w21 = NO_SYSCALL

    .endif

    /*

     * Set sp_el0 to current thread_info.

     */

    .if \el == 0                              //异常等级为0

    msr 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, tmp

    adr_l   \dst, \sym                                      //tsk = __entry_task

alternative_if_not ARM64_HAS_VIRT_HOST_EXTN

    mrs \tmp, tpidr_el1                                     //x20 = tpidr_el1  读取per_cpu变量时,计算偏移

alternative_else

    mrs \tmp, tpidr_el2                                    //x20 = tpidr_el2 TPIDR_EL1, EL1 Software Thread ID Register

alternative_endif

    ldr \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, tmp

    tbz \flgs, #TIF_SINGLESTEP, 9990f      //判断x19的#TIF_SINGLESTEP位是否为0,若为0,跳转到9990f

    mrs \tmp, mdscr_el1                    //x20 = mdscr_el1

    bic \tmp, \tmp, #1                     //清除x20的第0位,SS:Software step disabled

    msr mdscr_el1, \tmp                    //mdscr_el1 = x20

    isb // Synchronise with enable_dbg

9990:

    .endm

六、get_thread_info

   

 .macro  get_thread_info, rd

    mrs \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;

#else

    s32 syscallno;

    u32 unused2;

#endif

    u64 orig_addr_limit;

    u64 unused; // maintain 16 byte alignment

    u64 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_PAN

    u64         ttbr0;      /* saved TTBR0_EL1 */

#endif

    union {

        u64     preempt_count;  /* 0 => preemptible, <0 => bug */

        struct {

#ifdef CONFIG_CPU_BIG_ENDIAN

            u32 need_resched;

            u32 count;

#else

            u32 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) */


http://www.kler.cn/news/284941.html

相关文章:

  • MySQL 集群技术全攻略:从搭建到优化(上)
  • 分类模型评估指标——准确率、精准率、召回率、F1、ROC曲线、AUC曲线
  • 快递盒检测检测系统源码分享 # [一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
  • RAG 向量数据库:掌握 Elasticsearch 作为向量数据库的终极指南
  • 【Python零基础】文件使用和异常处理
  • Vue(四) 组件、单文件组件、非单文件组件,重要的内置关系
  • 【计组 | Cache原理】讲透Cache的所有概念与题型方法
  • 大模型好书案例——《BERT基础教程:Transformer大模型实战》(附PDF)
  • LuaJit分析(一)LuaJit交叉编译
  • TCP的连接与断开
  • java基础开发-xstream解析xml
  • 去中心化(Decentralization)
  • leetcode1514 最大概率路径(Bellman-ford算法详解)
  • 栈算法【基于顺序表】
  • centos 系统yum 安装 mariadb
  • UML类图中的组合关系
  • Vue3 + Axios双Token刷新解决方案
  • MySQL——多表操作(四)子查询(1)带 IN 关键字的子查询
  • Xilinx高速接口之GTP
  • CSS 预处理器
  • 10、ollama启动LLama_Factory微调大模型(llama.cpp)
  • opencv之形态学
  • 喜羊羊做Python真题
  • 基于Android+SQLite数据库开发Java考试App
  • 深度学习100问15:什么是交叉熵误差
  • 【Linux】Linux Bash Shell 教程
  • 工程师们都爱看的Docker容器技术,一看就会!保姆级教程(上)
  • Nginx负载均衡请求队列配置:优化流量管理
  • MySQL:简述事务的SQL操作
  • K8S Job