ARM Linux外部中断处理过程

发布者:纯真年代最新更新时间:2016-05-12 来源: eefocus关键字:ARM  Linux  外部中断  处理过程 手机看文章 扫描二维码
随时随地手机看文章
ARM Linux外部中断处理过程

最近在学习arm linux的整套外部中断的处理过程,在网上汇总了一些资料,整个过程差不多都了解到了。如果没有这些资料我真是没信心从汇编开始读代码,感谢 奔腾年代的jimmy.lee和 linux论坛的bx_bird。 
在下面的的注释中有一些我读代码时遇到的问题,要是大家知道是怎么回事,希望多多回复。 
============================================= 
一.ARM linux的中断向量表初始化分析 
ARM linux内核启动时,通过start_kernel()->trap_init()的调用关系,初始化内核的中断异常向量表. 
/* arch/arm/kernel/traps.c */ 
void __init trap_init(void) 

extern void __trap_init(unsigned long); 
unsigned long base = vectors_base(); 
__trap_init(base); 
if (base != 0) 
oopsprintk(KERN_DEBUG "Relocating machine vectors to 0x%08lxn", base); 
#ifdef CONFIG_CPU_32 
modify_domain(DOMAIN_USER, DOMAIN_CLIENT); 
#endif 

vectors_base是一个宏,它的作用是获取ARM异常向量的地址,该宏在include/arch/asm-arm/proc-armv/system.h中定义: 
extern unsigned long cr_no_alignment; /* defined in entry-armv.S */ 
extern unsigned long cr_alignment; /* defined in entry-armv.S */ 
#if __LINUX_ARM_ARCH__ >= 4 
#define vectors_base() ((cr_alignment & CR_V) ? 0xffff0000 : 0) 
#else 
#define vectors_base() (0) 
#endif 
  对于ARMv4以下的版本,这个地址固定为0;ARMv4及其以上的版本,ARM异常向量表的地址受协处理器CP15的c1寄存器(control register)中V位(bit[13])的控制,如果V=1,则异常向量表的地址为0x00000000~0x0000001C;如果V=0,则为:0xffff0000~0xffff001C。(详情请参考ARM Architecture Reference Manual) 
  下面分析一下cr_alginment的值是在哪确定的,我们在arch/arm/kernel/entry-armv.S找到cr_alignment的定义: 
.globl SYMBOL_NAME(cr_alignment) 
.globl SYMBOL_NAME(cr_no_alignment) 
SYMBOL_NAME(cr_alignment): 
.space 4 
SYMBOL_NAME(cr_no_alignment): 

.space 4 
  分析过head-armv.S文件的朋友都会知道,head-armv.S是非压缩内核的入口: 
