天天看點

LINUX-核心-中斷分析-中斷向量表(2)-mips

mips中斷概念

在《MIPS體系結構透視》的第5章說到,在MIPS中,中斷、陷阱、系統調用和任何可以中斷程式正常執行流的情況全被都被稱為異常。

以上這種統一到“異常”的概念及其邏輯當然會展現在MIPS的異常入口點的設計中,特别如MIPS中斷入口點的引出。

MIPS的異常入口點(中斷入口點)及異常向量概念的引出

非向量化中斷

根據《MIPS體系結構透視》第5章介紹,類似x86這樣的CISC處理器根據所發生的異常(用x86的話來說就是同步中斷+異步中斷)的種類把CPU指向不同的入口,具體到異步中斷,也即是根據激活的中斷輸入信号在不同的入口點處理這些中斷,這種做法叫做“向量化中斷”,而MIPS則不這樣做,理由有3:

  1. CISC需要花費時間指向不同的中斷入口,然後作業系統軟體再從入口處獲得一個序号,再花些時間調到通用中斷處理程式。MIPS認為這是一件繁瑣的事情;
  2. 即使是用硬體來分析這麼多種中斷,這個硬體會異常複雜,MIPS認為可以用軟體以其他的邏輯,更高的效率來應對這個問題;
  3. 在類似MIPS這樣的RISC架構中,CPU比外設快得實在太多。考慮到中斷服務程式一般都要和外設打交道,外設相對“慢”的速度導緻這種和外設的互動要花費大量的時間,此時,MIPS自己實作的中斷入口配置設定代碼的耗時相對而言就不算是什麼性能瓶頸了,不需要去用所謂“向量化中斷”;
  4. 對于應用于嵌入式系統的RISC體系來說,要面對的外設多種多樣,且各種形式的嵌入式硬體的外設都變化多端,如果給每一種可能的外設的中斷都像x86那樣使用“向量化中斷”的話,無法避免會很浪費向量資源。
    LINUX-核心-中斷分析-中斷向量表(2)-mips
    綜合以上理由,除了個别特殊中斷外,MIPS将幾乎所有的外部中斷都看作是同一種異常,同時也就為幾乎所有的外部中斷都隻準備了一個所謂異常入口。而在x86系統中,32~238的中斷向量全部供給了外部中斷使用。

向量化異常–異常向量

雖然将所有外部中斷都看作是同一種異常,但是MIPS體系結構中異常也是分很多種的。是以,雖然外部中斷沒有被向量化,但是MIPS中還是将異常給向量化了。根據《MIPS體系結構透視》第3章表3-2的介紹,通過Cause寄存器中的Excode值,MIPS定義并可以識别32種異常。MIPS為這32種異常準備了一個所謂異常向量表,這個異常向量表展現到核心代碼裡面本質上就是一個叫做exception_handlers[32]的數組。該數組的每一項都是一個函數指針,其中0号異常即為所有外部中斷的統一入口點。

當異常發生後,位于位址0x80000180處的函數except_vec3_generic()會根據Cause寄存器中的ExcCode位的值,調用exception_handlers[32]中相應成員的那個函數指針執行相關的處理函數。這個 exception_handlers[32]是traps.c下面定義的一個全局變量。

---------------------------------- arch\mips\kernel\traps.c
unsigned long exception_handlers[32];
           
LINUX-核心-中斷分析-中斷向量表(2)-mips

MIPS體系異常向量表的初始化

雖然中斷沒有被向量化,但是異常被向量化了,是以MIPS體系結構依然存在一個對異常向量表的初始化,而且同樣是在trap_init()中進行。

在中我們說到x86的trap_init()本質上是在給idt_table指派,而MIPS體系中不存在門的概念,取而代之的是上一節提到的exception_handlers[]數組。

在x86的trap_init()中調用了一系列的set_intr_gate()、set_system_intr_gate()、set_task_gate()函數來設定各種門。而在MIPS的trap_init()中則是全部使用set_except_vector(int n, void *addr)函數來為32個數組成員指派。而指派的方式就是直接給exception_handlers[]這個全局變量的各個成員指派—-實在是夠直接!!!

初始化大藍圖

LINUX-核心-中斷分析-中斷向量表(2)-mips

trap_init()函數設定異常處理函數

