3. x86 特性標誌

3.1. 介紹

/proc/cpuinfo 中的特性標誌列表並不完整,並且代表了很久以前試圖將特性標誌放在使用者空間易於查詢的地方的一個不幸嘗試。

然而,特性標誌的數量隨著 CPU 的發展而增長,導致 /proc/cpuinfo 難以解析和笨拙。

更重要的是,這些特性標誌甚至不需要在該檔案中,因為使用者空間並不關心它們 - glibc 等已經使用 CPUID 來找出目標機器支援什麼,不支援什麼。

即使它沒有顯示特定的特性標誌 - 儘管 CPU 仍然支援相應的硬體功能並且該 CPU 支援 CPUID 故障 - 使用者空間也可以簡單地探測該特性並確定它是否受支援,而不管它是否在某個地方被宣傳。

此外,這些標誌字串一旦出現在那裡就成為 ABI,並且在沒有任何東西使用它們的情況下永久維護它們是一種巨大的浪費。

因此,當前 /proc/cpuinfo 的用途是顯示核心已啟用支援的特性。例如:CPUID 特性標誌在那裡,核心在啟動時完成了額外的設定,並且該功能已準備好使用。一個完美的例子是“user_shstk”,其中核心中存在額外的程式碼啟用,以支援使用者程式的影子棧。

因此,如果使用者想知道某個特性在給定的系統上是否可用,他們會嘗試在 /proc/cpuinfo 中找到該標誌。如果存在給定的標誌,則意味著

  • 核心足夠了解該特性,擁有一個 X86_FEATURE 位

  • 核心支援它,並且目前正在將其提供給使用者空間或核心的某些其他部分

  • 如果該標誌代表硬體特性,則硬體支援它。

/proc/cpuinfo 中缺少標誌本身對終端使用者來說幾乎沒有任何意義。

一方面,在沒有定義 X86_FEATURE_VAES 的核心上,“vaes”等特性可能完全可供使用者應用程式使用,因此 /proc/cpuinfo 中沒有“vaes”。

另一方面,在非 VAES 硬體上執行的新核心在 /proc/cpuinfo 中也沒有“vaes”。應用程式或使用者無法區分差異。

最終結果是 /proc/cpuinfo 中的 flags 欄位對核心除錯略有幫助,但對其他任何事情都沒有真正幫助。應用程式應該使用 glibc 設施等來查詢 CPU 支援。使用者應該依賴 tools/arch/x86/kcpuid 和 cpuid(1) 等工具。

關於實現,出現在 /proc/cpuinfo 中的標誌在 arch/x86/include/asm/cpufeatures.h 中有一個 X86_FEATURE 定義。這些標誌代表硬體特性以及軟體特性。

如果核心關心一個特性或者 KVM 想要將該特性暴露給 KVM 訪客,那麼只有在訪客需要解析 /proc/cpuinfo 時才應該將其暴露給訪客。如上所述,這種情況極不可能發生。KVM 可以合成 CPUID 位,KVM 訪客可以簡單地查詢 CPUID 並找出虛擬機器監控程式支援什麼,不支援什麼。正如已經宣告的那樣,/proc/cpuinfo 不是無用特性標誌的轉儲場。

3.2. 如何建立特性標誌?

3.2.1. 特性標誌可以從 CPUID 葉的內容派生

這些特性定義按照 CPUID 葉的佈局進行組織,並以偏移量分組在字中,如 cpufeatures.h 中的 enum cpuid_leafs 中對映的那樣(有關詳細資訊,請參閱 arch/x86/include/asm/cpufeatures.h)。如果在 cpufeatures.h 中使用 X86_FEATURE_<name> 定義定義了一個特性,並且如果在執行時檢測到該特性,則標誌將相應地顯示在 /proc/cpuinfo 中。例如,標誌“avx2”來自 cpufeatures.h 中的 X86_FEATURE_AVX2。

3.2.2. 標誌可以來自分散的基於 CPUID 的特性

在稀疏填充的 CPUID 葉中列舉的硬體特性獲得軟體定義的值。儘管如此,仍然需要查詢 CPUID 才能確定是否存在給定的特性。這在 init_scattered_cpuid_features() 中完成。例如,X86_FEATURE_CQM_LLC 定義為 11*32 + 0,並且在執行時在相應的 CPUID 葉 [EAX=f, ECX=0] 位 EDX[1] 中檢查其存在。

