1. 实现分页机制的代码
1.1 由于loader.bin没有超过512,所以不需要改mbr.S
1.2.a 关于段界限的修改
在没有修改阶界限之前是不能访问1M以上字节的,因为刚开始code段的范围是0-0xFFFF
983040=0xF0000,info gdt后发现没有一个段可以到地址0xF0000
这儿需要添加一个数据段,放分页的数据
1.2.b
添加数据段之后忘改段界限了,info gdt 后还是只有3个段
1.2.c
有了数据段之后,就重新设置一个各个段寄存器
1.2.d
出现下面这个情况说明页表没有正确的设置好,PDE没有设置正确
1.2.e
info gdt 出现上万个,说明PDE的属性都设置成可用了,想少打印只需把用得着的属性设为可用即可
改完之后
1.3 正确代码分析
1.4 代码打包
4page.rar (下载后改名为4page.tar.gz)
1.5 注意
a. PDE[12-31]位存的是 PTE的[12-31]位,而PDE与PTE中存的是物理地址,但是后12位是不存的。
换句话话PDE与PTE只存物理地址的[12-31]位,低12位用来放属性,cpu在查找时直接把PDE与PTE的低12位设为0。
PDE的基地址是4K对齐的,PTE的基地址也是4K对齐的。
b. 这也就是为什么PAGE_POS不能在4K不对齐的地址上的原因
PAGE_POS equ 0x1F0000 ;0xF0000-->0xFFFFF-->64K
;PAGE_POS equ 0x1F0100 ;这儿测试PTE是否可以在 4K不对齐的地址上,答案是不可以,必须4K对齐
- org 0x9000
- jmp begin
- ;segment discript
- TYPE_8 equ 0x8 ;code -->exec
- TYPE_2 equ 0x2 ;data -->r/w
-
- S_0 equ (0x0<<4) ;s=0-->gate
- S_1 equ (0x1<<4) ;s=1-->data/code
-
- DPL_0 equ (0x0<<5) ;privilege level
- DPL_3 equ (0x3<<5)
-
- P_0 equ (0x00<<7) ;not in memory
- P_1 equ (0x01<<7) ;in memory
-
- AVL_0 equ (0x00<<8)
-
- DB_0 equ (0x00<<10) ;16bit data&code
- DB_1 equ (0x01<<10) ;32bit data&code
-
- G_0 equ (0x00<<11) ;g=0,byte
- G_1 equ (0x01<<11) ;g=1,4K
-
- TI_0 equ (0x0<<2) ;TI=0,gdt
- TI_1 equ (0x1<<2) ;TI=1,ldt
-
- RPL_0 equ (0x0)
-
- ;PAGE
- PAGE_POS equ 0xF0000 ;0xF0000-->0xFFFFF-->64K
- PG_P_0 equ (0x0) ;not in memory
- PG_P_1 equ (0x1) ;in memory
-
- PG_RW_0 equ (0x0<<1) ;read,not write
- PG_RW_1 equ (0x1<<1) ;read,write
-
- PG_US_0 equ (0x0<<2) ;supervisor
- PG_US_1 equ (0x1<<2) ;user
-
- PG_PWT_0 equ (0x0<<3) ;memory
- PG_PWT_1 equ (0x1<<3) ;memory and cache
-
- PG_PCD_0 equ (0x0<<4) ;disable page level cache
- PG_PCD_1 equ (0x1<<4) ;enable page level cache
-
- PG_A_0 equ (0x0<<5) ;not accessed
- PG_A_1 equ (0x1<<5) ;accessed
-
- PG_D_0 equ (0x0<<6) ;not dirty
- PG_D_1 equ (0x1<<6) ;dirty
-
- PG_PAT_0 equ (0x0<<7) ;page attribute tabel
- PG_PAT_1 equ (0x1<<7) ;
-
- PG_G_0 equ (0x0<<8) ;global-->TLB
- PG_G_1 equ (0x1<<8) ;
-
- PG_AVL_0 equ (0x0<<9) ;available
- PG_AVL_1 equ (0x1<<9) ;
- %macro DESC 3
- dw %1 & 0xFFFF ;limit[0-15]
- dw %2 & 0xFFFF ;base[0-15]
- db (%2>>16)&0xFF ;base[16-23]
- db (%3&0xFF) ;prop[0-7]
- db (((%1>>16)&0xF) | (%3>>4)&0xF0) ;limit[16-19]prop[8-11]
- db (%2>>24) & 0xFF
- %endmacro
- gdt_base: DESC 0, 0, 0
- gdt_code: DESC 0xFFFF, 0x0, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1|TYPE_8)
- gdt_video: DESC 0xFFFF, 0xB8000, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1|TYPE_2)
-
- gdt_ptr:
- dw 24 ;gdt_limit: 8*3=16, only 3 gdt item
- dd gdt_base ;gdt_base
-
- select_code equ (0x01<<3)|TI_0|RPL_0
- select_video equ (0x02<<3)|TI_0|RPL_0
- begin:
- mov ah, 0x88
- int 0x15
- mov cx, ax
- ;----------------- 打开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 select_code:protect_mode
- [bits 32]
- protect_mode:
- mov ax,select_video
- mov gs,ax
- mov byte [gs:160], 'P'
- mov byte [gs:161], 0xA4
-
- call setup_page_table
-
- sgdt [gdt_ptr]
-
- or dword [gdt_ptr+2], 0xC0000000 ; gdt_ptr
- or byte [gdt_base+7], 0xC0 ;change gdt base addr
- or byte [gdt_base+16+7], 0xC0 ;select_video: byte8
-
- or esp, 0xC0000000
-
- mov eax, PAGE_POS
- mov cr3, eax
-
- ;打开cr0的pg位
- mov eax, cr0
- or eax, 0x80000000
- mov cr0, eax
-
- lgdt [gdt_ptr]
-
- mov byte [gs:162], 'G'
- mov byte [gs:163], 0xA4
- jmp $
-
- setup_page_table:
- mov ecx, 0x1000 ;1PTE:4K=0x1000 -->清空4k个字节每次清4个字节,这儿应为0x1000/4=0x400
- xor esi, esi
- mov eax, (PG_US_1|PG_RW_1|PG_P_1) -->如果一开始就把PTE设为可用的话,bochs里面info tab会打印出上万条映射
- _init_page_table:
- mov [PAGE_POS+esi], eax
- add esi,0x04
- loop _init_page_table
-
- ;PDE0, PDE768 use the same PTE0
- or eax,(0x01<<12)
- mov [PAGE_POS+0x0], eax
- mov [PAGE_POS+0xc00], eax
-
- ;PTE for PDE0 and PDE768
- mov ecx,256
- xor esi,esi
- mov eax, (PG_US_1|PG_RW_1|PG_P_1)
- _create_pte:
- mov [PAGE_POS+0x1000+esi],eax
- add esi,0x04
- add eax,0x1000
- loop _create_pte
-
- ;create other PDE for kernel
-
- ret
1.1 由于loader.bin没有超过512,所以不需要改mbr.S
- cong@msi:/work/os/code/4page$ ls -l loader.bin
- -rw-rw-r-- 1 cong cong 263 Aug 19 18:18 loader.bin
在没有修改阶界限之前是不能访问1M以上字节的,因为刚开始code段的范围是0-0xFFFF
- <bochs:1> b 0x904e
- <bochs:2> c
- (0) Breakpoint 1, 0x0000904e in ?? ()
- Next at t=156817618
- (0) [0x00000000904e] 0008:0000904e (unk. ctxt): mov byte ptr gs:0x000000a1, 0xa4 ; 65c605a1000000a4
- <bochs:3> s
- Next at t=156817619
- (0) [0x000000009056] 0008:00009056 (unk. ctxt): call .+81 (0x000090ac) ; e851000000
- <bochs:4>
- Next at t=156817620
- (0) [0x0000000090ac] 0008:000090ac (unk. ctxt): mov ecx, 0x00001000 ; b900100000
- <bochs:5>
- Next at t=156817621
- (0) [0x0000000090b1] 0008:000090b1 (unk. ctxt): xor esi, esi ; 31f6
- <bochs:6>
- Next at t=156817622
- (0) [0x0000000090b3] 0008:000090b3 (unk. ctxt): mov eax, 0x00000007 ; b807000000
- <bochs:7>
- Next at t=156817623
- (0) [0x0000000090b8] 0008:000090b8 (unk. ctxt): mov dword ptr ds:[esi+983040], eax ; 898600000f00
- <bochs:8>
- (0).[156817623] [0x0000000090b8] 0008:000090b8 (unk. ctxt): mov dword ptr ds:[esi+983040], eax ; 898600000f00
- Next at t=156817624
- (0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b ; ea5be000f0 -->重启了
- <bochs:3> info gdt
- Global Descriptor Table (base=0x00009002, limit=24):
- GDT[0x00]=??? descriptor hi=0x00000000, lo=0x00000000
- GDT[0x01]=Code segment, base=0x00000000, limit=0x0000ffff, Execute-Only, Non-Conforming, Accessed, 32-bit -->code段的范轩是0-0xffff
- GDT[0x02]=Data segment, base=0x000b8000, limit=0x0000ffff, Read/Write, Accessed -->视频段的范围是0xb8000-(0xb8000+0xffff)
- You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'
1.2.b
添加数据段之后忘改段界限了,info gdt 后还是只有3个段
- :
- <bochs:14> info gdt
- Global Descriptor Table (base=0x00009002, limit=24):
- GDT[0x00]=??? descriptor hi=0x00000000, lo=0x00000000
- GDT[0x01]=Code segment, base=0x00000000, limit=0x000fffff, Execute-Only, Non-Conforming, Accessed, 32-bit
- GDT[0x02]=Data segment, base=0x00000000, limit=0x0000ffff, Read/Write
- You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'
1.2.c
有了数据段之后,就重新设置一个各个段寄存器
- 96 protect_mode:
- 97 mov ax, select_code --->这儿需要修改
- 98 mov ds, ax
- 99 mov es, ax
-
- <bochs:13>
- Next at t=156817514
- (0) [0x000000009040] 0008:00009040 (unk. ctxt): mov ds, ax ; 8ed8
- <bochs:14>
- (0).[156817514] [0x000000009040] 0008:00009040 (unk. ctxt): mov ds, ax ; 8ed8
- Next at t=156817515
- (0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b ; ea5be000f0
1.2.d
出现下面这个情况说明页表没有正确的设置好,PDE没有设置正确
- Next at t=1252900596
- (0) [0x0000000090a1] 0008:000090a1 (unk. ctxt): mov cr0, eax ; 0f22c0
- <bochs:38>
- Next at t=1252900597
- (0).[1252900597] ??? (physical address not available)
-
-
- <bochs:34> xp /31 0x1F0000
- [bochs]:
- 0x001f0000 <bogus+ 0>: 0x00001007 0x00000007 0x00000007 0x00000007
- 0x001f0010 <bogus+ 16>: 0x00000007 0x00000007 0x00000007 0x00000007
- 0x001f0020 <bogus+ 32>: 0x00000007 0x00000007 0x00000007 0x00000007
- 0x001f0030 <bogus+ 48>: 0x00000007 0x00000007 0x00000007 0x00000007
- 0x001f0040 <bogus+ 64>: 0x00000007 0x00000007 0x00000007 0x00000007
- 0x001f0050 <bogus+ 80>: 0x00000007 0x00000007 0x00000007 0x00000007
- 0x001f0060 <bogus+ 96>: 0x00000007 0x00000007 0x00000007 0x00000007
- 0x001f0070 <bogus+ 112>: 0x00000007 0x00000007 0x00000007
info gdt 出现上万个,说明PDE的属性都设置成可用了,想少打印只需把用得着的属性设为可用即可
- cr3: 0x000000100000
- 0x00000000-0x000fffff -> 0x000000000000-0x0000000fffff
- 0x00400000-0x00400fff -> 0x0000f000f000-0x0000f000ffff
- ...
- 0xffffe000-0xffffefff -> 0x000000000000-0x000000000fff
0xfffff000-0xffffffff -> 0x000000100000-0x000000100fff 106190个
- <bochs:37> info tab
- cr3: 0x000000100000
- 0x00000000-0x000fffff -> 0x000000000000-0x0000000fffff
- 0xc0000000-0xc00fffff -> 0x000000000000-0x0000000fffff
- 0xffc00000-0xffc00fff -> 0x000000101000-0x000000101fff
- 0xfff00000-0xfff00fff -> 0x000000101000-0x000000101fff
- 0xfffff000-0xffffffff -> 0x000000100000-0x000000100fff
- org 0x9000
- jmp begin
- ;segment discript
- TYPE_8 equ 0x8 ;code -->exec
- TYPE_2 equ 0x2 ;data -->r/w
-
- S_0 equ (0x0<<4) ;s=0-->gate
- S_1 equ (0x1<<4) ;s=1-->data/code
-
- DPL_0 equ (0x0<<5) ;privilege level
- DPL_3 equ (0x3<<5)
-
- P_0 equ (0x00<<7) ;not in memory
- P_1 equ (0x01<<7) ;in memory
-
- AVL_0 equ (0x00<<8)
-
- DB_0 equ (0x00<<10) ;16bit data&code
- DB_1 equ (0x01<<10) ;32bit data&code
-
- G_0 equ (0x00<<11) ;g=0,byte
- G_1 equ (0x01<<11) ;g=1,4K
-
- TI_0 equ (0x0<<2) ;TI=0,gdt
- TI_1 equ (0x1<<2) ;TI=1,ldt
-
- RPL_0 equ (0x0)
-
- ;PAGE
- PAGE_POS equ 0x1F0000 ;0xF0000-->0xFFFFF-->64K
- ;PAGE_POS equ 0x1F0100 ;这儿测试PTE是否可以在 4K不对齐的地址上,答案是不可以,必须4K对齐
- PG_P_0 equ (0x0) ;not in memory
- PG_P_1 equ (0x1) ;in memory
-
- PG_RW_0 equ (0x0<<1) ;read,not write
- PG_RW_1 equ (0x1<<1) ;read,write
-
- PG_US_0 equ (0x0<<2) ;supervisor
- PG_US_1 equ (0x1<<2) ;user
-
- PG_PWT_0 equ (0x0<<3) ;memory
- PG_PWT_1 equ (0x1<<3) ;memory and cache
-
- PG_PCD_0 equ (0x0<<4) ;disable page level cache
- PG_PCD_1 equ (0x1<<4) ;enable page level cache
-
- PG_A_0 equ (0x0<<5) ;not accessed
- PG_A_1 equ (0x1<<5) ;accessed
-
- PG_D_0 equ (0x0<<6) ;not dirty
- PG_D_1 equ (0x1<<6) ;dirty
-
- PG_PAT_0 equ (0x0<<7) ;page attribute tabel
- PG_PAT_1 equ (0x1<<7) ;
-
- PG_G_0 equ (0x0<<8) ;global-->TLB
- PG_G_1 equ (0x1<<8) ;
-
- PG_AVL_0 equ (0x0<<9) ;available
- PG_AVL_1 equ (0x1<<9) ;
- %macro DESC 3
- dw %1 & 0xFFFF ;limit[0-15]
- dw %2 & 0xFFFF ;base[0-15]
- db (%2>>16)&0xFF ;base[16-23]
- db (%3&0xFF) ;prop[0-7]
- db (((%1>>16)&0xF) | (%3>>4)&0xF0) ;limit[16-19]prop[8-11]
- db (%2>>24) & 0xFF
- %endmacro
- gdt_base: DESC 0, 0, 0 ;desc_dummy
- gdt_code: DESC 0xFFFFFF, 0x0, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1|TYPE_8)
- gdt_data: DESC 0xFFFFF, 0x0, (G_1|DB_1|AVL_0|P_1|DPL_0|S_1|TYPE_2) ;这儿加了一个data段且段界限是0xFFFFF×4K=4G
- gdt_video: DESC 0xFFFF, 0xB8000, (G_0|DB_1|AVL_0|P_1|DPL_0|S_1|TYPE_2)
-
- gdt_ptr:
- dw 32 ;gdt_limit: 4*8=32, only 4 gdt item ;加gdt_data后这儿还是24,死活调不出来
- dd gdt_base ;gdt_base
-
- select_code equ (0x01<<3)|TI_0|RPL_0
- select_data equ (0x02<<3)|TI_0|RPL_0
- select_video equ (0x03<<3)|TI_0|RPL_0
- begin:
- mov cx, ax
- ;----------------- 打开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 select_code:protect_mode
- [bits 32]
- protect_mode:
- mov ax, select_data
- mov ds, ax
- mov es, ax
- mov ax, select_video
- mov gs, ax
- mov byte [gs:160], 'P'
- mov byte [gs:161], 0xA4
-
- call setup_page_table
-
- sgdt [gdt_ptr]
-
- or dword [gdt_ptr+2], 0xC0000000 ; gdt_ptr
- or byte [gdt_base+7], 0xC0 ;change gdt base addr
- or byte [gdt_base+8+7], 0xC0 ;change gdt base addr
- or byte [gdt_base+16+7], 0xC0 ;select_video: byte8
-
- or esp, 0xC0000000
-
- mov eax, PAGE_POS
- mov cr3, eax
-
- ;打开cr0的pg位
- mov eax, cr0
- or eax, 0x80000000
- mov cr0, eax
-
- lgdt [gdt_ptr]
-
- mov byte [gs:162], 'G'
- mov byte [gs:163], 0xA4
- jmp $
-
- setup_page_table:
- mov ecx, 0x400 ;1PTE:4K=0x1000
- xor esi, esi
- xor eax, eax
- _init_page_table:
- mov [PAGE_POS+esi], eax
- add esi,0x04
- loop _init_page_table
-
- ;PDE0, PDE768 use the same PTE0
- mov eax, (PG_US_1|PG_RW_1|PG_P_1)
- or eax, ((PAGE_POS+0x1000)&0xFFF000)
- mov [PAGE_POS+0x0], eax
- mov [PAGE_POS+0xc00], eax
- sub eax, 0x1000
- mov [PAGE_POS+0xFFC], eax
-
- ;PTE for PDE0 and PDE768
- mov ecx,256
- xor esi,esi
- mov eax, (PG_US_1|PG_RW_1|PG_P_1)
- _create_pte:
- mov [PAGE_POS+0x1000+esi],eax
- add esi,0x04
- add eax,0x1000
- loop _create_pte
-
- ;create other PDE for kernel
-
- ret
4page.rar (下载后改名为4page.tar.gz)
1.5 注意
a. PDE[12-31]位存的是 PTE的[12-31]位,而PDE与PTE中存的是物理地址,但是后12位是不存的。
换句话话PDE与PTE只存物理地址的[12-31]位,低12位用来放属性,cpu在查找时直接把PDE与PTE的低12位设为0。
PDE的基地址是4K对齐的,PTE的基地址也是4K对齐的。
b. 这也就是为什么PAGE_POS不能在4K不对齐的地址上的原因
PAGE_POS equ 0x1F0000 ;0xF0000-->0xFFFFF-->64K
;PAGE_POS equ 0x1F0100 ;这儿测试PTE是否可以在 4K不对齐的地址上,答案是不可以,必须4K对齐