Linux内核异常处理体系结构

来源:互联网 发布:win10 重置网络 编辑:程序博客网 时间:2024/05/20 15:57

/*首先声明下,这是本人阅读韦东山老师后的一些学习记录和笔记*/

1. 对异常概念的理解

           异常就是可以打断CPU正常运行的事件,比如,外部中断、未定义的指令、软中断等。当这些异常发生时,就打断CPU的正常运行,跳到相应的异常处理程序去处理这些异常要求的一些操作。

2.  Linux内核中断处理流程


基于Linux-2.6.29.4

1Linux内核中异常向量表的拷贝


/*分析代码*/

内核启动时将异常向量表从物理地址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()中又调用了大量的函数。

/*init/main.c*/

asmlinkagevoid __init start_kernel(void)

{

.....................................

/*

*Interrupts are still disabled. Do necessary setups, then

  • enablethem

  • 调用setup_arch(),在该函数中调用了early_trap_init()

*/

setup_arch(&command_line);


mm_init_owner(&init_mm,&init_task);

setup_command_line(command_line);

.....................

sched_init();

.....................


init_IRQ();

/*

中断的初始化(init_IRQ函数),完成相关体系结构的设置

*/

/*Do the rest non-__init'ed, we're now alive */

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();

/*

就是early_trap_init()完成了异常向量表的拷贝

early_trap_init()arch/arm/kernel/trap.c中定义

,代码如下:

*/


}


void__init early_trap_init(void)

{

........................................

/*

* 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);

memcpy((void*)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

...............................

/*

其中vectors0xffff0000,__vectors_start~__vectors_end之间的代码就是向量表

__stubs_start__stubs_end为重新定位跳转位置

*/

}


2Linux内核中中断处理流程



首先,内核启动时完成中断初始化框架的流程


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();

/*

* Set up various architecture-specific pointers

*/


init_arch_irq= mdesc->init_irq;

system_timer= mdesc->timer;

init_machine= mdesc->init_machine;


early_trap_init();

}


/*

machine_desc结构体arch/arm/include/asm/mach/arch.h定义

*/

structmachine_desc{

/*

* Note! The first four elements are used

* by assembler code in head.S, head-common.S

*/

unsignedint nr; /* architecture number */

unsignedint phys_io; /* start of physical io */

unsignedint io_pg_offst; /* byte offset for io

* page tabe entry */


constchar *name; /* architecture name */

unsignedlong boot_params; /* tagged list */


unsignedint video_start; /* start of video RAM */

unsignedint video_end; /* end of video RAM */


unsignedint reserve_lp0 :1; /* never has lp0 */

unsignedint reserve_lp1 :1; /* never has lp1 */

unsignedint reserve_lp2 :1; /* never has lp2 */

unsignedint soft_reboot :1; /* soft reboot */

void (*fixup)(structmachine_desc *,

struct tag *, char **,

struct meminfo *);

void (*map_io)(void);/*IO mapping function */

void (*init_irq)(void);

structsys_timer *timer; /* system tick timer */

void (*init_machine)(void);

};



/*setup_machine的定义*/

static structmachine_desc * __initsetup_machine(unsignedint nr)

{

structmachine_desc *list;


/*

* locate machine in the list of supported machines.

*/

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;

}



/*lookup_machine_typearch/arm/kernel/head-common.S中定义*/

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)


/*

*Lookupmachine architecture in the linker-build list of architectures.

*Note that we can't use the absolute addresses for the __arch_info

*lists since we aren't running with the MMU on (and therefore, we are

*not in the correct address space). We have to calculate the offset.

*

* r1 = machine architecture number

*Returns:

* r3, r4, r6 corrupted

* r5 = mach_info pointer in physical address space

*/

__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_stub irq,IRQ_MODE, 4

这两句对应

.macro vector_stub,irq,IRQ_MODE,correction=4

.align 5


vector_irq:

.if4

sub lr,lr, #4计算返回地址

.endif


@

@Save r0, lr_<exception> (parent PC) and spsr_<exception>

@(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

/*

usr_entry所做的工作

.macro usr_entry

sub sp,sp, #S_FRAME_SIZE

stmib sp,{r1 - r12}


ldmia r0,{r1 - r3}

add r0,sp, #S_PC @ here for interlock avoidance

mov r4,#-1 @ "" "" "" ""


str r1,[sp] @ save the "real" r0 copied

@from the exception stack

*/

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

/*

irq_handler


#ifdefCONFIG_PREEMPT

ldr r0,[tsk, #TI_PREEMPT]

str r8,[tsk, #TI_PREEMPT]

teq r0,r7

strne r0,[r0, -r0]

#endif

#ifdefCONFIG_TRACE_IRQFLAGS

bl trace_hardirqs_on

#endif


mov why,#0

b ret_to_user

ENDPROC(__irq_usr)


.ltorg


.align 5



irq_handler就是中断的处理函数

/*

*Interrupt handling. Preserves r7, r8, r9

*/

.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

/*

最终调用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();


/*

* Some hardware gives randomly wrong interrupts. Rather

* than crashing, do something sensible.

*/

if(irq >= NR_IRQS)

handle_bad_irq(irq,&bad_irq_desc);

else

generic_handle_irq(irq);


/*AT91 specific workaround */

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; /* IRQ action list */

unsignedint status; /* IRQ status */


unsignedint depth; /* nested irq disables */

unsignedint wake_depth; /* nested wake enables */

unsignedint irq_count; /* For detecting broken IRQs */

unsignedlong last_unhandled; /* Aging timer for unhandled count */

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,为用户自定义。。。



             

原创粉丝点击