字符设备驱动-Linux内核异常处理体系结构

来源:互联网 发布:网络兼职平台 编辑:程序博客网 时间:2024/06/05 20:04

Linux异常处理体系结构

以中断这种异常来举例分析:

当我们在裸机操作中断时候:

① 构建异常向量表

② cpu发生中断,跳到异常向量入口执行

③ 跳转到某函数

③-a 保存被中断的现场

③-b 执行中断处理函数

④-c 恢复现场


Linux驱动层面同样如此:

① 通过trap_init构造异常向量表

② cpu发生中断,跳到异常向量入口执行(b vector_irq + stubs_offset)

③ 跳转到vector_irq用宏实现保存、执行、恢复


 下面来分析一下内核怎样处理中断这种异常的:

内核在asmlinkage void __init start_kernel(void)(源码在init/main.c)中调用trap_init函数来设置异常的处理函数。

Ⅰ.trap_init函数(arch/arm/kernel/traps.c)

通过memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);来将异常向量表拷贝到ARM架构CPU的异常向量基址0xffff0000(还有一种0x00000000)

void __init trap_init(void){#if   defined(CONFIG_KGDB)    return;}void __init early_trap_init(void){memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);    memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);    memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);}

Ⅱ. 搜索__vectors_start(arch\arm\kernel\entry-armv.S)查看异常向量表

.globl  __vectors_start__vectors_start:    swi SYS_ERROR0    b   vector_und + stubs_offset    ldr pc, .LCvswi + stubs_offset    b   vector_pabt + stubs_offset    b   vector_dabt + stubs_offset    b   vector_addrexcptn + stubs_offset    b   vector_irq + stubs_offset    b   vector_fiq + stubs_offset    .globl  __vectors_end__vectors_end:    .data    .globl  cr_alignment    .globl  cr_no_alignmentcr_alignment:    .space  4cr_no_alignment:    .space  4

Ⅲ.以irq中断异常为例b vector_irq + stubs_offset,搜索整个文件找不到vector_irq,往上查询得知这是通过vector_stub宏(arch\arm\kernel\entry-armv.S)来定义的:

vector_stub irq, IRQ_MODE, 4    .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_stub来查找宏定义

.macro  vector_stub, name, mode, correction=0    .align  5vector_\name:    .if \correction    sub lr, lr, #\correction    .endif    @    @ Save r0, lr_<exception> (parent PC) and spsr_<exception>    @ (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    .endm

Ⅴ. 将vector_stub irq, IRQ_MODE, 4参数代入进去

.macro  vector_stub, name, mode, correction=0    .align  5vector_irq:    sub lr, lr, #4    .endif    @    @ Save r0, lr_<exception> (parent PC) and spsr_<exception>    @ (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    .endm    .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

Ⅵ. 进入__irq_usr中断模式

__irq_usr:    usr_entry#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    .ltorg    .align  5

Ⅶ. 进入irq_handler来调用中断处理总入口函数asm_do_IRQ

.macro  irq_handler    get_irqnr_preamble r5, lr1:  get_irqnr_and_base r0, r6, r5, lr    movne   r1, sp    @    @ routine called with r0 = irq number, r1 = struct pt_regs *    @    adrne   lr, 1b    bne asm_do_IRQ

ARM架构Linux内核的异常处理体系结构

这里写图片描述
这里写图片描述