分享

Linux中PowerPC的中断原理分析

 ningmei0424 2014-04-16

    在了解中断处理机制前,先看一下PowerPC的中断源,这里使用的是e300c3的内核,从E300核的角度,中断源可分为异常和外部中断,异常是e300内核产生的,如出现非法指令,或者是访问存储器时出现TLB Miss等情况。这种情况太复杂了,没有深究,这里所说的中断为外部中断。所谓外部中断,就是通过e300外部引脚产生的中断。E300的外部中断主要有:int#, cint#, mcp#. 这三根pin分别对应一般中断,critical中断和machine check中断。

    在设备驱动过程中,用户可以使用request_irq函数将外设的中断服务例程挂载到外部中断处理程序中。外部中断处理程序,可以直接处理硬件中断,但是request_irq函数试用软件中断号进行挂载,因此linux必然采用了某种方式进行软硬件中断的映射。

    这里需要了解下中断向量表的概念,就是它将软件中断号与硬件中断号联系起来,在系统中,中断源有很多,当某一种中断发生时,处理器最早进入的是处理函数的地址,就是中断向量。每种类型对应一个中断向量号,一系列的中断号构成中断向量表。比如驱动程序关心的外部中断,当这种中断发生时,处理器内核(e300)就会到指定寄存器中去取得中断处理函数的入口地址。

    硬件中断号是当中断发生时,处理器从外部的PIC(Programable Interrupt Controller)上读到的数值。因为在PowerPC Linux中有dts的概念,驱动程序员需要知道自己所负责的设备对应的device node上的interrupt项如何写的问题,这里的interrupt上的数字就是硬件中断号。在Linux系统初始化期间,通过对PIC的配置,可以将硬件的连接转化成相应的硬件中断号,例如,8315上的8根外设中断线IRQ0~IRQ7,可以通过PIC的配置,分别映射成的硬件中断号为10~18,那么当IRQ0上有中断请求时,处理器通过读取PIC,就得到P1所对应的硬件中断号为10。这些都是硬件层面的东西,当驱动程序安装它们的中断处理程序时,是基于软件中断号的,也就是内核函数request_irq中的参数irq是个软件中断号。

    软件中断号是Linux下的概念,为了支持多平台的关系,Linux不直接用硬件中断号索引irq_desc。所以当外部中断发生时,在外部中断入口函数中会通过读取PIC来获得本次中断的硬件中断号,获得硬件中断号后,会将其映射成对应的软件中断号来索引irq_desc数组,从而获得该软件中断号上的中断处理函数。显然,这种硬件中断号到软件中断号的映射关系,应该在设备可以处理中断前就要建立好了。在Linux PowerPC中,这种映射关系是由函数irq_of_parse_and_map(struct device_node *dev, int index)来完成的。

    注意:在8315中,关于MSI的硬件中断号,可以在系统全局中断向量寄存器SIVCR中查询。另外,其实,如果没有软件中断号,设备驱动程序也能以硬件中断号作索引挂载处理程序,从功能上说没有任何问题。软件中断号的引入,是为了硬件中断号对OS透明。这样处理能减少OS对硬件平台的依赖性。

下面贴些内核的代码,方便细细分析:

    linux使用结构体struct irq_map_entry irq_map[NR_IRQS]完成软件与硬件的中断号映射,一般系统自动查找可用的软件号以对应请求的硬件中断号。

    在使用open firmware的系统中,驱动程序在执行ruquest_irq之前要先进行软硬件中断号的映射,具体通过函数:
irq_of_parse_and_map(struct device_node * dev, int index);
    实现,具体函数调用过程如下:
irq_create_of_mapping(struct device_node * controller, u32 * inspec, unsigned int intsize);
irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq);
    程序除了进行软硬件中断号的映射外还需要初始后相应结构。
