天天看點

萬字講解Linux中斷——ARM GIC 中斷控制器

作者:linux上的碼農

GIC(Generic Interrupt Controller)是ARM公司提供的一個通用的中斷控制器,其architecture specification目前有四個版本,V1~V4(V2最多支援8個ARM core,V3/V4支援更多的ARM core,主要用于ARM64伺服器系統結構)。目前在ARM官方網站隻能下載下傳到Version 2的GIC architecture specification,是以,本文主要描述符合V2規範的GIC硬體及其驅動。

具體GIC硬體的實作形态有兩種,一種是在ARM vensor研發自己的SOC的時候,會向ARM公司購買GIC的IP,這些IP包括的型号有:PL390,GIC-400,GIC-500。其中GIC-500最多支援128個 cpu core,它要求ARM core必須是ARMV8指令集的(例如Cortex-A57),符合GIC architecture specification version 3。另外一種形态是ARM vensor直接購買ARM公司的Cortex A9或者A15的IP,Cortex A9或者A15中會包括了GIC的實作,當然,這些實作也是符合GIC V2的規格。

1. ARM中斷控制器

1.1 ARM支援中斷類型ARM GIC-v2支援三種類型的中斷:

SGI:軟體觸發中斷(Software Generated Interrupt),通常用于多核間通訊,最多支援16個SGI中斷,硬體中斷号從ID0~ID15。SGI通常在Linux核心中被用作 IPI 中斷(inter-processor interrupts),并會送達到系統指定的CPU上。

PPI:私有外設中斷(Private Peripheral Interrupt),是每個CPU私有的中斷。最多支援16個PPI中斷,硬體中斷号從ID16~ID31。PPI通常會送達到指定的CPU上,應用場景有CPU本地時鐘。

SPI:公用外設中斷(Shared Peripheral Interrupt),最多可以支援988個外設中斷,硬體中斷号從ID32~ID1019。

1.2 GIC檢測中斷流程

GIC主要由兩部分組成,分别是仲裁單元(Distributor)和CPU接口(Interface)子產品

萬字講解Linux中斷——ARM GIC 中斷控制器

從圖中可以看出, GIC 可以清晰的劃分成兩個部分:仲裁( Distributor )和 CPU 接口(CPU Interface)。CPU interface有兩種,一種就是和普通processor接口,另外一種是和虛拟機接口的。Virtual CPU interface在本文中不會較長的描述。分發器其實應該叫彙聚器,在 IC 的後端設計中, layout 會把各個子產品引過來的中斷線(就是上面說的三種中斷)混接到 GIC 上,然後把混聚合的中斷接到 CPU 的 IRQ 和 FIQ 線上,這樣 CPU 就有觸覺了。

其中 Distribute 隻有一套,故基位址也隻有一個,而 Interface 有多套,因為每個 CPU 都對應一套 interface 。

其中各個子中斷使能,設定觸發方式,優先級排序,分發到哪個 CPU 上這些功能由 Distribute 負責,總的中斷的使能,狀态的維護由 Interface 負責。

更多linux核心視訊教程文檔資料免費領取背景私信【核心】自行擷取.

萬字講解Linux中斷——ARM GIC 中斷控制器
萬字講解Linux中斷——ARM GIC 中斷控制器

Distributor

Distributor的主要的作用是檢測各個interrupt source的狀态,控制各個interrupt source的行為,分發各個interrupt source産生的中斷事件分發到指定的一個或者多個CPU interface上。雖然Distributor可以管理多個interrupt source,但是它總是把優先級最高的那個interrupt請求送往CPU interface。Distributor對中斷的控制包括:

(1)中斷enable或者disable的控制。Distributor對中斷的控制分成兩個級别。一個是全局中斷的控制(GIC_DIST_CTRL)。一旦disable了全局的中斷,那麼任何的interrupt source産生的interrupt event都不會被傳遞到CPU interface。另外一個級别是對針對各個interrupt source進行控制(GIC_DIST_ENABLE_CLEAR),disable某一個interrupt source會導緻該interrupt event不會分發到CPU interface,但不影響其他interrupt source産生interrupt event的分發。

(2)控制将目前優先級最高的中斷事件分發到一個或者一組CPU interface。當一個中斷事件分發到多個CPU interface的時候,GIC的内部邏輯應該保證隻assert 一個CPU。

(3)優先級控制。

(4)interrupt屬性設定。例如是level-sensitive還是edge-triggered

(5)interrupt group的設定

Distributor可以管理若幹個interrupt source,這些interrupt source用ID來辨別,我們稱之interrupt ID。

