armv8 el0和el1 异常处理的过程

来源:互联网 发布:淘宝c店都去哪了 编辑:程序博客网 时间:2024/05/14 03:13
ARM64/ARMv8内核对用户态指令的异常处理流程如下:kernel对el1和el0 处理的vector table在如下路径中arch/arm64/kernel/entry.s/* * Exception vectors. */.pushsection ".entry.text", "ax".align11ENTRY(vectors)ventryel1_sync_invalid// Synchronous EL1tventryel1_irq_invalid// IRQ EL1tventryel1_fiq_invalid// FIQ EL1tventryel1_error_invalid// Error EL1tventryel1_sync// Synchronous EL1hventryel1_irq// IRQ EL1hventryel1_fiq_invalid// FIQ EL1hventryel1_error_invalid// Error EL1hventryel0_sync// Synchronous 64-bit EL0ventryel0_irq// IRQ 64-bit EL0ventryel0_fiq_invalid// FIQ 64-bit EL0ventryel0_error_invalid// Error 64-bit EL0#ifdef CONFIG_COMPATventryel0_sync_compat// Synchronous 32-bit EL0ventryel0_irq_compat// IRQ 32-bit EL0ventryel0_fiq_invalid_compat// FIQ 32-bit EL0ventryel0_error_invalid_compat// Error 32-bit EL0#elseventryel0_sync_invalid// Synchronous 32-bit EL0ventryel0_irq_invalid// IRQ 32-bit EL0ventryel0_fiq_invalid// FIQ 32-bit EL0ventryel0_error_invalid// Error 32-bit EL0#endifEND(vectors)由于用户态是在el0,因此当用户态指令发生异常时会陷入到kernel中的。会执行ventryel0_sync// Synchronous 64-bit EL0在el0_sync 中会进一步判断具体是哪种异常el0_sync:kernel_entry 0mrsx25, esr_el1// read the syndrome registerlsrx24, x25, #ESR_ELx_EC_SHIFT// exception classcmpx24, #ESR_ELx_EC_SVC64// SVC in 64-bit stateb.eqel0_svccmpx24, #ESR_ELx_EC_DABT_LOW// data abort in EL0b.eqel0_dacmpx24, #ESR_ELx_EC_IABT_LOW// instruction abort in EL0b.eqel0_iacmpx24, #ESR_ELx_EC_FP_ASIMD// FP/ASIMD accessb.eqel0_fpsimd_acccmpx24, #ESR_ELx_EC_FP_EXC64// FP/ASIMD exceptionb.eqel0_fpsimd_exccmpx24, #ESR_ELx_EC_SYS64// configurable trapb.eqel0_syscmpx24, #ESR_ELx_EC_SP_ALIGN// stack alignment exceptionb.eqel0_sp_pccmpx24, #ESR_ELx_EC_PC_ALIGN// pc alignment exceptionb.eqel0_sp_pccmpx24, #ESR_ELx_EC_UNKNOWN// unknown exception in EL0b.eqel0_undefcmpx24, #ESR_ELx_EC_BREAKPT_LOW// debug exception in EL0b.geel0_dbgbel0_inv这里以指令异常为例arch/arm64/kernel/entry.sel0_undef:/* * Undefined instruction */// enable interrupts before calling the main handlerenable_dbg_and_irqct_user_exitmovx0, sp//调用do_undefinstrbldo_undefinstrbret_to_userarch/arm64/kernel/traps.c asmlinkage void __exception do_undefinstr(struct pt_regs *regs){/* check for AArch32 breakpoint instructions */if (!aarch32_break_handler(regs))return;//调用call_undef_hook 处理异常if (call_undef_hook(regs) == 0)return;//如果处理不了kill掉这个进程force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0);}static int call_undef_hook(struct pt_regs *regs){struct undef_hook *hook;unsigned long flags;u32 instr;int (*fn)(struct pt_regs *regs, u32 instr) = NULL;void __user *pc = (void __user *)instruction_pointer(regs);//如果不是user space则退出if (!user_mode(regs))return 1;raw_spin_lock_irqsave(&undef_lock, flags);//在异常undef_hook这个list中找这个异常对应的回调函数,匹配规则是instr_val和pstate_val要和寄存器中的相等list_for_each_entry(hook, &undef_hook, node)if ((instr & hook->instr_mask) == hook->instr_val &&(regs->pstate & hook->pstate_mask) == hook->pstate_val)fn = hook->fn;raw_spin_unlock_irqrestore(&undef_lock, flags);exit:return fn ? fn(regs, instr) : 1;}所以什么时候往undef_hook中添加回调函数呢?不同的arm系列调用的函数不同,这里以armv8为例arch/arm64/kernel/armv8_deprecated.c/* * Invoked as late_initcall, since not needed before init spawned. */static int __init armv8_deprecated_init(void){从这里看到异常指令只要分为3类。都是调用register_insn_emulationif (IS_ENABLED(CONFIG_SWP_EMULATION))register_insn_emulation(&swp_ops);if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION))register_insn_emulation(&cp15_barrier_ops);if (IS_ENABLED(CONFIG_SETEND_EMULATION)) {if(system_supports_mixed_endian_el0())register_insn_emulation(&setend_ops);elsepr_info("setend instruction emulation is not supported on this system\n");}return 0;}late_initcall(armv8_deprecated_init);static void __init register_insn_emulation(struct insn_emulation_ops *ops){unsigned long flags;struct insn_emulation *insn;insn = kzalloc(sizeof(*insn), GFP_KERNEL);insn->ops = ops;insn->min = INSN_UNDEF;switch (ops->status) {case INSN_DEPRECATED:insn->current_mode = INSN_EMULATE;/* Disable the HW mode if it was turned on at early boot time */run_all_cpu_set_hw_mode(insn, false);insn->max = INSN_HW;break;case INSN_OBSOLETE:insn->current_mode = INSN_UNDEF;insn->max = INSN_EMULATE;break;}raw_spin_lock_irqsave(&insn_emulation_lock, flags);//把定义的异常指令加到insn_emulation 中list_add(&insn->node, &insn_emulation);nr_insn_emulated++;raw_spin_unlock_irqrestore(&insn_emulation_lock, flags);/* Register any handlers if required *///把异常指令注册到kernel中update_insn_emulation_mode(insn, INSN_UNDEF);}static int update_insn_emulation_mode(struct insn_emulation *insn,       enum insn_emulation_mode prev){int ret = 0;switch (insn->current_mode) {case INSN_UNDEF:break;case INSN_EMULATE:register_emulation_hooks(insn->ops);//如果指令是可以正常执行的,就调用此函数注册break;case INSN_HW:ret = run_all_cpu_set_hw_mode(insn, true);if (!ret)pr_notice("Enabled %s support\n", insn->ops->name);break;}return ret;}static void register_emulation_hooks(struct insn_emulation_ops *ops){struct undef_hook *hook;BUG_ON(!ops->hooks);for (hook = ops->hooks; hook->instr_mask; hook++)//haha,最终调用register_undef_hook 注册到undef_hook 这个list中register_undef_hook(hook);pr_notice("Registered %s emulation handler\n", ops->name);}//可以看出emulation的异常可以分为三类/INSN_UNDEF/INSN_EMULATE/INSN_HW。最终只有INSN_EMULATE类型的异常注册到undef_hookvoid register_undef_hook(struct undef_hook *hook){unsigned long flags;raw_spin_lock_irqsave(&undef_lock, flags);list_add(&hook->node, &undef_hook);raw_spin_unlock_irqrestore(&undef_lock, flags);}

原创粉丝点击