linux在ARM平台上的中断流程
2015-05-10 10:58
316 查看
当发生中断时,系统跳转到ARM平台的异常向量表(vector_irq位置):
以下的vector_stub irq位置为vector_irq跳转到的位置,后面跟着的是irq分发表,这里只区分了SVC和USR两种情况:
vector_stub宏的定义如下:
分别可以由svc和usr模式进入:
不论SVC还是USR都会调用IRQ handler,这里可以根据配置选择1:1或者1:N的方式:
当配置为CONFIG_MULTI_IRQ_HANDLER时,handle_arch_irq可以在2个地方初始化:
如果CONFIG_MULTI_IRQ_HANDLER未打开,则:
get_irqnr_preamble和get_irqnr_and_base调用具体chip的实现来获取中断号,比如对于mach-davinci而言,其实现如下:
然后就走到asm_do_IRQ这个C环境函数:
以下是根据映射的虚拟中断号,找到对应的struct irq_desc,并执行里面注册的中断处理函数:
在中断处理函数执行前后分别有irq_enter函数和irq_exit函数:
.section .vectors, "ax", %progbits __vectors_start: W(b) vector_rst W(b) vector_und W(ldr) pc, __vectors_start + 0x1000 W(b) vector_pabt W(b) vector_dabt W(b) vector_addrexcptn W(b) vector_irq W(b) vector_fiq
以下的vector_stub irq位置为vector_irq跳转到的位置,后面跟着的是irq分发表,这里只区分了SVC和USR两种情况:
/* * 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) .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宏的定义如下:
/* * Vector stubs. * * This code is copied to 0xffff1000 so we can use branches in the * vectors, rather than ldr's. Note that this code must not exceed * a page size. * * Common stub entry macro: * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC * * SP points to a minimal amount of processor-private memory, the address * of which is copied into r0 for the mode specific abort handler. */ .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 | PSR_ISETSTATE) msr spsr_cxsf, r0 @ @ the branch table must immediately follow this code @ and lr, lr, #0x0f THUMB( adr r0, 1f ) THUMB( ldr lr, [r0, lr, lsl #2] ) mov r0, sp ARM( ldr lr, [pc, lr, lsl #2] ) movs pc, lr @ branch to handler in SVC mode ENDPROC(vector_\name)
分别可以由svc和usr模式进入:
.align 5 __irq_svc: svc_entry irq_handler #ifdef CONFIG_PREEMPT get_thread_info tsk ldr r8, [tsk, #TI_PREEMPT] @ get preempt count ldr r0, [tsk, #TI_FLAGS] @ get flags teq r8, #0 @ if preempt count != 0 movne r0, #0 @ force flags to 0 tst r0, #_TIF_NEED_RESCHED blne svc_preempt #endif svc_exit r5, irq = 1 @ return from exception UNWIND(.fnend ) ENDPROC(__irq_svc) .align 5 __irq_usr: usr_entry kuser_cmpxchg_check irq_handler get_thread_info tsk mov why, #0 b ret_to_user_from_irq UNWIND(.fnend ) ENDPROC(__irq_usr)
不论SVC还是USR都会调用IRQ handler,这里可以根据配置选择1:1或者1:N的方式:
/* * Interrupt handling. */ .macro irq_handler #ifdef CONFIG_MULTI_IRQ_HANDLER ldr r1, =handle_arch_irq mov r0, sp adr lr, BSYM(9997f) ldr pc, [r1] #else arch_irq_handler_default #endif 9997: .endm
当配置为CONFIG_MULTI_IRQ_HANDLER时,handle_arch_irq可以在2个地方初始化:
#ifdef CONFIG_MULTI_IRQ_HANDLER void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) { if (handle_arch_irq) return; handle_arch_irq = handle_irq; } #endif void __init setup_arch(char **cmdline_p) { ..... #ifdef CONFIG_MULTI_IRQ_HANDLER handle_arch_irq = mdesc->handle_irq; #endif ..... }
如果CONFIG_MULTI_IRQ_HANDLER未打开,则:
.macro arch_irq_handler_default get_irqnr_preamble r6, lr 1: get_irqnr_and_base r0, r2, r6, lr movne r1, sp @ @ routine called with r0 = irq number, r1 = struct pt_regs * @ adrne lr, BSYM(1b) bne asm_do_IRQ #ifdef CONFIG_SMP /* * XXX * * this macro assumes that irqstat (r2) and base (r6) are * preserved from get_irqnr_and_base above */ ALT_SMP(test_for_ipi r0, r2, r6, lr) ALT_UP_B(9997f) movne r1, sp adrne lr, BSYM(1b) bne do_IPI #endif 9997: .endm
get_irqnr_preamble和get_irqnr_and_base调用具体chip的实现来获取中断号,比如对于mach-davinci而言,其实现如下:
.macro get_irqnr_preamble, base, tmp ldr \base, =davinci_intc_base ldr \base, [\base] .endm .macro get_irqnr_and_base, irqnr, irqstat, base, tmp #if defined(CONFIG_AINTC) && defined(CONFIG_CP_INTC) ldr \tmp, =davinci_intc_type ldr \tmp, [\tmp] cmp \tmp, #DAVINCI_INTC_TYPE_CP_INTC beq 1001f #endif #if defined(CONFIG_AINTC) ldr \tmp, [\base, #0x14] movs \tmp, \tmp, lsr #2 sub \irqnr, \tmp, #1 b 1002f #endif #if defined(CONFIG_CP_INTC) 1001: ldr \irqnr, [\base, #0x80] /* get irq number */ mov \tmp, \irqnr, lsr #31 and \irqnr, \irqnr, #0xff /* irq is in bits 0-9 */ and \tmp, \tmp, #0x1 cmp \tmp, #0x1 #endif 1002: .endm
然后就走到asm_do_IRQ这个C环境函数:
/* * handle_IRQ handles all hardware IRQ's. Decoded IRQs should * not come via this function. Instead, they should provide their * own 'handler'. Used by platform code implementing C-based 1st * level decoding. */ void handle_IRQ(unsigned int irq, struct pt_regs *regs) { __handle_domain_irq(NULL, irq, false, regs); } /* * asm_do_IRQ is the interface to be used from assembly code. */ asmlinkage void __exception_irq_entry asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { handle_IRQ(irq, regs); }
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq, bool lookup, struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); unsigned int irq = hwirq; int ret = 0; irq_enter(); #ifdef CONFIG_IRQ_DOMAIN if (lookup) irq = irq_find_mapping(domain, hwirq); #endif /* * Some hardware gives randomly wrong interrupts. Rather * than crashing, do something sensible. */ if (unlikely(!irq || irq >= nr_irqs)) { ack_bad_irq(irq); ret = -EINVAL; } else { generic_handle_irq(irq); } irq_exit(); set_irq_regs(old_regs); return ret; }
以下是根据映射的虚拟中断号,找到对应的struct irq_desc,并执行里面注册的中断处理函数:
/** * generic_handle_irq - Invoke the handler for a particular irq * @irq: The irq number to handle * */ int generic_handle_irq(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); if (!desc) return -EINVAL; generic_handle_irq_desc(irq, desc); return 0; } static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc) { desc->handle_irq(irq, desc); }
在中断处理函数执行前后分别有irq_enter函数和irq_exit函数:
/* * Enter an interrupt context. */ void irq_enter(void) { rcu_irq_enter(); if (is_idle_task(current) && !in_interrupt()) { /* * Prevent raise_softirq from needlessly waking up ksoftirqd * here, as softirq will be serviced on return from interrupt. */ local_bh_disable(); tick_irq_enter(); _local_bh_enable(); } __irq_enter(); } /* * Exit an interrupt context. Process softirqs if needed and possible: */ void irq_exit(void) { #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED local_irq_disable(); #else WARN_ON_ONCE(!irqs_disabled()); #endif account_irq_exit_time(current); preempt_count_sub(HARDIRQ_OFFSET); if (!in_interrupt() && local_softirq_pending()) invoke_softirq(); tick_irq_exit(); rcu_irq_exit(); trace_hardirq_exit(); /* must be last! */ }
相关文章推荐
- 析达芬奇DM644x平台ARM中断处理流程
- linux for arm的中断处理流程[转载自:http://hi.baidu.com/wudx05/blog/item/5314935c834f4e41fbf2c0dc.html]
- 多线程:ARM linux平台上线程栈信息的建立流程
- arm平台linux异常处理流程
- 浅析Arm Linux中断Vector向量表的建立流程
- 浅析达芬奇DM644x平台ARM中断处理流程
- ARM 平台上的Linux系统启动流程
- arm-Linux中断处理体系结构与处理流程分析
- linux摄像头驱动的拍照流程分析(针对展讯8810(ARM架构),android平台)
- ARM 平台上的Linux系统启动流程
- ARM linux启动的流程
- ARM AT91SAM9260 移植Linux-2.6.30流程
- arm-none-linux-gnueabi-gcc【实验环境】 1、 Ubuntu 10.10发行版 2、 FS2410平台 【实验步骤】
- 深入理解Linux内核(4)---中断和异常(x86平台)
- 转:arm linux QT 程序开发流程
- linux-2.6.26内核中ARM中断实现详解(1)
- ARM AT91SAM9260 移植Linux-2.6.30流程
- Arm&nbsp;linux&nbsp;启动流程
- 6410为例的ARM启动linux流程
- QT环境搭建: QT玩转在linux的x86平台或者ARM平台上 - (1)x86下安装配置及使用Qt-4.8.5