#define _PAGE_H
#include <kdef.h>
+#include <processor.h>
/*
-| CR0的PG | CR4的PAE | CR4的PSE | PDE的PS | 页面规模 | 物理地址规模 |
-| ------- | -------- | -------- | ------- | -------- | ------------ |
-| 0 | × | × | × | ― | 禁止分页 |
-| 1 | 0 | 0 | × | 4KB | 32位 |
-| 1 | 0 | 1 | 0 | 4KB | 32位 |
-| 1 | 0 | 1 | 1 | 4MB | 32位 |
-| 1 | 1 | × | 0 | 4KB | 36位 |
-| 1 | 1 | × | 1 | 2MB | 36位 |
+| CR0.PG | CR4.PAE | CR4.PSE | PDE.PS | PAGE SIZE | 物理地址规模 |
+| ------- | -------- | -------- | ------- | --------- | ---------- |
+| 0 | × | × | × | ― | disabled |
+| 1 | 0 | 0 | × | 4KB | 32bit |
+| 1 | 0 | 1 | 0 | 4KB | 32bit |
+| 1 | 0 | 1 | 1 | 4MB | 32bit |
+| 1 | 1 | × | 0 | 4KB | 36bit |
+| 1 | 1 | × | 1 | 2MB | 36bit |
*/
-#define PAGE_P 0x1 // 在内存中
-#define PAGE_WR 0x2 // 表示可读写
-#define PAGE_US 0x4 // 用户级
+#define PAGE_P 0x001 // 在内存中
+#define PAGE_WR 0x002 // 表示可读写
+#define PAGE_US 0x004 // 用户级
+#define PAGE_G 0x100 // 全局页表项
#define PAGE_SHIFT (12)
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PAGE_PDE_CNT 1024
#define PAGE_PTE_CNT 1024
-// P: 有效位。0 表示当前表项无效。
-// R/W: 0 表示只读。1表示可读写。
-// U/S: 0 表示只能0、1、2特权级可访问。3 表示只有特权级程序可访问
-// A: 0 表示该页未被访问,1表示已被访问。
-// D: 脏位。0表示该页未写过,1表示该页被写过
-// PS: 只存在于页目录表。在CR4中的PSE打开的情况下,0表示这是4KB页,指向一个页表。1表示这是4MB大页,直接指向物理页。
+// P: 有效位
+// 0 表示当前表项无效,访问该虚拟地址直接#PF缺页异常,CPU不翻译,也不看其它位,此时余下31位CPU完全不管,内核可以随便用。
+// 修改必须刷对应TLB
+// R/W: 0 表示只读写就#PF; 1表示可读写。PDE和PTE要同时看,逻辑与。比如PDE.RW=0 && PTE.RW=1 最终还是只读。
+// 另外还需要注意CR0.WP,如果CR0.WP=0(默认),那么只限制用户态,内核写只读页也不缺页; CR0.WP=1,那么内核写只读页也#PF。
+// U/S: 0 表示只能0、1、2特权级可访问; 3 表示只有特权级程序可访问。
+// 同样是PDE与PTE按逻辑与。
+// PWT: 0 表示写进缓存,迟早刷回内存; 1 表示写缓存同时写内存,慢但内存总是新的。
+// 普通内存映射一般PWT=0,对页表自身、设备内存特殊映射才考虑设置为1。
+// CR0.CD=1时这个位被忽略。
+// PCD: 0 表示可以进cache; 1表示这一页不进CPU cache。
+// 映射设备寄存器、MMIO、帧缓冲时用。普通RAM页不要开,开了性能会崩。
+// CR0.CD=1时这个位也被忽略。
+// A: 0 表示该页未被访问; 1表示已被访问。
+// 改此位从1清0时,如果P=1,建议顺手刷TLB,避免硬件状态不一致。
+// D: 脏位。0表示该页未写过; 1表示该页被写过。只有PTE用这位,PDE这个位忽略或保留。
+// 用于换出页面时判断要不要写回磁盘。D=0的页换出可直接丢弃,磁盘上已经有相同内容; D=1才需要写回。
+// 清D位也要刷新TLB。
+// PS: PAGE SIZE. 只存在于页目录表项。在CR4中的PSE打开的情况下,0表示这是4KB页,指向一个页表。1表示这是4MB大页,直接指向物理页。
+// PAT: PAGE ATTRIBUTE TABLE. 只存在于页表项。 Pentium III后引入的机制。
+// G: GLOBAL 全局位
+// 4K分页下PDE.G被忽略,如果用4MB大页映射内核空间,PDE.G有效
+// 内核空间映射在所有进程页表里都一样,设为全局,切换进程不刷新这些TLB项,减少内核映射的TLB miss。
+// 用户页不能随便设G,会跨进程泄露TLB翻译。改G相关属性也需要注意TLB一致性。
+// AVL: 内核可用
+// 常见用法:
+// 1. COW标记(软件位,不是硬件COW)
+// 2. 页类型标记: 匿名页、文件页、设备页、共享页
+// 3. 写时复制里记录"这是共享只读页"
+// 4. 页面锁标记,禁止换出
+
+
+// x
// |<------ 31~12------>|<------ 11~0 --------->| 比特
// |b a 9 8 7 6 5 4 3 2 1 0|
#define MAX_ORDER (11)
-#define set_cr3(cr3) asm volatile("movl %0, %%cr3" ::"r"(cr3))
-static inline unsigned long get_cr3() {
- unsigned long cr3;
- asm volatile("movl %%cr3, %0" : "=r"(cr3));
- return cr3;
-}
static inline pde_t* get_pgd() {
- return (pde_t*)pa2va((get_cr3() & PAGE_MASK));
+ return (pde_t*)pa2va((read_cr3() & PAGE_MASK));
}
enum page_flags {
pte_t* pg_table = 0;
void* alloc_from_bootmem(unsigned long size, char* title);
+ // 打开CR4的 PAGE GLOBAL 功能
+ uint32_t cr4 = read_cr4();
+ cr4 |= CR4_PGE;
+ write_cr4(cr4);
+
// 在multiboot.S是已经初始化了BOOT_INIT_PAGETBL_CNT个页
// 这里接着初始化剩余的页
// 最大限制内存1G
init_pgd[get_npde(page_addr)] = (pde_t)((unsigned long)(pg_table) | PAGE_P | PAGE_WR);
}
- pg_table[npte] = (pte_t)(page_addr | PAGE_P | PAGE_WR);
+ pg_table[npte] = (pte_t)(page_addr | PAGE_P | PAGE_WR | PAGE_G);
}
// paging for kernel space
}
}
- set_cr3(va2pa(init_pgd));
+ write_cr3(va2pa(init_pgd));
// 测试显存
for (int i = 0; i < system.x_resolution * (system.y_resolution - 32); i++) {