1. 对异常概念的理解
异常就是可以打断CPU正常运行的事件,比如,外部中断、未定义的指令、软中断等。当这些异常发生时,就打断CPU的正常运行,跳到相应的异常处理程序去处理这些异常要求的一些操作。
2. Linux内核中断处理流程
基于Linux-2.6.29.4
1、Linux内核中异常向量表的拷贝
内核启动时将异常向量表从物理地址0x00000000拷贝到0xffff0000的虚拟地址中去;
其中异常向量在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:
注:内核启动时调用start_kernel()函数,在start_kernel()中又调用了大量的函数。
asmlinkagevoid __init
start_kernel(void)
{
.....................................
setup_arch(&command_line);
mm_init_owner(&init_mm,&init_task);
setup_command_line(command_line);
.....................
sched_init();
.....................
init_IRQ();
rest_init();
}
函数setup_arch()在arch/arm/kernel/setup.c中
void__init
setup_arch(char **cmdline_p)
{
.........................
memcpy(boot_command_line,from,
COMMAND_LINE_SIZE);
boot_command_line[COMMAND_LINE_SIZE-1]=
'\0';
parse_cmdline(cmdline_p,from);
paging_init(mdesc);
request_standard_resources(&meminfo,mdesc);
.............................
...............................
early_trap_init();
}
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);
...............................
}
2、Linux内核中中断处理流程
首先,内核启动时完成中断初始化框架的流程
在start_kernel()中调用init_IRQ(),该函数在arch/arm/kernel/irq.c中被定义,被用来初始化中断的处理框架,设置各种中断的默认处理函数。当发生中断时,中断总入口函数asm_do_IRQ()就可以调用这些函数作进一步处理。
void__init
init_IRQ(void)
{
intirq;
for(irq =
0; irq < NR_IRQS; irq++)
irq_desc[irq].status|=
IRQ_NOREQUEST | IRQ_NOPROBE;
#ifdefCONFIG_SMP
bad_irq_desc.affinity=
CPU_MASK_ALL;
bad_irq_desc.cpu=
smp_processor_id();
#endif
init_arch_irq();
}
其中init_arch_irq()被定义为
void(*init_arch_irq)(void)
__initdata = NULL;
它的作用如下,
void__init
setup_arch(char **cmdline_p)
{
structmachine_desc *mdesc;
。。。。。。。。。。
setup_processor();
mdesc=
setup_machine(machine_arch_type);
machine_name=
mdesc->name;
memcpy(boot_command_line,from,
COMMAND_LINE_SIZE);
boot_command_line[COMMAND_LINE_SIZE-1]=
'\0';
parse_cmdline(cmdline_p,from);
paging_init(mdesc);
request_standard_resources(&meminfo,mdesc);
cpu_init();
init_arch_irq=
mdesc->init_irq;
system_timer=
mdesc->timer;
init_machine=
mdesc->init_machine;
early_trap_init();
}
structmachine_desc{
unsignedint
nr;
unsignedint
phys_io;
unsignedint
io_pg_offst;
constchar
*name;
unsignedlong
boot_params;
unsignedint
video_start;
unsignedint
video_end;
unsignedint
reserve_lp0 :1;
unsignedint
reserve_lp1 :1;
unsignedint
reserve_lp2 :1;
unsignedint
soft_reboot :1;
void
(*fixup)(structmachine_desc *,
struct tag
*, char **,
struct
meminfo *);
void
(*map_io)(void);
void (*init_irq)(void);
structsys_timer
*timer;
void
(*init_machine)(void);
};
static
structmachine_desc * __init setup_machine(unsignedint
nr)
{
structmachine_desc
*list;
list=
lookup_machine_type(nr);
if(!list)
{
printk("Machineconfiguration
botched (nr %d), unable "
"to
continue.\n", nr);
while(1);
}
printk("Machine:%s\n",
list->name);
returnlist;
}
ENTRY(lookup_machine_type)
stmfd
sp!,{r4 - r6, lr}
mov
r1,r0
bl
__lookup_machine_type
mov
r0,r5
ldmfd
sp!,{r4 - r6, pc}
ENDPROC(lookup_machine_type)
__lookup_machine_type:
adr
r3,3b
ldmia
r3,{r4, r5, r6}
sub r3,r3,
r4 @ get offset between virt&phys
add r5,r5,
r3 @ convert virt addresses to
add r6,r6,
r3 @ physical address space
1: ldr
r3,[r5, #MACHINFO_TYPE] @ get machine type
teq r3,r1 @
matches loader number?
beq 2f
@found
add r5,r5,
#SIZEOF_MACHINE_DESC @ next machine_desc
cmp
r5,r6
blo
1b
mov r5,#0 @
unknown machine
2: mov
pc,lr
ENDPROC(__lookup_machine_type)
接下来是内核完成启动后响应中断的流程
当CPU被中断时,强制跳转到异常中断向量表中的
b vector_fiq+ stubs_offset
如vector_irq这些是宏,定义在vector_stub
irq,IRQ_MODE, 4
vector_stubirq,IRQ_MODE,
4
这两句对应
.macrovector_stub,irq,IRQ_MODE,correction=4
.align 5
vector_irq:
.if4
sub lr,lr, #4计算返回地址
.endif
@
@Save r0, lr_ (parent PC) and spsr_
@(parent CPSR)
@保存被中断时CPU的现场
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_irq)
.endm
执行完上述语句后,跳转到
vector_stub
irq,IRQ_MODE, 4
.long
__irq_usr @ 0 (USR_26 / USR_32)
如果是用户态发生中断时,跳转到__irq_usr地址去执行
.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
kuser_cmpxchg_check
#ifdefCONFIG_TRACE_IRQFLAGS
bl
trace_hardirqs_off
#endif
get_thread_infotsk
#ifdefCONFIG_PREEMPT
ldr
r8,[tsk, #TI_PREEMPT] @ get preempt count
add r7,r8,
#1 @ increment it
str
r7,[tsk, #TI_PREEMPT]
#endif
.macro
irq_handler
get_irqnr_preambler5,
lr
1:
get_irqnr_and_baser0, r6, r5, lr
movne
r1,sp
@
@routine
called with r0 = irq number, r1 = struct pt_regs
*
@
adrne
lr,1b
bne asm_do_IRQ
***位于irq.c
asmlinkagevoid
__exception asm_do_IRQ(unsigned int irq, struct pt_regs
*regs)
{
structpt_regs *old_regs
= set_irq_regs(regs);
irq_enter();
if(irq
>= NR_IRQS)
handle_bad_irq(irq,&bad_irq_desc);
else
generic_handle_irq(irq);
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
irq_desc定义在include/linux/irq.h中
structirq_desc{
unsignedint
irq;
#ifdefCONFIG_SPARSE_IRQ
structtimer_rand_state
*timer_rand_state;
unsignedint
*kstat_irqs;
#ifdef
CONFIG_INTR_REMAP
structirq_2_iommu
*irq_2_iommu;
#endif
#endif
irq_flow_handler_t
handle_irq;
structirq_chip
*chip;
structmsi_desc
*msi_desc;
void
*handler_data;
void
*chip_data;
structirqaction
*action;
unsignedint
status;
unsignedint
depth;
unsignedint
wake_depth;
unsignedint
irq_count;
unsignedlong
last_unhandled;
unsignedint
irqs_unhandled;
spinlock_t
lock;
#ifdefCONFIG_SMP
cpumask_t
affinity;
unsignedint
cpu;
#endif
#ifdefCONFIG_GENERIC_PENDING_IRQ
cpumask_t
pending_mask;
#endif
#ifdefCONFIG_PROC_FS
structproc_dir_entry
*dir;
#endif
constchar
*name;
}
定义在include/linux/irq.h
staticinline void
generic_handle_irq(unsigned int irq)
{
generic_handle_irq_desc(irq,irq_to_desc(irq));
}
staticinline void
generic_handle_irq_desc(unsigned int irq, struct
irq_desc*desc)
{
#ifdefCONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
desc->handle_irq(irq,desc);
#else
if(likely(desc->handle_irq))
desc->handle_irq(irq,desc);
else
__do_IRQ(irq);
#endif
}
最终处理的函数为handle_irq,为用户自定义。。。