4.3.5 让我们进入保护模式
- 打开A20
- 加载GDT
- 将cr0的PE位置置为1
三个文件代码
1. boot.inc
;---loader 和 kernel ---
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
LOADER_STACK_TOP equ LOADER_BASE_ADDR
;--- gdt描述符属性 ---
DESC_G_4K equ 100000000000000000000000b
DESC_D_32 equ 10000000000000000000000b
DESC_L equ 0000000000000000000000b
;64位代码标记
DESC_AVL equ 000000000000000000000b
DESC_LIMIT_CODE2 equ 11110000000000000000b
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2 equ 0000000000000000000b
DESC_P equ 1000000000000000b
DESC_DPL_0 equ 000000000000000b
DESC_DPL_1 equ 010000000000000b
DESC_DPL_2 equ 100000000000000b
DESC_DPL_3 equ 110000000000000b
DESC_S_CODE equ 1000000000000b
DESC_S_DATA equ DESC_S_CODE
DESC_S_sys equ 0000000000000b
DESC_TYPE_CODE equ 100000000000b
;x1,e0,w1,a0,代码段,非一致性
DESC_TYPE_DATA equ 001000000000b
;x0,e0,w1,a0,数据段,向上延伸
DESC_CODE_HIGH4 equ (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE + \
DESC_TYPE_CODE + 0x00
DESC_DATA_HIGH4 equ (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x00
DESC_VIDEO_HIGH4 equ (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x0b
;---选择子属性---
RPL0 equ 00b
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b
TI_LDT equ 100b
2. mbr.asm 39行别忘了扩大cx改为4,意味着读取4块区域数据
%include "boot.inc"
section mbr vstart=0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax
;清屏
mov ax,0600h
mov bx,0700h
mov cx,0
mov dx,184fh
int 10h
;输出字符串:MBR
mov byte[gs:0x00],'1'
mov byte[gs:0x01],0xA4
mov byte[gs:0x02],' '
mov byte[gs:0x03],0xA4
mov byte[gs:0x04],'M'
mov byte[gs:0x05],0xA4
mov byte[gs:0x06],'B'
mov byte[gs:0x07],0xA4
mov byte[gs:0x08],'R'
mov byte[gs:0x09],0xa4
mov eax,LOADER_START_SECTOR ;其实栓去lba地址
mov bx ,LOADER_BASE_ADDR ;写入的地址
mov cx ,4 ;这里的cx就是读取扇区数
call rd_disk_m_16
mov byte[gs:160],'r'
mov byte[gs:162],'d'
jmp LOADER_BASE_ADDR
;===读取硬盘n个扇区===
rd_disk_m_16:
mov esi, eax
mov di,cx
;第一步,读取扇区
mov dx,0x1f2
mov al,cl
out dx,al
mov eax,esi
;第二步,将LBA地址存入0x1f3~0x1f6
;lba地址7~0写入端口0x1f3
mov dx,0x1f3
out dx,al
;lba地址15-8写入端口0x1f4
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
;lba地址32~16写入端口0x1f5
shr eax,cl
mov dx,0x1f5
out dx,al
;lba14~27
shr eax,cl
and al,0x0f
or al,0xe0 ;设置7-4位1110,表示lba模式。
mov dx,0x1f6
out dx,al
;第3步:向0x1f7端口写入命令,0x20
mov dx,0x1f7
mov al,0x20
out dx,al
;第4步:检测硬盘状态
.not_ready:
nop
in al,dx
and al,0x88
cmp al,0x08
jnz .not_ready
;第5步:从0x1f0端口读数据
mov ax,di
mov dx,256
mul dx
mov cx,ax
;di为要读取的扇区数,一个扇区512字节,每读取一个字;共需要di*512/2次,所以是256
mov dx,0x1f0
.go_on_read:
in ax,dx
mov [bx],ax
add bx,2
loop .go_on_read
ret
times 510-($-$$) db 0
db 0x55,0xaa
3. loader.asm
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
mov byte[gs:320],'J'
mov byte[gs:322],'M'
mov byte[gs:324],'P'
jmp loader_start
;------------------------------
;构建gdt及其内部的描述符
GDT_BASE: dd 0x00000000
dd 0x00000000
CODE_DESC: dd 0x0000FFFF
dd DESC_CODE_HIGH4
DATA_STACK_DESK: dd 0x0000FFFF
dd DESC_DATA_HIGH4
VIDEO_DESC: dd 0x80000007 ;limit=(0xbffff-0xb8000)/4k=0x7
dd DESC_VIDEO_HIGH4 ;此时dp1为0
GDT_SIZE equ $-GDT_BASE
GDT_LIMIT equ GDT_SIZE-1
times 60 dq 0
SELECTOR_CODE equ (0x0001<<3)+TI_GDT+RPL0 ;相当于 (CODE_DESC-GDT_BASE)/8+TI_GDT+RPL0
SELECTOR_DATA equ (0x0002<<3)+TI_GDT+RPL0
SELECTOR_VIDEO equ (0x0003<<3)+TI_GDT+RPL0
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
loadermsg db '2 loader in real.'
;------------------------------
mov byte[gs:480],'G'
mov byte[gs:482],'D'
mov byte[gs:484],'T'
loader_start: ;打印字符串
mov byte[gs:640],'L'
mov byte[gs:642],'O'
mov byte[gs:644],'A'
mov byte[gs:646],'D'
mov byte[gs:648],'E'
mov byte[gs:650],'R'
mov byte[gs:652],'_'
mov byte[gs:654],'S'
mov byte[gs:656],'T'
mov byte[gs:658],'A'
mov byte[gs:660],'R'
mov byte[gs:662],'T'
mov sp,LOADER_BASE_ADDR
mov bp,loadermsg
mov cx,17
mov ax,0x1301
mov bx,0x001f
mov dx,0x1800
int 0x10
;---准备进入保护模式---
;---打开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 ;刷新流水线
[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
mov byte [gs:800], 'P'
jmp $
编译文件
nasm 该文件路径/mbr.asm -o 该文件路径/mbr.bin
nasm 该文件路径/loader.asm -o 该文件路径/loader.bin
写入启动盘
;尽管只有762byte,只用了两块512,我个人在这里写入4块。
dd if=该文件路径/mbr.bin of=该文件路径/hd60M.img bs=512 count=1 seek=0 conv=notrunc
dd if=该文件路径/loader.bin of=该文件路径/hd60M.img bs=512 count=4 seek=2 conv=notrubc
运行结果
因为红字绿底是闪耀状态,所以截图两张。
更新:
boot.inc
; loader and kernel
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
; gdt 属性
DESC_G_4K equ 0x800000
DESC_D_32 equ 0x400000
DESC_L equ 0x000000 ;是否开启64位代码
DESC_AVL equ 0x000000 ;可用的
DESC_LIMIT_CODE2 equ 0xF0000
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2 equ 0x00000
DESC_P equ 0x8000
DESC_DPL_0 equ 0x0000
DESC_DPL_1 equ 0x2000
DESC_DPL_2 equ 0x4000
DESC_DPL_3 equ 0x6000
DESC_S_CODE equ 0x1000
DESC_S_DATA equ DESC_S_CODE ;相同的
DESC_S_SYS equ 0x0000
DESC_TYPE_CODE equ 0x800 ;x1 e0 w0 a0
DESC_TYPE_DATA equ 0x200 ;x0 e0 w1 a0
DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE + \
DESC_TYPE_CODE + 0x00
DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x00
DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x0b
;选择子 属性
RPL0 equ 00b
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b
TI_LDT equ 100b
loader.asm
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start
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 ;预留60个描述符的位子
;选择子
SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0
SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0
;gdt指针
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
loadermsg db '2 loader in real.'
loader_start:
mov sp,LOADER_BASE_ADDR
mov bx,160
mov cx,17
mov si,0
print_loader:
mov al,[loadermsg+si]
mov [gs:bx],al
inc bx
mov byte [gs:bx],0x24
inc bx
add si,1
loop print_loader
;1 打开 A20
in al,0x92
or al,0x02
out 0x92,al
;2 加载 GDT
lgdt [gdt_ptr]
;3 cr0 pe置1
mov eax,cr0
or eax,0x01
mov cr0,eax
jmp dword SELECTOR_CODE:p_mode_start
[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
mov byte [gs:320],'3'
mov byte [gs:322],' '
mov byte [gs:324],'P'
mov byte [gs:326],'r'
mov byte [gs:328],'o'
mov byte [gs:330],'t'
mov byte [gs:332],'e'
mov byte [gs:334],'c'
mov byte [gs:336],'t'
mov byte [gs:338],'i'
mov byte [gs:340],'o'
mov byte [gs:342],'n'
mov byte [gs:344],' '
mov byte [gs:346],'m'
mov byte [gs:348],'o'
mov byte [gs:350],'d'
mov byte [gs:352],'e'
mov byte [gs:354],'.'
jmp $
运行结果: