您的位置:首页 > 运维架构 > Linux

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

2017-08-30 15:27 459 查看

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_alignment
cr_alignment:
.space  4
cr_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  5

vector_\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  5

vector_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, lr
1:  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内核的异常处理体系结构



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: