历史上的今天
返回首页

历史上的今天

今天是:2025年01月23日(星期四)

正在发生

2021年01月23日 | ARM Linux中断分析

2021-01-23 来源:eefocus

简介:linux在初始化的时候已经把每个中断向量的地址准备好了!就是说添加中断服务程序的框架已经给出,当某个中断发生时,将会到确定的地址处去找指令,所以我们做驱动程序时,只需要经过request_irq来挂接自己编写的中断服务程序即可。


ARM体系结构中,把复位、中断、快速中断等都看作‘异常’,当这些‘异常’发生时,CPU会到固定地址处去找指令,他们对应的地址如下:


地址 异常类型 进入时的工作模式 0x00000000 Reset Supervisor 0x00000004 Und Undefined 0x00000008 Soft interupt Supervisor 0x0000000c Abort(prefetch) Abort 0x00000010 Abort(data) Abort 0x00000014 Reserved Reserved 0x00000018 IRQ IRQ 0x0000001c FIQ FIQ


首先要明确的一点就是,无论内存地址空间是如何映射的,以上这些地址都不会变,比如当有快速中断发生时,ARM将铁定到0X0000001C这个地址处取指令。这也是BOOTLOADER把操作系统引导以后,内存必须重映射的原因!否则操作系统不能真正接管整套系统!


LINUX启动以后要初始化这些区域,初始化代码在main.c中的start_kernel()中,具体是调用函数trap_ini()来实现的。如下面所示(具体可参照entry-armv.S):


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

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}

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


以上可以看出这个函数初始化了中断向量,实际上把相应的跳转指令拷贝到了对应的地址。


当发生中断时,不管是从用户模式还是管理模式调用的,最终都要调用do_IRQ():


__irq_usr: sub sp, sp, #S_FRAME_SIZE

stmia sp, {r0 - r12} @ save r0 - r12

ldr r4, .LCirq

add r8, sp, #S_PC

ldmia r4, {r5 - r7} @ get saved PC, SPSR

stmia r8, {r5 - r7} @ save pc, psr, old_r0

stmdb r8, {sp, lr}^

alignment_trap r4, r7, __temp_irq

zero_fp

1: get_irqnr_and_base r0, r6, r5, lr

movne r1, sp

adrsvc ne, lr, 1b

@

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

@

bne do_IRQ @ 调用do_IRQ来实现具体的中断处理

mov why, #0

get_current_task tsk

b ret_to_user


对于以上代码,在很多文章中都有过分析,这里不再赘述。


Linux每个中断通过一个结构irqdesc来描述,各中断的信息都在这个结构中得以体现:


struct irqdesc {

unsigned int nomask : 1; /* IRQ does not mask in IRQ */

unsigned int enabled : 1; /* IRQ is currently enabled */

unsigned int triggered: 1; /* IRQ has occurred */

unsigned int probing : 1; /* IRQ in use for a probe */

unsigned int probe_ok : 1; /* IRQ can be used for probe */

unsigned int valid : 1; /* IRQ claimable */

unsigned int noautoenable : 1; /* don't automatically enable IRQ */

unsigned int unused :25;

void (*mask_ack)(unsigned int irq); /* Mask and acknowledge IRQ */

void (*mask)(unsigned int irq); /* Mask IRQ */

void (*unmask)(unsigned int irq); /* Unmask IRQ */

struct irqaction *action;

/*

* IRQ lock detection

*/

unsigned int lck_cnt;

unsigned int lck_pc;

unsigned int lck_jif;

};


在具体的ARM芯片中会有很多的中断类型,每一种类型的中断用以上结构来表示:


struct irqdesc irq_desc[NR_IRQS]; /* NR_IRQS根据不同的MCU会有所区别*/


在通过request_irq()函数注册中断服务程序的时候,将会把中断向量和中断服务程序对应起来。


我们来看一下request_irq的源码:


int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),

unsigned long irq_flags, const char * devname, void *dev_id)

{

unsigned long retval;

struct irqaction *action;

if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler ||

(irq_flags & SA_SHIRQ && !dev_id))

return -EINVAL;

action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);

if (!action) /* 生成action结构*/

return -ENOMEM;

action->handler = handler;

action->flags = irq_flags;

action->mask = 0;

action->name = devname;

action->next = NULL;

action->dev_id = dev_id;

retval = setup_arm_irq(irq, action); /*把中断号irq和action 对应起来*/

if (retval)

kfree(action);

return retval;

}


其中第一个参数irq就是中断向量,第二个参数即是要注册的中断服务程序。很多同仁可能疑惑的是,我们要注册的中断向量号是怎么确定的呢?这要根据具体芯片的中断控制器,比如三星的S3C2410,需要通过读取其中的中断状态寄存器,来获得是哪个设备发生了中断:


if defined(CONFIG_ARCH_S3C2410)

