您好,登錄后才能下訂單哦!
本篇內容主要講解“如何使用PAE分頁模式”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何使用PAE分頁模式”吧!
前言
物理地址擴展 Physical Address Extension(縮寫PAE)。
解釋:正常情況32位CPU可以存取4G的物理內存,但在現實中,實際情況是內存的發展速度大于CPU的發展速度,所以才有了讓32位CPU存取超過4G內存的需求。實現方式就是給CPU增加了4根地址線,達到36根,于是可尋址64G大小的內存。
Intel到目前為止設計了4種分頁模式,分別是:32位、PAE、4-level、5-level這四種模式。本文主要講了Intel是如何設計PAE模式的頁表相關數據結構。
“Chapter 4 Paging”部分,是筆者在閱讀Intel手冊時做的翻譯筆記,對應原文的 Volume3: Chapter 4 Paging。翻譯有不準確的地方,以原文為準。可以在這里下載手冊。Intel? 64 and IA-32 Architectures Software Developer Manuals
“實驗題”部分,實現了一個可以瀏覽進程頁表的小工具,可以在這里查看源碼。github源碼
Chapter 4 Paging
4.1 分頁模式和控制位
分頁控制有關的寄存器
CR0 標志位:WP(bit 16)、PG(bit 31)
CR4 標志位:PSE(bit 4)、PAE(bit 5)、PGE(bit 7)、PCIDE(bit 17)、SMEP(bit 20)、SMAP(bit 21)、PKE(bit 22)、CET(bit 23)、PKS(bit 24)
IA32_EFER MSR 標志位:LEM(bit 8)、NXE(bit 11)
EFLAGS 標志位:AC(bit 18)
軟件(應該指操作系統)如何啟用分頁功能:確保CR3中是分頁結構表的物理內存地址,然后使用MOV指令置CR0.PG位。
4.1.1 四種分頁模式
本節內容:根據 CR0.PG、CR4.PAE、CR4.LA57和IA32_EFER.LME,判斷是否啟用分頁,以及是開啟的什么分頁模式。
CR0.PG = 0 表示未啟用分頁模式,此時會把線性地址直接當作物理地址使用。
CR0.PG = 1 表示啟用分頁模式。 Paging can be enabled only if protection is enabled (CR0.PE = 1)。此時由CR4.PAE、CR4.LA57、和IA32_EFER.LME決定啟用哪種分頁模式。
32-bit paging 模式 CR4.PAE = 0 (詳見4.3節)
PAE paging 模式 CR4.PAE = 1 and IA32_EFER.LME = 0 (詳見4.4節)
4-level paging 模式 CR4.PAE = 1, IA32_EFER.LME = 1, and CR4.LA57 = 0 (4表示4層表尋找Pages頁,詳見4.5節)
5-level paging 模式 CR4.PAE = 1, IA32_EFER.LME = 1, and CR4.LA57 = 1 (5表示5層表尋找Pages頁,詳見4.5節)
32-bit 和PAG模式用于保護模式32位模式,IA32_EFER.LME = 0
4-level和5-level模式用于64位模式(IA-32e表示64位模式),IA32_EFER.LME = 1
四種模式的區別:
線性地址寬度
物理地址寬度
分頁大小:4K、2M、4M、1G
支持execute-disable,數據執行保護
支持PCIDs,操作系統可以啟用緩存線性地址的功能,不是很懂,先放放
支持 protection keys,不知道干嘛的
4-level 和 5-level模式還有兩個子模式:
兼容模式:兼容32位的模式
64位模式:雖然是64位線性地址,但是實際上4-level只使用了低48位做線性地址,而5-level頁只使用低57位,物理地址線都只有52根。
4.1.2 啟用和切換分頁模式
本節內容:根據 CR0.PG、CR4.PAE、CR4.LA57和IA32_EFER.LME,如何啟用并且切換不同的分頁模式
講解在不同模式之間切換的規則,這部分不感興趣,略過。
4.1.3 分頁屬性控制
本節內容:通過CR0.WP、CR4.PSE、CR4.PGE、CR4.PCIDE、CR4.SMEP、CR4.SMAP、CR4.PKE、CR4.CET、CR4.PKS和IA32_EFER.NXE控制在不同分頁模式下Pages的屬性
CR0.WP
寫入數據保護標志位:
等于0, supervisor-mode(應該指0環應用程序)可以向具有只讀屬性的頁寫數據;等于1,則不可以操作。
這個標志位對User-mode(應該時3環應用程序)沒有影響,因為只要是只讀屬性的頁,3環程序都不能寫。
(4.6節有更詳細的介紹)
CR4.PSE
是否啟用4M分頁:
只對32-bit paging 模式作用,等于0,表示分頁大小只能是4K;等于1,可以選擇4K或4M分頁。其它三種模式的分頁大小可以自由選擇,不受該位的控制。(4.3節有更詳細的介紹)
CR4.PGE
是否啟用全局共享頁:
等于0,不同進程間不會共享物理內存;等于1,進程間可以共享物理內存。(可能翻譯的不對,帖上原文)
CR4.PGE enables global pages. If CR4.PGE = 0, no translations are shared across address spaces; if CR4.PGE = 1,
specified translations may be shared across address spaces.(4.10.2.4節有更詳細的介紹)
CR4.PCIDE
啟用process-context identifiers,對4-level 和 5-level模式作用。
PCIDs邏輯處理器緩存多個線性地址。(4.10.1節有更詳細的介紹)
CR4.SMEP
If CR4.SMEP = 1, software operating in supervisor mode cannot fetch instructions from linear addresses that are accessible in user mode.(4.6節有更詳細的介紹)
CR4.SMAP
If CR4.SMAP = 1, software operating in supervisor mode cannot access data at linear addresses that are accessible in user mode. Software can override this protection by setting EFLAGS.AC.
CR4.PKE and CR4.PKS
4-level和5-level模式將每一個線性地址與保護key相關聯。
CR4.PKE=1時,PKRU寄存器表示,user-mode的線性地址所關聯的保護key,是否可讀或可寫。
CR4.PKS=1時, the IA32_PKRS MSR does the same for supervisor-mode linear addresses.
CR4.CET
這個好難理解。
If CR4.CET = 1, certain memory accesses are identified as shadow-stack accesses and certain linear addresses translate to
shadow-stack pages.
IA32_EFER.NXE
執行保護,對4-level 和 5-level模式作用。如果設為1,則不能執行指令,但是可以讀指令。
4.1.4 Enumeration of Paging Features by CPUID
這部分保護標志位的意義和用法。
PSE: page-size extensions for 32-bit paging.
PAE: physical-address extension.
PGE: global-page support.
PAT: page-attribute table.
PSE-36: page-size extensions with 40-bit physical-address extension.
PCID: process-context identifiers.
SMEP: supervisor-mode execution prevention.
SMAP: supervisor-mode access prevention.
PKU: protection keys for user-mode pages.
OSPKE: enabling of protection keys for user-mode pages.
CET: control-flow enforcement technology.
LA57: 57-bit linear addresses and 5-level paging.
PKS: protection keys for supervisor-mode pages.
NX: execute disable.
Page1GB: 1-GByte pages.
LM: IA-32e mode support.
CPUID.80000008H:EAX[7:0] reports the physical-address width supported by the processor.
CPUID.80000008H:EAX[15:8] reports the linear-address width supported by the processor.
4.2 分層頁表結構概覽
不同模式使用的頁表結構是不一樣的,有的只是用2張表,有的則更多。
每張表的大小都是4096字節,對于32-bit模式,每一項是4字節,共1024項;對于其它三種模式,每一項是8字節,每張表512項。PAE模式是個特例,它的第一張表只有4項。
不同模式對線性地址的處理是不一樣的,詳細章節會講。
這里提出了兩個名詞:page frame 線性地址中用來尋址的部分;page offset 線性地址中用作偏移的部分。
每一項中的地址部分都是物理內存地址。
第一張表總是保存在CR3寄存器中。
四種模式解析線性地址(4K分頁舉例):
32-bit模式:32:22(10位)表1下標,21:12(10位)表2下標,11:0(12位)用作分頁內的偏移量。
PAE模式:31:30(2位)表1下標,29:21(9位)表2下標,20:12(9位)表3下標,11:0(12位)分頁內的偏移量。
4-level模式:每張表都是512項,總共4張表,所以47:39、38:30、29:21、20:12對應4張表的下標,11:0(12位)分頁內的偏移量。
5-level模式:使用5張表,56:48是第一張表下標,其余的根4-level模式的一樣。
總結上面的解析線性地址過程,其實就是查表,查表,再查表。有些情況查表過程可能會中斷,比如說遇到缺頁異常時。
還有兩種特殊的情況:
查表過程中,剩余沒有解析的線性地址寬度超過12位,如果當前表項的屬性位bit 7(PS位—page size)等于1,當前項就是最后的頁
查表過程中,剩余沒有解析的線性地址寬度等于12位,bit 7不再是PS位,另有它用,當前項則指向另一個表
對上述的第一種情況舉例:
32-bit模式,如果分頁大小是4M(CR4.PSE=1),那么表1就是存儲的頁,總共1024項,1024*4M=4G,正好尋址4G空間,表2就不存在了。再比如,PAE模式下,如果分頁大小是2M,查到第2張表就時頁了,此時沒有了表3。
不同的表結構都有名字,參考下圖:
對于上面的縮寫,通常我們有下面的叫法:
PTE 頁表,存放的每一項是最終的Pages物理地址,32位的分頁大小有4K、2M、4M,64位則能擴展到1G大小
PDE 頁目錄表,存放的每一項是PTE
PDPTE頁目錄指針表,PAE模式只有4項,4-level和5-level模式都是填滿的512項,每一項指向一個頁目錄表。
PML4E和PML5E暫時也不知道,實際上用法類似,各增加一層表的
4.4 PAE 分頁模式
寄存器標志位:
CR0.PG = 1
CR4.PAE = 1
IA32_EFER.LME = 0
4.4.1 PDPTE寄存器
CR3指向 page-directory-pointer表。其中:
4:0 沒有用
31:5 存放指向表的物理地址
63:52 沒有用
page-dirctory-pointer表中由4個8字節的大小的PDPTEs組成,每個可尋址1-GByte大小的線性地址空間。
對應這4個PDPTE,邏輯處理器內部維護著與之對應的四個non-architectural寄存器,分別是PDPTE0、PDPTE1、PDPTE2、PDPTE3。出現下面幾種情況時,邏輯處理器會重新加載內存中的PDPTEs到4個寄存器:
如果使用MOV to CR0或MOV to CR4指令修改了這些寄存器中的標志位(CR0.CD, CR0.NW, CR0.PG, CR4.PAE, CR4.PGE, CR4.PSE, or CR4.SMEP),PDPTESs會從CR3中執行的地址重新加載
在PAE分頁模式,如果下執行MOV to CR3指令,PDPTESs會從CR3中執行的地址重新加載
在PAE分頁模式,如果CR3中的值被task switch修改,會從新的CR3中加載PDPTEs
下表中說明了PDPTE的結構:
位 用途
0(P) Present,等于1表示此項指向一個頁目錄表,等于0則此項不包含頁目錄表
2:1 保留位,必須填0
3(PWT) Page-level write-through,用來間接確定訪問頁目錄表所需要的內存屬性(詳見4.9節)
4(PCD) Page-level cache disable,用來間接確定訪問頁目錄表所需要的內存屬性(詳見4.9節)
8:5 保留位,必須填0
11:9 忽略
(M-1):12 指向頁目錄表的4K對齊的物理地址
63:M 保留位,必須填0
注:M表示MAXPHYADDR,最大物理地址寬度,該手冊中是52根地址線。
4.4.2 線性地址轉物理地址
PAE模式下,可以使用4K或2M分頁。
如何確定是4K分頁還是2M分頁:
線性地址的31:30(2位)用來選擇4個PDPTE寄存器中一個,稱為PDPTEi,i等于這兩位的值。每個PDPTEi可以對應1G大小的線性地址空間。如果PDPTEi的P位是0,那么這一項就是無效的,就是說該項不包含對應的頁目錄表,同時會產生一個page-fault異常(詳見4.7節)。
如果PDPTEi的P位是1,那么其51:12(40位)指向了頁目錄表的物理地址。每個頁目錄表由512項8字節的PDEs組成。
意思就是線性地址的前兩位用于確定頁目錄指針表(PDPTE)的下標,找到4個中的一個頁目錄表。
頁目錄表,每個頁面錄表含有512個表項,每個表項8字節大小,指向一個頁表或者指向一個2M的頁,這要根據下面介紹的PS標志位來確定。
如果PDE’s頁目錄表項的PS位等于1,說明這一項指向的就是一個最終的2M的分頁,物理地址由該項的51:21(31位)和20:0(21位)確定。物理地址線最大52根,有的CPU是36根,那么高位就是35:21(15位),有的CPU有52根的,高位才是51:21。
如果PDE’s頁目錄表項的PS位等于0,說明這一項指向的是頁表。頁表的物理地址跟上面的情況一樣也分為36根物理地址線和52根物理地址線。
如果是2M分頁,那么線性地址后30位中,29:21(9位)用來作為表2(頁目錄表)的下標索引,來確定最終的頁地址;20:0(21位)作為2M頁內的偏移量。
如果是4K分頁,那么線性地址后30位中,29:21(9位)作為表2(頁目錄表)的下標索引,20:12(9位)作為表3(頁表)的下標索引,11:0(12位)作為4K頁內的偏移量。
如果頁目錄表項(PDE)或者頁表項(PTE)的P位(bit 0)等于0,或者置位它們的任意一位保留位,這個表項將會失效,并且會引起page-fault異常(詳見4.7節)。
PAE模式中的保留位有下面幾個:
If the P flag (bit 0) of a PDE or a PTE is 1, bits 62:MAXPHYADDR are reserved.
If the P flag and the PS flag (bit 7) of a PDE are both 1, bits 20:13 are reserved.
If IA32_EFER.NXE = 0 and the P flag of a PDE or a PTE is 1, the XD flag (bit 63) is reserved
If the PAT is not supported:
— If the P flag of a PTE is 1, bit 7 is reserved.
— If the P flag and the PS flag of a PDE are both 1, bit 12 is reserved.
下圖是4K分頁的線性地址轉物理地址的過程示意圖。
下圖是2M分頁的線性地址轉物理地址的過程示意圖。
下圖是PDPTE、PDE-2M、PDE-4K、PTE各表項結構的示意圖:
手冊中給出了PDE-2M、PDE、PTE結構的詳細解釋,是下面三個表:
首先是PDE-2M表項結構解析:Table 4-9. Format of a PAE Page-Directory Entry that Maps a 2-MByte Page
位 用途
0(P) Present,必須等于1,表示該項指向一個2M分頁
1(R/W) Read/Write,如果為0,不允許向2M的分頁寫數據
2(U/S) User/supervisor,權限標志位,等于0則3環程序不能訪問2M的分頁
3(PWT) Page-level write-through,用來間接確定訪問2M分頁所需要的內存屬性
4(PCD) Page-level cache disable,用來間接確定訪問2M分頁所需要的內存屬性
5(A) Accessed,標志這個2M分頁是否已經被訪問過
6(D) Dirty,標志這個2M分頁是否已經被寫入過數據
7(PS) Page size,分頁大小標志位,必須等于1(否則這項的意義變為指向4K分頁的頁表)
8(G) Gloabe,如果CR4.PGE等于1,該位用來確定這個頁是否是全局共享的頁
11:9 Ignored
12(PAT) 如果支持PAT,則用來間接確定訪問2M分頁所需要的內存屬性
20:13 Reserved,保留位,必須是0
(M-1):21 2M分頁的物理地址,36根物理線是35:21(15位),有的CPU有52根線的,是51:21
62:M Reserved,保留位,必須是0
63(XD) 如果 IA32_EFER.NXE標志位等于1,則該頁中的數據不可執行(從該頁中獲取指令將被禁止); IA32_EFER.NXE等于0,該位位保留位,必須填0
PDE表項結構解析:Table 4-10. Format of a PAE Page-Directory Entry that References a Page Table
位 用途
0(P) Present,必須等于1,表示該項指向一個頁表
1(R/W) Read/Write,如果為0,不允許向其包含的1024個頁(共2M)寫數據
2(U/S) User/supervisor,權限標志位,等于0則3環程序不能訪問其包含的1024個頁(共2M)
3(PWT) Page-level write-through,用來間接確定訪問頁表所需要的內存屬性
4(PCD) Page-level cache disable,用來間接確定訪問頁表所需要的內存屬性
5(A) Accessed,標志這個表項已經被訪問了(被線性地址翻譯器訪問了)
6(D) Ignored
7(PS) Page size,分頁大小標志位,必須等于0
11:8 Ignored
(M-1):12 頁表的物理地址,36根物理線的CPU是25:12(24位),有的CPU有52根線的,是51:12(40位)
62:M Reserved,保留位,必須是0
63(XD) 如果 IA32_EFER.NXE標志位等于1,則該項指向的頁表中的所有頁(1024個頁)中的數據不可執行; IA32_EFER.NXE等于0,該位位保留位,必須填0
PTE表項結構解析:Table 4-11. Format of a PAE Page-Table Entry that Maps a 4-KByte Page
位 用途
0(P) Present,必須等于1,表示該項指向一個4K分頁
1(R/W) Read/Write,如果為0,不允許向4K的分頁寫數據
2(U/S) User/supervisor,權限標志位,等于0則3環程序不能訪問4K的分頁
3(PWT) Page-level write-through,用來間接確定訪問4K分頁所需要的內存屬性
4(PCD) Page-level cache disable,用來間接確定訪問4K分頁所需要的內存屬性
5(A) Accessed,標志這個4K分頁是否已經被訪問過
6(D) Dirty,標志這個4K分頁是否已經被寫入過數據
7(PAT) 如果支持PAT,則用來間接確定訪問4K分頁所需要的內存屬性
8(G) Gloabe,如果CR4.PGE等于1,該位用來確定這個頁是否是全局共享的頁
11:9 Ignored
(M-1):12 4K分頁的物理地址,36根物理線的CPU是25:12(24位),有的CPU有52根線的,是51:12(40位)
62:M Reserved,保留位,必須是0
63(XD) 如果 IA32_EFER.NXE標志位等于1,則該頁中的數據不可執行(從該頁中獲取指令將被禁止); IA32_EFER.NXE等于0,該位位保留位,必須填0
實驗題:實現頁表瀏覽工具
0. 查看源碼
github源碼
1. 環境設置
測試操作系統:winxp sp3
開發環境配置:
VS2015 實現MFC對話框工程
WDK 7.1.0開發winxp驅動
如何啟用PAE模式?
讀者可以檢索關鍵字“winxp pae”,會搜索到很多關于winxp系統啟用PAE模式的教程。
2. MFC瀏覽工具的代碼實現
工具的使用效果如下圖:
第一欄,顯示系統中的所有進程,通過調用CreateToolhelp32Snapshot 實現遍歷進程。
第二欄,展示頁目錄指針表的表項(PDPTE),選中上級目錄的某個進程后,MFC程序調用驅動程序接口,加載該進程的PDPTE,最后展示有效表項。
第三欄,展示頁目錄表的表項(PDE),選中上級目錄的某個PDPTE表項后,與上述類似流程。
第四欄,展示頁表項(PTE),流程與上述類似。
關于加載進程頁表速度慢的問題:
對于PAE模式,進程的頁表項數量總共有:4 PDPTE x 512 PDE x 512 PTE
在最初版本的工具設計中,使用3層循環來遍歷頁表項,一次性加載某個進程的所有頁表。但是出現加載速度慢的問題。所以改進了加載方式,變成根據索引加載一個PDPTE表項下的512項PDE,或者加載一個PDE表項下的512個PTE,這樣改進之后,加載速度明顯改善。
代碼:定義保存頁表數據的結構體
namespace PAEPaging
{
struct PDE_T
{
PDE val;
PTE PTEs[512];
BOOL LoadedFlag;
};
struct PDPTE_T
{
PDPTE val;
PDE_T PDEs[512];
BOOL LoadedFlag;
};
struct PDPTE_TT
{
ULONG cr3Val;
PDPTE_T PDPTEs[4];
};
}
代碼:調用驅動接口,加載頁表的實現
BOOL COperateKernel::LoadPages(DWORD dwPID, __in DWORD dwPDPTEIdx, __in DWORD dwPDEIdx, PAEPaging::PDPTE_TT* tt)
{
...
if (DeviceIoControl(hFile, IOCTL_GET_PAGES_PAE, inBuff, sizeof(inBuff), outBuff, sizeof(outBuff),
&dwBytesRead, NULL))
{
DWORD dwOutBuffOffset = 0;
// 加載 PDPTE 表
if (dwPDPTEIdx == -1 && dwPDEIdx == -1)
{
for (int i = 0; i < 4; i++)
{
PAEPaging::PDPTE *pPDPTE = (PAEPaging::PDPTE*)((DWORD)outBuff + dwOutBuffOffset);
tt->PDPTEs[i].val = *pPDPTE;
dwOutBuffOffset += sizeof(PAEPaging::PDPTE);
}
}
// 加載 PDE 表
else if (dwPDPTEIdx != -1 && dwPDEIdx == -1)
{
for (int i = 0; i < 512; i++)
{
PAEPaging::PDE *pPDE = (PAEPaging::PDE*)((DWORD)outBuff + dwOutBuffOffset);
tt->PDPTEs[dwPDPTEIdx].PDEs[i].val.uint64 = pPDE->uint64;
dwOutBuffOffset += sizeof(PAEPaging::PDE);
}
}
// 加載 PTE 表
else if (dwPDPTEIdx != -1 && dwPDEIdx != -1)
{
for (int i = 0; i < 512; i++)
{
PAEPaging::PTE *pPTE = (PAEPaging::PTE*)((DWORD)outBuff + dwOutBuffOffset);
tt->PDPTEs[dwPDPTEIdx].PDEs[dwPDEIdx].PTEs[i] = *pPTE;
dwOutBuffOffset += sizeof(PAEPaging::PTE);
}
}
bRet = TRUE;
}
...
}
3. 驅動代碼的實現
NTSTATUS GetProcessPages(PVOID pInBuff, PVOID pOutBuff, ULONG nOutLength, ULONG* nBytes)
{
...
// 加載 PDPTE 表
if (nPDPTEIdx == -1 && nPDEIdx == -1)
{
for (int i = 0; i < 4; i++)
{
ULONG64 paPDPTE = paCR3 + 8 * i;
ULONG64 PDPTE = GetQuadByPA(paPDPTE);
*(ULONG64*)((ULONG)pOutBuff + nWriteOffset) = PDPTE;
nWriteOffset += sizeof(ULONG64);
}
}
// 加載 PDE 表
else if (nPDPTEIdx != -1 && nPDEIdx == -1)
{
ULONG64 paPDPTE = paCR3 + 8 * nPDPTEIdx;
ULONG64 PDPTE = GetQuadByPA(paPDPTE) & 0xFFFFFF000;
for (int i = 0; i < 512; i++)
{
ULONG64 paPDE = PDPTE + 8 * i;
ULONG64 PDE = GetQuadByPA(paPDE);
*(ULONG64*)((ULONG)pOutBuff + nWriteOffset) = PDE;
nWriteOffset += sizeof(ULONG64);
}
}
// 加載 PTE 表
else if (nPDPTEIdx != -1 && nPDEIdx != -1)
{
ULONG64 paPDPTE = paCR3 + 8 * nPDPTEIdx;
ULONG64 PDPTE = GetQuadByPA(paPDPTE) & 0xFFFFFF000;
ULONG64 paPDE = PDPTE + 8 * nPDEIdx;
ULONG64 PDE = GetQuadByPA(paPDE) & 0xFFFFFF000;
for (int i = 0; i < 512; i++)
{
ULONG64 paPTE = PDE + 8 * i;
ULONG64 PTE = GetQuadByPA(paPTE);
*(ULONG64*)((ULONG)pOutBuff + nWriteOffset) = PTE;
nWriteOffset += sizeof(ULONG64);
}
}
...
}
到此,相信大家對“如何使用PAE分頁模式”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。