下面看看与中断相关的另外几个主要数据结构:
1、结构体:struct irq_desc(include/linux/irq.h)

  1. struct irq_desc {  
  2.     unsigned int        irq;  
  3.     struct timer_rand_state *timer_rand_state;  
  4.     unsigned int            *kstat_irqs;  
  5. #ifdef CONFIG_INTR_REMAP  
  6.     struct irq_2_iommu      *irq_2_iommu;  
  7. #endif  
  8.     irq_flow_handler_t  handle_irq;  
  9.     struct irq_chip     *chip;  
  10.     struct msi_desc     *msi_desc;  
  11.     void            *handler_data;  
  12.     void            *chip_data;  
  13.     struct irqaction    *action;    /* IRQ action list */  
  14.     unsigned int        status;     /* IRQ status */  
  15.   
  16.     unsigned int        depth;      /* nested irq disables */  
  17.     unsigned int        wake_depth; /* nested wake enables */  
  18.     unsigned int        irq_count;  /* For detecting broken IRQs */  
  19.     unsigned long       last_unhandled; /* Aging timer for unhandled count */  
  20.     unsigned int        irqs_unhandled;  
  21.     raw_spinlock_t      lock;  
  22. #ifdef CONFIG_SMP  
  23.     cpumask_var_t       affinity;  
  24.     const struct cpumask    *affinity_hint;  
  25.     unsigned int        node;  
  26. #ifdef CONFIG_GENERIC_PENDING_IRQ  
  27.     cpumask_var_t       pending_mask;  
  28. #endif  
  29. #endif  
  30.     atomic_t        threads_active;  
  31.     wait_queue_head_t       wait_for_threads;  
  32. #ifdef CONFIG_PROC_FS  
  33.     struct proc_dir_entry   *dir;  
  34. #endif  
  35.     const char      *name;  
  36. } ____cacheline_internodealigned_in_smp;  

这个结构体用来描述中断源,是用来连接硬件中断和驱动程序中通过request_irq注册的中断处理函数之间的桥梁。数组irq_desc[NR_IRQS](NR_IRQS=225)中每一项都对应一个相应的中断源,他的每一项对应着中断向量表中的一项,即该数组的第一项对应着中断向量表中的第32项(中断向量号为0x20),往下依次对应。中断向量表一共有256项。
部分成员解释:
status:或者是0,或者是从一个特定的集合中抽取的一个标志位(不太清楚)。这些标志位代表了IRQ的状态--是否被禁止,有关IRQ的设备当前是否正被自动检测等。
chip:是一个指向hw_interrupt_type(或者irq_chip)的指针。其中定义的函数是平台相关的(更具体的,是中断控制器相关的),很显然不同平台的中断控制器拥有不同的操作函数,比如enable, disable, mask, unmask某个中断的操作。从平台移植的角度,chip屏蔽了底层硬件的不同,使得在内核中某些代码成了平台无关性。但是对于不同平台的Linux,必须由BSP部分负责初始化irq_desc中的chip变量。

action:是一个指向由irqaction结构体组成的一个单向链表的头的指针。若一个IRQ只被一个中断源使用,那么该链表的长度就是1,当有多个设备共享一个中断源时,该链表就会由多个irqaction结构体组成,下面对此有介绍。
depth:irq_desc_t的当前用户的个数,主要用来保证事件正在处理的过程中IRQ不会被禁止。

从驱动程序开发者角度,不需要直接调用chip中的函数。但是在驱动程序request_irq时,因为要在irq_desc所对应的某一项中安装中断处理函数,request_irq函数原型如下:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)

其中第三个参数irqflags会影响到内核对chip中函数的调用,所以从这个角度而言,了解一些chip相关的中断enable/disable/mask/unmask对驱动程序员是有帮助的。
2、结构体:struct irqaction(include/linux/interrupt.h)

  1. struct irqaction {  
  2.     irq_handler_t handler;  
  3.     unsigned long flags;  
  4.     const char *name;  
  5.     void *dev_id;  
  6.     struct irqaction *next;  
  7.     int irq;  
  8.     struct proc_dir_entry *dir;  
  9.     irq_handler_t thread_fn;  
  10.     struct task_struct *thread;  
  11.     unsigned long thread_flags;  
  12. };  

