# 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
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.