`
mmdev
  • 浏览: 13148919 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

linux-2.6.26内核中ARM中断实现详解(2)

阅读更多

三、中断处理过程

这一节将以S3C2410为例,描述linux-2.6.26内核中,从中断开始,中断是如何一步一步执行到我们注册函数的。

3.1 中断向量表 arch\arm\kernel\entry-armv.S

__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:

中断发生后,跳转到b vector_irq + stubs_offset的位置执行。注意现在的向量表的初始位置是0xffff0000。

3.2 中断跳转的入口位置 arch\arm\kernel\entry-armv.S

.globl __stubs_start
__stubs_start:
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4 @IRQ_MODEinclude\asm\ptrace.h中定义:0x12
.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宏的定义为:

.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)
msr spsr_cxsf, r0 @为后面进入svc模式做准备

@
@ the branch table must immediately follow this code
@
and lr, lr, #0x0f @进入中断前的mode的后4位
@#define USR_MODE 0x00000010
@#define FIQ_MODE 0x00000011
@#define IRQ_MODE 0x00000012
@#define SVC_MODE 0x00000013
@#define ABT_MODE 0x00000017
@#define UND_MODE 0x0000001b
@#define SYSTEM_MODE 0x0000001f
mov r0, sp
ldr lr, [pc, lr, lsl #2] @如果进入中断前是usr,则取出PC+4*0的内容,即__irq_usr@如果进入中断前是svc,则取出PC+4*3的内容,即__irq_svc
movs pc, lr @ 当指令的目标寄存器是PC,且指令以S结束,则它会把@ spsr的值恢复给cpsr branch to handler in SVC mode
.endm
.globl __stubs_start
__stubs_start:
/*
* 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)

用“irq, IRQ_MODE, 4”代替宏vector_stub中的“name, mode, correction”,找到了我们中断处理的入口位置为vector_irq(宏里面的vector_\name)。
从上面代码中的注释可以看出,根据进入中断前的工作模式不同,程序下一步将跳转到_irq_usr 、或__irq_svc等位置。我们先选择__irq_usr作为下一步跟踪的目标。

3.3 __irq_usr的实现 arch\arm\kernel\entry-armv.S

__irq_usr:
usr_entry @后面有解释
kuser_cmpxchg_check
#ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_off
#endif
get_thread_info tsk @获取当前进程的进程描述符中的成员变量thread_info的地址,并将该地址保存到寄存器tsk等于r9(在entry-header.S中定义)
#ifdef CONFIG_PREEMPT//如果定义了抢占,增加抢占数值
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
add r7, r8, #1 @ increment it
str r7, [tsk, #TI_PREEMPT]
#endif

irq_handler @中断处理,我们最关心的地方,3.4节有实现过程。
#ifdef CONFIG_PREEMPT
ldr r0, [tsk, #TI_PREEMPT]
str r8, [tsk, #TI_PREEMPT]
teq r0, r7
strne r0, [r0, -r0]
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_on
#endif

mov why, #0

b ret_to_user @中断处理完成,返回中断产生的位置,3.7节有实现过程

上面代码中的usr_entry是一个宏,主要实现了将usr模式下的寄存器、中断返回地址保存到堆栈中。

.macro usr_entry
sub sp, sp, #S_FRAME_SIZE @ S_FRAME_SIZE的值在arch\arm\kernel\asm-offsets.c
@ 中定义 DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));实际上等于72

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}^

@
@ Enable the alignment trap while in kernel mode
@
alignment_trap r0

@
@ Clear FP to mark the first stack frame
@
zero_fp
.endm

上面的这段代码主要在填充结构体pt_regs ,这里提到的struct pt_regs,在include/asm/ptrace.h中定义。此时sp指向struct pt_regs。

struct pt_regs {
long uregs[18];
};
#define ARM_cpsr uregs[16]
#define ARM_pc uregs[15]
#define ARM_lr uregs[14]
#define ARM_sp uregs[13]
#define ARM_ip uregs[12]
#define ARM_fp uregs[11]
#define ARM_r10 uregs[10]
#define ARM_r9 uregs[9]
#define ARM_r8 uregs[8]
#define ARM_r7 uregs[7]
#define ARM_r6 uregs[6]
#define ARM_r5 uregs[5]
#define ARM_r4 uregs[4]
#define ARM_r3 uregs[3]
#define ARM_r2 uregs[2]
#define ARM_r1 uregs[1]
#define ARM_r0 uregs[0]
#define ARM_ORIG_r0 uregs[17]

3.4 irq_handler的实现过程arch\arm\kernel\entry-armv.S

.macro irq_handler
get_irqnr_preamble r5, lr
@在include/asm/arch-s3c2410/entry-macro.s中定义了宏get_irqnr_preamble为空操作,什么都不做
1: get_irqnr_and_base r0, r6, r5, lr @判断中断号,通过R0返回,3.5节有实现过程
movne r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne lr, 1b
bne asm_do_IRQ @进入中断处理。
……
.endm

3.5 get_irqnr_and_base中断号判断过程,include/asm/arch-s3c2410/entry-macro.s

.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
mov \base, #S3C24XX_VA_IRQ
@@ try the interrupt offset register, since it is there
ldr \irqstat, [ \base, #INTPND ]
teq \irqstat, #0
beq 1002f
ldr \irqnr, [ \base, #INTOFFSET ] @通过判断INTOFFSET寄存器得到中断位置
mov \tmp, #1
tst \irqstat, \tmp, lsl \irqnr
bne 1001f
@@ the number specified is not a valid irq, so try
@@ and work it out for ourselves
mov \irqnr, #0 @@ start here
@@ work out which irq (if any) we got
movs \tmp, \irqstat, lsl#16
addeq \irqnr, \irqnr, #16
moveq \irqstat, \irqstat, lsr#16
tst \irqstat, #0xff
addeq \irqnr, \irqnr, #8
moveq \irqstat, \irqstat, lsr#8
tst \irqstat, #0xf
addeq \irqnr, \irqnr, #4
moveq \irqstat, \irqstat, lsr#4
tst \irqstat, #0x3
addeq \irqnr, \irqnr, #2
moveq \irqstat, \irqstat, lsr#2
tst \irqstat, #0x1
addeq \irqnr, \irqnr, #1
@@ we have the value
1001:

adds \irqnr, \irqnr, #IRQ_EINT0 @加上中断号的基准数值,得到最终的中断号,注意:此时没有考虑子中断的具体情况,(子中断的问题后面会有讲解)。IRQ_EINT0在include/asm/arch-s3c2410/irqs.h中定义.从这里可以看出,中断号的具体值是有平台相关的代码决定的,和硬件中断挂起寄存器中的中断号是不等的。

1002:
@@ exit here, Z flag unset if IRQ
.endm

3.6 asm_do_IRQ实现过程,arch/arm/kernel/irq.c

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
struct irq_desc *desc = irq_desc + irq;//根据中断号找到对应的irq_desc
/*
* 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);// 根据中断号和desc进入中断处理
/* AT91 specific workaround */
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}

static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
desc->handle_irq(irq, desc);//中断处理
}

上述asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)使用了asmlinkage标识。那么这个标识的含义如何理解呢?
该符号定义在kernel/include/linux/linkage.h中,如下所示:

#include <asm/linkage.h>//各个具体处理器在此文件中定义asmlinkage
#ifdef __cplusplus
#define CPP_ASMLINKAGE extern "C"
#else
#define CPP_ASMLINKAGE
#endif

#ifndef asmlinkage//如果以前没有定义asmlinkage
#define asmlinkage CPP_ASMLINKAGE
#endif

对于ARM处理器的<asm/linkage.h>,没有定义asmlinkage,所以没有意义(不要以为参数是从堆栈传递的,对于ARM平台来说还是符合ATPCS过程调用标准,通过寄存器传递的)。

但对于X86处理器的<asm/linkage.h>中是这样定义的:

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

表示函数的参数传递是通过堆栈完成的。

3.7 描述3.3节中的ret_to_user 中断返回过程,/arch/arm/kernel/entry-common.S

ENTRY(ret_to_user)
ret_slow_syscall:
disable_irq @ disable interrupts
ldr r1, [tsk, #TI_FLAGS]
tst r1, #_TIF_WORK_MASK
bne work_pending
no_work_pending:
/* perform architecture specific actions before user return */
arch_ret_to_user r1, lr

@ slow_restore_user_regs
ldr r1, [sp, #S_PSR] @ get calling cpsr
ldr lr, [sp, #S_PC]! @ get pc
msr spsr_cxsf, r1 @ save in spsr_svc
ldmdb sp, {r0 - lr}^ @ get calling r0 - lr
mov r0, r0
add sp, sp, #S_FRAME_SIZE - S_PC
movs pc, lr @ return & move spsr_svc into cpsr

第三章主要跟踪了从中断发生到调用到对应中断号的desc->handle_irq(irq, desc)中断函数的过程。后面的章节还会继续讲解后面的内容。 (作者:刘洪涛,华清远见嵌入式学院金牌讲师,ARM公司ATC授权培训讲师。)

分享到:
评论

相关推荐

    linux-2.6.26内核中ARM中断实现详解(1).doc

    ### Linux 2.6.26 内核中 ARM 中断实现详解 #### 一、中断注册方法 在深入探讨 Linux 内核中 ARM 架构的中断机制之前,我们首先来了解一下中断注册的核心——`request_irq()` 函数。这个函数在 `Kernel/irq/manage...

    了解linux-2.6.26内核中ARM中断实现.docx

    ### Linux 2.6.26 内核中 ARM 中断实现详解 #### 一、中断注册方法 在 Linux 内核中,处理中断的核心函数是 `request_irq()`,该函数用于请求并注册一个硬件中断。`request_irq()` 的定义位于 `Kernel/irq/manage....

    arm-linux内核中ARM中断实现详解

    在Linux内核中,特别是针对ARM架构的实现,有一系列的方法和机制来处理中断。 首先,中断注册是整个中断处理流程中的第一步。在Linux内核中,申请中断通常使用request_irq()函数。该函数的定义位于Kernel/irq/...

    嵌入式系统/ARM技术中的linux-2.6.26内核中ARM中断实现详解(2)

     这一节将以S3C2410为例,描述linux-2.6.26内核中,从中断开始,中断是如何一步一步执行到我们注册函数的。  3.1 中断向量表 arch\arm\kernel\entry-armv.S  __vectors_start:  swi SYS_ERROR0  b vector_...

    嵌入式系统/ARM技术中的linux-2.6.26内核中ARM中断实现详解(1)

     在linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义:  int request_irq(unsigned int irq, irq_handler_t handler,  unsigned long irqflags, const char *devnam

    嵌入式系统/ARM技术中的linux-2.6.26内核中ARM中断实现详解(3)

    作者:刘洪涛,华清远见嵌入式学院金牌讲师。  四、中断处理模型  要想弄清楚desc-&gt;handle_irq(irq, desc)和我们注册的中断有...Irq_desc_t结构体中的成员action指向该中断号对应的irqaction结构体链表。Irqaction

    linux-2.6.26.tar.gz

    12. **虚拟化技术**:在2.6.26版本中,内核支持KVM(Kernel-based Virtual Machine),这是Linux平台上的全虚拟化解决方案。 通过研究`linux-2.6.26`源码,开发者可以学习到操作系统设计的精髓,对计算机系统有更...

    linux-2.6.26.tar

    linux-2.6.26.tar 安装 Iso 格式

    linux-2.6.26-winsrc.tar.bz2

    linux-2.6.26 原始代码修改了大小写重命名问题,并修正了相关引用。

    内核ARM中断

    内核ARM中断是嵌入式系统中...提供的文档《linux-2.6.26内核ARM中断实现过程详解》系列可能涵盖了中断处理的具体细节,包括中断初始化、中断注册、中断处理流程以及相关的数据结构和函数,对于学习这一主题非常有帮助。

    libxml2-devel-2.6.26-2.1.2.8.i386.rpm

    学习linux下的libxml编程是必须的两个包 libxml2-2.6.26-2.1.2.8.i386.rpm libxml2-devel-2.6.26-2.1.2.8.i386.rpm

    linux-2.6.26下开发marvell-88w8686-wifi驱动.docx

    Linux 2.6.26 下开发 Marvell 88W8686 WiFi 驱动 本文档详细介绍了在 Linux 2.6.26 平台下开发 Marvell 88W8686 WiFi 驱动的过程。该驱动程序基于 PXA270 平台的 SPI 接口驱动程序,并移植到 2440 平台上。整个移植...

    linux-2.6.26 POWER驱动包

    2. 内核版本:Linux 2.6.26是2008年发布的一个稳定版本,它引入了许多新特性和改进,包括更好的电源管理、增强的网络堆栈、改进的安全特性以及对更多硬件的支持。对于Power架构来说,这个版本的内核提供了一个可靠的...

    libxml2-2.6.26-2.1.2.8.i386.rpm

    学习linux下的libxml编程是必须的两个包 libxml2-2.6.26-2.1.2.8.i386.rpm libxml2-devel-2.6.26-2.1.2.8.i386.rpm

    Python库 | discovery-transition-ds-2.6.26.tar.gz

    标题中的"Python库 | discovery-transition-ds-2.6.26.tar.gz"指的是一个针对Python编程语言的特定库,该库的版本为2.6.26,并且以tar.gz格式的压缩包形式存在。在软件开发中,库是一组预先编写好的函数或模块,...

    libxml2-2.6.26 x86-64

    总的来说,libxml2-2.6.26-x86-64.rpm是一个重要的软件包,为64位的CentOS 5系统提供了XML处理能力,确保了许多依赖XML的应用能够正常运行。安装此RPM包时,应确保系统满足所有依赖关系,并遵循正确的RPM安装流程。

    linux libxml2-2.6.26

    the proper functionning of libxml2 main APIs while testapi does a full coverage check. Report failures to the list. To report bugs, follow the instructions at: http://xmlsoft.org/bugs.html A ...

    Smarty-2.6.26.zip

    Smarty是一个知名的PHP模板引擎,它的版本为2.6.26,封装在名为"Smarty-2.6.26.zip"的压缩包中。这款模板引擎的出现旨在将设计人员和开发人员的工作分离,使他们可以更加专注于各自的专业领域,提高工作效率。下面,...

    linux-2.6.26

    在本文中,我们将深入探讨Linux内核2.6.26中的关键知识点。 首先,让我们关注内核调度器。在2.6.26版本中,Linux内核采用了新的完全公平调度器( Completely Fair Scheduler, CFS),这是由Ingo Molnar设计的。CFS...

Global site tag (gtag.js) - Google Analytics