Linux中PowerPC的中斷原理分析

    在瞭解中斷處理機制前,先看一下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)

struct irq_desc {
	unsigned int		irq;
	struct timer_rand_state *timer_rand_state;
	unsigned int            *kstat_irqs;
#ifdef CONFIG_INTR_REMAP
	struct irq_2_iommu      *irq_2_iommu;
#endif
	irq_flow_handler_t	handle_irq;
	struct irq_chip		*chip;
	struct msi_desc		*msi_desc;
	void			*handler_data;
	void			*chip_data;
	struct irqaction	*action;	/* IRQ action list */
	unsigned int		status;		/* IRQ status */

	unsigned int		depth;		/* nested irq disables */
	unsigned int		wake_depth;	/* nested wake enables */
	unsigned int		irq_count;	/* For detecting broken IRQs */
	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
	unsigned int		irqs_unhandled;
	raw_spinlock_t		lock;
#ifdef CONFIG_SMP
	cpumask_var_t		affinity;
	const struct cpumask	*affinity_hint;
	unsigned int		node;
#ifdef CONFIG_GENERIC_PENDING_IRQ
	cpumask_var_t		pending_mask;
#endif
#endif
	atomic_t		threads_active;
	wait_queue_head_t       wait_for_threads;
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry	*dir;
#endif
	const char		*name;
} ____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)

struct irqaction {
	irq_handler_t handler;
	unsigned long flags;
	const char *name;
	void *dev_id;
	struct irqaction *next;
	int irq;
	struct proc_dir_entry *dir;
	irq_handler_t thread_fn;
	struct task_struct *thread;
	unsigned long thread_flags;
};

這個結構體包含了處理一種中斷所需要的各種信息,它代表了內核接受到特定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)

struct irq_chip {
	const char	*name;
	unsigned int	(*startup)(unsigned int irq);
	void		(*shutdown)(unsigned int irq);
	void		(*enable)(unsigned int irq);
	void		(*disable)(unsigned int irq);

	void		(*ack)(unsigned int irq);
	void		(*mask)(unsigned int irq);
	void		(*mask_ack)(unsigned int irq);
	void		(*unmask)(unsigned int irq);
	void		(*eoi)(unsigned int irq);

	void		(*end)(unsigned int irq);
	int		(*set_affinity)(unsigned int irq,
					const struct cpumask *dest);
	int		(*retrigger)(unsigned int irq);
	int		(*set_type)(unsigned int irq, unsigned int flow_type);
	int		(*set_wake)(unsigned int irq, unsigned int on);

	void		(*bus_lock)(unsigned int irq);
	void		(*bus_sync_unlock)(unsigned int irq);

	/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
	void		(*release)(unsigned int irq, void *dev_id);
#endif
	/*
	 * For compatibility, ->typename is copied into ->name.
	 * Will disappear.
	 */
	const char	*typename;
};

它是用來描述中斷控制器的,也就是一個抽象的中斷控制器,其成員是一系列指向函數的指針。
typename:給相應的控制器起一個便於理解的名字。
startup:允許從給定的控制器的IRQ所產生的事件。(基本上與enable相同)
shutdown:禁止從給定的控制器的IRQ所產生的事件。(基本上與disable相同)
以上三個結構體的關係可以用下面一張圖來說明IRQ結構間的關係:

 

數組irq_desc_t用來描述中斷的相關信息,它有225項,每一項代表一箇中斷源,其中字段irq(注意這個字段就是上圖中的handler字段),此結構體用來描述中斷控制器,action字段用來描述處理一種中斷所需要的各種信息,它代表了內核接受到特定IRQ之後應該採取的操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章