linux的(de)中斷子(zǐ)系統簡介(彙編和(hé / huò)hardirq部分)_ARM平台(S - 新聞資訊 - 雲南小程序開發|雲南軟件開發|雲南網站建設-昆明融晨信息技術有限公司

159-8711-8523

雲南網建設/小程序開發/軟件開發

知識

不(bù)管是(shì)網站,軟件還是(shì)小程序,都要(yào / yāo)直接或間接能爲(wéi / wèi)您産生價值,我們在(zài)追求其視覺表現的(de)同時(shí),更側重于(yú)功能的(de)便捷,營銷的(de)便利,運營的(de)高效,讓網站成爲(wéi / wèi)營銷工具,讓軟件能切實提升企業内部管理水平和(hé / huò)效率。優秀的(de)程序爲(wéi / wèi)後期升級提供便捷的(de)支持!

您當前位置>首頁 » 新聞資訊 » 技術分享 >

linux的(de)中斷子(zǐ)系統簡介(彙編和(hé / huò)hardirq部分)_ARM平台(S

發表時(shí)間:2020-10-19

發布人(rén):融晨科技

浏覽次數:45


2011年9月份時(shí)刻做的(de)标記, 當時(shí)浏覽中斷子(zǐ)體系的(de)代碼後做的(de)一個(gè)PPT, 内核版本不(bù)記得了(le/liǎo), 硬件平台是(shì)samsung 的(de)S5PV210.
這(zhè)部分主如果針對彙編和(hé / huò)hard irq的(de)部分, 在(zài)hard irq處理後的(de)softirq的(de)處理, 以(yǐ)及下半部的(de)處理(tasklet/workqueue)都沒有涉及.
Agenda
?Interrupts in ARM
?Important structs
?External interrupt resources in S5PV210
?Code flow
?Kernel API
?Interrupts in ARM
ARM CPU CORE 中隻有兩根中斷引腳, 分别是(shì)IRQ和(hé / huò)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 {
<span style="color:#FF0000;">	unsigned int		irq;</span>
	struct timer_rand_state 	*timer_rand_state;
	unsigned int            		*kstat_irqs;
#ifdef CONFIG_INTR_REMAP
	struct irq_2_iommu      	*irq_2_iommu;
#endif
<span style="color:#FF0000;">	irq_flow_handler_t		handle_irq;	//high level irq-events handle
	struct irq_chip		*chip;</span>
	struct msi_desc		*msi_desc;
	void		*handler_data;
	void		*chip_data;
<span style="color:#FF0000;">	struct irqaction		*action;	/* IRQ action list */</span>
	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> {
<span style="color:#FF0000;">	irq_handler_t handler;  // handler assigned by request_irq</span>
	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;
};
之(zhī)間的(de)關系圖:
[img]http://img.blog.csdn.net/20150104101704954?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFja2pvbmVzXzAwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
?External interrupt resources in S5PV210
[img]http://img.blog.csdn.net/20150104101748946?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFja2pvbmVzXzAwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
?Code flow
起首可以(yǐ)查看體系中有哪些有效的(de)中斷以(yǐ)及相幹的(de)信息.
# cat /proc/interrupts  //隻顯示有響應的(de)action的(de)IRQ的(de)信息
           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

起首是(shì)初始化的(de)過程, 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直接是(shì)大(dà)年夜32開端的(de), 前面的(de)今朝看起來(lái)至少留給了(le/liǎo)timer和(hé / huò)UART)
	s3c_init_vic_timer_irq
	s3c_init_uart_irqs
個(gè)中的(de)early_trap_init, 根本的(de)思路就(jiù)是(shì), 對于(yú)有MMU的(de)體系, 異常向量的(de)虛拟地(dì / de)址被映射到(dào)0xFFFF0000, 所以(yǐ), 真正的(de)7個(gè)異常向量(__vectors_start~__vectors_end)是(shì)被拷貝到(dào)這(zhè)個(gè)0xFFFF0000開端的(de)處所了(le/liǎo). 接着, 異常處理代碼塊(__stubs_start~__stubs_end)被拷貝到(dào)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.
	 */
<span style="color:#FF0000;">	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);</span>

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

接下來(lái), 中斷産生後, 起首我們看到(dào)的(de)是(shì)前面注冊的(de)那些vectors. 異常向量表的(de)寫法重要(yào / yāo)應用跳轉指令B來(lái)進行. 因爲(wéi / wèi)異常向量表和(hé / huò)異常處理代碼塊之(zhī)間沒有跨越B指令請求的(de)2^24=32MB, 僅僅相差0x200. 然則因爲(wéi / wèi)vertor_swi不(bù)是(shì)在(zài)這(zhè)個(gè)文件中定義的(de), 所以(yǐ), 隻能用LDR指令來(lái)進行處理了(le/liǎo).
__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人(rén)口
	W(b)	vector_fiq + stubs_offset               //FIQ

	.globl	__vectors_end
__vectors_end:

@@@   中斷處理法度榜樣的(de) stub  
vector_irq: 
  @  調劑 LR_irq 
sub lr, lr, #4 
  
@  保存 R0, LR_irq(中斷之(zhī)前的(de) PC, 斷點), SPSR_irq(中斷之(zhī)前的(de) CPSR)  到(dào) irq模式的(de)┞坊中 
  stmia  sp, {r0, lr}    @ save r0, lr 
 mrs lr, spsr 
  str  lr, [sp, #8]    @ save spsr 
 
  @ SPSR  設置爲(wéi / wèi) SVC模式 
 mrs r0, cpsr 
  eor  r0, r0, #(\mode ^ SVC_MODE) 
 msr spsr_cxsf, r0

@  根據中斷前的(de)模式跳轉到(dào)響應的(de)處理法度榜樣 
 
@ lr是(shì)中斷剛開端時(shí)的(de) SPSR,即被中斷代碼的(de) CPSR,其低 4位表示中斷之(zhī)前的(de)模式
  and lr, lr, #0x0f    
 mov  r0, sp 
  ldr  lr, [pc, lr, lsl #2] 
 
@  跳轉到(dào)響應模式的(de)處理法度榜樣,模式變爲(wéi / wèi) SVC(SPSR 拷貝到(dào) CPSR ) 
 movs pc, lr  
 
 @  跳轉表,必須緊跟 ldr lr,[pc,lr,lsl #2]和(hé / huò) 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的(de)處理
@@@ USR模式中斷人(rén)口 
__irq_usr: 
     
@  在(zài)内核棧中産生 include/asm-arm/ptrace.h中 pt_regs 定義的(de)┞坊幀構造  
  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 
 
@  把被中斷義務的(de) 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

?@  輪回調用 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 
? bne <span style="color:#FF0000;">asm_do_IRQ</span>
? 
?#ifdef CONFIG_PREEMPT 
?  ldr  r0, [tsk, #TI_PREEMPT] 
?  str  r8, [tsk, #TI_PREEMPT] 
? teq r0, r7 
? strne  r0, [r0, -r0] 
?#endif 
?@  返回到(dào) user 模式   
? mov why, #0 
? <span style="color:#FF0000;">b ret_to_user</span>

svc mode的(de)處理
@@@ SVC模式中斷人(rén)口 
__irq_svc: 
@  在(zài)内核棧中産生 include/asm-arm/ptrace.h中 pt_regs 定義的(de)┞坊幀構造   
  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} 
 
@  把被中斷義務的(de) 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 
 bne asm_do_IRQ 
 
@  如不(bù)雅須要(yào / yāo)調劑,調用 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 
@  返回到(dào)内核空間 
  ldr  r0, [sp, #S_PSR]      @ irqs are already disabled 
 msr spsr_cxsf, r0 
  ldmia  sp, {r0 - pc}^      @ load r0 - pc, cpsr

可以(yǐ)看到(dào), 都調用了(le/liǎo) 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);

	<span style="color:#FF0000;">irq_enter();</span>

	/*
	 * 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;">generic_handle_irq(irq);</span>
	}

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

	irq_exit();
	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
	<span style="color:#FF0000;">desc->handle_irq(irq, desc);  // high level handle, take handle_level_irq for example</span>
#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);

	action_ret = <span style="color:#FF0000;">handle_IRQ_event(irq, action);</span>
	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);
}
然後是(shì)handle_IRQ_event, 這(zhè)邊調用了(le/liǎo)request_irq時(shí)刻注冊的(de)那個(gè)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);
		ret = <span style="color:#FF0000;">action->handler(irq, action->dev_id);	 //  handle registered by request_irq</span>
		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;
}
以(yǐ)圖表示的(de)話, 就(jiù)是(shì):
[img]http://img.blog.csdn.net/20150104110240871?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFja2pvbmVzXzAwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
?Kernel API
int request_irq(unsigned int irq, irq_handler_t handler,  unsigned long irqflags, const char *devname, void *dev_id) 
參數:
irq---中斷通道(dào)号,取值範圍爲(wéi / wèi) 0~NR_IRQS – 1 
handler---中斷處理法度榜樣,原型爲(wéi / wèi) irq_return_t isr_func(int irq, void *dev_id) 
irq_flags---标記位 
dev_name---名稱,将會顯示在(zài)/proc/interrupts中 
dev_id---區分共享同一個(gè)中斷通道(dào)的(de)不(bù)合的(de)處理法度榜樣


void free_irq(unsigned int irq, void *dev_id) 
參數:
irq---中斷通道(dào)号,取值範圍爲(wéi / wèi) 0~NR_IRQS – 1
dev_id---區分共享同一個(gè)中斷通道(dào)的(de)不(bù)合的(de)處理法度榜樣時(shí)才須要(yào / yāo)用到(dào).

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)
設置指定通道(dào)的(de)觸發類型

Ryan: PPT完成于(yú)2011.9.15, blog完成于(yú)2015.1.4

相關案例查看更多