# ARM 上的通用中斷控制器: GICv3/4 :::warning 由於 security 與 virtualization 非作者主要關注,本文目前未詳細說明相關部分。 ::: ## Brief history GIC(Generic Interrupt Controller) 是在 ARM 平台上的通用中斷控制器。可管理中斷、排定中斷的優先順序及路由(routing)等與中斷相關的工作。架構上,目前有 V1 至 V4 的 4 種版本。而使用 ARM 來生產 SOC 廠商可以直接向 ARM 公司購買符合架構規範的 GIC 具體硬體實現,例如 GIC-400、GIC-500、GIC-600 等[型號](https://www.arm.com/zh-TW/products/silicon-ip-system/system-controllers/gic)。 從 GIC v2 到 v3 是一個巨大的分水嶺。與前兩代相比,GICv3 實作了需多新的功能。例如能夠支援更多 CPU、更多的中斷 id、message-based 的中斷等等。而 GICv4 與 v3 相比,主要只是增加在虛擬化功能的支援。因此,本文將以 GICv3 為主要對象,深入探討相關的功能與使用方式。 ## Overview of GICv3 GICv3 主要應用於基於 ARMv8 設計的 SOC,例如 ARM Cortex-A53, ARM® Cortex-A57 and ARM Cortex-A72 等 CPU,而 GIC-500 或更新的 GIC 型號則能夠符合 GICv3 的規範。  > [How is the GICv3 registers context managed during system suspend state?](https://developer.arm.com/documentation/ka005993/1-0) ### PE 在 ARM 架構的設計上,以[處理元件(Processing element, 以下簡稱 PE)](https://developer.arm.com/documentation/102404/0202/Common-architecture-terms) 為術語來表示具有 Program Counter(PC) 並可以執行程式的任何單元。例如: - Cortex-A8 是單核心、單執行緒處理器。整個處理器就是一個PE。 - Cortex-A53 是一個多核心處理器,每個核心都是單執行緒。每個核心都是一個 PE。 - Cortex-A65AE是一款多核心處理器,每個核心有兩個 thread。則每個 thread 都是一個 PE。 ## GICv3 fundamentals ### GICv3 的中斷類型 在 GICv3 中,中斷可以分為以下幾種類型: * SPI (Shared Peripheral Interrupt): 這是一種全域週邊中斷,可以 route 到指定的 PE,或路由到一組 PE * PPI(Private Peripheral Interrupt: 針對單一特定的 PE 發送 * SGI(Software generated interrupt): 可以通過寫入 GIC 的 SGI 暫存器來觸發軟體中斷,通常此種中斷應用在處理器內部的交互中(inter-processor communication) * LPI(Locality-specific Peripheral Interrupt): LPI 是在 v3 中新增的中斷類型。具體來說,LPI 以基於訊息的方式中斷,並且它們的配置保存在記憶體而不是寄存器,以換取在特定情形下的效能優勢。詳細會在後面章節說明。 根據中斷類型不同,它們的 interrupt ID(INTID) 在 ARM 架構中規範為以下表格所示:  硬體層面上,在 GICv3 的架構下,中斷的觸發可以藉由外部的訊號(例如 GPIO)通知中斷控制器,中斷控制器再根據設定將中斷分配給正確的 PE。如下圖所示:  而相比於前面版本,GICv3 還支援基於訊息的中斷。中斷的方式是藉由寫入中斷控制器中的暫存器,來設定或清除中斷。這種機制的優勢是: 每個中斷來源不再需要各自對應的專用訊號,換言之,硬體的設計上使用的 pin 可以大幅減少。  在 GICv3 中,SPI 可以是基於訊息的中斷,但 LPI 只能是基於訊息的中斷。 ### Interrupt State Machine 中斷控制器為每個 SPI、PPI 和 SGI 中斷維護一個狀態機,後者由四種狀態組成:  - Inactive: 中斷尚未被 assert - Pending: 中斷已 assert,但尚未 PE 所確認(Ack) - Active: 中斷已 assert,且已被 PE 所確認 - Active and Pending: 中斷已被確認的同時,另一個相同來源的中斷(但發生在下個時間點)處於待處理狀態 :::info LPI 中不存在這些狀態的概念 ::: 而中斷的生命週期(何時 active)取決於它是 Edge-triggered 或者 Level sensitive。 #### Edge-triggered 在中斷訊號的 rising edge 時 assert,並持續直到根據 GIC 定義的方式清除狀態。  #### Level sensitive 當 interrupt 訊號 active,則 interrupt 被判定為 assert,直到訊號非 active 時,判定為未發生。  ### Affinity routing GICv3 支援 Affinity routing,以識別能將中斷路由到哪些特定的 PE 或 PE 組。以四個 8 位元的欄位來表示 PE 的 affinity(定義在 `MPIDR_EL1` 暫存器中)。 ``` <aff level 3>.<aff level 2>.<aff level 1>.<aff level 0> ```  在 level 0 處有一個 Redistributor,負責控制 SGI、PPI、LPI 在對應 CPU Interface 上的管理(後續章節說明)。而不同 affinity level 的確切意義可以處理器和 SoC 廠商自行定義。例如: * `<group of groups>. <group of processors>.<processor>.<core>` * `<group of processors>.<processor>.<core>.<thread>` ### Security model GICv3 支援 [ARM TrustZone 技術](https://www.arm.com/zh-TW/technologies/trustzone-for-cortex-a)。每個 INTID 都必須對應到一個 "Group" 和安全性設定。具體來說,有三種選擇。  根據選擇,在不同 EL 層級使用的中斷機制(IRQ/FIQ)也會有所差異。  下圖展示一種 routing 的模型: 指定 IRQ 在 EL1(`SCR_EL3.IRQ==0`) 而 FIQ 在 EL3 處理(`SCR_EL3.FIQ==1`)。則根據上表: * 在 secure EL0 下: * Secure Group 1 中是 IRQ,在 EL1 處理 * Non-Secure Group 1 是 FIQ,在 EL3 處理 * Group 0 永遠是 FIQ,在 EL3 處理 * 在 non-secure EL0 下: * Secure Group 1 中是 FIQ,在 EL3 處理 * Non-Secure Group 1 是 IRQ,在 EL1 處理 * Group 0 永遠是 FIQ,在 EL3 處理  ### Programmers’ model  在 GICv3 中斷控制器中,暫存器可以分三個部份的組件: - Distributor interface - Redistributor interface - CPU interface #### Distributor`(GICD_*)` Distributor 暫存器是記憶體映射的,包含影響所有 PE 的全域設定,提供了以下介面: * 中斷優先權和 SPI 分配 * 啟用和停用 SPI * 設定每個 SPI 的優先權 * 每個 SPI 的路由資訊 * 將每個 SPI 設定為 edge-trigger 或 level sensitive * 產生 message-based 的 SPI * 控制 SPI active/pending 狀態 * 控制 security 相關的設定 #### Redistributors `(GICR_*)` 每個 PE 都有一個 Redistributors。Redistributors 提供的介面有: * 啟用和停用 SGI 和 PPI * 設定 SGI 和 PPI 的優先權 * 將每個 PPI 設定為 edge-trigger 或 level sensitive * 將每個 SGI 和 PPI 指派給一個中斷群組 * 控制 SGI 和 PPI 的狀態 * LPI 相關的中斷屬性、待處理狀態在記憶體中資料結構的 base address 設定 * 對 PE 提供電源管理支援 #### CPU interfaces `(ICC_*_ELn)` 每個 Redistributor 都連接到一個 CPU interface。 CPU interface 提供以下介面: * 實現中斷處理的通用控制和配置 * 中斷的確認(Ack) * 執行優先權降低和中斷停用 * 為 PE 設定中斷優先權的 mask * 定義 PE 的中斷搶佔策略 * 決定 PE 的待處理中斷中之最高優先權者 :::info 這些暫存器對應到 GICv1/GICv2 的 `GICC_*` 系列 ::: ## Interrupt Handling 當中斷變成 pending 狀態時,GIC 決定是否發送中斷到被連接的 PE 中的其一。選擇的 PE 的取決於以下: * Group: 每個 INTID 都屬於一個 Group(Group 0, Secure Group 1 或 Non-secure Group 1)。可以在 Distributor 或 CPU Interface 階層禁用某個 Group 的中斷 * Interrupt enables: 中斷可以被單獨禁用,則該中斷會保持在 pending 狀態,但不會被轉送到 PE。 * Routing controls: * SPI: 由 `GICD_IROUTERn` 控制 * LPI: 藉由 ITS 決定配發的 PE * PPI: 天生就只能轉送到特定的一個 PE * SGI: 源頭的 PE 可以定義目標 PE 的清單 * Interrupt priority: 每個 PE 在其 CPU interface 中都有一個 Priority Mask register `ICC_PMR_EL1`,可設定轉送中斷到此 PE 所需的最低優先權 * Running priority: 如果 PE 不是在處理中斷,它的 Running priority 是 `0xFF`。而中斷只有在 priority 高於 running priority 的時候,可以搶佔當前中斷。 ### Interrupt Acknowledge CPU interface 有兩個 IAR 暫存器 `ICC_IAR0_EL1`、`ICC_IAR1_EL1`,分別用來讀取 Group0 和 Group1 的中斷。讀取 IAR 能夠得到 `INTID` 並推進中斷狀態機。 可能得到的 `INTID` 其中 1020、1021、1022、1023 這四個編號被保留來表示特殊的中斷狀況: * 1020: 只能從 `ICC_IAR0_EL1` 讀到。當針對 Trusted OS 的中斷發生在 PE 執行於 non-secure state 時會讀到 * 1021: 只能從 `ICC_IAR0_EL1` 讀到。當針對 Rich OS 的中斷發生在 PE 執行於 secure state 時會讀到 * 1022: 用來相容 legacy * 1023: Spurious interrupt,在沒有 pending 的中斷時嘗試讀取此暫存器會得到此值 以一個行動系統使用 modem 的中斷來發出來電訊號的案例說明: 此中斷是一個 non-secure group 1 的中斷,需要由 non-secure state 下的 Rich OS 處理。假設 rounting 設定如下:  1. 當 PE 在 secure EL1 上執行 Trusted OS 時發生 modem 中斷。由於後者預期是 non-secure group 1 的中斷,因此將以 FIQ 形式發出訊號,並被帶到 EL3。 2. 在 EL3 上執行的 secure monitor 讀取 IAR 後會得到 1021,表示中斷預計在 non secure state 下處理,secure monitor 因此做相關切換。 3. PE 現在處於 non secure state,中斷以 IRQ 形式發出並進入 non secure EL1,這時就可由 Rich OS 處理之。 也可以有其他的處理模型,詳見 [GICv3 and GICv4 Software Overview](https://developer.arm.com/documentation/dai0492/latest/) 5.3 章節。 ### Running priority & preemption Priority Mask register(PMR) 設定了將中斷轉送至特定 PE 所必須具有的最低優先級。而 GICv3 架構還有 "Running priority" 的概念: 當 PE ack 中斷時,其 Running priority 變成中斷的 priority。當 PE 寫入 EOI(end of interrupt) 暫存器時,Running priority 返回到前一個數值。 Running priority 對於搶佔時的優先順序很重要。當高優先權中斷發生,而 PE 在比較低優先的 running priority 時,搶佔就會發生。 目前的 running priority 可以在 CPU interface 的 `ICC_RPR_EL1` 暫存器中得知。   如上圖描述了允許與不允許搶佔時的 running priority 變化。 不想要搶佔發生的情況下,在 GICv3 可以透過暫存器 `ICC_BPRn_EL1` 來控制相關設定。暫存器的欄位設計如下:  對於搶占只需要考慮 Group 即可。具體案例中,考慮以下三個中斷: * INTID A 的優先權為 0x10 * INTID B 的優先權為 0x20 * INTID C 的優先權為 0x21 在這種情況下,可以決定: * A 可以搶佔 B 或 C * B 不能搶佔 C,因為 B 和 C 的優先權相似 ### End of Interrupt 軟體必須通知 GIC 中斷已處理完成,以便 state machine 可以轉換到下一個狀態。在 GICv3 架構將此視為兩個階段: * Priority Drop: 將 running prioriy 復原到中斷發生之前的值 * Deactivation: 更新目前正在處理之中斷的 state machine,通常是從 active 轉換為 deactive 狀態 取決於 `ICC_CTLR_ELn.EOImode` 的設定,這兩步驟可以同時或者獨立發生: * EOImode = 0: 寫入 `ICC_EOIR0_EL1` (Group0)或 `ICC_EOIR1_EL1`(Group 1) 會同時執行 Priority Drop 和 `Deactivation` * EOImode = 1: 寫入 `ICC_EOIR0_EL1` (Group0)或 `ICC_EOIR1_EL1`(Group 1) 會導致 Priority Drop。Deactivation 則需要另外寫入 `ICC_DIR_EL1`。通常用於虛擬化目的。 ### Interrupt State 如果想確認 PE 目前最高優先級的 pending interrupt,能存取暫存器 `ICC_HPPIR0_EL1` 和 `ICC_HPPIR1_EL1` 對應中斷的 INTID。對於 PE 當前的 running priority 則可以讀取 `ICC_RPR_EL1` 來得知。 Distributor 上的暫存器可以提供每個 SPI 的目前狀態。而 Redistributor 的暫存器則可提供所連接之 PE 的 PPI 和 SGI 狀態。 可以參考以下表格:  ## LPI 的使用說明 LPI 是一種 message-based 的中斷,屬於可選的中斷類型(透過 `GICD_TYPER.LPIS` 可確認),且只有在 Affinity routing 啟用時才支援。與其他中斷類型的設定方式不同,LPI 的使用涉及以下設定: * Redistributors * ITS(Interrupt Translation Service): 非必要,可選 ITS 的功能是接收來自週邊裝置的中斷,並將其以 LPI 形式轉發到相應的 Redistributor。一個系統可能包含多個 ITS,在這種情況下,每個 ITS 都必須單獨配置。 也可以選擇繞過 ITS 直接將 LPI 傳送到 Redistributor。但 ITS 提供了許多功能,可以有效處理大量中斷來源。 ### ITS 週邊裝置向 ITS 發送以下兩項資訊來產生 LPI: * EventID: 寫入 `GITS_TRANSLATER` 標識裝置正在發送的中斷種類。EventID 可以與 INTID 相同,也可以由 ITS 轉換為 INTID。 * DeviceID: DeviceID 用來識別週邊裝置。DeviceID 的產生由實作定義。例如 AXI user signals。 LPI INTID 被分組到各個 *collections* 中,其中的所有 INTID 都會 route 到同一個 Redistributor。軟體藉由將 LPI INTID 分配給 *collections*,從而能夠有效地將中斷從一個 PE 移動到另一個 PE。  ITS 使用三種類型的表來處理 LPI 的轉換與 routing。 * Device Table: 將 DeivceID 對應到 Interrupt Translation Tables * Interrupt Translation Tables: 包含 EventID 和 INTID 之間特定於 DeviceID 的對應。還包含 INTID 所屬的 collection 的資訊。 * Collection Table: collections 到 Redistributor 的對應 當週邊寫入 `GITS_TRANSLATER` 時,ITS 會進行下列步驟: 1. 透過 DeviceID 從 Device Table 中選擇對應 entry 以找到正確的 Interrupt Translation Table 2. 使用 EventID 從 Interrupt Translation Table 中選擇對應 entry。由此得到 INTID 和 Collection ID。 3. 使用 Collection ID 在 Collection Table 中選擇對應 entry,以得知 routing 資訊。 4. 藉由 routing 將中斷轉發至目標 Redistributor。 ITS 的具體實作和設定細節請參考 [GICv3 and GICv4 Software Overview Chapter 6.1](https://developer.arm.com/documentation/dai0492/latest/)。 ### Redistributor Redistributor 可以使用記憶體中的表來處理 LPI 的控制、優先權和 pending 資訊。  暫存器 `GICR_PROPBASER` 會指向記憶體中 LPI Configuration tables,後者描述 LPI 設定的相關資訊。LPI 的設定是全域的。換言之,一個系統只有一個 LPI 配置表,由所有 Redistributor 共用。 類似的,LPI 的狀態資訊也儲存在記憶體中的 LPI Pending tables,由 `GICR_PENDBASER` 指向。每個 Redistributor 都有自己的 LPI Pending tables。 Redistributor 的具體實作和設定細節請參考 [GICv3 and GICv4 Software Overview Chapter 6.2](https://developer.arm.com/documentation/dai0492/latest/)。 ## Reference * [GICv3 and GICv4 Software Overview](https://developer.arm.com/documentation/dai0492/latest/) * [Arm Generic Interrupt Controller (GIC) Architecture Specification](https://developer.arm.com/documentation/ihi0069/latest/) * [linux kernel的中断子系统之(七):GIC代码分析](http://www.wowotech.net/irq_subsystem/gic_driver.html) * [LPI pending table]( https://patchwork.kernel.org/project/xen-devel/patch/20170130183153.28566-4-andre.przywara@arm.com/)
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up