[armv8-arch64]linux kernel 5.9的异常量表介绍(irq,fiq,sync,svc)
2023-09-27 14:26:31 时间
在entry.S中,定义了异常向量表,从代码中我们可以知道以下信息:
- 该表的基地址在vectors处(在开机的时候,会将其写入到vbar_el1中)
- 这个表以".align 11"方式对齐,即这张表至少占据2^11 size
- kernel_entry是一个用于定义向量表offset的宏,在kernel_entry中,是以“.align 7”方式对齐的,即2^8=0x80方式对齐,与ARMV8中的向量表offset一致
- armv8的向量表中又16个offset,在Linux Kernel中,仅实现了6个
- Linux Kernel中没有实现 FIQ
(linux_5.9/arch/arm64/kernel/entry.S)
/*
* Exception vectors.
*/
.pushsection ".entry.text", "ax"
.align 11
SYM_CODE_START(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
#endif
SYM_CODE_END(vectors)
我们再来看看kernel_ventry宏,它是用于定义向量表offset的宏,,从代码中我们可以知道以下信息:
- kernel_ventry定义的offset以“.align 7”方式对齐的,即2^8=0x80方式对齐,与ARMV8中的向量表offset一致
- 该宏定义最终调用了“b el()\el()_\label”,即:
– kernel_ventry 1, sync //调用了 el1_sync
– kernel_ventry 1, irq //调用了 el1_irq
– kernel_ventry 1, error //调用了 el1_error
– kernel_ventry 0, sync //调用了 el0_sync
– kernel_ventry 0, irq //调用了 el0_irq
– kernel_ventry 0, error //调用了 el0_error
(linux_5.9/arch/arm64/kernel/entry.S)
.macro kernel_ventry, el, label, regsize = 64
.align 7
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
.if \el == 0
alternative_if ARM64_UNMAP_KERNEL_AT_EL0
.if \regsize == 64
mrs x30, tpidrro_el0
msr tpidrro_el0, xzr
.else
mov x30, xzr
.endif
alternative_else_nop_endif
.endif
#endif
sub sp, sp, #S_FRAME_SIZE
#ifdef CONFIG_VMAP_STACK
/*
* Test whether the SP has overflowed, without corrupting a GPR.
* Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT)
* should always be zero.
*/
add sp, sp, x0 // sp' = sp + x0
sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp
tbnz x0, #THREAD_SHIFT, 0f
sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0
sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp
b el\()\el\()_\label
0:
/*
* Either we've just detected an overflow, or we've taken an exception
* while on the overflow stack. Either way, we won't return to
* userspace, and can clobber EL0 registers to free up GPRs.
*/
/* Stash the original SP (minus S_FRAME_SIZE) in tpidr_el0. */
msr tpidr_el0, x0
/* Recover the original x0 value and stash it in tpidrro_el0 */
sub x0, sp, x0
msr tpidrro_el0, x0
/* Switch to the overflow stack */
adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0
/*
* Check whether we were already on the overflow stack. This may happen
* after panic() re-enables interrupts.
*/
mrs x0, tpidr_el0 // sp of interrupted context
sub x0, sp, x0 // delta with top of overflow stack
tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range?
b.ne __bad_stack // no? -> bad stack pointer
/* We were already on the overflow stack. Restore sp/x0 and carry on. */
sub sp, sp, x0
mrs x0, tpidrro_el0
#endif
b el\()\el\()_\label
.endm
在__primary_switched函数中,将向量表的基地址vectors写入到了vbar_el1中.
(linux_5.9/arch/arm64/kernel/entry.S)
SYM_FUNC_START_LOCAL(__primary_switched)
adrp x4, init_thread_union
add sp, x4, #THREAD_SIZE
adr_l x5, init_task
msr sp_el0, x5 // Save thread_info
#ifdef CONFIG_ARM64_PTR_AUTH
__ptrauth_keys_init_cpu x5, x6, x7, x8
#endif
adr_l x8, vectors // load VBAR_EL1 with virtual
msr vbar_el1, x8 // vector table address
isb
stp xzr, x30, [sp, #-16]!
mov x29, sp
#ifdef CONFIG_SHADOW_CALL_STACK
adr_l scs_sp, init_shadow_call_stack // Set shadow call stack
#endif
str_l x21, __fdt_pointer, x5 // Save FDT pointer
ldr_l x4, kimage_vaddr // Save the offset between
sub x4, x4, x0 // the kernel virtual and
str_l x4, kimage_voffset, x5 // physical mappings
// Clear BSS
adr_l x0, __bss_start
mov x1, xzr
adr_l x2, __bss_stop
sub x2, x2, x0
bl __pi_memset
dsb ishst // Make zero page visible to PTW
#ifdef CONFIG_KASAN
bl kasan_early_init
#endif
#ifdef CONFIG_RANDOMIZE_BASE
tst x23, ~(MIN_KIMG_ALIGN - 1) // already running randomized?
b.ne 0f
mov x0, x21 // pass FDT address in x0
bl kaslr_early_init // parse FDT for KASLR options
cbz x0, 0f // KASLR disabled? just proceed
orr x23, x23, x0 // record KASLR offset
ldp x29, x30, [sp], #16 // we must enable KASLR, return
ret // to __primary_switch()
0:
#endif
add sp, sp, #16
mov x29, #0
mov x30, #0
b start_kernel
SYM_FUNC_END(__primary_switched)
而在开机时的在__primary_switched函数中被调用的流程如下所示
_head —> primary_entry —> __primary_switch —> __primary_switched —> 将向量表基地址vectors写入到vbar_el1寄存器
相关代码:
(linux_5.9/arch/arm64/kernel/head.S)
SYM_CODE_START(primary_entry)
bl preserve_boot_args
bl el2_setup // Drop to EL1, w0=cpu_boot_mode
adrp x23, __PHYS_OFFSET
and x23, x23, MIN_KIMG_ALIGN - 1 // KASLR offset, defaults to 0
bl set_cpu_boot_mode_flag
bl __create_page_tables
/*
* The following calls CPU setup code, see arch/arm64/mm/proc.S for
* details.
* On return, the CPU will be ready for the MMU to be turned on and
* the TCR will have been set.
*/
bl __cpu_setup // initialise processor
b __primary_switch
SYM_CODE_END(primary_entry)
__HEAD
_head:
/*
* DO NOT MODIFY. Image header expected by Linux boot-loaders.
*/
#ifdef CONFIG_EFI
/*
* This add instruction has no meaningful effect except that
* its opcode forms the magic "MZ" signature required by UEFI.
*/
add x13, x18, #0x16
b primary_entry
#else
b primary_entry // branch to kernel start, magic
.long 0 // reserved
#endif
......
相关文章
- Linux IPC实践(1) -- 概述
- 【ARM-Linux开发】Gstreamer+QT+摄像头 编程总结
- mysql的主从复制(windows主,linux从)
- LINUX系统运行查看
- linux常见进程与内核线程
- Linux学习笔记之Linux文件系统详解
- Linux - 工作管理(job control),jobs,fg,bg,kill
- 内核操作系统Linux内核变迁杂谈——感知市场的力量
- Linux netstat命令详解
- Linux sleep命令
- 【转】Gvim配置(Windows and Linux)for C++
- linux kernel的异常量表介绍(irq,fiq,swi,svc...)
- Linux使用普通用户登录到系统后,提示符异常
- Redhat Linux内核升级全记录(转)
- Linux之手动设置IP地址
- linux下神奇的script
- Linux下使用CMake进行编译的时候寻找Boost库
- 嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十七) 异常与中断的概念及处理流程
- Linux内核虚拟内存管理之匿名映射缺页异常分析
- 深入理解Linux内核-中断和异常
- Linux C/C++ 获取系统时间