分散 CPUID 葉的目的是避免不必要地膨脹 struct cpuinfo_x86.x86_capability[]。例如,CPUID 葉 [EAX=7, ECX=0] 有 30 個特性並且是密集的,但 CPUID 葉 [EAX=7, EAX=1] 只有一個特性,並且會在 x86_capability[] 陣列中浪費 31 位空間。由於每個可能的 CPU 都有一個 struct cpuinfo_x86,因此浪費的記憶體並非微不足道。

3.2.3. 在某些條件下,可以為硬體特性合成建立標誌

條件的示例包括 MSR_IA32_CORE_CAPS 中是否存在某些特性或是否識別出特定的 CPU 型號。如果滿足所需的條件,則透過 set_cpu_cap 或 setup_force_cpu_cap 宏啟用這些特性。例如,如果在 MSR_IA32_CORE_CAPS 中設定了位 5,則將啟用特性 X86_FEATURE_SPLIT_LOCK_DETECT 並且將顯示“split_lock_detect”。只有在 INTEL_XEON_PHI_[KNL|KNM] 處理器上執行時才會顯示標誌“ring3mwait”。

3.2.4. 標誌可以表示純軟體特性

這些標誌不代表硬體特性。相反,它們代表核心中實現的軟體特性。例如,核心頁面表隔離純粹是軟體特性,其特性標誌 X86_FEATURE_PTI 也在 cpufeatures.h 中定義。

3.3. 標誌的命名

指令碼 arch/x86/kernel/cpu/mkcapflags.sh 處理來自 cpufeatures.h 的 #define X86_FEATURE_<name> 並生成 kernel/cpu/capflags.c 中的 x86_cap/bug_flags[] 陣列。生成的 x86_cap/bug_flags[] 中的名稱用於填充 /proc/cpuinfo。 x86_cap/bug_flags[] 中標誌的命名如下

3.3.1. 預設情況下,標誌不會出現在 /proc/cpuinfo 中

預設情況下,/proc/cpuinfo 中省略了特性標誌,因為在大多數情況下,將特性暴露給使用者空間沒有意義。例如,X86_FEATURE_ALWAYS 在 cpufeatures.h 中定義,但該標誌是替代執行時修補功能中使用的內部核心特性。因此,該標誌不會出現在 /proc/cpuinfo 中。

3.3.2. 如果絕對需要,請指定標誌名稱

如果 #define X86_FEATURE_* 行的註釋以雙引號字元(“”)開頭,則雙引號字元內的字串將是標誌的名稱。例如,標誌“sse4_1”來自 X86_FEATURE_XMM4_1 定義後面的註釋“sse4_1”。

在某些情況下,需要覆蓋標誌的顯示名稱。例如,/proc/cpuinfo 是一個使用者空間介面,必須保持不變。如果由於某種原因,X86_FEATURE_<name> 的命名發生變化,則應使用 /proc/cpuinfo 中已使用的名稱覆蓋新命名。

3.4. 發生以下一種或多種情況時,標誌會丟失

3.4.1. 硬體不支援列舉它

例如,當新核心在舊硬體上執行時,或者引導韌體未啟用該特性時。即使硬體是新的,在執行時啟用該特性也可能存在問題,該標誌將不會顯示。

3.4.2. 核心不知道該標誌

例如,當舊核心在新硬體上執行時。

3.4.3. 核心在編譯時停用了對它的支援

例如,如果在構建時未啟用線性地址遮蔽 (LAM)(即,未選擇 CONFIG_ADDRESS_MASKING),則不會顯示標誌“lam”。即使仍然可以透過 CPUID 檢測到該特性,核心也會透過 setup_clear_cpu_cap(X86_FEATURE_LAM) 清除來停用它。

3.4.4. 該特性在啟動時被停用

可以使用命令列引數停用特性,或者因為它未能啟用。可以使用命令列引數 clearcpuid= 來使用 /arch/x86/include/asm/cpufeatures.h 中定義的特性編號停用特性。例如,可以使用 clearcpuid=514 停用使用者模式指令保護。數字 514 是從 #define X86_FEATURE_UMIP (16*32 + 2) 計算得出的。

此外,還存在各種自定義命令列引數,用於停用特定特性。引數列表包括但不限於 nofsgsbase、nosgx、noxsave 等。還可以使用“no5lvl”停用 5 級分頁。

3.4.5. 已知該特性無法正常工作

由於執行時缺少依賴項,已知該特性無法正常工作。例如,如果 XSAVE 特性被停用,AVX 標誌將不會顯示,因為它們依賴於 XSAVE 特性。另一個例子是損壞的 CPU 並且它們缺少微碼補丁。因此,核心決定不啟用某個特性。