<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > arm linux 下中斷流程簡(jiǎn)要分析中斷處理流程

arm linux 下中斷流程簡(jiǎn)要分析中斷處理流程

作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò ) 收藏
三 響應中斷

首先在分析源碼之前,讓我們了解一些原理性的東西,我們都知道在處理中斷要保存當前現場(chǎng)狀態(tài),然后才能處理中斷,處理完之后還要把現場(chǎng)狀態(tài)恢復過(guò)來(lái)才能返回到被中斷的地方繼續執行,這里要說(shuō)明的是在指令跳轉到中斷向量的地方開(kāi)始執行之前,CPU幫我們做了哪些事情:

本文引用地址:http://dyxdggzs.com/article/201611/317838.htm


R14_irq =要執行的下條指令地址+ 4//這里的下條指令是相對于被中斷指令的下條。即返回地址

SPSR_irq = CPSR//保存的現場(chǎng)狀態(tài),r0到r12要由我們軟件來(lái)保存(如果需要的話(huà))。

CPSR[4:0] = 0b10010//進(jìn)入中斷模式

CPSR[5] = 0//在A(yíng)RM模式下執行(不是Thumb下)

CPSR[7] = 1//關(guān)掉IRQ中斷,FIQ還是開(kāi)著(zhù)

PC = 0Xffff0018/0x00000018//根據異常向量表的位置,跳轉到特定的中斷向量處去執行。

更詳細的關(guān)于異常處理的細節可參考<>

接下來(lái)我們在來(lái)分析watchdog產(chǎn)生中斷后的處理流程:

當watchdog超時(shí)時(shí)將會(huì )產(chǎn)生中斷,中斷號就是IRQ_WDT,當產(chǎn)生中斷時(shí),系統將從跳轉表中的中斷位置開(kāi)始運行,對于我們這篇文章來(lái)說(shuō):是從0xffff0000 + 24處開(kāi)始運行。 這個(gè)地址的指令是:

bvector_irq + stubs_offset

即直接跳轉到vector_irq處去運行。這些都在中斷初始化的時(shí)候分析過(guò)了。

我們來(lái)看vector_irq,它是通過(guò)宏vector_stub來(lái)定義的:

arch/arm/kernel/entry-armv.S:

/*

* Interrupt dispatcher

*/

vector_stubirq, IRQ_MODE, 4/*這是個(gè)宏定義*/

/*下面這些都是不同模式下的irq處理函數*/

.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

來(lái)看宏vector_stub

arch/arm/kernel/entry-armv.S:

.macrovector_stub, name, mode, correction=0

.align5

vector_/name:

.if /correction

sublr, lr, #/correction

.endif

@

@ Save r0, lr_ (parent PC) and spsr_

@ (parent CPSR)

@

stmiasp, {r0, lr}@ save r0, lr

mrslr, spsr