1 .section ".text.init",#alloc,#execinstr 
2 .type stext, #function 
3ENTRY(stext) 
4 mov r12, r0 
5 
6 mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode 
7 msr cpsr_c, r0 @ and all irqs disabled 
8 bl __lookup_processor_type 
9 teq r10, #0 @ invalid processor? 
10 moveq r0, #'p' @ yes, error 'p' 
11 beq __error 
12 bl __lookup_architecture_type 
13 teq r7, #0 @ invalid architecture? 
14 moveq r0, #'a' @ yes, error 'a' 
15 beq __error 
16 bl __create_page_tables 
17 adr lr, __ret @ return address 
18 add pc, r10, #12 @ initialise processor 
19 @ (return control reg) 
20 
21 .type __switch_data, %object 
22__switch_data: .long __mmap_switched 
23 .long SYMBOL_NAME(__bss_start) 
24 .long SYMBOL_NAME(_end) 
25 .long SYMBOL_NAME(processor_id) 
26 .long SYMBOL_NAME(__machine_arch_type) 
27 .long SYMBOL_NAME(cr_alignment) 
28 .long SYMBOL_NAME(init_task_union)+8192 
29 
30 .type __ret, %function 
31__ret: ldr lr, __switch_data 
32 mcr p15, 0, r0, c1, c0 
33 mrc p15, 0, r0, c1, c0, 0 @ read it back. 
34 mov r0, r0 
35 mov r0, r0 
36 mov pc, lr 
这里我们关心的是从17行开始,17行code处将lr放置为__ret标号处的相对地址,以便将来某处返回时跳转到31行继续运行18行,对于我所分析的pxa270平台,它将是跳转到arch/arm/mm/proc-xscale.S中执行__xscale_setup函数,(在s3c2410平台中,它跳转到arch/arm/mm/proc-arm920.S,在 
type __arm920_proc_info,#object 
__arm920_proc_info: 
.long 0x41009200 
.long 0xff00fff0 
.long 0x00000c1e @ mmuflags 
b __arm920_setup 
.long cpu_arch_name 
.long cpu_elf_name 
.long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB 
.long cpu_arm920_info 
.long arm920_processor_functions 
可以知道add pc, r10, #12 的#12意思是跳过3个指令,执行b _arm920_setup 
在arm920_setup设置完协处理器和返回寄存器r0之后,跳回到__ret:(31行)。 
在__xscale_setup中会读取CP15的control register(c1)的值到r1寄存器,并在r1寄存器中设置相应的标志位(其中包括设置V位=1),但在__xscale_setup中,r1寄存器并不立即写回到Cp15的control register中,而是在返回后的某个地方,接下来会慢慢分析到。__xscale_setup调用move pc, lr指令返回跳转到31行。 
  31行,在lr寄存器中放置__switch_data中的数据__mmap_switched,在36行程序会跳转到__mmap_switched处。 
  32,33行,把r0寄存器中的值写回到cp15的control register(c1)中,再读出来放在r0中。 
   
  接下来再来看一下跳转到__mmap_switched处的代码: 
40 _mmap_switched: 
41 adr r3, __switch_data + 4 
42 ldmia r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat 
43 @ sp = stack pointer 
44 
45 mov fp, #0 @ Clear BSS (and zero fp) 
46 1: cmp r4, r5 
47 strcc fp, [r4],#4 
48 bcc 1b 
49 
50 str r9, [r6] @ Save processor ID 
51 str r1, [r7] @ Save machine type 
52 bic r2, r0, #2 @ Clear 'A' bit 
53 stmia r8, {r0, r2} @ Save control register values 
54 b SYMBOL_NAME(start_kernel) 

41~42行的结果是:r4=__bss_start,r5=__end,...,r8=cr_alignment,..,这里r8保存的是cr_alignment变量的地址. 
  到了53行,由于之前r0保存的是cp15的control register(c1)的值,这里把r0的值写入r8指向的地址,即cr_alignment=r0.到此为止,我们就看清楚了cr_alignment的赋值过程。 
   
  让我们回到trap_init()函数,经过上面的分析,我们知道vectors_base返回0xffff0000。函数__trap_init由汇编代码编写,在arch/arm/kernel/entry-arm.S: 
    .align 5 
__stubs_start: 
vector_IRQ: 
     ... 
vector_data: 
    .... 
vector_prefetch: 
     ... 
vector_undefinstr: 
     ... 
vector_FIQ: disable_fiq 
     subs pc, lr, #4 
vector_addrexcptn: 
     b vector_addrexcptn 
    ... 
__stubs_end: 
     .equ __real_stubs_start, .LCvectors + 0x200 
.LCvectors: swi SYS_ERROR0 
     b __real_stubs_start + (vector_undefinstr - __stubs_start) 
     ldr pc, __real_stubs_start + (.LCvswi - __stubs_start) 
     b __real_stubs_start + (vector_prefetch - __stubs_start) 
     b __real_stubs_start + (vector_data - __stubs_start) 
     b __real_stubs_start + (vector_addrexcptn - __stubs_start) 
     b __real_stubs_start + (vector_IRQ - __stubs_start) 
     b __real_stubs_start + (vector_FIQ - __stubs_start) 
ENTRY(__trap_init) 
    stmfd sp!, {r4 - r6, lr} /* 压栈,保存数据*/ 
    /* 复制异常向量表(.LCvectors起始的8个地址)到r0指向的地址(异常向量地址),r0就是__trap_init(base)函数调用时传递的参数,不明白的请参考ATPCS*/(传递参数顺次利用r0,r1,r2,r3) 
    adr r1, .LCvectors @ set up the vectors 
    ldmia r1, {r1, r2, r3, r4, r5, r6, ip, lr} 
     stmia r0, {r1, r2, r3, r4, r5, r6, ip, lr} 
/* 在异常向量地址后的0x200偏移处,放置散转代码,即__stubs_start~__stubs_end之间的各个异常处理代码*/ 
     add r2, r0, #0x200 
     adr r0, __stubs_start @ copy stubs to 0x200 
     adr r1, __stubs_end 
1: ldr r3, [r0], #4 
     str r3, [r2], #4 
     cmp r0, r1 
blt 1b 
LOADREGS(fd, sp!, {r4 - r6, pc}) /*出栈,恢复数据,函数__trap_init返回*/ 
__trap_init函数填充后的向量表如下: 
虚拟地址 异常 处理代码 
0xffff0000 reset swi SYS_ERROR0 
0xffff0004 undefined b __real_stubs_start + (vector_undefinstr - __stubs_start) 
0xffff0008 软件中断 ldr pc, __real_stubs_start + (.LCvswi - __stubs_start) 
0xffff000c 取指令异常 b __real_stubs_start + (vector_prefetch - __stubs_start) 
0xffff0010 数据异常 b __real_stubs_start + (vector_data - __stubs_start) 
0xffff0014 reserved b __real_stubs_start + (vector_addrexcptn - __stubs_start) 
0xffff0018 irq b __real_stubs_start + (vector_IRQ - __stubs_start) 
0xffff001c fiq b __real_stubs_start + (vector_FIQ - __stubs_start) 
   当有异常发生时,处理器会跳转到对应的0xffff0000起始的向量处取指令,然后,通过b指令散转到异常处理代码.因为ARM中b指令是相对跳转,而且只有+/-32MB的寻址范围,所以把__stubs_start~__stubs_end之间的异常处理代码复制到了0xffff0200起始处.这里可直接用b指令跳转过去,这样比使用绝对跳转(ldr)效率高。 


关键字:ARM  Linux  外部中断  处理过程 引用地址:ARM Linux外部中断处理过程

上一篇:ARM Linux 外部中断
下一篇:GNU ARM汇编(四)中断汇编之非嵌套中断处理

推荐阅读最新更新时间:2024-03-16 14:53

LPC ARM使用SPI总线控制8个LED灯
下面给出一个LPC ARM7通过SPI总线控制8路流水等的实例,让大家对SPI总线传输、发送数据有一个清晰的了解。 电路图如下: 开发板通过SPI总线控制74HC595驱动8盏LED灯。 程序清单如下: #include config.h #define SPI_CS (1 16) // P0.16口为片选脚 #define CE_Low() (IO0CLR |= SPI_CS) // 选中从器件 #define CE_High() (IO0SET |= SPI_CS) /* 流水灯花样,低电平点亮,注意调用时候用了取反操作 */ const uint32
[单片机]
LPC <font color='red'>ARM</font>使用SPI总线控制8个LED灯
OK6410A 开发板 (八) 5 linux-5.11 OK6410A usb 移植
s3c6410 有两个USB实例(板子也都引出来了), 分别是 USB host 和 USB OTG , 这里说的是 usb host 代码 : https://github.com/lisider/linux/tree/ok6410a-linux-5.11 提交id : dc485e937e8bccc1fc01b5e6d9025bf83b2b6f83 defconfig : arch/arm/configs/ok6410A_sdboot_mini_net_lcd_x11_usb_defconfig CONFIG_USB_NET_DRIVERS=y CONFIG_USB_HID=y CONFIG_USB_OHCI_LI
[单片机]
OK6410A 开发板 (八) 5 <font color='red'>linux</font>-5.11 OK6410A usb 移植
基于UDP协议的ARM与X86平台之间的通讯方案
  随着人工智能的应用,ARM产品已经遍布到各个领域:工业控制、无线通讯领域、消费类电子产品、成像和安全产品,包括现在流行的数码相机和打印机中绝大部分采用ARM技术,手机中的32位SIM智能卡也采用了ARM技术。除此以外,ARM微处理器及技术还应用到许多不同的领域,并会在将来取得更加广泛的应用,因此,ARM与其它平台之间通讯就显得尤为重要。   1.UDP协议本质   UDP协议是英文User Datagram Protocol的缩写,即用户数据报协议,主要用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽
[单片机]
基于ARM设计的多生理参数嵌入式监护系统
1、引言 多生理参数监护系统用来对病人的生理或生化参数进行连续、长时间、自动、实时监测,并经分析、处理后实现多类别自动报警、自动记录,并具有对结果的综合判断能力,便于医护人员及时发现病人的病情变化,随时采取必要的护理与急救措施,从而大幅度减少危重病人的死亡率。 国外的人体生理监护仪发展趋势是采用移动式监护技术。在欧洲,借助现在小巧、价低的嵌入式计算机和无线网络技术,开发各种能穿在身上的监护系统的研究工作已经开展得非常之好,爱立信、诺基亚和飞利浦等公司都在进行相应的研究开发工作。在国内,目前普遍采用的人体生理监护系统中,大致可分为三种:1)基于PC机平台的监护系统,功能强大且完善,但其体积庞大,不便于移动。2)微型和便携式监护仪
[单片机]
基于<font color='red'>ARM</font>设计的多生理参数嵌入式监护系统
LPC1768菜鸟学习之GPIO和外部中断
LPC1768的P0口和P2口既能做输入输出,还可以配置为中断源。 现在P2.10作为按键,配置如下: void BUTTON_init(void) { LPC_GPIO2- FIODIR &=~(1 10); /* PORT2.10 definedas input */ LPC_GPIOINT- IO2IntEnF |= (1 10); /* enable falling edgeirq */ NVIC_EnableIRQ(EINT3_IRQn); /* enable irq in nvic */ } void EINT3_IRQHandler() { LPC_GPIO
[单片机]
ARM7系统中实现CF卡存储的文件系统设计
摘要:介绍针对ARM7架构的嵌入式系统中,以CF卡作为存储介抽的文件系统的设计,并通过GPS车辆导航系统中地图信息读写的具体应用介绍了其软硬件电路的实现方法。 关键词:嵌入式文件系统 CF卡 ARM7 随着微控制器性能的不断提高,嵌入式应用越来越广泛。但是目前市场上的大型商用嵌入式实时系统,价格昂贵,而且都针对特定的硬件平台。对于中小型系统开发,购买商用实时系统并不划算。 目前我们正着手将嵌入式系统软件应用于汽车卫星导航仪系统的一步开发。传统的嵌入式应用并不包括文件系统,而我们要实现的文件系统需要在车辆导航系统中实现地图数据文件的读写。因此它既要支持与MS-DOS兼容的文件系统也要支持其它类型的文件系统。 另一方面,从数码相机到M
[嵌入式]
基于Linux系统的软PLC设计
引言 可编程控制器(PLC,Programmable LogicController)经过几十年的发展,现在已经成为了最重要、最可靠、应用场合最广泛的工业控制微型计算机。然而,人们在使用过程中也逐渐发现了传统PLC的缺点:兼容性差,由于生产厂家众多,各种机型互不兼容,没有统一的标准,难以构造统一的硬件结构;封闭、扩展能力差:产品能力的功能实现依赖硬件;对使用者的要求高:现行的PLC 产品,其编程方式要求使用者对PLC的硬件结构、电器原理、编程指令都要有相当的了解;可维护性差:PLC 出现故障时需要专业人员用专业工具进行检测和维修;成本较高,传统PLC 被几家厂商所垄断,性价比增长缓慢。这些问题都制约着传统PLC 的发展。近年来,
[嵌入式]
SDCC-Linux下的51 MCU编译器
SDCC (小型设备C编译器)是为8位微控制器开发的免费C编译器。尽管兼容多种不同体系结构,但SDCC C编译器更适合8051内核。 SDCC是命令行固件开发工具,含预处理器、编译器、汇编器、链接器和优化器。安装文件中还捆绑了SDCDB、类似于gdb (GNU调试器)的源码级调试器。无错的程序采用SDCC编译、链接后,生成一个Intel十六进制格式的加载模块。 安装SDCC免费C编译器 如果需要安装SDCC,请从网址下载SDCC最新版本。虽然也可使用该软件的日常构建(nightly builds)版,但通常最安全的方式是下载经过完全测试的最新发布版。 在“Download”页为不同的操作系统提供不同的SDCC。如果您
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved