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

常州专业网站建设公司咨询深圳建设门户网站

常州专业网站建设公司咨询,深圳建设门户网站,株洲市网站关键词优化公司,个人可以做网站导航的网站吗http://blog.chinaunix.net/uid-69947851-id-5830546.html 本章接着《Linux内核启动》部分讲解#xff0c;我们知道了在进入start_kernel之前#xff0c;通过指令adr_l x8, vectors#xff1b;msr vbar_el1, x8设置了异常向量表#xff0c;那么异常向量表的结构是怎么样…http://blog.chinaunix.net/uid-69947851-id-5830546.html 本章接着《Linux内核启动》部分讲解我们知道了在进入start_kernel之前通过指令adr_l   x8, vectorsmsr vbar_el1, x8设置了异常向量表那么异常向量表的结构是怎么样的呢在armv8中每个异常的 向量地址不再是4字节而是0x80字节可以放更多的代码在向量表里面因此 点击(此处)折叠或打开 ENTRY(vectors)    kernel_ventry 1, sync_invalid // Synchronous EL1t    kernel_ventry 1, irq_invalid // IRQ EL1t    kernel_ventry 1, fiq_invalid // FIQ EL1t    kernel_ventry 1, error_invalid // Error EL1t    kernel_ventry 1, sync // Synchronous EL1h    kernel_ventry 1, irq // IRQ EL1h    kernel_ventry 1, fiq_invalid // FIQ EL1h    kernel_ventry 1, error // Error EL1h    kernel_ventry 0, sync // Synchronous 64-bit EL0    kernel_ventry 0, irq // IRQ 64-bit EL0    kernel_ventry 0, fiq_invalid // FIQ 64-bit EL0    kernel_ventry 0, error // Error 64-bit EL0#ifdef CONFIG_COMPAT    kernel_ventry 0, sync_compat, 32 // Synchronous 32-bit EL0    kernel_ventry 0, irq_compat, 32 // IRQ 32-bit EL0    kernel_ventry 0, fiq_invalid_compat, 32 // FIQ 32-bit EL0    kernel_ventry 0, error_compat, 32 // Error 32-bit EL0#else    kernel_ventry 0, sync_invalid, 32 // Synchronous 32-bit EL0    kernel_ventry 0, irq_invalid, 32 // IRQ 32-bit EL0    kernel_ventry 0, fiq_invalid, 32 // FIQ 32-bit EL0    kernel_ventry 0, error_invalid, 32 // Error 32-bit EL0#endifEND(vectors) 从上图可以了解到一条kernel_ventry 为一个异常但是kernel_ventry的展开需要对齐到0x80不够的部分用nop填充。通过上图我们可以知道armv8有4张向量表每张向量表有4中异常同步异常、irq异常、fiq异常、系统错误异常而4张表分别对应 1、发生中断时异常等级不发生变化并且不管怎么异常模式sp只用SP_EL0 2、发生中断时异常等级不发生变化并且sp用对应异常私有的SP_ELx 3、发生中断时异常等级发生变化这种情况一般是用户态向内核态发生迁移当前表示64位用户态向64位内核态发生迁移 4、发生中断时异常等级发生变化这种情况一般是用户态向内核态发生迁移当前表示32位用户态向64位/32位内核发生迁移 下面我们来看看kernel_ventry的实现 点击(此处)折叠或打开 .macro kernel_ventry, el, label, regsize  64    .align 7    sub sp, sp, #S_FRAME_SIZE // 将sp预留一个fram_size 这个size 就是struct pt_regs的大小#ifdef CONFIG_VMAP_STACK    ....这里省略掉检查栈溢出的代码#endif    b el\()\el\()_\label    // 跳转到对应级别的异常处理函数 kernel_entry 1, irq为el1_irq.endm 对于向量表vectors中的kernel_ventry 1, irq ,  则 b el\()\el\()_\label跳转到el1_irq函数。 其中1表示的是从哪个异常模式产生的比如是User-kernel就是0. kernel-kernel就是1. 下面接着看el1_irq实现 el1_irq是内核运行期间产生了中断 点击(此处)折叠或打开 el1_irq:    kernel_entry 1    // 跟进入C函数需要压栈的道理一样 这里进入内核空间需要保存寄存器到pt_regs也就是前面kernel_ventry  sp预留的空间当中。    enable_da_f    irq_handler   // 中断处理函数#ifdef CONFIG_PREEMPT    ldr w24, [tsk, #TSK_TI_PREEMPT] // get preempt count    cbnz w24, 1f // preempt count ! 0    ldr x0, [tsk, #TSK_TI_FLAGS] // get flags    tbz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling?    bl el1_preempt     // 支持内核抢占会在这里判断是否需要调度到新的进程。1:#endif    kernel_exit 1  // 这里是kernel_entry 1的逆向过程弹出栈就是还原寄存器ENDPROC(el1_irq)el1_preempt:  // 内核抢占    mov x24, lr  // 保存lr寄存器1:  bl  preempt_schedule_irq        // irq en/disable is done inside    ldr x0, [tsk, #TSK_TI_FLAGS]    //获取新进程的标志TI_FLAGS    tbnz    x0, #TIF_NEED_RESCHED, 1b   // 如果还有需要调度的进程继续抢占    ret x24 下面看看kernel_entry的实现 点击(此处)折叠或打开 .macro kernel_entry, el, regsize  64    /* 这里用stp指令将x0-x29保存到预留的栈中保存顺序为下面结构体顺序    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];    }   */    stp x0, x1, [sp, #16 * 0]  -pt_regs.regs[0] 和pt_regs.regs[1]    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]    //如果el为0 表示从用户态产生的异常    .if \el  0    clear_gp_regs   // 清除 x0-x29寄存器    mrs x21, sp_el0 // 将用户态的sp指针保存到x21寄存器    ldr_this_cpu tsk, __entry_task, x20 // 从当前per_cpu读取当前的task_struct地址    ldr x19, [tsk, #TSK_TI_FLAGS] // 获取task-flag标记    disable_step_tsk x19, x20 // exceptions when scheduling.    .else    // 从内核状态产生的异常    add x21, sp, #S_FRAME_SIZE  // X21保存压入pt_regs数据之前的栈地址也就是异常时内核的栈地址    get_thread_info tsk   // 这里是从sp_el0从获取到当前task_struct结构在启动篇看到内核状态的时候sp_el0用于保存内核的task_struct结构用户态的时候 这个sp_el0是用户态的sp    /* 保存tasks original addr_limit 然后设置USER_DS */    ldr x20, [tsk, #TSK_TI_ADDR_LIMIT]    str x20, [sp, #S_ORIG_ADDR_LIMIT]    mov x20, #USER_DS    str x20, [tsk, #TSK_TI_ADDR_LIMIT]    .endif /* \el  0 */    // x22保存异常地址    mrs x22, elr_el1    // x23保存程序状态寄存器    mrs x23, spsr_el1    stp lr, x21, [sp, #S_LR] // 将lr和sp保存到pt_regs-x[30], pt_rets-sp    // 如果发生在el1模式则将x29和异常地址保存到pt_regs-stackframe    .if \el  0    stp xzr, xzr, [sp, #S_STACKFRAME]    .else    stp x29, x22, [sp, #S_STACKFRAME]    .endif    add x29, sp, #S_STACKFRAME    stp x22, x23, [sp, #S_PC] // 将异常和程序状态 保存到pt_regs-pstate和pt__regs-pc    // 如果是el0-el1发了变迁 那么将当前的task_struct给sp_el0保存    .if \el  0    msr sp_el0, tsk    .endif    /*     * Registers that may be useful after this macro is invoked:     *     * x21 - aborted SP     * x22 - aborted PC     * x23 - aborted PSTATE    */.endm irq_handler为中断实现函数具体实现如下 点击(此处)折叠或打开 .macro irq_handler    ldr_l x1, handle_arch_irq   // 将handle_arch_irq的地址放入x1寄存器    mov x0, sp    irq_stack_entry  // 中断入口 这里主要是切换成中断栈    blr x1          // 跳转到handle_arch_irq函数运行这个函数是gic驱动加载的时候设置的否则是invilid    irq_stack_exit.endm//C语言设置回调函数int __init set_handle_irq(void (*handle_irq)(struct pt_regs *)){    if (handle_arch_irq)        return -EBUSY;    handle_arch_irq  handle_irq;    return 0;}   点击(此处)折叠或打开 .macro irq_stack_entry    mov x19, sp // 保存当前sp到x19    /*     * 判断当前栈是不是中断栈 如果是任务栈就从per_cpu中读取中断栈地址并切换到中断栈     */    ldr x25, [tsk, TSK_STACK]    eor x25, x25, x19    and x25, x25, #~(THREAD_SIZE - 1)    cbnz x25, 9998f    ldr_this_cpu x25, irq_stack_ptr, x26  // 读取per_cpu的irq_stack_ptr    mov x26, #IRQ_STACK_SIZE    add x26, x25, x26    /* 切换到中断栈 */    mov sp, x269998:.endm 如果是用户态发生中断异常则进入el0_irq, 实现如下 点击(此处)折叠或打开 el0_irq:    kernel_entry 0   // 和el1_irq一样只是这传入的是0表示 用户态发生异常    enable_da_f    ct_user_exit     irq_handler  // 中断回调    b ret_to_user  // 中断返回ENDPROC(el0_irq) 点击(此处)折叠或打开 work_pending:    mov x0, sp // regs    bl do_notify_resume     // 用户抢占调度和处理信号    ldr x1, [tsk, #TSK_TI_FLAGS] // re-check for single-step    b finish_ret_to_userret_to_user:    disable_daif    ldr x1, [tsk, #TSK_TI_FLAGS]    and x2, x1, #_TIF_WORK_MASK    cbnz x2, work_pending   // 判断是否有信号或者任务挂起finish_ret_to_user:    enable_step_tsk x1, x2    kernel_exit 0           // 恢复栈ENDPROC(ret_to_user) do_notify_resume函数用于用户抢占和信号处理 实现大概如下 点击(此处)折叠或打开 asmlinkage void do_notify_resume(struct pt_regs *regs,                 unsigned long thread_flags){    trace_hardirqs_off();    do {        /* Check valid user FS if needed */        addr_limit_user_check();        if (thread_flags _TIF_NEED_RESCHED) {            /* Unmask Debug and SError for the next task */            local_daif_restore(DAIF_PROCCTX_NOIRQ);            schedule();   // 重新调度新的进程        } else {            local_daif_restore(DAIF_PROCCTX);            if (thread_flags _TIF_UPROBE)                uprobe_notify_resume(regs);            if (thread_flags _TIF_SIGPENDING)                do_signal(regs);   // 信号处理            if (thread_flags _TIF_NOTIFY_RESUME) {                clear_thread_flag(TIF_NOTIFY_RESUME);                tracehook_notify_resume(regs);  // 工作队列                rseq_handle_notify_resume(NULL, regs);            }            if (thread_flags _TIF_FOREIGN_FPSTATE)                fpsimd_restore_current_state();        }        local_daif_mask();        thread_flags  READ_ONCE(current_thread_info()-flags);    } while (thread_flags _TIF_WORK_MASK);} 至于el1_sync同步异常包含系统调用数据异常指令异常等这里就不多做说明了 原理是一样的如 1、数据异常:el0/1_sync-el1_da-do_mem_abort-do_page_fault. 2、系统调用:el0_sync-el0_svc-el0_svc_handler-el0_svc_common(__NR_syscalls, sys_call_table)-invoke_syscall
http://www.hkea.cn/news/14413116/

