linux的中断子系统简介(汇编和hard irq部分)_ARM平台(S5PV210)

2011年9月份时候做的笔记, 当时阅读中断子系统的代码后做的一个PPT, 内核版本不记得了, 硬件平台是samsung 的S5PV210.

这部分主要是针对汇编和hard irq的部分, 在hard irq处理后的softirq的处理, 以及下半部的处理(tasklet/workqueue)都没有涉及.

Agenda

?Interrupts in ARM

?Important structs

?External interrupt resources in S5PV210

?Code flow

?Kernel API

?Interrupts in ARM

ARM CPU CORE 中只有两根中断引脚, 分别是IRQ和FIQ.

?IRQ

–Why chip can handle so many IRQS?      ===>  VIC

?FIQ

?Important structs

struct irq_desc <strong>irq_desc</strong>[NR_IRQS] __cacheline_aligned_in_smp = {
	[0 ... NR_IRQS-1] = {
		.status = IRQ_DISABLED,
		.chip = &no_irq_chip,
		.handle_irq = handle_bad_irq,
		.depth = 1,
		.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
	}
}; //NR_IRQS = 393 in R70

irq_desc is a global struct describing all the interrupt lines in the system.

struct irq_desc {
<strong>	unsigned int		irq;</strong>
	struct timer_rand_state 	*timer_rand_state;
	unsigned int            		*kstat_irqs;
#ifdef CONFIG_INTR_REMAP
	struct irq_2_iommu      	*irq_2_iommu;
#endif
<strong>	irq_flow_handler_t		handle_irq;	//high level irq-events handle
	struct irq_chip		*chip;</strong>
	struct msi_desc		*msi_desc;
	void		*handler_data;
	void		*chip_data;
<strong>	struct irqaction		*action;	/* IRQ action list */</strong>
	unsigned int		status;	/* IRQ status */

	unsigned int		depth;	/* nested irq disables */
	unsigned int		wake_depth;	/* nested wake enables */
	unsigned int		irq_count;	/* For detecting broken IRQs */
	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
	unsigned int		irqs_unhandled;
	raw_spinlock_t		lock;
#ifdef CONFIG_SMP
	cpumask_var_t		affinity;
	const struct cpumask		*affinity_hint;
	unsigned int		node;
#ifdef CONFIG_GENERIC_PENDING_IRQ
	cpumask_var_t		pending_mask;
#endif
#endif
	atomic_t		threads_active;
	wait_queue_head_t       	wait_for_threads;
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry		*dir;
#endif
	const char		*name;
} ____cacheline_internodealigned_in_smp;
struct <strong>irq_chip</strong> {
	const char		*name;
	unsigned int		(*startup)(unsigned int irq);
	void		(*shutdown)(unsigned int irq);
	void		(*enable)(unsigned int irq);
	void		(*disable)(unsigned int irq);

	void		(*ack)(unsigned int irq);
	void		(*mask)(unsigned int irq);
	void		(*mask_ack)(unsigned int irq);
	void		(*unmask)(unsigned int irq);
	void		(*eoi)(unsigned int irq);

	void		(*end)(unsigned int irq);
	int		(*set_affinity)(unsigned int irq, const struct cpumask *dest);
	int		(*retrigger)(unsigned int irq);
	int		(*set_type)(unsigned int irq, unsigned int flow_type);
	int		(*set_wake)(unsigned int irq, unsigned int on);

	void		(*bus_lock)(unsigned int irq);
	void		(*bus_sync_unlock)(unsigned int irq);

	/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
	void		(*release)(unsigned int irq, void *dev_id);
#endif
	/*
	 * For compatibility, ->typename is copied into ->name.
	 * Will disappear.
	 */
	const char		*typename;
};
struct <strong>irqaction</strong> {
<strong>	irq_handler_t handler;  // handler assigned by request_irq</strong>
	unsigned long flags;
	const char *name;
	void *dev_id;
	struct irqaction *next;
	int irq;
	struct proc_dir_entry *dir;
	irq_handler_t thread_fn;
	struct task_struct *thread;
	unsigned long thread_flags;
};

