文章目录
- 实模式的缺陷
- 保护模式的扩展
- 段寄存器的变化
- 寄存器扩展
- 寻址扩展
- 全局描述符表GDT
- 描述符格式
- 字段含义
- 进入保护模式
- 步骤
- 解释
- 示例
- 说明
- 程序编写
- boot.inc
- mbr.s
- loader.s
- 编译并写入硬盘
- 启动bochs执行
实模式的缺陷
1、实模式下用户程序所引用的地址都是指向真实的物理地址,用户程序可以自由修改段基址进而轻而易举地访问所有内存,这就意味着用户程序乃至操作系统的数据都可以被随意的修改,没有安全性可言
2、实模式下访问超过64KB的内存区域需要切换段基址,一次只能运行一个程序,无法充分利用计算机的资源,总共20条地址线,最大可用内存为1MB
保护模式的扩展
段寄存器的变化
描述符索引值:GDT/LDT的index
TI:0表示在GDT中索引,1表示在LDT中索引
RPL:表示请求特权级
寄存器扩展
寻址扩展
全局描述符表GDT
描述符格式
字段含义
段基址:32位,表示段的起始地址
段界限:20位,表示段的最大偏移量
G位:
0:粒度为1B
1:粒度为4KB
D/B字段:
对于代码段(D)来说
0:按照16位模式译码(表示指令中的有效地址与操作数是16位)
1:按照32位模式译码(表示指令中的有效地址与操作数是32位)
对于栈段(B)来说
0:表示使用的是sp寄存器
1:表示使用的是esp寄存器
L字段:
0:32位代码段
1:64位代码段
AVL字段:4位,保留,用户可用
P字段:
0:表示此段不在内存中
1:表示此段在内存中
DPL字段:2位,表示段的特权级,等级分别为0、1、2、3级特权,内核处于0特权级
S位:0:表示系统段
1:表示非系统段
type字段:4位,表示段的类型,分为系统段或非系统段,由S位决定
系统段:
非系统段:
进入保护模式
步骤
1、构造段描述符
2、打开A20地址线
3、加载gdt寄存器
4、将CR0的pe位置1
5、刷新流水线
解释
构造段描述符:
保护模式的运行依赖于GDT,所以进入保护模式之前需要构造GDT
打开A20地址线:
实模式下仅有0~19号地址线可用,保护模式下地址线扩展到了32位,所以需要打开高位的地址线
加载gdt寄存器:
在CPU中有一个寄存器专用于记录GDT的地址,所以使用GDT时需要将其地址加载到gdt寄存器
将CR0的pe位置1:
CR0控制寄存器中有许多控制信息,其中pe位用于开启保护模式
刷新流水线:
由于CPU存在指令预取的操作,使得在开启保护模式之前CPU已经取得了保护模式下的指令,此时段寄存器cs中的信息仍为16实模式下的信息,所以在指令译码阶段是按照16位译码,保护模式下的指令按照实模式运行方式移码在执行阶段可能就会出错,所以需要一个远跳转来刷新cs寄存器中的信息以及清空流水线,使得32位代码以32位方式译码
示例
说明
boot.inc包含一些loader加载和gdt设置的宏定义
mbr.s用于加载loader.s,同时会在屏幕上输出"enter mbr"
loader.s用于开启保护模式,开启保护模式之前会在屏幕上输出“enter loader”,开启保护模式之后会在屏幕上输出"enter protect mode"
程序编写
boot.inc
;--------- mbr & loader ---------
LBA_START_SECTOR equ 0x1
SECTOR_COUNT equ 0x4
LOADER_BASE_ADDR equ 0x100
LOADER_OFF_ADDR equ 0x0
LOADER_ADDR equ 0x1000
;--------- 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_000000000000000b
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 ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0.
DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0.
;code section
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
;data section
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
;video section
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
mbr.s
;主引导程序
;------------------------------------------------------------
%include "boot.inc"
SECTION MBR vstart=0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov ax, 0xB800
mov gs, ax
mov sp,0x7c00
; 清屏 利用0x06号功能,上卷全部行,则可清屏。
; -----------------------------------------------------------
;INT 0x10 功能号:0x06 功能描述:上卷窗口
;------------------------------------------------------
;输入:
;AH 功能号= 0x06
;AL = 上卷的行数(如果为0,表示全部)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
;无返回值:
mov ax, 0x600
mov bx, 0x700
mov cx, 0 ; 左上角: (0, 0)
mov dx, 0x184f ; 右下角: (80,25),
; VGA文本模式中,一行只能容纳80个字符,共25行。
; 下标从0开始,所以0x18=24,0x4f=79
int 0x10 ; int 0x10
;;;;;;;;; 打印字符串 ;;;;;;;;;;;
mov cx, sx - msg
mov si, msg
mov di, 0
show_str:
mov byte al, [si]
mov byte ah, [sx]
mov word [gs:di], ax
inc si
add di, 2
loop show_str
jmp L0
msg db "enter mbr"
sx db 0x24
;;;;;;;;; 打字字符串结束 ;;;;;;;;;;;;;;;
L0:
push ds
push di
mov eax, LBA_START_SECTOR
push eax
mov ax, SECTOR_COUNT
push ax
mov ax, LOADER_BASE_ADDR
push ax
mov ax, LOADER_OFF_ADDR
push ax
call read_disk
add sp, 10
pop di
pop ds
jmp LOADER_ADDR
;;;;;;;;; read disk ;;;;;;;;;
; LBA: [bp+10]
; sector count: [bp+8]
; destination: sec=[bp+6], off=[bp+4]
read_disk:
push bp
mov bp, sp
;sector_count
mov dx,0x1f2
mov ax, [bp+8]
out dx, al
;sector_addr
mov dx, 0x1f3
mov ax, [bp+10]
out dx, al
mov dx, 0x1f4
mov al, ah
out dx, al
mov dx, 0x1f5
mov ax, [bp+12]
out dx, al
mov dx, 0x1f6
mov al, ah
and al, 0x0f
or al, 0xe0
out dx, al
;command_write
mov dx, 0x1f7
mov al, 0x20
out dx, al
disk_test:
nop ;give disk a moment
in al, dx
and al, 0x88 ;7: BUSY, 3: READY
cmp al, 0x08 ; (BUSY=0 & READY=1) or not?
jnz disk_test
;data_read:
mov ax, [bp+8]
mov dx, 256
mul dx
mov cx, ax
mov bx, [bp+4]
mov ax, [bp+6]
mov ds, ax
mov dx, 0x1f0
go_on_read:
in ax, dx
mov [bx], ax
add bx,2
loop go_on_read
mov sp, bp
pop bp
ret
;;;;;;;;; read disk ;;;;;;;;;
times 510-($-$$) db 0
db 0x55,0xaa
loader.s
%include "boot.inc"
section loader vstart=LOADER_ADDR
;;;;;;;;; 打印字符串 ;;;;;;;;;;;
mov cx, sx-msg1
mov si, msg1
mov di, 160
show_str:
mov byte al, [si]
mov byte ah, [sx]
mov word [gs:di], ax
inc si
add di, 2
loop show_str
jmp Jmp_there
msg1 db "enter loader"
sx db 0x24
;;;;;;;;; 打字字符串结束 ;;;;;;;;;;;;;;;
Jmp_there:
;----- open A20 -----
in al, 0x92
or al, 00000010b
out 0x92, al
;----- load gdtr-----
lgdt [gdt_ptr]
;----- cr0 set -----
mov eax, cr0
or eax, 0x00000001
mov cr0, eax
;----- clear instruction pipeline -----
jmp dword SELECTOR_CODE:p_m_start
[bits 32]
p_m_start:
mov ax, SELECTOR_DATA
mov ds, ax
mov es, ax
mov ss, ax
mov esp, LOADER_ADDR
mov ax, SELECTOR_VIDEO
mov gs, ax
mov ecx, GDT_BASE-msg2
mov si, msg2
mov di, 320
show_str2:
mov byte al, [si]
mov byte [gs:di], al
inc si
add di, 2
loop show_str2
jmp $
;string
msg2 db "enter protect mode"
;GDT_CREATE
GDT_BASE:
dd 0x00000000, 0x00000000
CODE_DESC:
dd 0x0000FFFF, DESC_CODE_HIGH4
DATA_DESC:
dd 0x0000FFFF, DESC_DATA_HIGH4
VIDEO_DESC:
dd 0x80000007, DESC_VIDEO_HIGH4
GDT_SIZE equ $ - GDT_BASE
GDT_LIMIT equ GDT_SIZE - 1
times 30 dq 0
;SELECTOR_SET
SELECTOR_CODE equ 0000000000001_000b + TI_GDT + RPL0
SELECTOR_DATA equ 0000000000010_000b + TI_GDT + RPL0
SELECTOR_VIDEO equ 0000000000011_000b + TI_GDT + RPL0
;GDT_pointer
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
编译并写入硬盘
nasm -I 配置文件所在目录 -o mbr.bin mbr.s所在目录/mbr.s
nasm -I 配置文件所在目录 -o loader.s.bin loader.s所在目录/loader.s
dd if=mbr.s所在目录/mbr.bin of=硬盘所在目录/hd60M.img bs=512 count=1 seek=0 conv=notrunc
dd if=loader.s所在目录/loader.bin of=硬盘所在目录/hd60M.img bs=512 count=4 seek=1 conv=notrunc
由于loader程序大小可能超过1个扇区的大小,所以此处一次性读取4个扇区
启动bochs执行
./bochs安装目录/bochs/bin/bochs -f bochs配置文件所在目录/boot.disk