相关文章:

  • 我想学习做网站做渠道的网站有哪些
  • ip38域名信息查询网站找做网站的客户
  • dw做aspx网站如何做产品的网络推广
  • 域名估价网站wordpress 媒体
  • 怎么做二维码让别人扫码进入网站装修公司最怕三种人
  • 网站改版的方式大致为局网站建设管理整改情况
  • 鹤壁做网站公司国外免费ip地址
  • 推广网站2024如皋建设医院网站
  • 天河区门户网站教育专栏天元建设集团有限公司王士坤
  • 百度搜索什么关键词能搜到网站网站资讯创作
  • 最有效的网站推广公司杭州专业网站营销
  • 贵州建设监理网站培训通知栏营销型网站建设的特点表现
  • 绍兴做网站的公司电子商务网站建设ppt模板下载
  • 怎样查到一些做品牌包的网站网站备案一天通过
  • 蓝色系的网站优秀的企业网站
  • 福州网站建设效果营销型网站建设实战
  • 唐山网站建设找煌途微信小程序制作文档
  • 2023免费网站推广沈阳做机床的公司网站
  • 如何看网站做没做推广wordpress 本地 搭建
  • 学校英文版网站建设方案ios wordpress使用
  • 建设银行网站查询网站专业优化公司
  • 网站的360度全景图片怎么做关键词优化哪家好
  • 网站外连移动建站平台
  • 合肥建站公司哪益阳网络营销
  • 网站数据库是干什么的网站设计与建设第一章
  • 广东万高建设网站免费建站免费网站
  • 优惠券怎做网站wordpress自动采集源码
  • 做网站编程语言用软件做的网站权限管理
  • 贵州网站开发流程北京app开发制作
  • 做网站是不是就能上传东西网页动效 wordpress