linux kernel 之底层中断机制entry_armv.S

文章详细介绍了ARM处理器在发生异常或中断时如何跳转到异常向量表进行处理,以及中断机制初始化过程中对entry-armv.S中的异常向量表和处理程序的重定位。通过memcpy函数将向量、stub和kuser_helper复制到特定内存位置,确保中断处理的正常执行。
摘要由CSDN通过智能技术生成

entry-armv.s中
当ARM处理器发生异常(中断是一种异常)时,会跳转到异常向量表(起始地址为0xFFFF_0000或0x0000_0000)。 如3.2节中所述,在中断机制的初始化过程中,把在arch/arm/kernel/entry-armv.S中的异常向量表及其处理程序的stub重定位到了0xFFFF_0000处,这样才使得ARM处理器能够正常处理异常,而且事实上执行的就是entry-armv.S中的异常处理程序

.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
异常向量表:
@下面这些才是最初的入口点,__vector_start和__vector_end之间的代码会被移动到CONFIG_VECTORS_BASE开始的地方,例如0xc000000
.globl __vectors_start
__vectors_start:
swi SYS_ERROR0 @Reset
b vector_und + stubs_offset
ldr pc, .LCvswi + stubs_offset @swi instructionSWI指令用于产生软件中断,从而实现从用户模式到管理模式的变换
b vector_pabt + stubs_offset
b vector_dabt + stubs_offset
b vector_addrexcptn + stubs_offset
b vector_irq + stubs_offset @irq中断入口
b vector_fiq + stubs_offset
.globl __vectors_end
__vectors_end:
///
__stubs_start和__stubs_end之间,实际上就是异常处理程序的入口
.globl __stubs_start
__stubs_start:
/*

  • Interrupt dispatcher
    /
    vector_stub irq, IRQ_MODE, 4 @此处便是vector_irq的声明调用
    .long __irq_usr @ 0 (USR_26 / USR_32) 用户模式
    .long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
    .long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
    .long __irq_svc @ 3 (SVC_26 / SVC_32) 系统模式
    .long __irq_invalid @ 4
    .long __irq_invalid @ 5
    .long __irq_invalid @ 6
    .long __irq_invalid @ 7
    .long __irq_invalid @ 8
    .long __irq_invalid @ 9
    .long __irq_invalid @ a
    .long __irq_invalid @ b
    .long __irq_invalid @ c
    .long __irq_invalid @ d
    .long __irq_invalid @ e
    .long __irq_invalid @ f
    //
    /
  • Vector stubs.
  • This code is copied to 0xffff0200 so we can use branches in the
  • vectors, rather than ldr’s. Note that this code must not
  • exceed 0x300 bytes.
  • Common stub entry macro:
  • Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
  • SP points to a minimal amount of processor-private memory, the address
  • of which is copied into r0 for the mode specific abort handler.
    */
    .macro vector_stub, name, mode, correction=0 @看看 vector_stub irq, IRQ_MODE, 4 是如何变成vector_irq的。
    .align 5
    vector_\name:
    .if \correction
    sub lr, lr, #\correction @修正返回地址,如果必要的话
    .endif
    @
    @ Save r0, lr_ (parent PC) and spsr_
    @ (parent CPSR)
    @
    stmia sp, {r0, lr} @ save r0, lr
    mrs lr, spsr
    str lr, [sp, #8] @ save spsr
    @
    @ Prepare for SVC32 mode. IRQs remain disabled.
    @
    mrs r0, cpsr
    eor r0, r0, #(\mode ^ SVC_MODE)
    msr spsr_cxsf, r0
    @
    @ the branch table must immediately follow this code
    @
    and lr, lr, #0x0f
    mov r0, sp
    ldr lr, [pc, lr, lsl #2]
    movs pc, lr @ branch to handler in SVC mode
    ENDPROC(vector_\name)
    .endm
    //如果中断以前是svc模式,就会跳到__irq_svc
    http://blog.chinaunix.net/uid-12567959-id-160972.html

    .align 5
    __irq_svc:
    svc_entry
    #ifdef CONFIG_TRACE_IRQFLAGS
    bl trace_hardirqs_off
    #endif
    #ifdef CONFIG_PREEMPT
    get_thread_info tsk
    ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
    add r7, r8, #1 @ increment it
    str r7, [tsk, #TI_PREEMPT]
    #endif
    irq_handler @调用中断处理程序
    #ifdef CONFIG_PREEMPT
    str r8, [tsk, #TI_PREEMPT] @ restore preempt count
    ldr r0, [tsk, #TI_FLAGS] @ get flags
    teq r8, #0 @ if preempt count != 0
    movne r0, #0 @ force flags to 0
    tst r0, #_TIF_NEED_RESCHED
    blne svc_preempt
    #endif
    ldr r0, [sp, #S_PSR] @ irqs are already disabled
    msr spsr_cxsf, r0
    #ifdef CONFIG_TRACE_IRQFLAGS
    tst r0, #PSR_I_BIT
    bleq trace_hardirqs_on
    #endif
    ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
    ENDPROC(__irq_svc)
    ///
    @usr模式中断入口
    .align 5
    __irq_usr:
    usr_entry
    kuser_cmpxchg_check
    #ifdef CONFIG_TRACE_IRQFLAGS
    bl trace_hardirqs_off
    #endif
    get_thread_info tsk
    #ifdef CONFIG_PREEMPT
    ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
    add r7, r8, #1 @ increment it
    str r7, [tsk, #TI_PREEMPT]
    #endif
    irq_handler @调用中断处理程序
    #ifdef CONFIG_PREEMPT
    ldr r0, [tsk, #TI_PREEMPT]
    str r8, [tsk, #TI_PREEMPT]
    teq r0, r7
    strne r0, [r0, -r0]
    #endif
    #ifdef CONFIG_TRACE_IRQFLAGS
    bl trace_hardirqs_on
    #endif
    mov why, #0
    b ret_to_user
    ENDPROC(__irq_usr)
    回看__irq_usr和__irq_svc标号处的代码,在完成了irq_handler中断处理函数后,要完成从中断异常处理程序返回到中断点的工作。如果中断产生于用户空间,则调用ret_to_user来恢复中断现场并返回用户空间继续运行:

//
entry_common.s中

ENTRY(ret_to_user)
ret_slow_syscall:
disable_irq @ disable interrupts
ldr r1, [tsk, #TI_FLAGS]
tst r1, #_TIF_WORK_MASK
bne work_pending
no_work_pending:
/* perform architecture specific actions before user return */
arch_ret_to_user r1, lr
@ slow_restore_user_regs
ldr r1, [sp, #S_PSR] @ get calling cpsr
ldr lr, [sp, #S_PC]! @ get pc
msr spsr_cxsf, r1 @ save in spsr_svc
ldmdb sp, {r0 - lr}^ @ get calling r0 - lr
mov r0, r0
add sp, sp, #S_FRAME_SIZE - S_PC
movs pc, lr @ return & move spsr_svc into cpsr
ENDPROC(ret_to_user)
/
.macro irq_handler
get_irqnr_preamble r5, lr
1: get_irqnr_and_base r0, r6, r5, lr
宏查询ISPR(IRQ待定中断服务寄存器,当有需要处理的中断时,
这个寄存器的相应位会置位,任意时刻,最多一个位会置位)计算出的中断号放在irqnr指定的寄存器中
movne r1, sp @如果中断号不等于0,将r1=sp,即pt_regs结构体首地址
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne lr, 1b @返回到1处,asm_do_IRQ返回后将再次查询发生的中断???
@如果r0(中断号)不等于0, lr(返回地址)等于标号1处,即get_irqnr_and_base r0, r6, r5, lr的那行,即循环处理所有的中断

bne asm_do_IRQ @kernel的中断处理函数
//中断底层汇编可知asm_do_IRQ函数式中断的C语言总入口函数
///
traps.c中的early_trap_init()函数中
unsigned long vectors = CONFIG_VECTORS_BASE;
/*

  • Copy the vectors, stubs and kuser helpers (in entry-armv.S)
  • into the vector page, mapped at 0xffff0000, and ensure these
  • are visible to the instruction stream.
    */
    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
    memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start
    //entry_armv.s中有.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
    memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

实际copy动作一目了然,就是两个memcpy(第三个实际上是拷贝一些别的东西,原理是一样的,这里不提了). copy的目的地是vectors,这个值是CONFIG_VECTORS_BASE,在2.6.32.7内核中CONFIG_VECTORS_BASE是在各个平台的配置文件中设定的,如: arch/arm/configs/S3C2410_defconfig中
CONFIG_VECTORS_BASE=0xffff0000

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xx-xx-xxx-xxx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值