x86系统上的内存分页

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行我们定义了一个设置页表的函数,首先设置页目录的相关信息,然后设置子页表的相关信息,最后再开启分页设置。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值