之间的关系图:

?External interrupt resources in S5PV210

?Code flow

首先可以查看系统中有哪些有效的中断以及相关的信息.

# cat /proc/interrupts  //只显示有相应的action的IRQ的信息
           CPU0
IRQ_NR	         count	desc->chip->name action->name
 16:        	 43    	s3c-uart 	 s5pv210-uart
 18:        	 59    	s3c-uart  	s5pv210-uart
 33:         	 1	s5p_vic_eint 	mmc1
 36:          	 0 	s5p_vic_eint  	a700_ts
 37:          	 1 	s5p_vic_eint  	aic3254 headset irq
 38:          	 0  	s5p_vic_eint  	keypad
 39:          	 0  	s5p_vic_eint  	keypad
 40:         	 0  	s5p_vic_eint  	keypad
 41:        	 0  	s5p_vic_eint  	keypad
 42:          	0  	s5p_vic_eint  	keypad
 43:        	0  	s5p_vic_eint  	keypad
 45:         	1  	s5p_vic_eint  	hpd
 46:          	1  	s5p_vic_eint  	USB wak up
 50:          	0        	 VIC  	s3c-pl330.0
 51:          	0         	VIC  	s3c-pl330.1
 52:          	0         	VIC  	s3c-pl330.2
 58:          	0         	VIC  	System timer
 59:          	0         	VIC  	s3c2410-wdt
 61:      	14772         	VIC  	rtc-tick
 78:        	220        	 VIC  	s3c2440-i2c.0
 83:      	27985         	VIC  	s3c2440-i2c.2
 88:          	1         	VIC  	s3c-udc
 90:     	 52662         	VIC  	mmc0
 92:          	268         	VIC  	mmc1
 93:         	 0         	VIC  	s3c-csis
 97:       	2582        	VIC  	s3cfb, s3cfb
102:          	0         	VIC  	s3c-fimc1
103:          	0         	VIC  	s3c-fimc2
105:          	0         	VIC  	s3c-g2d
106:        	747         	VIC  	pvrsrvkm
107:          	0         	VIC  	s5p-tvout
108:          	0         	VIC  	s5p-tvout
109:          	0         	VIC  	s3c2440-i2c.1
110:          	0         	VIC  	s3c-mfc
111:          	0        	VIC  	s5p-tvout
130:         	13         	VIC  	mmc2
170:         	0     s5p-eint  	Bq27520_INT
Err:          	0

首先是初始化的过程, IRQ init sequence:

start_kernel
  setup_arch
     early_trap_init
  early_irq_init //没做什么事
  init_IRQ
    s5pv210_init_irq
       s5p_init_irq
	 vic_init(irq_nr直接是从32开始的, 前面的目前看起来至少留给了timer和UART)
	s3c_init_vic_timer_irq
	s3c_init_uart_irqs

其中的early_trap_init, 基本的思路就是, 对于有MMU的系统, 异常向量的虚拟地址被映射到0xFFFF0000, 所以, 真正的7个异常向量(__vectors_start~__vectors_end)是被拷贝到这个0xFFFF0000开始的地方了. 接着, 异常处理代码块(__stubs_start~__stubs_end)被拷贝到0xFFFF0200处.

void __init early_trap_init(void)
{
	unsigned long vectors = CONFIG_VECTORS_BASE;   //0xFFFF0000
	extern char __stubs_start[], __stubs_end[];
	extern char __vectors_start[], __vectors_end[];
	extern char __kuser_helper_start[], __kuser_helper_end[];
	int kuser_sz = __kuser_helper_end - __kuser_helper_start;

	/*
	 * 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.
	 */
<strong>	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);</strong>

	/*
	 * Copy signal return handlers into the vector page, and
	 * set sigreturn to be a pointer to these.
	 */
	memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,  sizeof(sigreturn_codes));
	memcpy((void *)KERN_RESTART_CODE, syscall_restart_code, sizeof(syscall_restart_code));

	flush_icache_range(vectors, vectors + PAGE_SIZE);
	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}