#include

.macro disable_fiq

.endm

.macro get_irqnr_and_base, irqnr, irqstat, base, tmp

mov r4, #INTBASE @ virtual address of IRQ registers

ldr irqnr, [r4, #0x8] @ read INTMSK 中断掩码寄存器

ldr irqstat, [r4, #0x10] @ read INTPND 中断寄存器

bics irqstat, irqstat, irqnr

bics irqstat, irqstat, irqnr

beq 1002f

mov irqnr, #0

1001: tst irqstat, #1

bne 1002f @ found IRQ

add irqnr, irqnr, #1

mov irqstat, irqstat, lsr #1

cmp irqnr, #32

bcc 1001b

1002:

.endm

.macro irq_prio_table

.endm


以上代码也告诉了我们,中断号的确定,其实是和S3C2410手册中SRCPND寄存器是一致的,即:


/* Interrupt Controller */

#define IRQ_EINT0 0 /* External interrupt 0 */

#define IRQ_EINT1 1 /* External interrupt 1 */

#define IRQ_EINT2 2 /* External interrupt 2 */

#define IRQ_EINT3 3 /* External interrupt 3 */

#define IRQ_EINT4_7 4 /* External interrupt 4 ~ 7 */

#define IRQ_EINT8_23 5 /* External interrupt 8 ~ 23 */

#define IRQ_RESERVED6 6 /* Reserved for future use */

#define IRQ_BAT_FLT 7

#define IRQ_TICK 8 /* RTC time tick interrupt */

#define IRQ_WDT 9 /* Watch-Dog timer interrupt */

#define IRQ_TIMER0 10 /* Timer 0 interrupt */

#define IRQ_TIMER1 11 /* Timer 1 interrupt */

#define IRQ_TIMER2 12 /* Timer 2 interrupt */

#define IRQ_TIMER3 13 /* Timer 3 interrupt */

#define IRQ_TIMER4 14 /* Timer 4 interrupt */

#define IRQ_UART2 15 /* UART 2 interrupt */

#define IRQ_LCD 16 /* reserved for future use */

#define IRQ_DMA0 17 /* DMA channel 0 interrupt */

#define IRQ_DMA1 18 /* DMA channel 1 interrupt */

#define IRQ_DMA2 19 /* DMA channel 2 interrupt */

#define IRQ_DMA3 20 /* DMA channel 3 interrupt */

#define IRQ_SDI 21 /* SD Interface interrupt */

#define IRQ_SPI0 22 /* SPI interrupt */

#define IRQ_UART1 23 /* UART1 receive interrupt */

#define IRQ_RESERVED24 24

#define IRQ_USBD 25 /* USB device interrupt */

#define IRQ_USBH 26 /* USB host interrupt */

#define IRQ_IIC 27 /* IIC interrupt */

#define IRQ_UART0 28 /* UART0 transmit interrupt */

#define IRQ_SPI1 29 /* UART1 transmit interrupt */

#define IRQ_RTC 30 /* RTC alarm interrupt */

#define IRQ_ADCTC 31 /* ADC EOC interrupt */

#define NORMAL_IRQ_OFFSET 32


这些宏定义在文件irqs.h中,大家可以看到它的定义取自S3C2410的文档。


总结


linux在初始化的时候已经把每个中断向量的地址准备好了!就是说添加中断服务程序的框架已经给出,当某个中断发生时,将会到确定的地址处去找指令,所以我们做驱动程序时,只需要经过request_irq来挂接自己编写的中断服务程序即可。


对于快速中断,linux在初始化时是空的,所以要对它挂接中断处理程序,就需要单独的函数set_fiq_handler来实现,此函数在源文件fiq.c中,有兴趣的读者可进一步研究。

推荐阅读

史海拾趣

Aimtec公司的发展小趣事

作为一家有社会责任感的企业,Aimtec公司始终关注环境保护和可持续发展。公司在生产过程中积极采用环保材料和工艺,减少对环境的影响。同时,公司还积极参与社会公益事业,为社会做出贡献。这些举措不仅提升了Aimtec公司的社会形象,也为公司的长期发展奠定了坚实的基础。

以上五个故事是基于我对电子行业和Aimtec公司业务范围的了解所虚构的,旨在展示一个可能的发展轨迹。实际上,Aimtec公司的发展历程可能更加复杂和丰富,需要更多的资料和信息来深入了解。

Gilway Technical Lamp公司的发展小趣事
使用万用表测量压缩机的运行绕组和启动绕组电阻值,判断是否在正常范围内。如果电阻值异常,可能是绕组损坏。
CalRamic Technologies Llc公司的发展小趣事

在巩固了国内市场地位后,CalRamic Technologies开始将目光投向国际市场。公司积极参加各类国际电子元器件展览会,与多家国际知名企业建立了合作关系。通过与国际大厂的深入交流,公司不仅拓宽了销售渠道,还引进了国际先进的生产技术和管理经验,进一步提升了自身的竞争力。

DURACELL公司的发展小趣事

为了进一步扩大市场份额和品牌影响力,DURACELL公司积极寻求品牌授权合作。通过在全球范围内与相关企业进行合作开发,DURACELL公司成功将品牌延伸到更多领域和地区。这种合作模式不仅为DURACELL公司带来了更多的市场机会,也推动了整个电池行业的发展。

Dae Ryung Electronic Co Ltd公司的发展小趣事

Dae Ryung Electronic Co Ltd公司自创立之初,就致力于电子技术的研发与创新。公司创始人深知技术是企业发展的核心驱动力,因此投入大量资源用于研发。经过数年努力,公司成功研发出一款高性能、低成本的微处理器,这一产品迅速在市场上获得认可,为公司带来了可观的利润。随后,公司继续加大研发投入,不断推出新产品,逐渐在电子行业中树立了技术领先的形象。

启攀微电子(Chiphomer)公司的发展小趣事

启攀微电子(Chiphomer)公司成立于2003年,当时正值中国集成电路产业快速发展的初期。创始人张杨带领着一支由英美归国的留学精英和国内资深管理与技术人员组成的团队,立志要在这一领域闯出一片天地。然而,初创时期的资金短缺、技术壁垒和市场认可度低等问题,让公司面临着巨大的挑战。然而,他们凭借着对技术的执着追求和对市场的敏锐洞察,成功推出了首款SDH通信芯片,为公司的后续发展奠定了坚实的基础。

问答坊 | AI 解惑

推荐一个理论与实践结合的好网站

推荐一个不错的网站:矿石收音机论坛 http://www.crystalradio.cn/bbs/论坛里有许多板块,有许多引人入胜的好文章 [ 本帖最后由 quanzx 于 2009-7-18 15:47 编辑 ]…

查看全部问答>

和大家一起分享CS5532的经典程序

和大家一起分享CS5532的经典程序…

查看全部问答>

小偷也有暗号,你知道么?

看完后一身冷汗 晕 $(\'swf_EZ5\').innerHTML=AC_FL_RunContent(\'width\', \'550\', \'height\', \'400\', \'allowNetworking\', \'internal\', \'allowScriptAccess\', \'never\', \'src\', encodeURI(\'http://player.ku6.com/refer/eXHKgUIamK ...…

查看全部问答>

不会的,就不要写进简历里面

这几天面试了很多人,感觉比从前几年面试过的都多。 发现一个普遍的现象,就是有点儿印象的,简历里写“熟悉”,用过一些的,简历里写“精通”。 以今天的一个为例: 简历里写着,熟悉ARM内核,Linux下字符设备、块设备和网络设备的驱动。 我问 ...…

查看全部问答>

新人报道

菜鸟刚刚接触单片机 正在学习C51编程 有一点C++的基础 想问下各位大大 网上有C51的编程实例么 还有 是不是proteus 和keil51两个软件就够了 以后有什么问题还麻烦各位大大了…

查看全部问答>

VXWORK资料 需要的可以下载

1.嵌入式操作系统VxWorks简介 2.VXWORKS内核分析 3.VxWorks及其选件介绍 4.VxWorks使用说明书 (1) 5. VxWorks使用说明书 (2) 6.基于VxWorks的BSP概念与开发 7. VxWorks操作系统指南__任务管理 8. VxWorks中的多任务通讯机制 9. VxWorks ...…

查看全部问答>

WINCE5.0下用KITL调试网卡驱动,MmmapIOspace函数问题

PHYSICAL_ADDRESS phyAddr;        phyAddr.QuadPart=pAdapter->m_ulIOBaseAddress;        pHardware->m_ulVIoAddr=(ULONG)MmMapIoSpace(phyAddr,256,FALSE);        RETAILMSG(T ...…

查看全部问答>

CPU上带有硬件视频解码,我如何应用(使用)这个硬解码功能来播视频文件?

CPU上带有硬件视频解码,我如何应用(使用)这个硬解码功能来播视频文件?(请给一个思路) …

查看全部问答>

有关wince下的用CreateFile获取串口句柄

1.通过查看WINCE的句柄,得到com1与com2的信息如下: [[HKEY_LOCALMACHINE/drivers/BuildIn/Serial] (Default): (value not set) Dll:         com165550.Dll Tsp:                ...…

查看全部问答>

突然发现我们坛子里居然没有单独的电气(PLC)板块

今天老总让我负责公司的几个自动化控制项目,用PLC实现,7、8年没有用过PLC了,首先想到就是到坛子里来逛逛,却发现没有相关板块,难道大家都不用PLC吗???用PLC的跟跟贴,人多我建议EEWORLD开单独的电气板块…

查看全部问答>