之前的文章有提到,PC(一般为32位)的启动过程为 BIOS -> MBR -> loader -> kernel。
BIOS在休眠后的最后任务是从硬盘中的第0柱面第0磁头第1扇区中读取MBR,其实就是逻辑0扇区。因为扇区是从1开始计数的。
BIOS所做的内部原理对我来说是从来不去涉及的,所以也就不讨论这个。
从MBR开始才是我们程序猿所能任意写的内容,而MBR同BIOS一样,也需要对下一阶段loader进行过渡。
而一个特别麻烦的地方就是,没有任何库与函数供我们调用……因此我们只能在汇编层面从硬盘读取数据。
老实说,我也不清楚应该从哪开始讲,后续内容都建立在以下基础知识之上:
- CPU通过端口访问设备
- 硬盘中的块表示有CHS和LBA方式
- 通道的概念
- 一个PATA线可以挂两个硬盘,一个主盘,一个从盘
- 以前一般主板只支持4个PATA硬盘,所以只需两个通道,这里也只提两个通道
- 硬盘的IO接口称为硬盘控制器
硬盘控制器的主要端口寄存器
Primary 通道 | Secondary 通道 | 读操作 | 写操作 |
---|---|---|---|
0x1F0 | 0x170 | Data | Data |
0x1F1 | 0x171 | Error | Features |
0x1F2 | 0x170 | Sector count | Sector count |
0x1F3 | 0x170 | LBA low | LBA low |
0x1F4 | 0x170 | LBA mid | LBA mid |
0x1F5 | 0x170 | LBA high | LBA high |
0x1F6 | 0x170 | Device | Device |
0x1F7 | 0x170 | Status | Command |
0x3F6 | 0x170 | Alternate status | Device Control |
常用读写硬盘的操作方法
- 先选择通道,往该通道的 sector count 寄存器中写入待操作扇区数
- 往该通道的3个LBA寄存器中写入扇区起始地址
- 往Device寄存器中写入低4位( 0 ~ 3 )数据,第6位置1,表示选择LBA,第4位选择主从盘,其他两位置1.
- 往通道上的command 寄存器中写入命令
- 读取status寄存器,判断是否完成
- 读硬盘或者完工
最后是取数据,这个在操作系统课上有详说,什么DMA啊,通道啊,中断啊,什么的。这里不详说。
最后附上一个较为完整的 MBR ,取自郑钢的《操作系统真象还原》
书上的注释乱码了,只能我自己写一点
<code class="language-null">%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
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 ; loader起始扇区
mov bx,LOADER_BASE_ADDR ; 载入内存的起始位置
mov cx,1 ; 读取一个扇区
call rd_disk_m_16
jmp LOADER_BASE_ADDR
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
; 向command 寄存器中写入读命令 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
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