1、mbr主引导程序代码
其中mbr程序中,需要将读取内核加载器代码的地方修改一下,因为我们的loader已经超过一个扇区了,mov cx,4 call rd_disk_m_16
;主引导程序
;-------------------------------------------------------------
%include "boot.inc"
SECTION MBR vstart=0x7c00
mov ax,cs
mov ss,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax
;利用0x06号功能,上卷全部行,则可清屏
mov ax,0x0600
mov bx,0x0700 ;BH是设置缺省属性,属性是指背景色,前景色,是否闪烁等,例如07H表示黑底白字,70H表示灰底黑字等等。
mov cx,0x0000
mov dx,0x184f
int 0x10
;输出背景绿色,前景色红色,并且跳动的字符串"1 MBR"
mov byte [gs:0x00],'1'
mov byte [gs:0x01],0xA4 ;A表示绿色背景闪烁1010 0100b
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
mov bx,LOADER_BASE_ADDR
mov cx,4
call rd_disk_m_16
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位
mov dx,0x1f3
out dx,al
;LBA地址15-8位
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
;LBA地址23-16位
shr eax,cl
mov dx,0x1f5
out dx,al
;LBA地址24-27位
shr eax,cl
and al,0x0f
or al,0xe0 ;设置7-4位为1110,表示lba模式
mov dx,0x1f6
out dx,al
;向0x1f7端口写入读命令,0x20
mov dx,0x1f7
mov al,0x20
out dx,al
.not_ready:
nop
in al,dx
and al,0x88
cmp al,0x08
jnz .not_ready
;读数据
mov ax,di
mov dx,256
mul dx
mov cx,ax ;di*512/2,di*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
2、内核加载器代码
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start
;构建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
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
loadermsg db '2 loader in real.'
loader_start:
;int 0x10 功能号 0x13 打印字符串
mov sp,LOADER_BASE_ADDR
mov bp,loadermsg ;es:bp 字符串地址
mov cx,17 ;字符串长度
mov ax,0x1301 ;ah=13h,al=01h
mov bx,0x001f ;bh=0页号bl=1fh蓝底粉红字
mov dx,0x1800
int 0x10
;-------------------------准备进入保护模式-----------------------
;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
[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:160],'P'
jmp $
3、 加载器和将来内核需要的配置项
主要是段描述符定义和段选择子的定义
注意:DESC_VIDEO_HIGH4,关于书中显存段的定义中有误,高四字节中的最后一字节是显存段的段基址的23~16位,我们显存段目前使用:0xb8000-0xbffff,段界限limit=(0xbffff-0xb8000)/4k=7,也即低四字节为0x80000007,高四字节中的最后一字节段基址是0xb8000-0x8000=0xb
;--------------------loader和kernel----------------------
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
;-------------------gdt描述符属性-------------------------
DESC_G_4K equ 1_00000000000000000000000b
DESC_D_32 equ 1_0000000000000000000000b
DESC_L equ 0_000000000000000000000b
DESC_AVL equ 0_00000000000000000000b
DESC_LIMIT_CODE2 equ 1111_0000000000000000b
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2 equ 0000_0000000000000000b
DESC_P equ 1_000000000000000b
DESC_DPL_0 equ 00_0000000000000b
DESC_DPL_1 equ 01_0000000000000b
DESC_DPL_2 equ 10_0000000000000b
DESC_DPL_3 equ 11_0000000000000b
DESC_S_CODE equ 1_000000000000b
DESC_S_DATA equ DESC_S_CODE
DESC_S_sys equ 0_000000000000b
DESC_TYPE_CODE equ 1000_00000000b
DESC_TYPE_DATA equ 0010_00000000b
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
4、调试运行
nasm -I include/ -o mbr.bin mbr.S
dd if=./mbr.bin of=/*/*/bochs/hd60M.img bs=512 count=1 conv=notruncnasm -I include/ -o loader.bin loader.S
dd if=./loader.bin of=/*/*/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc
进行到指令lgdt后,通过info gdt,观察gdt表加载的情况看是否和定义一致
进入保护模式后,通过creg指令,观察cr0控制寄存器的pe位是否置1(若为大写PE,则表示为1,开启保护模式)
控制台按c键,继续执行,观察显存的输出