CPU interface

CPU interface這個block主要用于和process進行接口。該block的主要功能包括:

(a)enable或者disable CPU interface向連接配接的CPU assert中斷事件。對于ARM,CPU interface block和CPU之間的中斷信号線是nIRQCPU和nFIQCPU。如果disable了中斷,那麼即便是Distributor分發了一個中斷事件到CPU interface,但是也不會assert指定的nIRQ或者nFIQ通知processor。

(b)ackowledging中斷。processor會向CPU interface block應答中斷,中斷一旦被應答,Distributor就會把該中斷的狀态從pending狀态修改成active。如果沒有後續pending的中斷,那麼CPU interface就會deassert nIRQCPU和nFIQCPU信号線。如果在這個過程中又産生了新的中斷,那麼Distributor就會把該中斷的狀态從pending狀态修改成pending and active。這時候,CPU interface仍然會保持nIRQ或者nFIQ信号的asserted狀态,也就是向processor signal下一個中斷。

(c)中斷處理完畢的通知。當interrupt handler處理完了一個中斷的時候,會向寫CPU interface的寄存器進而通知GIC CPU已經處理完該中斷。做這個動作一方面是通知Distributor将中斷狀态修改為deactive,另外一方面,可以允許其他的pending的interrupt向CPU interface送出。

(d)設定priority mask。通過priority mask,可以mask掉一些優先級比較低的中斷,這些中斷不會通知到CPU。

(e)設定preemption的政策

(f)在多個中斷事件同時到來的時候,選擇一個優先級最高的通知processor

GIC仲裁單元:為每一個中斷維護一個狀态機,分别是:inactive、pending、active and pending、active。

下面是來自IHI0048B GIC-V2規格書3.2.4 Interrupt handling state machine截圖:

萬字講解Linux中斷——ARM GIC 中斷控制器

GIC檢測中斷流程如下:

(1) 當GIC檢測到一個中斷發生時,會将該中斷标記為pending狀态(A1)。

(2) 對處于pending狀态的中斷,仲裁單元回确定目标CPU,将中斷請求發送到這個CPU上。

(3) 對于每個CPU,仲裁單元會從衆多pending狀态的中斷中選擇一個優先級最高的中斷,發送到目标CPU的CPU Interface子產品上。

(4) CPU Interface會決定這個中斷是否可以發送給CPU。如果該終端優先級滿足要求,GIC會發生一個中斷信号給該CPU。

(5) 當一個CPU進入中斷異常後,會去讀取GICC_IAR 寄存器來響應該中斷(一般是Linux核心的中斷處理程式來讀寄存器)。寄存器會傳回硬體中斷号(hardware interrupt ID),對于SGI中斷來說是傳回源CPU的ID。

當GIC感覺到軟體讀取了該寄存器後,又分為如下情況:

  • 如果該中斷源是pending狀态,那麼轉改将變成active。**(C) **
  • 如果該中斷又重新産生,那麼pending狀态變成active and pending。(D)
  • 如果該中斷是active狀态,現在變成active and pending。(A2)

    (6) 當處理器完成中斷服務,必須發送一個完成信号EOI(End Of Interrupt)給GIC控制器。軟體寫GICC_EOIR寄存器,狀态變成inactive。(E1)

補充:

(7) 對于level triggered類型中斷來說,當觸發電平消失,狀态從active and pending變成active。(B2)

常用路徑是A1->D->B2->E1。

1.2.1 GIC中斷搶占

GIC中斷控制器支援中斷優先級搶占,一個高優先級中斷可以搶占一個低優先級且處于active狀态的中斷,即GIC仲裁單元會記錄和比較目前優先級最高的pending狀态,然後去搶占目前中斷,并且發送這個最高優先級的中斷請求給CPU,CPU應答了高優先級中斷,暫停低優先級中斷服務,進而去處理高優先級中斷。

GIC會将pending狀态優先級最高的中斷請求發送給CPU。

1.2.2 Linux對中斷搶占處理

從GIC角度看,GIC會發送高優先級中斷請求給CPU。

但是目前CPU處于關中斷狀态,需要等低優先級中斷處理完畢,直到發送EOI給GIC。

然後CPU才會響應pending狀态中優先級最高的中斷進行處理。

是以Linux下:

  1. 高優先級中斷無法搶占正在執行的低優先級中斷。

2.同處于pending狀态的中斷,優先響應高優先級中斷進行處理。

1.3 GIC中斷時序

