10-Armv8-A memory model guide
目錄
- 1 簡介
- 2 內存模型是什么,為什么需要它?
- 3 頁表中的Describing
- 3.1. 屬性的層次
- 3.2. 關閉MMU
- 4 內存訪問排序
- 5 內存類型
- 6 Normal memory
- 6.1. 訪問排序
- 6.2. 重新排序
- 7 Device memory
- 8 Describing the memory type
- 9 Cacheability 和 shareability屬性
- 10 權限屬性
- 10.1. 對非特權數據的特權訪問
- 10.2. 執行權限
- 11 Access Flag :訪問標志
- 11.1. 更新 AF 位
- 11.2. Dirty狀態
- 12 對?和大小端
- 12.1. 對齊
- 12.2. 大小端
- 13 內存別名和不匹配的內存類型
- 14 Stage 1 和 Stage 2 的屬性位的結合
- 14.1 錯誤處理
1 簡介
本文介紹了 Armv8-A 中的內存模型。 它首先解釋描述內存的屬性來自哪里以及它們如何分配給內存區域。 然后介紹可用的不同屬性并解釋內存排序的基礎知識。
此信息對于開發低級代碼(如引導代碼或驅動程序)的任何人都很有用。 它與編寫代碼來設置或管理內存管理單元 (MMU) 的任何人都特別相關。
2 內存模型是什么,為什么需要它?
內存模型是一種組織和定義記憶行為的方式。 它提供了一種結構和一組規則,供您在配置如何在系統中訪問和使用地址或地址區域時遵循。
內存模型提供了可以應用于地址的屬性,它定義了與內存訪問順序相關的規則。 考慮一個帶有地址空間的簡單系統,如下圖所示:
地址空間中內存區域的排列稱為地址映射。 在這里,map包含:
- 內存和外圍設備
- 內存中的代碼和數據中
- 屬于操作系統的資源和屬于用戶應用程序的資源
您希望處理器與外設交互的方式與它應該與內存交互的方式不同。 您通常希望緩存內存,但不想緩存外圍設備。 緩存是將信息的副本從內存存儲到某個位置(稱為緩存)的行為。 緩存更靠近內核,因此內核訪問速度更快。 同樣,您通常希望處理器阻止用戶訪問內核資源。 此圖顯示了具有一些您可能希望應用于內存區域的不同內存屬性的地址映射:
您需要能夠向處理器描述這些不同的屬性,以便處理器適當地訪問每個位置。
3 頁表中的Describing
虛擬地址空間和物理地址空間之間的映射在一組轉換表中定義,有時也稱為頁表。 對于虛擬地址的每個塊或頁面,轉換表提供相應的物理地址和訪問該頁面的屬性。 每個轉換表條目稱為塊或頁描述符。 在大多數情況下,屬性來自這個描述符。 此圖顯示了一個示例塊描述符,以及其中的屬性字段:
重要的比特位:
- SH - The shareable attribute
- AP - The access permission
- UXN and PXN – Execution permissions
3.1. 屬性的層次
某些內存屬性可以在更高級別表的表描述符中指定。 這些是分層屬性。 這適用于訪問權限、執行權限和物理地址空間。
如果這些位被設置,那么它們將覆蓋較低級別的條目,如果這些位被清除,則較低級別的條目將不加修改地使用。 一個例子,使用PXNTable(執行權限)
從 Armv8.1-A 開始,您可以禁用對使用表描述符中的分層屬性設置訪問權限和執行權限的支持。 這是通過相關的 TCR_ELx 寄存器控制的。 禁用時,以前用于分層控制的位可供軟件用于其他用途。
3.2. 關閉MMU
總而言之,地址的屬性來自轉換表。 轉換表位于內存中,用于存儲虛擬地址和物理地址之間的映射。 這些表還包含物理內存位置的屬性。
轉換表由內存管理單元 (MMU) 訪問。 如果 MMU 被禁用會發生什么? 在編寫將在重置后立即運行的代碼時,這是一個需要解決的重要問題。
當 Stage 1 MMU 被禁用時:
- 所有數據訪問都是 Device_nGnRnE。 我們將在本指南的后面解釋這一點。
- 所有指令提取都被視為可緩存的。
- 所有地址都具有讀/寫訪問權限并且是可執行的。
對于虛擬化涵蓋的異常級別,當禁用第 2 階段時,將使用未經修改的第 1 階段屬性。
4 內存訪問排序
在我們的指南 Armv8-A 指令集架構中,我們介紹了簡單順序執行 (SSE)。 SSE 是指令排序的概念模型。內存訪問順序和指令順序是兩個不同但相關的概念。了解它們之間的區別很重要。
SSE 描述了處理器執行指令的順序。總而言之,現代處理器擁有長而復雜的管道。這些流水線通常能夠重新排序指令或并行執行多條指令,以幫助它們最大限度地提高性能。 SSE 意味著處理器必須像處理器一次執行一條指令一樣,按照它們在程序代碼中給出的順序。這意味著硬件對指令的任何重新排序或多次發布必須對軟件不可見。
內存排序是關于內存訪問在內存系統中出現的順序。由于寫緩沖區和緩存等機制,即使指令按順序執行,相關的內存訪問也可能不會按順序執行。這就是為什么即使處理器遵循 SSE 模型來獲取指令,內存排序也是一個需要考慮的重要事項。
5 內存類型
系統中所有未被標記為故障的地址都被分配了一個內存類型。 內存類型是處理器應如何與地址區域交互的高級描述。 Armv8-A中有兩種內存類型:Normal memory和device。
注意:Armv6 和 Armv7 包括第三種內存類型:Strongly Ordered。 在 Armv8 中,這映射到 Device_nGnRnE。
6 Normal memory
普通內存類型用于任何行為類似于內存的東西,包括 RAM、閃存或 ROM。 代碼只能放置在標記為 Normal 的位置。 Normal 通常是系統中最常見的內存類型,如下圖所示:
6.1. 訪問排序
傳統上,計算機處理器按照程序中指定的順序執行指令。事情按照程序中指定的次數發生,并且一次只發生一次。這稱為簡單順序執行 (SSE) 模型。大多數現代處理器似乎都遵循此模型,但實際上,許多優化都已應用并可供您使用,以幫助提高性能。我們將在這里介紹其中的一些優化。
標記為 Normal 的位置在訪問時沒有直接的副作用。這意味著讀取位置只是將數據返回給我們,但不會導致數據更改或直接觸發另一個進程。因此,對于標記為“正常”的位置,處理器可能會:
? 合并訪問。代碼可以多次訪問一個位置,或訪問多個連續的位置。為了效率,允許處理器檢測這些訪問并將這些訪問合并為單個訪問。例如,如果軟件多次寫入一個變量,處理器可能只顯示最后一次寫入內存系統。
? 推測性地執行訪問。允許處理器讀取標記為“正常”的位置,而無需軟件特別請求。例如,處理器可能會根據先前訪問的模式,在軟件請求數據之前使用模式識別來預取數據。該技術用于通過預測行為來加快訪問速度。
? 重新排序訪問。在內存系統中看到的訪問順序可能與軟件發出訪問的順序不匹配。例如,處理器可能會重新排序兩次讀取以允許它生成更有效的總線訪問。對同一位置的訪問不能重新排序,但可能會合并。
想想這些優化,比如允許處理器采用技術來加速性能和提高電源效率的自由。這意味著 Normal 內存類型通常會提供最佳性能。
注意:允許處理器以這些方式進行優化,但這并不意味著它總是如此。給定處理器對這些自由的利用程度取決于其微架構。從軟件的角度來看,您應該假設處理器可能會執行其中的任何一項或全部。
6.2. 重新排序
概括地說,對標記為“正常”的位置的訪問可以重新排序。 讓我們考慮這個示例代碼,其中包含三個內存訪問、兩個存儲和一個加載的序列:
如果處理器對這些訪問進行重新排序,這可能會導致內存中出現錯誤的值,這是不允許的。對于對相同字節的訪問,必須保持順序。處理器需要檢測危險并確保為預期結果正確排序訪問。
這并不意味著本示例沒有優化的可能性。處理器可以將兩個存儲合并在一起,向內存系統呈現一個合并的存儲。它還可以檢測到加載操作來自存儲指令寫入的字節,以便它可以返回新值而無需從內存中重新讀取它。
注意:示例中給出的序列是故意設計來說明這一點的。在實踐中,這些類型的危害往往更加微妙。
還有其他強制排序的情況,例如地址依賴關系。地址依賴是指加載或存儲使用先前加載的結果作為地址。在此代碼示例中,第二條指令取決于第一條指令的結果
此示例還顯示了地址依賴關系,其中第二條指令依賴于第一條指令的結果:
LDR X0,[X1] STR X2,[X5, X0] // The result of the previous load is used to calculate the address.在兩次內存訪問之間存在地址相關性的情況下,處理器必須保持順序。 此規則不適用于控制依賴項。 控制依賴是指使用先前加載的值來做出決定。 此代碼示例顯示了一個負載,然后是一個依賴于負載值的比較和零分支操作:
LDRX0, [X1] CBZ X0, <somewhere_else> STRX2, [X5][Symbol] // There is a control dependency on X0, this does not guarantee ordering.在某些情況下,需要在訪問 Normal 內存或訪問 Normal 和 Device 內存之間強制執行排序。 這可以使用屏障指令來實現
7 Device memory
設備內存類型用于描述外設。 外設寄存器通常稱為內存映射 I/O (MMIO)。 在這里,我們可以看到在我們的示例地址映射中通常標記為設備的內容:
回顧一下,Normal 內存類型意味著訪問沒有副作用。對于設備類型內存,情況正好相反。設備內存類型用于可能產生副作用的位置。
例如,對 FIFO 的讀取通常會導致它前進到下一個數據。這意味著對 FIFO 的訪問次數很重要,因此處理器必須遵守程序指定的內容。設備區域永遠不可緩存。這是因為您不太可能希望緩存對外圍設備的訪問。
不允許對標記為設備的區域進行推測數據訪問。如果在架構上訪問該位置,則處理器只能訪問該位置。這意味著已在架構上執行的指令已訪問該位置。
說明不應放置在標記為設備的區域中。我們建議始終將設備區域標記為不可執行。否則,處理器可能會推測性地從中獲取指令,這可能會導致讀取敏感設備(如 FIFO)出現問題。
注意:這里有一個很容易被忽略的細微差別。將區域標記為設備僅可防止推測性數據訪問。將區域標記為不可執行可防止推測性指令訪問。這意味著,為了防止任何推測性訪問,必須將區域標記為設備和不可執行。
以下對device memory的類型進行總結
- Device-nGnRnE : 處理器必須嚴格按照代碼中內存訪問來進行、必須嚴格執行program order(無需重排序)、寫操作的ack必須來自最終的目的地
- Device-nGnRE : 處理器必須嚴格按照代碼中內存訪問來進行、必須嚴格執行program order(無需重排序)、寫操作的ack可以來自中間的write buffer
- Device-nGRE : 處理器必須嚴格按照代碼中內存訪問來進行、內存訪問指令可以進行重排、寫操作的ack可以來自中間的write buffer
- Device-GRE : 處理器對多個memory的訪問是否可以合并、內存訪問指令可以進行重排、寫操作的ack可以來自中間的write buffer
?Gathering和non Gathering(G or nG):表示對多個memory的訪問是否可以合并,如果是nG,表示處理器必須嚴格按照代碼中內存訪問來進行,不能把兩次訪問合并成一次。例如:代碼中有2次對同樣的一個地址的讀訪問,那么處理器必須嚴格進行兩次read transaction
?Reordering(R or nR):表示是否允許處理器對內存訪問指令進行重排。nR表示必須嚴格執行program order
?Early Write Acknowledgement(E or nE):PE訪問memory是有問有答的(更專業的術語叫做transaction),對于write而言,PE需要write ack操作以便確定完成一個write transaction。為了加快寫的速度,系統的中間環節可能會設定一些write buffer。nE表示寫操作的ack必須來自最終的目的地而不是中間的write buffer
8 Describing the memory type
9 Cacheability 和 shareability屬性
-
如果將block的內存屬性配置成Non-cacheable,那么數據就不會被緩存到cache,那么所有observer看到的內存是一致的,也就說此時也相當于Outer Shareable。
其實官方文檔,也有這一句的描述:
在B2.7.2章節 “Data accesses to memory locations are coherent for all observers in the system, and correspondingly are treated as being Outer Shareable” -
如果將block的內存屬性配置成write-through cacheable 或 write-back cacheable,那么數據會被緩存cache中。write-through和write-back是緩存策略。
-
如果將block的內存屬性配置成 non-shareable, 那么core0訪問該內存時,數據緩存的到Core0的L1 d-cache 和 cluster0的L2 cache,不會緩存到其它cache中
-
如果將block的內存屬性配置成 inner-shareable, 那么core0訪問該內存時,數據只會緩存到core 0和core 1的L1 d-cache中, 也會緩存到clustor0的L2 cache,不會緩存到clustor1中的任何cache里。
-
如果將block的內存屬性配置成 outer-shareable, 那么core0訪問該內存時,數據會緩存到所有cache中
以下也總結了一下shareable、cacheable屬性對緩存策略的影響:
| non-shareable | 數據不會緩存到cache (對于觀察則而言,又相當于outer-shareable) | core訪問該內存時,數據只緩存的到Core的 cache 中,不會緩存到其它cache中 | 同左側 |
| inner-shareable | 數據不會緩存到cache (對于觀察則而言,又相當于outer-shareable) | core訪問該內存時,數據只會緩存到core的cache和 cluster的 cache中,該地址的TAG也不會存到snoop filter中,即不會被其它ACE Master snoop | 同左側 |
| outer-shareable | 數據不會緩存到cache (對于觀察則而言,又相當于outer-shareable) | core訪問該內存時,數據只會緩存到core的cache和 cluster的 cache中,該地址的TAG會存到snoop filter中,會被其它ACE Master snoop | 同左側 |
10 權限屬性
訪問權限 (AP) 屬性控制是否可以讀取和寫入位置,以及需要什么權限。下表顯示了 AP 位設置:
如果訪問破壞了指定的權限,例如對只讀區域的寫入,則會生成異常(標記為權限錯誤)。
10.1. 對非特權數據的特權訪問
標準權限模型是特權較高的實體可以訪問屬于特權較低的實體的任何內容。換句話說,操作系統 (OS) 可以看到分配給應用程序的所有資源。例如,hypervisor可以查看分配給虛擬機的所有資源。這是因為在更高的異常級別執行意味著特權級別也更高。
然而,這并不總是可取的。惡意應用程序可能會試圖欺騙操作系統代表應用程序訪問數據,而應用程序不應該看到這些數據。這需要操作系統檢查系統調用中的指針。
Arm 架構提供了幾個控件來簡化此操作。首先,有 PSTATE.PAN(從不特權訪問)位。
當該位置位時,從 EL1(或 E2H==1 時的 EL2)加載和存儲到非特權區域將產生異常(Permission Fault),如下圖所示:
(注意是在 Armv8.1?A 中添加的PAN。)
PAN 允許對非特權數據的意外訪問被捕獲。例如,操作系統執行訪問認為目的地是特權的。事實上,目的地是沒有特權的。這意味著操作系統的期望(目的地是特權)和現實(目的地是非特權的)之間存在不匹配。這可能是由于編程錯誤,也可能是系統受到攻擊的結果。在任何一種情況下,PAN 都允許我們在訪問發生之前對其進行捕獲,從而確保安全操作。
有時操作系統確實需要訪問非特權區域,例如,寫入應用程序擁有的緩沖區。為了支持這一點,指令集提供了 LDTR 和 STTR 指令。
LDTR 和 STTR 是非特權加載和存儲。即使在 EL1 或 EL2 由操作系統執行時,它們也會根據 EL0 權限檢查進行檢查。因為這些是明確的非特權訪
問,所以它們不會被 PAN 阻止,如下圖所示:
這允許操作系統區分旨在訪問特權數據的訪問和預期訪問非特權數據的訪問。這也允許硬件使用該信息來檢查訪問。
注意: LDTR 中的 T 代表翻譯。這是因為第一個支持虛擬到物理轉換的 Arm 處理器只針對用戶模式應用程序,而不是操作系統。為了讓操作系統訪問應用程序數據,它需要一個特殊的負載,一個帶有翻譯的負載。
當然,今天所有軟件都可以看到虛擬地址,但名稱仍然存在。
10.2. 執行權限
除了訪問權限,還有執行權限。這些屬性允許您指定不能從地址獲取指令:
- UXN. User (EL0) Execute Never (Not used at EL3, or EL2 when HCR_EL2.E2H==0) :EL0不允許執行
- PXN. Privileged Execute Never (Called XN at EL3, and EL2 when HCR_EL2.E2H==0):特權程序不允許執行
有單獨的 Privileged 和 Unprivileged 位,因為應用程序代碼需要在用戶空間 (EL0) 中可執行,但絕不應使用內核權限 (EL1/EL2) 執行,如下圖所示:
該架構還在系統控制寄存器 (SCTLR_ELx) 中提供控制位,以使所有可寫地址都不可執行。
具有 EL0 寫入權限的位置永遠不能在 EL1 上執行。注意:請記住,Arm 建議始終將設備區域標記為從不執行 (XN)。
11 Access Flag :訪問標志
您可以使用訪問標志 (AF) 位來跟蹤轉換表條目所覆蓋的區域是否已被訪問。你可以設置AF 位:
- AF=0. Region not accessed.
- AF=1. Region accessed.
AF 位對操作系統很有用,因為您可以使用它來識別哪些頁面當前未被使用并且可以被調出(從 RAM 中刪除)。
注意:訪問標志通常不用于裸機環境,您可以使用預先設置的 AF 位生成表。
11.1. 更新 AF 位
當使用 AF 位時,會創建轉換表,且 AF 位最初是清零的。當一個頁面被訪問時,它的 AF 位被置位。軟件可以解析表格去檢查 AF 位是設置還是清除。AF=0表示該頁面沒用被訪問,將其頁面換出去可能是更好選擇.
有兩種方法可以在訪問時設置 AF 位:?
- 軟件更新:訪問頁面會導致同步異常(Access Flag fault)。在異常處理程序中,軟件負責設置相關轉換表條目中的AF位并返回。
- 硬件更新:訪問頁面會導致硬件自動設置 AF 位,而無需生成異常,此行為需要啟用并已添加到 Armv8.1?A 中。
11.2. Dirty狀態
Armv8.1?A 引入了處理器管理塊或頁面的臟狀態的能力。Dirty狀態記錄塊或頁是否已被寫入。這很有用,因為如果塊或頁面被調出,Dirty狀態會告訴管理軟件是否需要將 RAM 的內容寫出到存儲中。
例如,讓我們考慮一個文本文件。該文件最初從磁盤(閃存或硬盤驅動器)加載到 RAM 中。當它稍后從內存中刪除時,操作系統需要知道 RAM 中的內容是否比磁盤上的內容更新。如果 RAM 中的內容更新,則需要更新磁盤上的副本。如果不是,則可以刪除 RAM 中的副本。
當啟用Dirty狀態管理時,軟件最初創建轉換表條目,并將訪問權限設置為只讀,并設置 DBM(Dirty位修飾符)位。如果該頁面被寫入,硬件會自動將訪問權限更新為讀寫。
將 DBM 位設置為 1 會更改訪問權限位(AP[2] 和 S2AP[1])的功能,以便記錄Dirty狀態而不是記錄訪問權限。這意味著當 DBM 位設置為 1 時,訪問權限位不會導致訪問錯誤。
注意:不使用硬件更新選項也可以獲得相同的結果。該頁面將被標記為只讀,導致第一次寫入時出現異常(權限錯誤)。異常處理程序會手動將頁面標記為可讀寫,然后返回。如果軟件想要進行寫時復制,則可能仍會使用此方法。
12 對?和大小端
12.1. 對齊
如果地址是元素大小的倍數,則訪問被描述為對?。
對于 LDR 和 STR 指令,元素大小是訪問的大小。例如,一條 LDRH 指令加載一個 16 位的值,并且必須來自一個 16 位的倍數的地址才能被視為對?。
LDP 和 STP 指令分別加載和存儲一對元素。要對?,地址必須是元素大小的倍數,而不是兩個元素的組合大小。例如:LDP X0, X1, [X2] .
此示例加載兩個 64 位值,因此總共 128 位。 X2 中的地址需要是 64 位的倍數才能被視為對? . 同樣的原則也適用于向量加載和存儲。
當地址不是元素大小的倍數時,訪問是不對?的。允許對標記為正常的地址進行未對?的訪問,但不允許對設備區域進行非對?訪問。對設備區域的未對?訪問將觸發異常(對?錯誤)。
通過設置 SCTLR_ELx.A 可以捕獲對標記為 Normal 的區域的未對?訪問。如果該位置位,對正常區域的未對?訪問也會產生對?錯誤。
12.2. 大小端
在 Armv8?A 中,指令提取始終被視為小端
對于數據訪問,是否支持 little?endian 和 big?endian 由實現定義。如果只支持一個,則由實現定義支持哪一個。
對于同時支持大端和小端的處理器,字節序是按異常級別配置的。
注意:如果您不記得 IMPLEMENTATION DEFINED 的定義,請閱讀介紹 Arm 架構。Arm Cortex?A 處理器同時支持大端和小端。
13 內存別名和不匹配的內存類型
當物理地址空間中的給定位置有多個虛擬地址時,這稱為別名。
屬性基于虛擬地址。這是因為屬性來自翻譯表。當一個物理位置有多個別名時,所有虛擬別名都必須具有兼容的屬性,這一點很重要。我們將兼容描述為:
- Same memory type, and for Device the same sub-type
- For Normal locations, the same cacheability and shareability
如果屬性不兼容,則內存訪問的行為可能與您預期的不同,這可能會影響性能。
此圖顯示了別名的兩個示例。位置 A 的兩個別名具有兼容的屬性。這是推薦的方法。位置 B 的兩個別名具有不兼容的屬性(Normal 和 Device),這會對一致性和性能產生負面影響:
Arm 強烈建議軟件不要將不兼容的屬性分配給同一位置的不同別名。
14 Stage 1 和 Stage 2 的屬性位的結合
使用虛擬化時,虛擬地址要經過兩個轉換階段。一個階段在操作系統的控制下,另一階段在管理程序的控制下。 Stage 1 和 Stage 2 表都包含屬性。這些是如何結合的?
下圖顯示了一個示例,其中階段 1 將位置標記為device,但相應的階段 2 轉換標記為normal。結果類型應該是什么?
在 Arm 架構中,默認是使用最嚴格的類型。在此示例中,Device 比 Normal 更嚴格。因此,生成的類型是 Device。
對于類型和可緩存性,附加控件 (HCR_EL2.FWB) 允許覆蓋此行為。設置 FWB 后,Stage 2 可以覆蓋 Stage 1 的類型和可緩存性設置,而不是組合行為。
(注意: HCR_EL2.FWB 是在 Armv8.4?A 中引入的。)
14.1 錯誤處理
我們先看一下 下圖的兩個示例;
在這兩個示例中,結果屬性都是 RO(只讀)。如果軟件要寫入位置,則會產生錯誤(權限錯誤)。但是,在第一種情況下,這是一個階段 1 故障,而在第二種情況下,這將是一個階段 2 故障。在該示例中,階段 1 故障將發送到 EL1 的操作系統,而階段 2 故障將發送到 EL2 并由管理程序處理。
最后,我們來看一個 Stage 1 和 Stage 2 屬性相同的例子:
在這里,結果屬性很簡單。是 R0。但如果軟件寫入該位置,是否會產生階段 1 或階段 2 故障?答案是第 1 階段。如果第 1 階段和第 2 階段會引發不同的故障類型,這也是正確的。階段 1 故障總是優先于階段 2 故障。
總結
以上是生活随笔為你收集整理的10-Armv8-A memory model guide的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 09-LearnTheArchitect
- 下一篇: 编写TA链接静态库的方法