Forráskód Böngészése

启用分页机制

runningwater 11 hónapja
szülő
commit
ad16459db0
3 módosított fájl, 111 hozzáadás és 5 törlés
  1. 11 3
      boot/include/boot.inc
  2. 100 2
      boot/loader.S
  3. BIN
      hd30M.img

+ 11 - 3
boot/include/boot.inc

@@ -2,6 +2,7 @@
 MBR_BASE_ADDR equ 0x7c00   ; mbr 在内存中的位置
 LOADER_BASE_ADDR equ 0x900 ; loader 在内存中的位置 
 LOADER_START_SECTOR equ 0x2 ; loader 在硬盘上的逻辑扇区地址,即 LBA 地址 
+PAGE_DIR_TABLE_POS equ 0x100000 ; 页目录表物理地址
 
 ; ---------------------------- GDT 描述符属性 -----------------------------------------------------------------------------------------------------------------------
 ; 段描述符格式
@@ -9,12 +10,12 @@ LOADER_START_SECTOR equ 0x2 ; loader 在硬盘上的逻辑扇区地址,即 LBA 
 ;  ----------------------------------------------------------
 ; |Base | G | D | L |AVL| Limit | P | DPL | S | TYPE | Base |
 ;  ----------------------------------------------------------
-; 
+
 ;        31~16                           15~0
 ;  ----------------------------------------------------------
 ; |         Base                |        Limit              |
 ;  ----------------------------------------------------------
-;
+
 DESC_G_4K equ 1000_0000_0000_0000_0000_0000b ; 第23位G 表示4K或者1MB位 段界限的单位值 此时为1则为4k
 DESC_D_32 equ  100_0000_0000_0000_0000_0000b ; 第22位D/B位 表示地址值用32位EIP寄存器 操作数与指令码32位
 
@@ -49,4 +50,11 @@ RPL1 equ 01b
 RPL2 equ 10b
 RPL3 equ 11b
 TI_GDT equ 000b
-TI_LDT equ 100b
+TI_LDT equ 100b
+
+; ---------------------- 页表相关属性 ----------------------------------------------------------------------------------------------------------------------------
+PG_P equ 1b
+PG_RW_R equ 00b
+PG_RW_W equ 10b 
+PG_US_S equ 000b
+PG_US_U equ 100b

+ 100 - 2
boot/loader.S

@@ -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

BIN
hd30M.img