x86系列处理器上的页式内存管理
硬件层直接支持内存分页机制
默认情况下不使用内存分页机制(段式内存管理)
分页机制启动后,使用二级页表对内存进行管理
x86系列处理器的分页方式
x86分页机制示意图
x86系列处理器的分页方式
一些重要结论(针对32位x86处理器)
页目录占用1内存页(可访问1024个子页表)
单个子页表占用1内存页(可访问1024个页面)
页面起始地址按4K字节对齐(总是4096整数倍)
分页后可访问的虚拟内存空间为:4K * (1024 * 1024) = 4G
最简单的分页构建方式
x86系列处理器的页属性
由于物理页面的地址必须按照4K字节对齐,所以它低12位都为0
因此,页目录(页表)可使用地址的低12位进行属性描述
x86系列处理器上的页属性
x86对分页的硬件支持
将cr3指向页目录地址(可切换不同的页目录)
将cr0最高位置1(硬件级开启分页机制)
汇编贴士
%include "inc.asm"
PageDirBase equ 0x200000
PageTblBase equ 0x201000
org 0x9000
jmp ENTRY_SEGMENT
[section .gdt]
; GDT definition
; 段基址, 段界限, 段属性
GDT_ENTRY : Descriptor 0, 0, 0
CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_32 + DA_C
VIDEO_DESC : Descriptor 0xb8000, 0x7fff, DA_32 + DA_DRWA
DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_32 + DA_DRW
STACK32_DESC : Descriptor 0, TopOfStack32, DA_32 + DA_DRW
PAGE_DIR_DESC : Descriptor PageDirBase, 4095, DA_32 + DA_DRW
PAGE_TBL_DESC : Descriptor PageTblBase, 1023, DA_32 + DA_DRW + DA_LIMIT_4K
; GDT end
GdtLen equ $ - GDT_ENTRY
GdtPtr:
dw GdtLen - 1
dd 0
; GDT Selector
Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0
Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0
Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0
PageDirSelector equ (0x0005 << 3) + SA_TIG + SA_RPL0
PageTblSelector equ (0x0006 << 3) + SA_TIG + SA_RPL0
; end of [section .gdt]
TopOfStack16 equ 0x7c00
[section .dat]
[bits 32]
DATA32_SEGMENT:
DTOS db "D.T.OS!", 0
DTOS_Offset equ DTOS - $$
HELLOWORLD db "Hello, World!", 0
HELLOWORLD_Offset equ HELLOWORLD - $$
Data32SegLen equ $ - DATA32_SEGMENT
[section .s16]
[bits 16]
ENTRY_SEGMENT:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, TopOfStack16
; initialize GDT for 32 bits code segment
mov esi, CODE32_SEGMENT
mov edi, CODE32_DESC
call InitDescItem
mov esi, DATA32_SEGMENT
mov edi, DATA32_DESC
call InitDescItem
mov esi, STACK32_SEGMENT
mov edi, STACK32_DESC
call InitDescItem
; initalize GDT pointer struct
mov eax, 0
mov ax, ds
shl eax, 4
add eax, GDT_ENTRY
mov dword [GdtPtr + 2], eax
; 1. load GDT
lgdt [GdtPtr]
; 2. close interrupt
cli
; 3. open A20
in al, 0x92
or al, 00000010b
out 0x92, al
; 4. enter protect mode
mov eax, cr0
or eax, 0x01
mov cr0, eax
; 5. jump to 32 bits code
jmp dword Code32Selector : 0
; esi --> code segment labelBACK_ENTRY_SEGMENT
; edi --> descriptor label
InitDescItem:
push eax
mov eax, 0
mov ax, cs
shl eax, 4
add eax, esi
mov word [edi + 2], ax
shr eax, 16
mov byte [edi + 4], al
mov byte [edi + 7], ah
pop eax
ret
[section .s32]
[bits 32]
CODE32_SEGMENT:
mov ax, VideoSelector
mov gs, ax
mov ax, Stack32Selector
mov ss, ax
mov eax, TopOfStack32
mov esp, eax
mov ax, Data32Selector
mov ds, ax
mov ebp, DTOS_Offset
mov bx, 0x0c
mov dh, 13
mov dl, 33
call PrintString
mov ebp, HELLOWORLD_Offset
mov bx, 0x0c
mov dh, 14
mov dl, 30
call PrintString
call SetupPage
jmp $
;
;
SetupPage:
push es
push eax
push ecx
push edi
mov ax, PageDirSelector
mov es, ax
mov edi, 0
mov ecx, 1024 ; 1K sub page tables
mov eax, PageTblBase | PG_P | PG_USU | PG_RWW
cld
stdir:
stosd
add eax, 4096
loop stdir
mov ax, PageTblSelector
mov es, ax
mov edi, 0
mov ecx, 1024 * 1024 ; 1M pages
mov eax, PG_P | PG_USU | PG_RWW
cld
sttbl:
stosd
add eax, 4096
loop sttbl
mov eax, PageDirBase
mov cr3, eax
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
pop edi
pop ecx
pop eax
pop es
ret
; ds:ebp --> string address
; bx --> attribute
; dx --> dh : row, dl : col
PrintString:
push ebp
push cx
push eax
push dx
push edi
print:
mov cl, [ds:ebp]
cmp cl, 0
je end
mov eax, 80
mul dh
add al, dl
shl eax, 1
mov edi, eax
mov ah, bl
mov al, cl
mov [gs:edi], ax
inc ebp
inc dl
jmp print
end:
pop edi
pop dx
pop eax
pop cx
pop ebp
ret
Code32SegLen equ $ - CODE32_SEGMENT
[section .gs]
[bits 32]
STACK32_SEGMENT:
times 1024 * 4 db 0
Stack32SegLen equ $ - STACK32_SEGMENT
TopOfStack32 equ Stack32SegLen - 1
因为我们要实现x86处理器上的分页,所以我需要先定义页目录和子页表,我们定义页目录的起始地址位0x200000,大小为 4 * 1024 = 4K字节,子页表的起始地址为0x201000,大小为 4 * 1024 *1024 = (4K * 1024)字节,随后我们给页目录和子页表定义了相应的段描述符和选择子,19行中的DA_LIMIT_4K告诉我们段界限这里使用的单位不是字节,而是页,所以把子页表的段界限设为1023。
149行-192行我们定义了一个设置页表的函数,首先设置页目录的相关信息,然后设置子页表的相关信息,最后再开启分页设置。