|
|
@@ -185,6 +185,104 @@ p_mode_start:
|
|
|
mov ax, SELECTOR_VIDEO
|
|
|
mov gs, ax
|
|
|
|
|
|
- mov byte [gs:160], 'P'
|
|
|
+ ; 创建页目录及页表并初始化内存位图
|
|
|
+ call setup_page
|
|
|
|
|
|
- jmp $
|
|
|
+ ; 将描述符表地址及偏移量写入内存 gdt_ptr, 一会用新地址重新加载
|
|
|
+ sgdt [gdt_ptr]
|
|
|
+
|
|
|
+ ; 将 gdt 描述符中视频段描述符中的段基地+0xc0000000
|
|
|
+ mov ebx, [gdt_ptr + 2] ; gdt_base 地址
|
|
|
+ ; 视频段是 3 个段描述符,第个描述符 8 字节, 故 0x18
|
|
|
+ ; 段描述符的高 4 字节的第 31~24 位是段基址
|
|
|
+ or dword [ebx + 0x18 + 4], 0xC0000000
|
|
|
+
|
|
|
+ ; 将 gdt 的基址加上 0xC0000000 使其成人内核所在的高地址
|
|
|
+ add dword [gdt_ptr + 2], 0xC0000000
|
|
|
+
|
|
|
+ add esp, 0xC0000000 ; 将栈指针同样映射到内核地址
|
|
|
+
|
|
|
+ ; 把页目录地址赋给 cr3
|
|
|
+ mov eax, PAGE_DIR_TABLE_POS
|
|
|
+ mov cr3, eax
|
|
|
+
|
|
|
+ ; 打开 cr0 的 pg 位(第 31 位).
|
|
|
+ mov eax, cr0
|
|
|
+ or eax, 0x80000000
|
|
|
+ mov cr0, eax
|
|
|
+
|
|
|
+ ; 在开启分页后,用 gdt 新的地址重新加载
|
|
|
+ lgdt [gdt_ptr]
|
|
|
+
|
|
|
+ mov byte [gs:160], 'V' ; 视频段段基址已经被更新,用字符 V 表示 virtual addr
|
|
|
+
|
|
|
+ jmp $
|
|
|
+
|
|
|
+; -------------------------- 创建页目录及页表 ----------------------------------------
|
|
|
+; 页目录项
|
|
|
+; 31 12 11 9 8 7 6 5 4 3 2 1 0
|
|
|
+; -----------------------------------------------------------------------------
|
|
|
+; | 页表物理页地址 | AVL | G | 0 | D | A | PCD | PWT | US | RW | P |
|
|
|
+; -----------------------------------------------------------------------------
|
|
|
+;
|
|
|
+; 页表项
|
|
|
+; 31 12 11 9 8 7 6 5 4 3 2 1 0
|
|
|
+; -----------------------------------------------------------------------------
|
|
|
+; |物理页地址31-12 | AVL | G | PAT | D | A | PCD | PWT | US | RW | P |
|
|
|
+; -----------------------------------------------------------------------------
|
|
|
+setup_page:
|
|
|
+ ; 先把页目录占用的空间逐字节清 0
|
|
|
+ mov ecx, 4096 ; 页目录大小 4KB = 4096B = 0x1000
|
|
|
+ mov esi, 0
|
|
|
+ .clear_page_dir:
|
|
|
+ mov byte [PAGE_DIR_TABLE_POS + esi], 0
|
|
|
+ inc esi
|
|
|
+ loop .clear_page_dir
|
|
|
+
|
|
|
+ ; 开始创建页目录项(PDE)
|
|
|
+ .create_pde: ; 创建 Page Directory Entry Table
|
|
|
+ mov eax, PAGE_DIR_TABLE_POS ; 0x100000
|
|
|
+ add eax, 0x1000 ; 此时 eax 为第一个页表的位置及属性 0x100000 + 0x1000 = 0x101000
|
|
|
+ mov ebx, eax ; 此处为 ebx 赋值,是为 .create_pte 做准备,ebx 为基址
|
|
|
+ ; 下面将页目录 0 和 0xc00 都存为第一个页表的地址,第个页表 4MB 内存
|
|
|
+ ; 这样 0xc03fffff 以下的地址和 0x003fffff 以下的地址都指向相同的页表
|
|
|
+ ; 这是为将地址映射为内核地址做准备
|
|
|
+ or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性 RW 和 P 位为 1, US 为 1,表示用户属性,所有特权级别都可以访问
|
|
|
+ mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 页目录表中的第一个目录项写入第一个页表的位置(0x101000) 及属性 (7)
|
|
|
+ mov [PAGE_DIR_TABLE_POS + 0xC00], eax ; 一个页目录项占 4 字节, 0xc00 表示第 768 个页表占用的目录项
|
|
|
+ ;; 0x00000000 ~~ 0x3fffffff 是第一个 1 GB 内存
|
|
|
+ ;; 0x40000000 ~~ 0x7fffffff 是第二个 1GB 内存
|
|
|
+ ;; 0x80000000 ~~ 0xbfffffff 是第三个 1GB 内存
|
|
|
+ ;; 0xc0000000 ~~ 0xffffffff 是第四个 1GB 内存
|
|
|
+ ;; 0xc00 以上的目录项用于内核空间
|
|
|
+ ;; 也就是页表的 0xc0000000 ~~ 0xffffffff 共计 1G 属于内核
|
|
|
+ ;; 0x0 ~~ 0xbfffffff 共计 3G 属于用户进程
|
|
|
+
|
|
|
+ sub eax, 0x1000
|
|
|
+ mov [PAGE_DIR_TABLE_POS + 4092], eax ; 1024 * 4 - 4 = 4092 使最后一个目录项指向页目录表自己的地址
|
|
|
+
|
|
|
+ ; 下面创建页表项(PTE) 256 个页表项
|
|
|
+ ;; 第 0 页, 分配物理地址 0~0x3fffff 之间的物理页
|
|
|
+ ;; 1M 低端内存中,虚拟地址等于物理地址
|
|
|
+ mov ecx, 256 ; 1M 低端内存(1024K) / 每页大小 4K = 256
|
|
|
+ mov esi, 0
|
|
|
+ mov edx, PG_US_U | PG_RW_W | PG_P ; 属性 7, US=1, RW=1,P=1
|
|
|
+ .create_pte: ; 创建 Page Table Entry
|
|
|
+ mov [ebx+esi*4], edx ; 此时ebx=0x101000,第一个页表地址
|
|
|
+ add edx, 4096
|
|
|
+ inc esi
|
|
|
+ loop .create_pte
|
|
|
+
|
|
|
+ ; 创建内核其它页表的 PDE
|
|
|
+ mov eax, PAGE_DIR_TABLE_POS
|
|
|
+ add eax, 0x2000 ; 此时 eax 为第二个页表的位置
|
|
|
+ or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项属性都为 1
|
|
|
+ mov ebx, PAGE_DIR_TABLE_POS
|
|
|
+ mov ecx, 254 ; 范围为第 769 ~ 1022 的所有目录项数量
|
|
|
+ mov esi, 769
|
|
|
+ .create_kernel_pde:
|
|
|
+ mov [ebx+esi*4], eax
|
|
|
+ inc esi
|
|
|
+ add eax, 0x1000
|
|
|
+ loop .create_kernel_pde
|
|
|
+ ret
|