Local APIC
Local APIC 是在CPU內部的,每個邏輯處理核心都配有一的對應的local APIC。
Local APIC 能產生、發送和接受中斷,CPU之間的通訊IPI也是通過Local APIC來實現的。
Local APIC 使用一組寄存器來控制,之前的APIC 是將寄存器映射到內存來使用,新的x2APIC模式則是將寄存器映射到MSR空間中了,適應RDMSR和WRMSR指令進行讀寫了。
Local APIC 有地址編號,這個編號同時也是CPU核心的編號。
在 xAPIC 模式下,APIC寄存器是映射到一塊連續的4K內存中的,由 IA32_APIC_BASE (0x1B)這個MSR寄存器提供控制。
在x2APIC模式下,所有寄存器全被映射到了MSR空間中,地址從802H 到 83FH。
IA32_APIC_BASE提供了一些基本的控制
第8位: 是否是主核心BSP標志位
第10位:是否啟用x2APIC模式
第11位:APIC的global啟用,雖然是global但是只是正對本核心
第12開始:是xAPIC模式的時候的APIC_BASE的地址,具體是幾位取決於地址線的寬度,這是物理地址
APIC寄存器參考x86/x64體系探索及編程 page. 606
以下介紹幾個重要的寄存器
APIC ID寄存器(APIC_BASE+ 20H)
APIC 是根據 Cluster、Package、Core、SMT(邏輯運算單元)多級來組織的,每一級占一定的位數,所以實際上每個邏輯運算單元的編號就不連續了
xAPIC 中是沒有Cluster這個層級的並且APIC ID只有8位有效,是數據的最高8位,而x2APIC則是32位都有效的。CLuster應該是對應的多CPU多主板
具體是幾級,每一級別的位寬都需要通過CPUID指令來獲取。
APIC 版本寄存器(APIC_BASE+30H)
version:指示Local APIC使用外部8249DX芯片還是內部APIC
Max LVT Entry: 則是LVT寄存器的數量,這個值需要+1才是真實數量
24位:SVR寄存器的Bit12位是否支持(bit12 被置位將抑制EOI命令被發送到IO APIC)
LVT Entry寄存器組
一般是7個LVT 寄存器,用於對7種中斷的配置,根據不同的中斷有不同位的解釋也不太同,但是他們都提供了一個中斷向量值用於指示當本中斷對應的IDT中斷向量。
Timer mode: 值用於Timer 用於設置時間計數的模式
00: 一次性計數
01: 周期計數
10: tsc指定計數
mask: 屏蔽位,當置位的時候屏蔽本中斷
Trigger mode:僅用於LINT0和LINT1寄存器,設置他們的觸發模式
0: edge觸發
1: level觸發
Remote IRR flag: 僅用於LINT0和LINT1寄存器,只有在delivery mode = fixed 並且 Trigger mode = level時有效
1: 表示Local APIC 已經接受並處理 本中斷
0: 表示接受到EOI命令
Interrupt input pin polary: 僅用於LINT0和LINT1寄存器,設置Trigger mode = level時到觸發條件
0: high-level,低電平轉高電平觸發
1: low-level,高電平轉低電平觸發
Delivery status:指定交付的狀態
0: IDLE 當前沒有待提交給CPU的終端,可能是沒中中斷,也可能是中斷已經提交給CPU,CPU還在處理。
1: Send Pending 當前有中斷要需要提交給CPU
Delivery mode:配置中斷的交付模式
000: Fixed,允許vector里提供自定義的vector值
010: SMI,vector的值必須為0
100: NMI,vector被忽略,強制認為值是 2
111: ExtInt,使用外部中斷器發送過來的vector值
101: INIT,vectort的值必須是0
vector: 指定本中斷的在IDT的中斷向量,如果不是交付模式配置,則0-15是無效內容
ICR(APIC_LOCA + 300H / 310H)
因為ICR寄存器是64位的,所以分為高低兩部分占兩個地址。
這個寄存器是用來發送核心間通訊指令 IPI的
Destination Field: 配合 Destination ShortHand 來指定發送的目標
Destination ShortHand:
00: No ShortHand,用戶指定發送的目標
01: Self ,發送給自己
10: All Includeing Self, 發送給所有核心包括自己
11: All Exclude Self,發送給出了自己以外的所有核心
Trigger Mode: 同LVT的解釋,設置他們的觸發模式
0: Edge
1: Level
Level:
0: De-assert
1: Assert
Delivery Status: 同LVT的解釋,指定交付的狀態
0: IDLE,當前沒有待提交給CPU的終端,可能是沒中中斷,也可能是中斷已經提交給CPU,CPU還在處理。
1: end Pending, 當前有中斷要需要提交給CPU
Destination Mode: 當使用 No ShortHand 的時候,指示如何解釋 Destination Field 指定的目標
0: Physical, 物理ID,就是APIC ID
1: Logincal,邏輯ID,這個邏輯ID可以在LDR和DFR寄存器中由軟件指定
Delivery Mode: 配置中斷的交付模式
000: Fixed,允許vector使用自定義的值
001: Lowest Priority,目標核心運行中斷的時候將運行在較低的特權級,允許vector使用自定義的值
010: SMI, vector的值必須為0
011: Reserved
100: NMI NMI,vector被忽略,強制認為值是 2
101: INIT,初始化核心,初始化完的核心將運行在實模式
110: Start up,啟動核心(SPIPI),當處理完INIT中斷之后,核心處於Wait-For-SIPI狀態,使用這個模式將指示核心從 Vector * 100H的物理內存地址執行Start-up代碼,進行核心的啟動。
111: Reserved
Vector: 指定目標核心接收到本IPI中斷的時候執行IDT中的哪個中斷,當IPI是SIPI即 Delivery Mode = Start up 的時候 Vector指定的是要執行的代碼的物理地址,實際地址需要vector * 100H(這是由於在Start up執行之前,核心剛初始化完成,還是實模式,並沒有IDT,所以直接執行指定位置的代碼),如果不是交付模式配置,則0-15是無效內容
優先級
優先級是根據vector的大小來決定的,vector的越大優先級越高,同時vector被分為2級,前四位制定優先級的成為中斷優先等級,在同一等級之下在根據后四位的值來確定優先級。
同時還又一個TPR寄存器用來屏蔽中斷,之后vector大於這個值的中斷才能被相應,當然實際能不能相應還要看ISR寄存器也就是當前正在相應的中斷的等級。
在64位模式下CR8 = TPR寄存器,寫CR8寄存器會將值同步到TPR寄存器中
IPI的工作機制
IPI 是核心之間的通訊機制,也是采用中斷的形式的
1. 首先使用通過 Destination ShortHand 來指定確定目標的方式,除了No ShortHand以外,目標都是固定的
2. 如果采用了 No ShortHand 模式,則是要使用Destination Field的值確定目標核心
3. 然后根據 Destination Mode 來解釋 Destination Field的值
Physical: 說明Field的值就是 APIC ID
Logincal:Destinal Field 就是一個MASK值
在LDR寄存器的23-31位可以給這個核心定義個邏輯ID,當使用Logical模式的時候 就用Destianal Field中的MASK和LDR中的邏輯ID進行匹配,如果匹配成功就表明這是核心是目標核心
匹配的規則則由DFR寄存器的 28-31位給出
1111B: 與匹配,MASK和LDR進行AND匹配,結果為TRUE表示匹配,這表示可以多匹配
0000B:等於匹配:MASK = ID 匹配
多核心的初始化工作
當CPU啟動的時候會優先執行CPU內部的代碼,他的主要作用是枚舉出所有的核心並給這些核心分配APIC ID號,同時會推舉一個核心作為主核心成為BSP,並將BSP核心的IA32_APIC_BASE寄存器的第八位置1 ,其余核心稱為AP,並清理AP處理器IA32_APIC_BASE的第8位,然后與BSP處理器開始執行BIOS的代碼。
當我們結果BSP處理的控制權以后,一般要執行以下步驟
1. 初始化BSP寄存器使其進入保護模式
2. 通過ICR寄存器 發送 Delivery Mod = INIT,Destination ShortHand = 11(All Exclude Self) 的INIT廣播指令
3. 延遲至少10MS 后 發送兩次 Delivery Mod = Start Up,Destination ShortHand = 11(All Exclude Self)(SIPI)指令,這兩個指令要間隔10US發送,內容是一樣的,其中vector 需要指定在一個用於AP初始化時運行的代碼地址,這段代碼需要在BSP初始化的時候就配置好
4. 接下來就是AP之行初始化代碼,AP初始化的主要工作就是也將之際進入保護模式,這里有兩種策略,可以所有核心共用頁表,IDT,GDT等,這樣AP的初始化就是簡單的給個寄存器賦值,當然也可以各核心設置各自的數據,這樣就要求各AP進行完整的初始化,
注意: 由於消息時廣播的,所以核心的執行順序是不一定,這里要處理號數據的同步問題,可以使用帶LOCK的匯編之行來進行同步操作
5. BSP等待所有AP初始化完成之后CPU的初始化就完成了。
中斷的處理
APIC的LVT寄存器能產生中斷,其中LVT LINT0 能接受8259控制器的中斷請求,而LVT LINT1能接受NMI中斷請求。
所以中斷有本地中斷和來自system bus上的中斷兩種。根據這兩種來源的不同有不同的處理流程
Fixed 模式下的流程:
IRR,ISR,TMR(tigger Mode Register) 都是255位了,所以可以記錄所有255個中斷的狀態
1. 當中斷發生的時候會設置IRR寄存器的相應位
2. 判斷MASK屏蔽位
3. 判斷優先級
4. 清理IRR位置ISR位提交CPU運行
5. 發送EOI清理ISR相應位。
其他模式流程
其他模式是不判斷優先級也不設置IRR和ISR寄存器的,也不用發送EOI命令
本地:判斷MSAK標志
SYSTEM BUS:啥都不判斷直接執行
注意:他們都能被標志寄存器的IF標志位屏蔽
LVT TIME / LVT ERROR page.663
LVT LINT0 / LINT1
這兩個寄存器連接了8259和NMI口,實際上是只鏈接了BSP芯片,所以只有BSP芯片才能收到這兩個中斷。
觸發模式只有在使用fixed模式時能設置0:edge 1:level,其余模式默認使用edge模式觸發。
Remote IRR 也只有Fixed模式並且觸發模式時:level時可用,用來指示 中斷服務是否運行中,ISR時是否提交。
一般lint0 使用 ExtINT模式。LINT1使用NMI模式。
I/O APIC
8259已經不適合多核的處理器了,所以I/O APIC 取代了8259,I/O APIC能發送指定的中斷到指定的核心上(通過核心的LOCAL APIC)。
直接訪問的寄存器
寄存器映射到了內存地址空間上
訪問方式通過向index寄存器寫入要訪問的地址,然后通過data寄存器獲取值,和IO端口的操作差不多
其中的xx是可變的地址,這個xx記錄在OIC寄存器中
OIC寄存器的地址又位於RCBA寄存器的31FEh位置,而RCBA(root complex base address)寄存器又位於PCI BUS的device 31 設備的0f0偏移處。(關於PCI設備看 下一章節)
間接訪問的寄存器
間接訪問的寄存器就是通過上面直接訪問的寄存器的 index 和 data訪問的寄存器,一共三組
1. ID寄存器,用於唯一標識一個IO APIC,IO APIC可以存在多個?,有一張叫MADT的配置表,這里面有詳細的關系IO APIC的信息,包括多少個IO APIC和對應的直接訪問寄存器的地址,參見文末的文獻
2. version寄存器,主要確定了硬件的連接方式
3. 24個 中斷轉發寄存器,interrupt redirection table 寄存器
他是local APIC中的LVT和ICR寄存器的結合,他首先和LVT寄存器一樣配置了一些中斷觸發時的配置信息,然后每個中斷轉發寄存器都和一條IRQ線相連,當對應的IRQ線觸發中斷的時候,就會向ICR寄存器一樣,將對應的信息發送到對應的核心處,其中IRQ0 是連接着8259芯片的,所以irq0可以使用ExtInt模式,直接使用8259提交的Vector。
中斷過程
IO APIC對中斷的仲裁 是有Local APIC來完成的,IO APIC將中斷提交到Local APIC后他的工作就結束了。所以IO APIC的捉妖責任就是當IRQ先觸發中斷,IO APIC根據對應的interrupt redirection table 的配置向指定的LOCAL APIC提交中斷,之后就全是LOCAL APIC的工作了。
其他文獻
https://wiki.osdev.org/IOAPIC
https://wiki.osdev.org/MADT
https://blog.csdn.net/gaojy19881225/article/details/80018761
http://blog.chinaunix.net/uid-20499746-id-786.html