操作系统真象还原-读磁盘与保护模式
操作系统真象还原-读磁盘与保护模式
0x00 代码
头文件boot.inc
;一些宏,预处理指令
;loader的加载地址和磁盘位置
LOADER_BASE_ADDR equ 0x0900
LOADER_START_SECTOR equ 0x02
;gdt描述符属性
DESC_G_4K equ 1_000_0000_0_00_0_0000_00000000b
DESC_D_32 equ 100_0000_0_00_0_0000_00000000b
DESC_L equ 0;不设置L位 不是64位系统
DESC_AVL equ 0;不使用AVL位
DESC_LIMIT_CODE2 equ 1111_0_00_0_0000_00000000b;段界限16-19位
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2;平坦模型 都是全1,最终合成的段界限0xfffff 粒度4K 即(0xfffff+1)*4096= 4GB
DESC_LIMIT_VIDEO2 equ 0;显存区域不是平坦模型
DESC_P equ 1_00_0_0000_00000000b;表示该段存在内存里,由cpu负责检查,软件设置
DESC_DPL_0 equ 00_0_0000_00000000b;dpl0
DESC_DPL_1 equ 01_0_0000_00000000b;dpl1
DESC_DPL_2 equ 10_0_0000_00000000b;dpl2
DESC_DPL_3 equ 11_0_0000_00000000b;dpl3
DESC_S_CODE equ 1_0000_00000000b;非系统段 S为1
DESC_S_DATA equ DESC_S_CODE
DESC_S_SYS equ 0_0000_00000000b;系统段
DESC_TYPE_CODE equ 1000_00000000b;可执行 不可读 非依从 ACCESSED 位由cpu访问后置1
DESC_TYPE_DATA equ 0010_00000000b;不可执行 向上扩展 可写 ACCESSED 0
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 ;合成高32位中的中间16位 基地址全0
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 ;平坦模型 4GB 基地址全0
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 ;
;---选择自 SELECTOR
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"
;引入头文件,和c中.h没区别 直接加进来,主要写一些宏
SECTION MBR vstart=0x7c00
mov ax,0
mov ds,ax
mov ss,ax
mov sp,0x7c00
call .clear
mov ax,0
mov ds,ax
mov bx,message
mov dh,1
mov dl,0
call .print_string
mov ax,0
mov ds,ax
mov eax,LOADER_START_SECTOR
mov si,LOADER_BASE_ADDR
mov cx,4;读4个扇区到内存
call .rd_disk
;以下将print_string的入口地址保存在0x7c00+506,4字节 低2字节段地址 高2字节偏移地址
mov bx,addr
mov word [ds:bx],.print_string;不需要再加0x7c00 因为vstart 0x7c00了
mov word [ds:bx+2],0
jmp 0:LOADER_BASE_ADDR
.clear:
push si
push es
push cx
mov si,0xb800
mov es,si
mov si,0
mov cx,2000
.clear_loop:
mov byte [es:si],0
add si,2
loop .clear_loop
pop cx
pop es
pop si
ret
.print_string:; ds:bx 字符串地址 dh 行 dl 列
push ax
push bx
push cx
push dx
push es
push si
mov ax,0xb800
mov es,ax
mov al,160
mul dh
add dl,dl;
mov dh,0
add ax,dx
mov si,ax;si即显存地址
.print_loop:
mov al,[ds:bx]
cmp al,0
je .print_end
mov [es:si],al
inc bx
add si,2
jmp .print_loop
.print_end:
pop si
pop es
pop dx
pop cx
pop bx
pop ax
ret
.rd_disk:; eax LBA扇区号 ds:si 读入内存的地址 cx 读入的扇区数
push cx
push ds
push si
push di
mov ebp,eax;//实模式下还是可以使用32位寄存器
mov di,cx;//对eax cx备份
mov dx,0x1f2
mov al,cl
out dx,al;写入数据
mov eax,ebp
mov dx,0x1f3;从 eax写入0-27位地址到对应端口
out dx,al
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
shr eax,cl
mov dx,0x1f5
out dx,al
shr eax,cl
and al,0x0f
or al,0xe0;将4-7位设置成0xe0 使用lba模式
mov dx,0x1f6
out dx,al
mov dx,0x1f7
mov al,0x20
out dx,al;读命令
.not_ready:;阻塞式读取
nop
in al,dx;0x1f7既用来写命令 又用来查询命令后的状态
and al,0x88
cmp al,0x08;如果第3位为1 就准备就绪了 并不需要第7位也为1
jnz .not_ready;不是0x88就循环等
mov ax,di
mov dx,256
mul dx
mov cx,ax;//16位*16位 结果 dx:ax ax是低16位 保存着需要读取的次数
;一个扇区512字节 需要读 256次(1次16位)
mov dx,0x1f0
.go_on_read:
in ax,dx;这里是x一次读16位,16位的端口而不是8位
mov [ds:si],ax
add si,2
loop .go_on_read
pop di
pop si
pop ds
pop cx
ret
message db "mbr program is running!",0
times 506-($-$$) db 0
addr times 4 db 0;用来保存print的地址
db 0x55,0xaa
loader.s 设置GDT进入保护模式
%include "boot.inc"
LOADER_STACK_TOP equ LOADER_BASE_ADDR
SECTION loaders vstart=LOADER_BASE_ADDR
jmp near loader_start
;---GDT
GDT_BASE dd 0x00000000;0号描述符
dd 0x00000000;
CODE_DESC dd 0x0000FFFF;段描述符低32位
dd DESC_CODE_HIGH4 ;段描述符高32位 基地址0 段界限 0xfffff 4k粒度 因此范围0-4GB
DATA_STACK_DESC dd 0x0000FFFF;
dd DESC_DATA_HIGH4;同样是0-4GB 向上扩展(段界限作为上界检查)的数据段 但是没关系啦,向下扩展的栈段也可以使用
VIDEO_DESC dd 0x80000007; 基地址0xb8000 低32位的高16位是基地址的低16位 为 0x8000 范围 是0xb8000-0xbffff ,因此段界限是0x8000
;使用4K粒度是 段界限填0x07即可 (0x07+1)*0x1000-1
dd DESC_VIDEO_HIGH4
GDT_SIZE equ $-GDT_BASE
GDT_LIMIT equ GDT_SIZE-1
times 120 dd 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_ptr dw GDT_LIMIT
dd GDT_BASE
loader_msg db "2 loader in real.",0
loader_start:
mov sp,LOADER_STACK_TOP
mov bp,loader_msg
mov cx,17
mov ax,0x1301
mov bx,0x001f
mov dx,0x1800
int 0x10 ;调用int 10 13h子功能显示字符串
;---进入保护模式 打开a20 (第21条地址线)
in al,0x92
or al,0000_0010b
out 0x92,al
;--加载gdt
lgdt [gdt_ptr]
;---cr0 开启pe(protection enable)
mov eax,cr0
or eax,0x01
mov cr0,eax
jmp dword SELECTOR_CODE:p_mode_start;刷新流水线,使用dword 0x66反转 使用32位数据
;使用code选择子,那么会d位被置1,因此会使用32位模式工作,我们要对应编码32位
[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 $
上述代码编译通过运行正确。