本博客参考书籍 《操作系统 真象还原》作者:郑钢
1、 背景
上一章,我们在MBR(磁盘的最开始位置)中,写入的程序是在屏幕上显示字符。这样,当BIOS将MBR调入内存的时候,屏幕上自动显示字符。但是这样的MBR没有实际作用。
2、本章目的
MBR比较小,无法加载内核,所以我们需要一个加载程序loader,以加载内核。所以在loader加载内核之前,MBR将loader从磁盘的指定位置(第二扇区)加载到内存的指定位置(0x900)
3、要求
显存的写入,硬盘的控制。(详细见:《真象还原》第三章)
4、程序
4.1 MBR程序
从硬盘中,将指定位置的loader加载到指定的内存。
; 主引导程序
;-----------------------------------------------
%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], 'A'
mov byte [gs:0x09], 0xA4
mov eax, LOADER_START_SECTOR
mov bx, LOADER_BASE_ADDR
mov cx, 1
call rd_disk_m_16
jmp LOADER_BASE_ADDR ;回到loader的起始位置
;-----------------------------------------------------------
; 读取磁盘的n个扇区,用于加载loader
; eax保存从硬盘读取到的数据的保存地址,ebx为起始扇区,cx为读取的扇区数
rd_disk_m_16:
;-----------------------------------------------------------
mov esi, eax
mov di, cx
;设置要读取的扇区数目
mov dx, 0x1f2
mov al, cl
out dx, al
mov eax, esi
;将LBA(逻辑块地址),也是我们要访问的起始地址
mov dx, 0x1f3
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
mov dx, 0x1f6
out dx, al
;第三步,向端口写入读取命令
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
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
编译并放入磁盘的第零个扇区
sudo nasm -I /usr/share/bochs/my_truth_like_system/code/include/ -o mbr.bin mbr
#file mbr.bin
sudo dd if=mbr.bin of=/usr/share/bochs/hd60M.img bs=512 count=1 conv=notrunc
4.2 loader程序
这个咱们还没学到,先写一个显示程序就好。
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
; 简单的输出跳动的字符串 "1 MBR"
mov byte [gs:0x00], '2'
mov byte [gs:0x01], 0xA4
mov byte [gs:0x02], ' '
mov byte [gs:0x03], 0xA4
mov byte [gs:0x04], 'L'
mov byte [gs:0x05], 0xA4
mov byte [gs:0x06], 'O'
mov byte [gs:0x07], 0xA4
mov byte [gs:0x08], 'A'
mov byte [gs:0x09], 0xA4
mov byte [gs:0x0a], 'D'
mov byte [gs:0x0b], 0xA4
mov byte [gs:0x0c], 'E'
mov byte [gs:0x0d], 0xA4
mov byte [gs:0x0e], 'R'
mov byte [gs:0x0f], 0xA4
jmp $
编译并放入磁盘的第二个扇区
sudo nasm -I /usr/share/bochs/my_truth_like_system/code/include/ -o loader.false.bin loader.false
#file mbr.bin
sudo dd if=loader.false.bin of=/usr/share/bochs/hd60M.img count=1 seek=2 conv=notrunc
4.3 头文件中的boot.inc
;---------------------------loader和kernel-------------
LOADER_BASE_ADDR equ 0x900 ;loader放入内存的位置
LOADER_START_SECTOR equ 0x2 ;loader从硬盘读取loader的位置。即loader在硬盘的存放位置
5、其他
5.1 bochs的调试方法。(这个很重要,以后用多了,回头再整理)。
5.2 vstart:告诉编译器一个初始地址。这对于拷贝程序中的标号影响很大。标号处理 ,这是我当年的标号处理过程,将复制程序放在开头。用vstart更好些。
5.3 bochs的日志存储在bochs.out中。bochs应该会对这个文件采用日志轮询操作吧。