这个结构体包含了处理一种中断所需要的各种信息,它代表了内核接受到特定IRQ之后应该采取的操作。
主要成员:
handler—该指针所指向的函数就是在中断服务程序,当中断发生时内核便会调用这个指针指向的函数。
flags:该标志位可以是0,也可以是:SA_INTERRUPT(表示此中断处理程序是一个快速中断处理程序,在2.6中默认情况下没有这个标志)SA_SAMPLE_RANDOM(表示这个中断对内核池有贡献,我理解为就是在中断时产生一些随机数,这些随机数被用来作为加密密匙,因为中断是随机发生的,如果某种中断是有频率的被产生,那么它就不要设置此标志位,还有就是那种设备容易被攻击也不应该设置此标志位)SA_SHIRQ(此标志位表示允许多个中断服务程序共享一个中断号,如不设则一个程序对应一个中断线)。
mask:在x86上不会用到。
name:产生中断的硬件的名字.
dev_id:该标志位主要在共享中断号时使用,即你设置flags=SA_SHIRQ时,有多个中断服务程序共享一个中断号时,内核就需要知道在用完中断程序后该删除那个中断服务程序。不共享时此成员为null。
next:如果flags=SA_SHIRQ,那么这就是指向对列中下一个struct irqaction结构体的指针,否则为空。
irq:不用说这就是中断号了。
3 结构体:struct hw_interrupt_type(include/linux/irq.h)

  1. struct irq_chip {  
  2.     const char  *name;  
  3.     unsigned int    (*startup)(unsigned int irq);  
  4.     void        (*shutdown)(unsigned int irq);  
  5.     void        (*enable)(unsigned int irq);  
  6.     void        (*disable)(unsigned int irq);  
  7.   
  8.     void        (*ack)(unsigned int irq);  
  9.     void        (*mask)(unsigned int irq);  
  10.     void        (*mask_ack)(unsigned int irq);  
  11.     void        (*unmask)(unsigned int irq);  
  12.     void        (*eoi)(unsigned int irq);  
  13.   
  14.     void        (*end)(unsigned int irq);  
  15.     int     (*set_affinity)(unsigned int irq,  
  16.                     const struct cpumask *dest);  
  17.     int     (*retrigger)(unsigned int irq);  
  18.     int     (*set_type)(unsigned int irq, unsigned int flow_type);  
  19.     int     (*set_wake)(unsigned int irq, unsigned int on);  
  20.   
  21.     void        (*bus_lock)(unsigned int irq);  
  22.     void        (*bus_sync_unlock)(unsigned int irq);  
  23.   
  24.     /* Currently used only by UML, might disappear one day.*/  
  25. #ifdef CONFIG_IRQ_RELEASE_METHOD  
  26.     void        (*release)(unsigned int irq, void *dev_id);  
  27. #endif  
  28.     /* 
  29.      * For compatibility, ->typename is copied into ->name. 
  30.      * Will disappear. 
  31.      */  
  32.     const char  *typename;  
  33. };  

它是用来描述中断控制器的,也就是一个抽象的中断控制器,其成员是一系列指向函数的指针。
typename:给相应的控制器起一个便于理解的名字。
startup:允许从给定的控制器的IRQ所产生的事件。(基本上与enable相同)
shutdown:禁止从给定的控制器的IRQ所产生的事件。(基本上与disable相同)
以上三个结构体的关系可以用下面一张图来说明IRQ结构间的关系:

 

数组irq_desc_t用来描述中断的相关信息,它有225项,每一项代表一个中断源,其中字段irq(注意这个字段就是上图中的handler字段),此结构体用来描述中断控制器,action字段用来描述处理一种中断所需要的各种信息,它代表了内核接受到特定IRQ之后应该采取的操作。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多