這個trap_init()本身一直都是一個體系相關函數,在MIPS體系中,該函數有兩個主要工作:

  1. 将統一異常處理入口函數excep_vec3_generic()加載到位址BASE+0X180;
  2. 為exception_handlers[]中的32種異常設定各自的處理函數;

early_irq_init()函數對IRQ子系統的前期初始化

函數本身是體系平台無關的,主要任務也是為所有irq_desc設定預設的irq_chip為no_irq_chip,預設的電源處理函數為handle_bad_irq()等,這些内容的代碼各個體系都是一樣的。

------kernel/irq/irqdesc.c------->
static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
        struct module *owner)
{
    int cpu;

    desc->irq_data.irq = irq;
    desc->irq_data.chip = &no_irq_chip;
    desc->irq_data.chip_data = NULL;
    desc->irq_data.handler_data = NULL;
    desc->irq_data.msi_desc = NULL;
    irq_settings_clr_and_set(desc, ~, _IRQ_DEFAULT_INIT_FLAGS);
    irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
    desc->handle_irq = handle_bad_irq;
    desc->depth = ;
    desc->irq_count = ;
    desc->irqs_unhandled = ;
    desc->name = NULL;
    desc->owner = owner;
    for_each_possible_cpu(cpu)
        *per_cpu_ptr(desc->kstat_irqs, cpu) = ;
    desc_smp_init(desc, node);
}
           

第二步就與硬體平台相關了,調用arch_early_irq_init()進行一些平台相關的irq系統前期初始化。在x86體系中,arch_early_irq_init()主要是處理的8259和IOAPC的差異相關,這導緻對前16個固定IRQ的處理會有些差別,但是這個函數基本上僅用于x86和ppc體系,包括MIPS在内的其他體系基本上arch_early_irq_init()函數就直接傳回了,幹脆直接取名叫做x86_arch_early_irq_init()。

LINUX-核心-中斷分析-中斷向量表(2)-mips

init_IRQ()對外設中斷進行初始化

init_IRQ()是一個體系相關函數,在MIPS中,除了将所有的NR_IRQS個irq_desc中設定noprobe外,就是調用一個MIPS專用的arch_init_irq()函數,這個函數是體系且平台相關的,裡面主要是設定一堆IRQ的irq_chip和電流處理函數,在arch_init_irq()内部,僅有mips_cpu_irq_init()是僅體系相關(體系相關但平台無關)。

LINUX-核心-中斷分析-中斷向量表(2)-mips

下面以QCA的WIFI晶片AR955X為例進行簡要說明。

mips_cpu_irq_init()函數固定設定IP2~IP8這6個外部中斷引腳的IRQ

mips_cpu_irq_init()函數基本上在所有的MIPS體系中都應該是一樣的,與不同廠家不同平台無關,這從該函數位于檔案arch/mips/kernel/irq_cpu.c中這一點也可以得到證明。從代碼來看,該函數一股腦地将IP2~IP8這6個外部中斷的irq_chip都設定為叫做“MIPS”的mips_cpu_irq_controller型中斷控制器。

ath_misc_irq_init(ATH_MISC_IRQ_BASE)在AR955X中設定MISC相關IRQ

ath_misc_irq_init(ATH_MISC_IRQ_BASE) 在AR955X中設定從IRQ16開始的MISC相關的27個IRQ。将這27個IRQ的irq_chip都設定為叫做“ATH MISC”的ath_misc_irq_controller型中斷控制器,将電流處理函數設定為handle_percpu_irq()。

ath_gpio_irq_init(ATH_GPIO_IRQ_BASE)在AR955X中設定GPIO相關IRQ

ath_gpio_irq_init(ATH_GPIO_IRQ_BASE)在AR955X中負責設定從43開始的GPIO相關的32個IRQ。将這32個IRQ的irq_chip都設定為叫做“ATH GPIO”的ath_gpio_intr_controller型中斷控制器,将電流處理函數設定為handle_percpu_irq()。

ath_arch_init_irq()在AR955X中負責無線外界晶片的IRQ

955x中5G無線外接晶片使用IRQ2号,是以事實上該函數覆寫了mips_cpu_irq_init()對IRQ2的設定,對IRQ2進行了重新設定,将IRQ2的irq_chip設定為叫做”dummy”的dummy_irq_chip型中斷控制器,将電流處理函數設定為handle_percpu_irq()。

繼續閱讀