萬字講解Linux中斷——ARM GIC 中斷控制器

首先給出前提條件:

  • N和M用來辨別兩個外設中斷,N的優先級大于M
  • 兩個中斷都是SPI類型,level trigger,active-high
  • 兩個中斷被配置為去同一個CPU
  • 都被配置成 group 0,通過FIQ觸發中斷

(1) T1時刻:GIC的總裁單元檢測到中斷M的電平變化。

(2) T2時刻:仲裁單元設定中斷M的狀态為pending。

(3) T17時刻:CPU Interface子產品會拉低nFIQCPU[n]信号。在中斷M的狀态變成pending後,大概需要15個時鐘周期後會拉低nFIQCPU[n]信号來向CPU報告中斷請求(assertion)。仲裁單元需要這些時間來計算哪個是pending狀态下優先級最高的中斷。

(4) T42時刻:仲裁單元檢測到另外一個優先級更高的中斷N。

(5) T43時刻:仲裁單元用中斷N替換中斷M為目前pending狀态下優先級最高的中斷,并設定中斷N為pending狀态。

(6) T58時刻:經過tph個時鐘後,CPU Interface拉低你FIOCPU[n]信号來通知CPU。因為此信号在T17時刻已經被拉低,CPU Interface子產品會更新GICC_IAR寄存器的Interrupt ID域,該域的值變成中斷N的硬體中斷号。

(7) T61~T131時刻:Linux對中斷N的服務程式---------------------中斷服務程式處理段,從GICC_IAR開始到GICC_EOIR結束。

  •   T61時刻:CPU(Linux中斷服務例程)讀取GICC_IAR寄存器,即軟體響應了中斷N。這時仲裁單元把中斷N的狀态從pending變成active and pending。
  •   T64時刻:在中斷N被Linux相應3個時鐘内,CPU Interface子產品完成對nFIQCPU[n]信号的deasserts,即拉高nFIQCPU[n]信号。
  •   T126時刻:外設也deassert了該中斷N。
  •   T128時刻:仲裁單元移出了中斷N的pending狀态。
  •   T131時刻:Linux服務程式把中斷N的硬體ID号寫入GICC_EOIR寄存器來完成中斷N的全部處理過程。

(8) T146時刻:在向GICC_EOIR寄存器寫入中斷N中斷号後的tph個時鐘後,仲裁單元會選擇下一個最高優先級中斷,即中斷M,發送中斷請求給CPU Interface子產品。CPU Interface會拉低nFIQCPU[n]信号來向CPU報告外設M的中斷請求。

(9) T211時刻:Linux中斷服務程式讀取GICC_IAR寄存器來響應中斷,仲裁單元設定中斷M的狀态為active and pending。

(10) T214時刻:在CPU響應中斷後的3個時鐘内,CPU Interface子產品拉高nFIOCPU[n]信号來完成deassert動作。

1.4 GIC中 寄存器描述

1.4.1 Distributor

萬字講解Linux中斷——ARM GIC 中斷控制器

一種實作是:

偏移 名稱 可讀寫 複位後 作用
0x000 GICD_CTLR RW 0x00000000[c] Distributor Control Register寫入1使能控制器,必備
0x004 GICD_TYPER RO Configuration-dependent[d] Interrupt Controller Type Register其中可以檢視中斷線的總數
0x008 GICD_IIDR RO 0x0200143B Distributor Implementer Identification Register, GICD_IIDR存了一些版本資訊
0x080-0x0BC GICD_IGROUPRn RW 0x00000000 Interrupt Group Registers[e]一個位圖,控制中斷屬于A組還是B組
0x100 GICD_ISENABLERn RW[f] SGIs and PPIs:0x0000FFFF Interrupt Set-Enable Registers一個位圖,用于使能各個中斷,寫入1使能。有用
0x104-0x13C SPIs:0x00000000
0x180 GICD_ICENABLERn RW[f] 0x0000FFFF Interrupt Clear-Enable Registers和上一個寄存器類似,作用相反,寫入1禁止。
0x184-0x1BC 0x00000000
0x200-0x23C GICD_ISPENDRn RW 0x00000000 Interrupt Set-Pending Registerspend位圖,寫入1可以進入pend狀态
0x280-0x2BC GICD_ICPENDRn RW 0x00000000 Interrupt Clear-Pending Registers同上,寫入1效果相反,阻止pend狀态
0x300-0x33C GICD_ISACTIVERn RW 0x00000000 Interrupt Set-Active Registers位圖,寫入1可以激活中斷
0x380-0x3BC GICD_ICACTIVERn RW 0x00000000 Interrupt Clear-Active Registers寫入1反激活中斷
0x400-0x5FC GICD_IPRIORITYRn RW 0x00000000 Interrupt Priority Registers存着各個中斷的優先級,每8位算一個
0x800-0x81C GICD_ITARGETSRn RO[h] - Interrupt Processor Targets Registers[i]某個中斷應該發往哪個處理器進行處理
0x820-0x9FC RW 0x00000000
0xC00 GICD_ICFGRn RO SGIs:0xAAAAAAAA Interrupt Configuration Registers, GICD_ICFGRn配置中斷是低電平觸發還是下降沿觸發
0xC04 RO PPIs:0x55540000
0xC08-0xC7C RW[j] SPIs:0x55555555
0xD00 GICD_PPISR RO 0x00000000 Private Peripheral Interrupt Status Register, GICD_PPISR一般沒用
0xD04-0xD3C GICD_SPISRn RO 0x00000000 Shared Peripheral Interrupt Status Registers, GICD_SPISRn沒用
0xF00 GICD_SGIR WO - Software Generated Interrupt Register控制軟中斷
0xF10-0xF1C GICD_CPENDSGIRn RW 0x00000000 SGI Clear-Pending Registers軟中斷的pend位
0xF20-0xF2C GICD_SPENDSGIRn RW 0x00000000 SGI Set-Pending Registers同上,不過寫入1時停止pend
0xFD0 GICD_PIDR4 RO 0x00000004 Peripheral ID 4 Register
0xFD4 GICD_PIDR5 RO 0x00000000 Peripheral ID 5 Register
0xFD8 GICD_PIDR6 RO 0x00000000 Peripheral ID 6 Register
0xFDC GICD_PIDR7 RO 0x00000000 Peripheral ID 7 Register
0xFE0 GICD_PIDR0 RO 0x00000090 Peripheral ID 0 Register
0xFE4 GICD_PIDR1 RO 0x000000B4 Peripheral ID 1 Register
0xFE8 GICD_PIDR2 RO 0x0000002B Peripheral ID 2 Register
0xFEC GICD_PIDR3 RO 0x00000000 Peripheral ID 3 Register
0xFF0 GICD_CIDR0 RO 0x0000000D Component ID 0 Register
0xFF4 GICD_CIDR1 RO 0x000000F0 Component ID 1 Register
0xFF8 GICD_CIDR2 RO 0x00000005 Component ID 2 Register
0xFFC GICD_CIDR3 RO 0x000000B1 Component ID 3 Register

1.4.2 CPU Interface

Offset Name Type Reset Description[a]
0x0000 GICC_CTLR RW 0x00000000 CPU Interface Control Register 使能位。寫入1使能
0x0004 GICC_PMR RW 0x00000000 Interrupt Priority Mask Register限制中斷最低優先級,高于此值無法中斷,最好寫大一點
0x0008 GICC_BPR RW 0x00000002 Binary Point RegisterThe minimum value of the Binary Point Register depends on which security-banked copy is considered:0x2Secure copy0x3Non-secure copy優先級分組
0x000C GICC_IAR RO 0x000003FF Interrupt Acknowledge Register隻讀,中斷id
0x0010 GICC_EOIR WO - End of Interrupt Register寫入以告知cpu已經處理完中斷
0x0014 GICC_RPR RO 0x000000FF Running Priority Register目前中斷優先級
0x0018 GICC_HPPIR RO 0x000003FF Highest Priority Pending Interrupt Register最高優先級中斷号及其pend值
0x001C GICC_ABPR RW 0x00000003 Aliased Binary Point RegisterThe minimum value of the Aliased Binary Point Register is0x3.别名寄存器
0x0020 GICC_AIAR RO 0x000003FF Aliased Interrupt Acknowledge Register别名寄存器
0x0024 GICC_AEOIR WO - Aliased End of Interrupt Register别名寄存器
0x0028 GICC_AHPPIR RO 0x000003FF Aliased Highest Priority Pending Interrupt Register别名寄存器
0x00D0 GICC_APR0 RW 0x00000000 Active Priority Register用于儲存和恢複
0x00E0 GICC_NSAPR0 RW 0x00000000 Non-Secure Active Priority Register用于儲存和恢複
0x00FC GICC_IIDR RO 0x0202143B CPU Interface Identification Register, GICC_IIDR存着版本資訊
0x1000 GICC_DIR WO - Deactivate Interrupt Register
萬字講解Linux中斷——ARM GIC 中斷控制器