sv39分頁:虛擬地址只用了39位,所以才有這個名字
問題一: PTE的格式怎麼樣? 不同的模式格式都是一樣的嗎?
問題二: MMU需要什麼樣的數據,怎麼樣提前準備呢?
問題三: 怎麼配置一個虛擬地址到物理地址到映射呢?
問題四: 內存的場景的位操作是怎麼樣的呢?
問題五: cpu怎麼知道頁的大小呢?
在RISC-V架構中,無論是Sv32、Sv39還是Sv48模式,頁表條目(PTE)的格式都是固定的,都是64位。這種設計提供了一致性和靈活性,使得操作系統和硬件可以更容易地管理和轉換地址。
知道一個地址怎麼設置,就能知道一堆地址怎麼設置。這個過程不能剩下!!!
我們需要設置一個頁表條目,將虛擬地址 0x0000003F_FFFF_FFFF 映射到物理地址 0x12345000。假設我們要設置的頁表條目在第三級頁表中。
#include <stdint.h>
#define PAGE_SIZE_4KB 4096
#define PAGE_SIZE_2MB (2 * 1024 * 1024)
#define PAGE_SIZE_1GB (1024 * 1024 * 1024)
typedef uint64_t pte_t;
#define PTE_VALID 0x001
#define PTE_READ 0x002
#define PTE_WRITE 0x004
#define PTE_EXECUTE 0x008
#define PTE_USER 0x010
#define PTE_GLOBAL 0x020
#define PTE_ACCESSED 0x040
#define PTE_DIRTY 0x080
#define PTE_PAGE_SIZE 0x100 // 示例標誌位,用於指示大頁
void set_pte(pte_t *pte, uint64_t pa, uint64_t flags) {
//以物理地址的低 12 位是頁內偏移,不需要存儲在 PTE 中。
// PTE 的物理頁號(PPN)字段從第 10 位開始,因此需要將物理頁號對齊到 PTE 的正確位置。
*pte = (pa >> 12) << 10 | flags;
}
int main() {
// 假設頁表的基地址
pte_t pgd[512] __attribute__((aligned(PAGE_SIZE_4KB)));
pte_t pmd[512] __attribute__((aligned(PAGE_SIZE_4KB)));
pte_t pte[512] __attribute__((aligned(PAGE_SIZE_4KB)));
// 虛擬地址和物理地址
uint64_t va = 0x0000003F_FFFF_FFFF;
uint64_t pa = 0x12345000;
// 頁表條目索引
//0x1FF 是一個掩碼,二進制表示為 111111111(即 9 個 1)。
uint64_t pgd_idx = (va >> 30) & 0x1FF;
uint64_t pmd_idx = (va >> 21) & 0x1FF;
uint64_t pte_idx = (va >> 12) & 0x1FF;
// 設置第三級頁表條目(PTE)
set_pte(&pte[pte_idx], pa, PTE_VALID | PTE_READ | PTE_WRITE | PTE_EXECUTE | PTE_USER);
// 設置第二級頁表條目(PMD),使其指向第三級頁表
set_pte(&pmd[pmd_idx], (uint64_t)pte, PTE_VALID);
// 設置第一級頁表條目(PGD),使其指向第二級頁表
set_pte(&pgd[pgd_idx], (uint64_t)pmd, PTE_VALID);
return 0;
}
cpu 怎麼知道是那個2MB大頁,還是!1GB的大頁,或者標準頁4KB?
- 檢查頁表條目的有效性:
首先檢查頁表條目的有效標誌(V位),確保該條目是有效的。 - 檢查頁表條目的權限位:
如果頁表條目指向下一級頁表,那麼它的讀(R)、寫(W)和執行(X)權限位都應該為0。
如果頁表條目直接指向物理頁(即葉子頁),那麼它至少會有一個權限位(R、W或X)被設置。
+-----------+-----------+-----------+-----------------+
| VPN[2] | VPN[1] | VPN[0] | 頁內偏移 |
+-----------+-----------+-----------+-----------------+
| 38-30 | 29-21 | 20-12 | 11-0 |
+-----------+-----------+-----------+-----------------+
void check_page_size(pte_t *pgd, uint64_t va) {
uint64_t vpn2 = (va >> 30) & 0x1FF;
uint64_t vpn1 = (va >> 21) & 0x1FF;
uint64_t vpn0 = (va >> 12) & 0x1FF;
pte_t pte = pgd[vpn2];
if (!(pte & PTE_VALID)) {
printf("Invalid PTE\n");
return;
}
// 判斷是否是非葉子頁
if (!(pte & (PTE_READ | PTE_WRITE | PTE_EXECUTE))) {
pte_t *pmd = (pte_t *)((pte >> 10) << 12);
pte = pmd[vpn1];
if (!(pte & PTE_VALID)) {
printf("Invalid PTE\n");
return;
}
// 判斷是否是非葉子頁
if (!(pte & (PTE_READ | PTE_WRITE | PTE_EXECUTE))) {
pte_t *pte_table = (pte_t *)((pte >> 10) << 12);
pte = pte_table[vpn0];
if (!(pte & PTE_VALID)) {
printf("Invalid PTE\n");
return;
}
printf("4KB page\n");
} else {
printf("2MB page\n");
}
} else {
printf("1GB page\n");
}
}