操作系统真象还原:第五章 保护模式进阶-开启分页机制

1、修改loader.S程序

开启分页机制的三个步骤:

1)增加了创建页目录表和页表的代码

另外需要将GDT的基址加上0xc0000000,同时显存段描述符基址和我们的内核栈esp,也加上0xc0000000,这样映射到内核的虚拟地址上

2)页目录表地址赋值cr3

3)打开cr0的pg位(第31位)

%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
	LOADER_STACK_TOP equ LOADER_BASE_ADDR


	;构建GDT及其内部描述符
	GDT_BASE:	dd	0x00000000
				dd 	0x00000000
	CODE_DESC:	dd	0x0000FFFF
				dd	DESC_CODE_HIGH4
	DATA_STACK_DESC:	dd	0x0000FFFF
						dd	DESC_DATA_HIGH4			
	VIDEO_DESC:	dd	0x80000007
				dd	DESC_VIDEO_HIGH4
				
	GDT_SIZE equ $ - GDT_BASE	
	GDT_LIMIT equ GDT_SIZE - 1
	times 60 dq 0
;构建选择子
	SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0
	SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0
	SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0
	
	total_mem_bytes dd 0	;用于保存最终获得的内存容量


	gdt_ptr dw GDT_LIMIT
			dd GDT_BASE
	;人工对齐
	ards_buf times 244 db 0
	ards_nr dw 0

loader_start:
	;int 15h eax=0000e820h,edx=534D4150h获取内存布局
	xor ebx,ebx
	mov edx,0x534d4150
	mov di,ards_buf
.e820_mem_get_loop:
	mov eax,0x0000e820
	mov ecx,20
	int 0x15
	jc .e820_failed_so_try_e801
	
	add di,cx
	inc word [ards_nr]
	cmp ebx,0
	jnz .e820_mem_get_loop
	
	mov cx,[ards_nr]
	mov ebx,ards_buf
	xor edx,edx
.find_max_mem_area:
	mov eax,[ebx]
	add eax,[ebx+8]
	add ebx,20
	cmp edx,eax
	jge .next_ards
	mov edx,eax
	
.next_ards:
	loop .find_max_mem_area
	jmp .mem_get_ok
	
;int 15h ax=e801获取内存大小
.e820_failed_so_try_e801:
	mov ax,0xe801
	int 0x15
	jc .e801_failed_so_try88
	;算出低端15MB内存
	mov cx,0x400
	mul cx
	shl edx,16
	and eax,0x0000FFFF
	or edx,eax
	add edx,0x100000;加上不可探测的1MB
	mov esi,edx
	;16MB以上内存
	xor eax,eax
	mov ax,bx
	mov ecx,0x10000
	mul ecx
	
	add esi,eax
	mov edx,esi
	jmp .mem_get_ok	;edx为总内存大小
	
;int 15h ah=0x88获取内存大小,只能获取64MB之内
.e801_failed_so_try88:
	mov ah,0x88
	int 0x15
	jc .error_hlt
	and eax,0x0000FFFF
	
	mov cx,0x400
	mul cx
	shl edx,16
	or edx,eax
	add edx,0x100000;0x88只返回1MB以上内存
	
.mem_get_ok:
	mov [total_mem_bytes],edx
	
;-------------------------准备进入保护模式-----------------------
;1	打开A20
;2	加载GDT
;3	将cr0的pe位置1

;-------------------------打开A20--------------------------------
	in al,0x92
	or al,0000_0010b
	out 0x92,al
;-------------------------加载GDT--------------------------------
	lgdt [gdt_ptr]
;------------------------cr0第0位置1-----------------------------
	mov eax,cr0
	or eax,0x00000001
	mov cr0,eax
	;刷新流水线 
	jmp dword SELECTOR_CODE:p_mode_start
	
.error_hlt:	
	hlt	;出错则挂起
	
[bits 32]
p_mode_start:
	mov ax,SELECTOR_DATA
	mov ds,ax
	mov es,ax
	mov ss,ax
	
	mov esp,LOADER_STACK_TOP
	mov ax,SELECTOR_VIDEO
	mov gs,ax	
	
	
;创建页母及页表并初始化页内存位图
	call setup_page
	
	sgdt [gdt_ptr]	;先备份描述符表地址,一会儿需要用新地址重新加载
	
	;gdt中视频端描述符段基址+0xc0000000
	mov ebx,[gdt_ptr+2]
	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'
	
	jmp $
	
;-----------------------------------创建页目录及页表----------------------------------
setup_page:
;页目录占用的空间逐字节清0
	mov ecx,4096
	mov esi,0
.clear_page_dir:
	mov byte [PAGE_DIR_TABLE_POS + esi],0
	inc esi
	loop .clear_page_dir

;----------------------------页目录项----------	
;创建页目录项(PDE)
;先创建低端4M内存的页目录项,其中第768项和第0项相同,第1023项指向页目录表自己
.create_pde:
	mov eax,PAGE_DIR_TABLE_POS
	add eax,0x1000	;eax为第一个页表的位置及属性
	mov ebx,eax
	
	;将页目录项0和0xc00(第768个)都存为第一个页表的地址
	or eax, PG_US_U | PG_RW_W | PG_P
	mov [PAGE_DIR_TABLE_POS + 0x0],eax	
	mov [PAGE_DIR_TABLE_POS + 0xc00],eax
	
	;使最后一个页目录项指向页目录表自己的地址
	sub eax,0x1000
	mov [PAGE_DIR_TABLE_POS + 4092],eax
	
;-------------------------------页表项----------	
;下面创建页表项(PTE)
	;低端1M内存页表项,1M/4K=256
	mov ecx,256
	mov esi,0
	mov edx,PG_US_U | PG_RW_W | PG_P
.create_pte:
	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
	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

2、boot.inc配置文件修改

 增加页目录表的物理地址,同时增加页表和页目录项的属性位

PAGE_DIR_TABLE_POS equ 0x100000
;---------------------------页表相关属性--------------------------------
PG_P equ 1b
PG_RW_R equ 00b
PG_RW_W equ 10b
PG_US_S equ 000b
PG_US_U equ 100b

3、上机测试运行

检查分页机制下的工作是否正常,mov byte [gs:160],'V',在第二行的第一个字符显示了我们大写字母'V'

分页之后,GDT的基地址已经是3GB之上的虚拟地址了,显存段基址也变成了3GB以上的虚拟地址,如图所示

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值