strlr, [sp, #8]@ save spsr

@

@ Prepare for SVC32 mode.IRQs remain disabled.

@

mrsr0, cpsr

eorr0, r0, #(/mode ^ SVC_MODE)

msrspsr_cxsf, r0

@

@ the branch table must immediately follow this code

@

andlr, lr, #0x0f

movr0, sp

ldrlr, [pc, lr, lsl #2]

movspc, lr@ branch to handler in SVC mode

.endm

這樣展開(kāi)后vector_irq如下所示:

arch/arm/kernel/entry-armv.S:

vector_irq:

.if 4

@ lr保存的是被打斷指令處地址+8的值,(看上面的分析,由PC得到), 這里-4則就是中斷

@處理完后的返回地址,在中斷處理完后該值會(huì )賦給PC

sublr, lr, #4

.endif

@

@ Save r0, lr_ (parent PC) and spsr_

@ (parent CPSR)

@ r0后面會(huì )用到所以要保存。

stmiasp, {r0, lr}@ save r0, lr,保存r0,lr到棧上,這里的棧是中斷模式下的。

mrslr, spsr@獲取spsr的值,該值保存了被中斷處執行環(huán)境的狀態(tài)(參考上面的分析)

strlr, [sp, #8]@ save spsr, 保存到棧上

@

@ Prepare for SVC32 mode.IRQs remain disabled.

@

mrsr0, cpsr

eorr0, r0, #( IRQ_MODE ^ SVC_MODE)

msrspsr_cxsf, r0@把spsr設置成管理模式

@

@ the branch table must immediately follow this code

@

andlr, lr, #0x0f

movr0, sp

ldrlr, [pc, lr, lsl #2]

movspc, lr@ branch to handler in SVC mode @ pc = lr, cpsr = spsr

.endm

movs的目的對象如果是pc的話(huà),則還會(huì )把spsr賦值給cpsr,上面我們看到spsr被設成管理模式,因此這條語(yǔ)句過(guò)后的代碼也就跑在了管理模式下。

此時(shí)的棧情況如下:



S_FRAME_SIZE,S_PC在arch/arm/kernel/Asm-offsets.c:中定義

DEFINE(S_FRAME_SIZE,sizeof(struct pt_regs));

DEFINE(S_PC,offsetof(struct pt_regs, ARM_pc));

include/asm-arm/Ptrace.h:

struct pt_regs {

long uregs[18];

};

#define ARM_pcuregs[15]

,pt_regs中對應的就是上面棧上的18個(gè)寄存器,ARM_pc是pc寄存器存放在這個(gè)數組中的偏移。

接著(zhù)看get_thread_info, 它也是個(gè)宏,用來(lái)獲取當前線(xiàn)程的地址。在我的一篇linux啟動(dòng)代碼分析里曾寫(xiě)過(guò)線(xiàn)程的定義方式:

include/linux/Sched.h:

union thread_union {

struct thread_info thread_info;/*線(xiàn)程屬性*/

unsigned long stack[THREAD_SIZE/sizeof(long)];/*棧*/

};

由它定義的線(xiàn)程是8K字節對齊的, 并且在這8K的最低地址處存放的就是thread_info對象,即該棧擁有者線(xiàn)程的對象,而get_thread_info就是通過(guò)把sp低13位清0(8K邊界)來(lái)獲取當前thread_info對象的地址。

arch/arm/kernel/entry-armv.S:

.macroget_thread_info, rd

mov/rd, sp, lsr #13

mov/rd, /rd, lsl #13

.endm

調用該宏后寄存器tsk里存放的就是當前線(xiàn)程的地址了,tsk是哪個(gè)寄存器呢,我們在看:

arch/arm/kernel/entry-header.S:

tsk.reqr9@ current thread_info

,tsk只是r9的別名而已, 因此這時(shí)r9里保存的就是當前線(xiàn)程的地址。

我們接著(zhù)看irq_handler:

arch/arm/kernel/entry-armv.S:

.macroirq_handler

1:get_irqnr_and_base r0, r6, r5, lr@平臺相關(guān),獲取中斷號

movner1, sp@如果r0(中斷號)不等于0,則r1指向sp所在地址,即pt_regs對象地址(看上圖)

@

@ routine called with r0 = irq number, r1 = struct pt_regs *

@

adrnelr, 1b@如果r0(中斷號)不等于0,lr(返回地址)等于標號1處,即

@ get_irqnr_and_base r0, r6, r5, lr的那行,即循環(huán)處理所有的中斷。

bneasm_do_IRQ@處理該中斷

#ifdef CONFIG_SMP

/*

* XXX

*

* this macro assumes that irqstat (r6) and base (r5) are

* preserved from get_irqnr_and_base above

*/

test_for_ipi r0, r6, r5, lr

movner0, sp

adrnelr, 1b

bnedo_IPI

#ifdef CONFIG_LOCAL_TIMERS

test_for_ltirq r0, r6, r5, lr

movner0, sp

adrnelr, 1b

bnedo_local_timer

#endif

#endif

.endm

get_irqnr_and_base是平臺相關(guān)的,這里就不列出來(lái)了,對于s3c2410,代碼在include/asm-arm/s3c2410/entry-macro.S里,該宏處理完后,r0 =中斷號,接下來(lái)r1賦值為sp地址(pt_regs對象地址), 最后調用c函數asm_do_IRQ, r0, r1作為參數被傳遞進(jìn)去。asm_do_IRQ()處理完后將返回到lr指向的地址處即上面匯編部分標號為1的地址處繼續執行。

我們把__irq_usr的匯編部分分析完后再來(lái)分析asm_do_IRQ()等c函數。

Arch/arm/kernel/entry-armv.S:

__irq_usr:

……

……

mov why, #0@ why = 0, why是r8的別名,

b ret_to_user@返回到用戶(hù)模式下

我們看ret_to_user

arch/arm/kernel/entry-common.S:

ENTRY(ret_to_user)

ret_slow_syscall:

disable_irq@ disable interrupts@關(guān)中斷,

ldrr1, [tsk, #TI_FLAGS] @獲取thread_info中flags域的值

tstr1, #_TIF_WORK_MASK@判斷task是否被阻塞

bnework_pending@根據需要進(jìn)行進(jìn)程的切換。

no_work_pending:

@ slow_restore_user_regs

ldrr1, [sp, #S_PSR]@ get calling cpsr獲取被中斷代碼處的狀態(tài)(cpsp)

ldrlr, [sp, #S_PC]!@ get pc獲取返回地址(被中斷代碼的下條代碼處的地址)

msrspsr_cxsf, r1@ save in spsr_svc, spsr里保存好被中斷代碼處的狀態(tài)(cpsp)

ldmdbsp, {r0 - lr}^@ get calling r1 – lr從棧上獲取用戶(hù)態(tài)下的r0到lr的值

movr0, r0

addsp, sp, #S_FRAME_SIZE - S_PC@棧地址恢復,避免多個(gè)中斷后溢出

movspc, lr@ return & move spsr_svc into cpsr, 返回被中斷代碼處繼續執行,并把spsr賦給cpsp,即恢復被中斷處的現場(chǎng)狀態(tài)。這樣CPU又可以從被中斷的地方繼續執行了,而且這個(gè)時(shí)候所有的寄存器值(r0到r12),包括狀態(tài)寄存器值(cpsr)都是源碼被中斷時(shí)的值。

我們順便看下work_pending

arch/arm/kernel/entry-common.S:

work_pending:

tstr1, #_TIF_NEED_RESCHED@判斷是否需要調度進(jìn)程

bnework_resched@進(jìn)程調度

tstr1, #_TIF_NOTIFY_RESUME | _TIF_SIGPENDING

beqno_work_pending@無(wú)需調度,返回

movr0, sp@ regs

movr2, why@ syscall

bldo_notify_resume

bret_slow_syscall@ Check work again

由該匯編可知,如果在用戶(hù)模式下產(chǎn)生中斷的話(huà),在返回的時(shí)候,會(huì )根據需要進(jìn)行進(jìn)程調度,而從代碼可知,如果中斷發(fā)生在管理等內核模式下的話(huà)是不會(huì )進(jìn)行進(jìn)程調度的。

Ok,中斷的流程大體就是這樣的,下面我們就開(kāi)始分析c函數里的中斷流程。

先來(lái)看asm_do_IRQ

arch/arm/kernel/Irq.c:

/*

* do_IRQ handles all hardware IRQs.Decoded IRQs should not

* come via this function.Instead, they should provide their

* own handler

*/

asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

{

struct irqdesc *desc = irq_desc + irq; /*獲取中斷描述符*/

/*

* Some hardware gives randomly wrong interrupts.Rather

* than crashing, do something sensible.

*/

if (irq >= NR_IRQS)/*參數檢查*/

desc = &bad_irq_desc;

irq_enter();

desc_handle_irq(irq, desc, regs);/*中斷處理*/

/* AT91 specific workaround */

irq_finish(irq);

irq_exit();

}

該函數的調用desc_handle_irq()來(lái)繼續處理中斷。

include/asm-arm/mach/Irq.h:

/*

* Obsolete inline function for calling irq descriptor handlers.

*/

static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc,

struct pt_regs *regs)

{

desc->handle_irq(irq, desc, regs);

}

調用中斷描述符的handler_irq函數來(lái)處理該中斷,對于IRQ_WDT就是do_edge_IRQ(前面分析過(guò))。

include/asm-arm/mach/Irq.h:

#define do_edge_IRQhandle_edge_irq

kernel/irq/Chip.c:

/

*handle_edge_irq - edge type IRQ handler

*@irq:the interrupt number

*@desc:the interrupt description structure for this irq

*@regs:pointer to a register structure

*

*Interrupt occures on the falling and/or rising edge of a hardware

*signal. The occurence is latched into the irq controller hardware

*and must be acked in order to be reenabled. After the ack another

*interrupt can happen on the same source even before the first one

*is handled by the assosiacted event handler. If this happens it

*might be necessary to disable (mask) the interrupt depending on the

*controller hardware. This requires to reenable the interrupt inside

*of the loop which handles the interrupts which have arrived while

*the handler was running. If all pending interrupts are handled, the

*loop is left.

*/

void fastcall

handle_edge_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs)

{

const unsigned int cpu = smp_processor_id();

spin_lock(&desc->lock);

desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);

/*

* If were currently running this IRQ, or its disabled,

* we shouldnt process the IRQ. Mark it pending, handle

* the necessary masking and go out

*/

/*

*如果該中斷正在處理或者該中斷被disable掉了的話(huà),就不處理該中斷,并清掉pending

*寄存器里的相應位

*/

if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||

!desc->action)) {

desc->status |= (IRQ_PENDING | IRQ_MASKED);

mask_ack_irq(desc, irq);/*mask該中斷,清pending標志位*/

goto out_unlock;

}

kstat_cpu(cpu).irqs[irq]++;/*統計中斷數量*/

/* Start handling the irq */

/*開(kāi)始處理中斷,先清掉pending標志位*/

desc->chip->ack(irq);

/* Mark the IRQ currently in progress.*/

desc->status |= IRQ_INPROGRESS;/*標上正在處理的標記*/

do {

struct irqaction *action = desc->action;/*獲取該中斷的action*/

irqreturn_t action_ret;

if (unlikely(!action)) {

desc->chip->mask(irq)/*如果沒(méi)有注冊action,則mask該中斷*/;

goto out_unlock;

}

/*

* When another irq arrived while we were handling

* one, we could have masked the irq.

* Renable it, if it was not disabled in meantime.

*/

/*

*如果以前被mask掉的話(huà),在這里把它打開(kāi)

*/

if (unlikely((desc->status &

(IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==

(IRQ_PENDING | IRQ_MASKED))) {

desc->chip->unmask(irq); /*unmask該中斷*/

desc->status &= ~IRQ_MASKED;

}

desc->status &= ~IRQ_PENDING;

spin_unlock(&desc->lock);

action_ret = handle_IRQ_event(irq, regs, action);/*處理中斷事件*/

if (!noirqdebug)

note_interrupt(irq, desc, action_ret, regs);

spin_lock(&desc->lock);

/*如果有IRQ_PENDING狀態(tài),則說(shuō)明又有中斷產(chǎn)生過(guò),則繼續執行*/

} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);

desc->status &= ~IRQ_INPROGRESS;

out_unlock:

spin_unlock(&desc->lock);

}

該函數的大體功能都在函數體內解釋出來(lái)了,這里我們對調用的每個(gè)函數在進(jìn)行分析。

先看mask_ack_irq

kernel/irq/Chip.c:

static inline void mask_ack_irq(struct irq_desc *desc, int irq)

{

if (desc->chip->mask_ack)/*對于IRQ_WDT, 該函數沒(méi)定義*/

desc->chip->mask_ack(irq);

else {

desc->chip->mask(irq); /*對于IRQ_WDT,該函數就是s3c_irq_mask*/

desc->chip->ack(irq);/*對于IRQ_WDT,該函數就是s3c_irq_ack*/

}

}

可以看到它調用具體平臺相關(guān)的mask函數來(lái)處理該中斷。

我們來(lái)看s3c_irq_mask

arch/arm/mach-s3c2410/Irq.c:

static void

s3c_irq_mask(unsigned int irqno)

{

unsigned long mask;

irqno -= IRQ_EINT0;

mask = __raw_readl(S3C2410_INTMSK);

mask |= 1UL << irqno;/*mask掉對應的中斷號*/

__raw_writel(mask, S3C2410_INTMSK);/*寫(xiě)MASK寄存器*/

}

改函數僅僅是把MASK寄存器中對應的中斷mask掉,即不再響應該中斷

arch/arm/mach-s3c2410/Irq.c:

static inline void

s3c_irq_ack(unsigned int irqno)

{

unsigned long bitval = 1UL << (irqno - IRQ_EINT0);

/*清除pending寄存器的相應位*/

__raw_writel(bitval, S3C2410_SRCPND);

__raw_writel(bitval, S3C2410_INTPND);

}

由上面這兩個(gè)函數可以看出來(lái)mask_ack_irq的作用是先mask掉該中斷,并清除pending位,中斷被mask掉后系統就不再響應了, 而pending位被清掉說(shuō)明系統中該中斷沒(méi)有觸發(fā)。一般在中斷處理完后都要清pending位, 要不然系統會(huì )認為該中斷又被觸發(fā)了。

handle_edge_irq()里調用的unmask函數,其實(shí)就是打開(kāi)相應的中斷,讓系統響應這個(gè)中斷,代碼就不列出來(lái)了。

接下來(lái)中斷看handle_IRQ_event(),它才是真正的中斷處理函數。

kernel/irq/handle.c:

/

* handle_IRQ_event - irq action chain handler

* @irq:the interrupt number

* @regs:pointer to a register structure

* @action:the interrupt action chain for this irq

*

* Handles the action chain of an irq event

*/

irqreturn_t handle_IRQ_event(unsigned int irq, struct pt_regs *regs,

struct irqaction *action)

{

irqreturn_t ret, retval = IRQ_NONE;

unsigned int status = 0;

handle_dynamic_tick(action);

/*下面這個(gè)if判斷:當執行action操作時(shí)是否可以打開(kāi)中斷*/

if (!(action->flags & IRQF_DISABLED))

local_irq_enable_in_hardirq();/*打開(kāi)中斷*/

do {

/*

*中斷handler,也就是我們通過(guò)request_irq注冊的中斷函數,對于IRQ_WDT就是

* s3c2410wdt_irq

*/

ret = action->handler(irq, action->dev_id, regs);

if (ret == IRQ_HANDLED)

status |= action->flags;

retval |= ret;

action = action->next; /*記得嗎,如果該中斷可以共享的話(huà),它就不為NULL*/

} while (action);

if (status & IRQF_SAMPLE_RANDOM)

add_interrupt_randomness(irq);

local_irq_disable();

return retval;

}

該函數主要就是調用了action的handler函數,也就是我們用request_irq注冊的中斷例程。這里需要注意的是:如果我們注冊中斷的時(shí)候指明可以共享的話(huà),則必須在我們的中斷例程里判斷當前產(chǎn)生的中斷是否就是我們自己的中斷,這可以通過(guò)傳進(jìn)來(lái)的參數來(lái)判斷(該參數就是我們注冊時(shí)提供的)。

OK,到這里整個(gè)中斷的流程就大致分析完了。



關(guān)鍵詞: armlinux中斷處

評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>