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

Linux内核中断机制(三):中断处理上

2016-12-27 22:03 281 查看
内核中断处理过程

S3C2410和Linux2.6.26内核为例讲解处理过程

 

1.中断向量表arch\arm\kernel\entry-armv.S

__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:

中断发生后,跳转到 b vector_irq + stubs_offset
的位置执行。注意现在的向

量表的初始位置是 0xffff0000。

 

2.向量表中找到入口位置(同一个文件中)

.globl __stubs_start

__stubs_start:

/*

* Interrupt dispatcher

*/

vector_stub irq, IRQ_MODE, 4 @IRQ_MODE


include\asm\ptrace.h 中定义:0x12

.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

@

andlr, lr, #0x0f  @进入中断前的
mode 的后
4 位

@#define USR_MODE 0x00000010

@#define FIQ_MODE 0x00000011

@#define IRQ_MODE 0x00000012

@#define SVC_MODE 0x00000013

@#define ABT_MODE 0x00000017

@#define UND_MODE 0x0000001b

@#define SYSTEM_MODE 0x0000001f

mov r0, sp

ldrlr, [pc, lr, lsl #2]   //根据user还是svc模式而不同,决定进入_irq_user还是_irq_svc

movs pc, lr @ branch to handler in SVC mode

.endm

 

 

 

 

 

 

.globl __stubs_start

__stubs_start:

/*

* Interrupt dispatcher

*/

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)

 

用“irq, IRQ_MODE, 4”代替宏
vector_stub 中的“name, mode, correction”,

找到了我们中断处理的入口位置为 vector_irq(宏里面的
vector_\name)。

从上面代码中的注释可以看出,根据进入中断前的工作模式不同,程序下一步将

跳转到_irq_usr
、或__irq_svc
等位置。我们先选择__irq_usr
作为下一步跟踪

的目标。

 

3.__irq_usr的实现(同样的位置)

__irq_usr:

usr_entry

 

#ifdef CONFIG_TRACE_IRQFLAGS

bl trace_hardirqs_off

#endif

get_thread_info tsk        //获取进程中描述符threa_info的地址,存到寄存器里面

#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

bret_to_user   //中断处理完成,返回中断产生位置

 

.ltorg

 

.align 5

 

①usr_entry

是一个宏:将usr模式下的寄存器和中断返回地址保存到堆栈(保护现场)

 

②ret_to_user中断返回过程
/arch/arm/kernel/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

 

 

 

 

 

 

 

4.irq_handler的实现(同一个地址)

.macro irq_handler

get_irqnr_preamble r5, lr  //include/asm/arch-s3c2410/entry-macro.s
中定义,为空操作

1: get_irqnr_and_base r0, r6, r5, lr //判断中断号,R0返回

movne r1, sp

@

@ routine called with r0 = irq number, r1 = struct pt_regs *

@

adrne lr, 1b

bneasm_do_IRQ  //进入中断处理

①判断中断号

include/asm/arch-s3c2410/entry-macro.s

.macro get_irqnr_and_base, irqnr, irqstat, base, tmp

mov \base, #S3C24XX_VA_IRQ

@@ try the interrupt offset register, since it is there

ldr \irqstat, [ \base, #INTPND ]

teq \irqstat, #0

beq 1002f

ldr \irqnr, [ \base, #INTOFFSET ] @通过判断
INTOFFSET 寄

存器得到中断位置

mov \tmp, #1

tst \irqstat, \tmp, lsl \irqnr

bne 1001f

@@ the number specified is not a valid irq, so try

@@ and work it out for ourselves

mov \irqnr, #0 @@ start here

@@ work out which irq (if any) we got

movs \tmp, \irqstat, lsl#16

addeq \irqnr, \irqnr, #16

moveq \irqstat, \irqstat, lsr#16

tst \irqstat, #0xff

addeq \irqnr, \irqnr, #8

moveq \irqstat, \irqstat, lsr#8

tst \irqstat, #0xf

addeq \irqnr, \irqnr, #4

moveq \irqstat, \irqstat, lsr#4

tst \irqstat, #0x3

addeq \irqnr, \irqnr, #2

moveq \irqstat, \irqstat, lsr#2

tst \irqstat, #0x1

addeq \irqnr, \irqnr, #1

@@ we have the value

1001:

adds \irqnr, \irqnr, #IRQ_EINT0 @加上中断号的基准 数值,得到最

终的中断号,注意:此时没有考虑子中断的具体情况,(子中断的问题后面会

有讲解)。IRQ_EINT0
在 include/asm/arch- s3c2410/irqs.h
中定义.从这里可

以看出,中断号的具体值是有平台相关的代码决定的,和硬件中断挂起寄存器

中的中断号是不等的。

1002:

@@ exit here, Z flag unset if IRQ

.endm

 

5.asm_do_IRQ
实现过程 arch/arm/kernel/irq.c

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

{

struct pt_regs *old_regs = set_irq_regs(regs);

struct irq_desc *desc = irq_desc + irq; //根据参数irq找到具体的中断号

 

/*

 * Some hardware gives randomly wrong interrupts.  Rather

 * than crashing, do something sensible.

 */

if (irq >= NR_IRQS)

desc = &bad_irq_desc;trap_init

 

irq_enter(); //没用

 

desc_handle_irq(irq, desc);  //根据中断号和desc结构进入中断处理

 

/* AT91 specific workaround */

irq_finish(irq);

 

irq_exit();

set_irq_regs(old_regs);

}

static inline void desc_handle_irq(unsigned int irq, struct irq_desc

*desc)

{

desc->handle_irq(irq, desc);//中断处理

}

 

①asmlinkage标志含义:

#include <asm/linkage.h>//各个具体处理器在此文件中定义 asmlinkage

#ifdef __cplusplus

#define CPP_ASMLINKAGE extern "C"

#else

#define CPP_ASMLINKAGE

#endif

#ifndef asmlinkage//如果以前没有定义 asmlinkage

#define asmlinkage CPP_ASMLINKAGE

#endif

对于 ARM
处理器的<asm/linkage.h>,没有定义
asmlinkage,所以没有意义(不

要以为参数是从堆栈传递的,对于 ARM
平台来说还是符合 ATPCS
过程调用标准,

通过寄存器传递的)。

 

但对于 X86 处理器的<asm/linkage.h>中是这样定义的:

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

表示函数的参数传递是通过堆栈完成的。

 

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