接下来, 中断发生后, 首先我们看到的是前面注册的那些vectors. 异常向量表的写法主要使用跳转指令B来进行. 因为异常向量表和异常处理代码块之间没有超过B指令要求的2^24=32MB, 仅仅相差0x200. 但是因为vertor_swi不是在这个文件中定义的, 所以, 只能用LDR指令来进行处理了.

__vectors_start:
 ARM(	swi	SYS_ERROR0	)
 THUMB(	svc	#0		)
 THUMB(	nop			)
	W(b)	vector_und + stubs_offset
	W(ldr)	pc, .LCvswi + stubs_offset
	W(b)	vector_pabt + stubs_offset              // prefetch abort
	W(b)	vector_dabt + stubs_offset              // data abort
	W(b)	vector_addrexcptn + stubs_offset        //
	W(b)	vector_irq + stubs_offset		//IRQ入口
	W(b)	vector_fiq + stubs_offset               //FIQ

	.globl	__vectors_end
__vectors_end:
@@@   中断处理程序的 stub
vector_irq:
  @  调整 LR_irq
sub lr, lr, #4 

@  保存 R0, LR_irq(中断之前的 PC, 断点), SPSR_irq(中断之前的 CPSR)  到 irq模式的栈中
  stmia  sp, {r0, lr}    @ save r0, lr
 mrs lr, spsr
  str  lr, [sp, #8]    @ save spsr 

  @ SPSR  设置为 SVC模式
 mrs r0, cpsr
  eor  r0, r0, #(\mode ^ SVC_MODE)
 msr spsr_cxsf, r0

@  根据中断前的模式跳转到相应的处理程序 

@ lr是中断刚开始时的 SPSR,即被中断代码的 CPSR,其低 4位表示中断之前的模式
  and lr, lr, #0x0f
 mov  r0, sp
  ldr  lr, [pc, lr, lsl #2] 

@  跳转到相应模式的处理程序,模式变为 SVC(SPSR 拷贝到 CPSR )
 movs pc, lr  

 @  跳转表,必须紧跟 ldr lr,[pc,lr,lsl #2]和 movs pc,lr 两条指令(ARM 流水线机制??)
    .long  __irq_usr         @  0  (USR)
  .long  __irq_invalid      @  1  (FIQ)
  .long  __irq_invalid      @  2  (IRQ)
  .long  __irq_svc         @  3  (SVC)
 .long __irq_invalid   @  4
 .long __irq_invalid   @  5
  .long  __irq_invalid      @  6  (ABT)
 .long __irq_invalid   @  7
 .long __irq_invalid   @  8
 .long __irq_invalid   @  9
 .long __irq_invalid   @  a
 .long __irq_invalid   @  b (UND)
 .long __irq_invalid   @  c
 .long __irq_invalid   @  d
 .long __irq_invalid   @  e
 .long __irq_invalid   @  f (SYS)

user mode的处理

@@@ USR模式中断入口
__irq_usr: 

@  在内核栈中产生 include/asm-arm/ptrace.h中 pt_regs 定义的栈帧结构
  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
  @ We are now ready to fill in the remaining blanks on the stack:
  @  r2 - lr_<exception>, already fixed up for correct return/restart
  @  r3 - spsr_<exception>
  @    r4 - orig_r0 (see pt_regs definition in ptrace.h)
  @ Also, separately save sp_usr and lr_usr
  stmia  r0, {r2 - r4}
  stmdb  r0, {sp, lr}^ 

  @ Clear FP to mark the first stack frame
 zero_fp 

@  把被中断任务的 preempt_count  增加 1
 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

<div><div>[email protected]  循环调用 asm_do_IRQ()   
</div><div>?1:  get_irqnr_and_base r0, r6, r5, lr
</div><div>? movne r1, sp 
</div><div>[email protected] routine called with r0 = irq number, r1 = struct pt_regs *
</div><div>? adrne lr, 1b
</div><div>? <span style="color:red;"><strong>bne asm_do_IRQ</strong></span>
</div><div>? 
</div><div>?#ifdef CONFIG_PREEMPT
</div><div>?  ldr  r0, [tsk, #TI_PREEMPT]
</div><div>?  str  r8, [tsk, #TI_PREEMPT]
</div><div>? teq r0, r7
</div><div>? strne  r0, [r0, -r0]
</div><div>?#endif
</div><div>[email protected]  返回到 user 模式  
</div><div>? mov why, #0
</div><div>?<strong><span style="color:#FF0000;"> b ret_to_user</span></strong>
</div></div>

svc mode的处理

@@@ SVC模式中断入口
__irq_svc:
@  在内核栈中产生 include/asm-arm/ptrace.h中 pt_regs 定义的栈帧结构
  sub  sp, sp, #S_FRAME_SIZE
 tst sp, #4
  bicne  sp, sp, #4
  stmib  sp, {r1 - r12} 

  ldmia  r0, {r1 - r3}
  add  r5, sp, #S_SP    @ here for interlock avoidance
  mov r4, #-1      @  ""  ""      ""       ""
  add r0, sp, #S_FRAME_SIZE   @  ""  ""      ""       ""
 addne r0, r0, #4
str  r1, [sp]    @ save the "real" r0 copied  from the exception stack
 mov r1, lr 

  @ We are now ready to fill in the remaining blanks on the stack:
  @  r0 - sp_svc
  @  r1 - lr_svc
  @  r2 - lr_<exception>, already fixed up for correct return/restart
  @  r3 - spsr_<exception>
  @    r4 - orig_r0 (see pt_regs definition in ptrace.h)
  stmia  r5, {r0 - r4} 

@  把被中断任务的 preempt_count  增加 1
#ifdef CONFIG_PREEMPT
 get_thread_info tsk
  ldr  r8, [tsk, #TI_PREEMPT]    @ get preempt count
 add r7, r8, #1   @ increment it
  str  r7, [tsk, #TI_PREEMPT]
#endif

@  循环调用 asm-do_IRQ()
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
<span style="color:#FF0000;"><strong> bne asm_do_IRQ </strong></span>

@  如果需要调度,调用 svc_preempt进行内核抢占
#ifdef CONFIG_PREEMPT
  ldr  r0, [tsk, #TI_FLAGS]    @ get flags
 tst r0, #_TIF_NEED_RESCHED
 blne svc_preempt
preempt_return:
  ldr  r0, [tsk, #TI_PREEMPT]    @ read preempt value
  str  r8, [tsk, #TI_PREEMPT]    @ restore preempt count
 teq r0, r7
 strne  r0, [r0, -r0]   @ bug()
#endif
@  返回到内核空间
  ldr  r0, [sp, #S_PSR]      @ irqs are already disabled
 msr spsr_cxsf, r0
  ldmia  sp, {r0 - pc}^      @ load r0 - pc, cpsr

可以看到, 都调用了 asm_do_IRQ

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs(regs);

<strong>	irq_enter();</strong>

	/*
	 * Some hardware gives randomly wrong interrupts.  Rather
	 * than crashing, do something sensible.
	 */
	if (unlikely(irq >= NR_IRQS)) {
		if (printk_ratelimit())
			printk(KERN_WARNING "Bad IRQ%u\n", irq);
		ack_bad_irq(irq);
	} else {
<span style="color:#FF0000;"><strong>		generic_handle_irq(irq);</strong></span>
	}

	/* AT91 specific workaround */
	irq_finish(irq);

<strong>	irq_exit();</strong>
	set_irq_regs(old_regs);
}
static inline void generic_handle_irq(unsigned int irq)
{
	generic_handle_irq_desc(irq, irq_to_desc(irq));
}

static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
<strong>	desc->handle_irq(irq, desc);  // high level handle, take handle_level_irq for example</strong>
#else
	if (likely(desc->handle_irq))
		desc->handle_irq(irq, desc);
	else
		__do_IRQ(irq);
#endif
}

然后进入 handle_level_irq

Void  handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
	struct irqaction *action;
	irqreturn_t action_ret;

	raw_spin_lock(&desc->lock);
	mask_ack_irq(desc, irq);

	if (unlikely(desc->status & IRQ_INPROGRESS))
		goto out_unlock;
	desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
	kstat_incr_irqs_this_cpu(irq, desc);

	action = desc->action;
	if (unlikely(!action || (desc->status & IRQ_DISABLED)))
		goto out_unlock;

	desc->status |= IRQ_INPROGRESS;
	raw_spin_unlock(&desc->lock);

<strong>	action_ret = handle_IRQ_event(irq, action);</strong>
	if (!noirqdebug)
		note_interrupt(irq, desc, action_ret);

	raw_spin_lock(&desc->lock);
	desc->status &= ~IRQ_INPROGRESS;

	if (!(desc->status & (IRQ_DISABLED | IRQ_ONESHOT)))
		unmask_irq(desc, irq);
out_unlock:
	raw_spin_unlock(&desc->lock);
}

然后是handle_IRQ_event, 这边调用了request_irq时候注册的那个handle.

irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
	irqreturn_t ret, retval = IRQ_NONE;
	unsigned int status = 0;

	do {
		trace_irq_handler_entry(irq, action);
<strong>		ret = action->handler(irq, action->dev_id);	 //  handle registered by request_irq</strong>
		trace_irq_handler_exit(irq, action, ret);

		switch (ret) {
		case IRQ_WAKE_THREAD:
			…
			/* Fall through to add to randomness */
		case IRQ_HANDLED:
			status |= action->flags;
			break;

		default:
			break;
		}

		retval |= ret;
		action = action->next;
	} while (action);

	if (status & IRQF_SAMPLE_RANDOM)
		add_interrupt_randomness(irq);
	local_irq_disable();

	return retval;
}

以图表示的话, 就是:

?Kernel API

int request_irq(unsigned int irq, irq_handler_t handler,  unsigned long irqflags, const char *devname, void *dev_id)
参数:
irq---中断通道号,取值范围为 0~NR_IRQS – 1
handler---中断处理程序,原型为 irq_return_t isr_func(int irq, void *dev_id)
irq_flags---标志位
dev_name---名称,将会显示在/proc/interrupts中
dev_id---区分共享同一个中断通道的不同的处理程序

void free_irq(unsigned int irq, void *dev_id)
参数:
irq---中断通道号,取值范围为 0~NR_IRQS – 1
dev_id---区分共享同一个中断通道的不同的处理程序时才需要用到.
int set_irq_chip(unsigned int irq, struct irq_chip *chip)
设置 chip 

int set_irq_chip_data(unsigned int irq, void *data)
设置 chip_data 

int set_irq_handle(unsigned int irq, irq_flow_handler_t handle)
设置 handle_irq 

int set_irq_data(unsigned int irq, void *data)
设置 handler_data 

int set_irq_type(unsigned int irq, unsigned int type)
设置指定通道的触发类型

Ryan: PPT完成于2011.9.15, blog完成于2015.1.4

时间: 2024-10-12 23:52:49

linux的中断子系统简介(汇编和hard irq部分)_ARM平台(S5PV210)的相关文章

Linux kernel中断子系统之(五):驱动申请中断API

一.前言 本文主要的议题是作为一个普通的驱动工程师,在撰写自己负责的驱动的时候,如何向Linux Kernel中的中断子系统注册中断处理函数?为了理解注册中断的接口,必须了解一些中断线程化(threaded interrupt handler)的基础知识,这些在第二章描述.第三章主要描述了驱动申请 interrupt line接口API request_threaded_irq的规格.第四章是进入request_threaded_irq的实现细节,分析整个代码的执行过程. 二.和中断相关的lin

linux kernel 中断子系统之(一)-- ARM GIC 硬件

一个系统中,中断是很重要的组成部分之一,有了中断,系统才可以不用一直轮询(polling)是否有事件发生,系统效率才得以提高,而且对中断的控制又通常分散在各个地方,不同的部分由不同功能的程序控制,做到了各司其职,配合无误,系统才能正常工作.一般系统中,中断控制分为三个地方:模块.中断控制器.处理器,模块通常有寄存器可以控制是否使能中断功能,中断触发条件等:中断控制器可以管理中断的优先级等,而处理所有中断的处理器则有寄存器设置是否响应中断. 1. 全景 作为 ARM 系统中通用中断控制器的是 GI

linux中断子系统:中断号的映射与维护

写在前沿: 好久好久没有静下心来整理一些东西了,开始工作已有一个月,脑子里想整理的东西特别多.记录是一种很好的自我学习方式,静下来多思考多总结,三年的工作目标不能发生变化,作为职场菜鸟即将进入全世界半导体第一的Intel working,是机遇更是一种挑战,困难也是可想而知.脚踏实地.仰望星空,以结果为导向,以目标为准则,争取每天进步一点点. Linux内核版本:3.4.39 一. linux中断子系统的irq_desc初始化 linux内核最初的中断初始化过程入口为start_kernel.在

linux kernel的中断子系统之(三):IRQ number和中断描述符【转】

转自:http://www.wowotech.net/linux_kenrel/interrupt_descriptor.html 一.前言 本文主要围绕IRQ number和中断描述符(interrupt descriptor)这两个概念描述通用中断处理过程.第二章主要描述基本概念,包括什么是IRQ number,什么是中断描述符等.第三章描述中断描述符数据结构的各个成员.第四章描述了初始化中断描述符相关的接口API.第五章描述中断描述符相关的接口API. 二.基本概念 1.通用中断的代码处理

linux kernel的中断子系统之(四):High level irq event handler

一.前言 当外设触发一次中断后,一个大概的处理过程是: 1.具体CPU architecture相关的模块会进行现场保护,然后调用machine driver对应的中断处理handler 2.machine driver对应的中断处理handler中会根据硬件的信息获取HW interrupt ID,并且通过irq domain模块翻译成IRQ number 3.调用该IRQ number对应的high level irq event handler,在这个high level的handler中

linux kernel的中断子系统之(八):softirq

一.前言 对于中断处理而言,linux将其分成了两个部分,一个叫做中断handler(top half),是全程关闭中断的,另外一部分是deferable task(bottom half),属于不那么紧急需要处理的事情.在执行bottom half的时候,是开中断的.有多种bottom half的机制,例如:softirq.tasklet.workqueue或是直接创建一个kernel thread来执行bottom half(这在旧的kernel驱动中常见,现在,一个理智的driver厂商是

linux kernel的中断子系统之(七):GIC代码分析

一.前言 GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器,其architecture specification目前有四个版本,V1-V4(V2最多支持8个ARM core,V3/V4支持更多的ARM core,主要用于ARM64服务器系统结构).目前在ARM官方网站只能下载到Version 2的GIC architecture specification,因此,本文主要描述符合V2规范的GIC硬件及其驱动. 具体GIC硬件的实现形态有两

Linux kernel的中断子系统之(六):ARM中断处理过程

一.前言 本文主要以ARM体系结构下的中断处理为例,讲述整个中断处理过程中的硬件行为和软件动作.具体整个处理过程分成三个步骤来描述: 1.第二章描述了中断处理的准备过程 2.第三章描述了当发生中的时候,ARM硬件的行为 3.第四章描述了ARM的中断进入过程 4.第五章描述了ARM的中断退出过程 本文涉及的代码来自3.14内核.另外,本文注意描述ARM指令集的内容,有些source code为了简短一些,删除了THUMB相关的代码,除此之外,有些debug相关的内容也会删除. 二.中断处理的准备过

Linux kernel的中断子系统之(二):IRQ Domain介绍

一.概述 在linux kernel中,我们使用下面两个ID来标识一个来自外设的中断: 1.IRQ number.CPU需要为每一个外设中断编号,我们称之IRQ Number.这个IRQ number是一个虚拟的interrupt ID,和硬件无关,仅仅是被CPU用来标识一个外设中断. 2.HW interrupt ID.对于interrupt controller而言,它收集了多个外设的interrupt request line并向上传递,因此,interrupt controller需要对