分页机制上
在上篇博客中可以得知对x86进行分页-需要准备页目录与页表,并且书写了一个SetupPage函数,在这里提出一个问题--如下图所示
如上图所示,问题是--当call SetupPage在不同的位置的时候程序会有什么变化,同时还引出另外一个问题--进入32位保护模式代码后,应该在什么时候启动分页机制,并建立页表?
1.两个不同位置调用是不同的,第一个调用返回之后,下面所需要访问地址时需要映射,称为了虚地址,通过至此调用之后出现的问题是程序会发生变化?
通过实验结果--如下所示
如图所示,发现程序并没有发生改变。
在这里对一个示例进行详细分析来解释为什么程序不会发生变化
从上篇博客中可以知道1.在低12位中,表示的是页内偏移地址2.在中间10位,用于在子页表中查找目标页地址3.在最高的10位中,用于子页目录中查找页表地址
由这里的0x00804ABC可以得知ABC为低12位,代表额度是偏移地址,而中间的10位与高10又是怎样得到的?
在这里可以得知将0x00804的16进制位转换为二进制位为100000000100将其分开为10与0000000100分别对应的是2与4,说明了2的偏移量查找页目录与4的偏移量查找子页表,通过计算得出物理地址与虚地址是相等的,该结果解释了SetupPage的不同位置的调用不会影响结果。
小结论--当前的分页方式使得:任意虚地址都被直接映射为物理地址
实验--通过bochs下对0X00804ABC进行验证
1.首先通过反编译找到需要设置断点的地方,也就是程序跳转的地方。
2.然后对其进行分析
首先可以指导0X00804ABC根据虚拟地址的计算可以得到k=2,j=4,offset=0XABC,
在这里读取内存的计算为PageDirBase 0x200000+24=0x200008,然后在bochs通过命令x /1wx 对内存进行查看,7,为什么不是4k对齐,在这里不用担心,这里的007表示的是低12为页表属性,所以应得的地址为0x00203000,接下来对PageTblBase进行计算,可得PageTblBase=0x00203000+44=0x00203000+0x10=0x00203010,通过内存查看,为0x00804007,同样的是低12为属性,这样的话地址为0x00804000,最终的物理内存为pagebase+offset=0x00804000+0xABC=0x00804ABC
实验--页表映射实验
1.创建多个页表,可自由切换当前使用的页表
2.将同一个虚地址映射到不同的物理地址
3.加载不同的页表,并读取同一个虚拟地址中的内容
进行该实验需要一些准备工作
1.需要创建InitPageTable函数-通过页目录地址,页表起始地址构建二级页表
2.创建SwitchPageTable函数--根据页目录起始地址切换页表,开启分页机制
代码实现
%include "inc.asm"
PageDirBase0 equ 0x200000
PageTblBase0 equ 0x201000
PageDirBase1 equ 0x300000
PageTblBase1 equ 0x301000
org 0x9000
jmp ENTRY_SEGMENT
[section .gdt]
; GDT definition
; 段基址, 段界限, 段属性
GDT_ENTRY : Descriptor 0, 0, 0
CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32
VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32
DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DRW + DA_32
STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32
PAGE_DIR_DESC0 : Descriptor PageDirBase0, 4095, DA_DRW + DA_32
PAGE_TBL_DESC0 : Descriptor PageTblBase0, 1023, DA_DRW + DA_LIMIT_4K + DA_32
PAGE_DIR_DESC1 : Descriptor PageDirBase1, 4095, DA_DRW + DA_32
PAGE_TBL_DESC1 : Descriptor PageTblBase1, 1023, DA_DRW + DA_LIMIT_4K + DA_32
; 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
PageDirSelector0 equ (0x0005 << 3) + SA_TIG + SA_RPL0
PageTblSelector0 equ (0x0006 << 3) + SA_TIG + SA_RPL0
PageDirSelector1 equ (0x0007 << 3) + SA_TIG + SA_RPL0
PageTblSelector1 equ (0x0008 << 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 - $$
HELLO_WORLD db "Hello World!", 0
HELLO_WORLD_OFFSET equ HELLO_WORLD - $$
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
; initialize 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 label
; 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, 12
mov dl, 33
call PrintString
mov ebp, HELLO_WORLD_OFFSET
mov bx, 0x0C
mov dh, 13
mov dl, 31
call PrintString
mov eax, PageDirSelector0
mov ebx, PageTblSelector0
mov ecx, PageTblBase0
call InitPageTable
mov eax, PageDirSelector1
mov ebx, PageTblSelector1
mov ecx, PageTblBase1
call InitPageTable
mov eax, PageDirBase0
call SwitchPageTable
mov eax, PageDirBase1
call SwitchPageTable
jmp $
; eax --> page dir base selector
; ebx --> page table base selector
; ecx --> page table base
InitPageTable:
push es
push eax ; [esp + 12]
push ebx ; [esp + 8]
push ecx ; [esp + 4]
push edi ; [esp]
mov es, ax
mov ecx, 1024 ; 1K sub page tables
mov edi, 0
mov eax, [esp + 4]
or eax, PG_P | PG_USU | PG_RWW
cld
stdir:
stosd
add eax, 4096
loop stdir
mov ax, [esp + 8]
mov es, ax
mov ecx, 1024 * 1024 ; 1M pages
mov edi, 0
mov eax, PG_P | PG_USU | PG_RWW
cld
sttbl:
stosd
add eax, 4096
loop sttbl
pop edi
pop ecx
pop ebx
pop eax
pop es
ret
; eax --> page directory base
SwitchPageTable:
push eax
mov eax, cr0
and eax, 0x7FFFFFFF
mov cr0, eax ;cr0最高位置0,暂时关闭分页机制
mov eax, [esp]
mov cr3, eax
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
pop eax
ret
; ds:ebp --> string address
; bx --> attribute
; dx --> dh : row, dl : col
PrintString:
push ebp
push eax
push edi
push cx
push dx
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 dx
pop cx
pop edi
pop eax
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
页表切换实现
在Bochs下运行的结果为
可知,运行成功
















