文章目录
- 前言
- 前置知识
- 代码说明
- 实验操作
前言
本博客记录《操作系统真象还原》第二章第2个实验操作~
实验环境:ubuntu18.04+VMware , Bochs下载安装
实验内容:从磁盘读入Loader加载器
实验思路: MBR 受到512字节大小的限制,小的内存空间无法为内核准备环境。因此,我们要在另一个程序中完成初始化环境及加载内核的任务,这个程序称之loader ,即加载器。现在MBR要做的工作是:负责从硬盘上把 loader 加载到内存,并将接力棒交给它。
前置知识
硬盘的工作原理
硬盘的内部结构
硬盘的表面由一些磁性物质组成,可以用这些磁性物质来记录二进制数据。
硬盘整体示意图
说明:
- 盘片固定在主轴上随主轴高速转动。
- 每个盘片分上下两面,每面都存储数据。
- 每个盘面都各由一个磁头来读取数据,故一个盘片上对应一个磁头。故用磁头号来表示盘面。
- 所有盘面中相对位置相同的磁道组成柱面。
因此,可用(柱面号,盘面号,扇区号)
来定位任意一个“磁盘块”。
- CHS(Cylinder Head Sector):用
(柱面号,盘面号,扇区号)
来定位任意一个“磁盘块”。 - LAB(Logical Block Address):用比特位来描述扇区的信息。
磁盘,磁道,扇区
- 磁道:磁盘的盘面被划分成一个个磁道。这样的一个“圈”就是一个磁道。如下图中指向的灰色圈就是一个扇区。
- 扇区:一个磁道又被划分成一个个扇区,每个扇区就是一个“磁盘块”。各个扇区存放的数据量相同。【最内侧磁道上的扇区面积最小,因此数据密度最大】
硬盘控制器端口
硬盘控制器属于 IO 接口,端口就是位于 IO制器上的寄存器,此处的端口是指硬盘控制器上的寄存器。
(待继续填坑。。。)
硬盘操作方法
硬盘操作流程
- 先选择通道,往该通道的 sector count 寄存器中写入待操作的扇区数。
- 往该通道上的三个 LBA 寄存器写入扇区起始地址的低 24 位。
- 往 device 寄存器中写入 LBA 地址的 24 ~ 27 位,并置第 6 位为 1 ,使其为 LBA 模式,设置第 4 位,选择操作的硬盘( master 硬盘或 slave 硬盘)。
- 往该通道上的 command 寄存器写入操作命令。
- 读取该通道上的 status 寄存器,判断硬盘工作是否完成。
- 如果以上步骤是读硬盘,进入下 一 个步骤。否则,完工。
- 将硬盘数据读出。
硬盘工作完成后,需要设定数据传送的方式。一般用“查询传送方式”和“中断传送方式”传送数据。
代码说明
include/boot.inc
boot.inc文件:关于加载器的配置信息。
;------------- loader和kernel ----------
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
boot.inc代码说明:
- LOADER BASE ADDR 定义了 loader 在内存中的位置, MBR 要把 loader 硬盘读入后放在内存地址 0x900 处。
- LOADER_START SECTOR 定义 loader 在硬盘上的逻辑扇区地址,即 LBA 地址。这里等于 0x2 ,说明 loader 放在了第 2 块扇区。
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 sp,0x7c00
mov ax,0xb800
mov gs,ax
; 清屏
;利用0x06号功能,上卷全部行,则可清屏。
; -----------------------------------------------------------
;INT 0x10 功能号:0x06 功能描述:上卷窗口
;------------------------------------------------------
;输入:
;AH 功能号= 0x06
;AL = 上卷的行数(如果为0,表示全部)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
;无返回值:
mov ax, 0600h
mov bx, 0700h
mov cx, 0 ; 左上角: (0, 0)
mov dx, 184fh ; 右下角: (80,25),
; 因为VGA文本模式中,一行只能容纳80个字符,共25行。
; 下标从0开始,所以0x18=24,0x4f=79
int 10h ; 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 ;A表示绿色背景闪烁,4表示前景色为红色
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 ; 起始扇区lba地址
mov bx,LOADER_BASE_ADDR ; 写入的地址
mov cx,1 ; 待读入的扇区数
call rd_disk_m_16 ; 以下读取程序的起始部分(一个扇区)
jmp LOADER_BASE_ADDR
;-------------------------------------------------------------------------------
;功能:读取硬盘n个扇区
rd_disk_m_16:
;-------------------------------------------------------------------------------
; eax=LBA扇区号
; ebx=将数据写入的内存地址
; ecx=读入的扇区数
mov esi,eax ;备份eax
mov di,cx ;备份cx
;读写硬盘:
;第1步:设置要读取的扇区数
mov dx,0x1f2
mov al,cl
out dx,al ;读取的扇区数
mov eax,esi ;恢复ax
;第2步:将LBA地址存入0x1f3 ~ 0x1f6
;LBA地址7~0位写入端口0x1f3
mov dx,0x1f3
out dx,al
;LBA地址15~8位写入端口0x1f4
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
;LBA地址23~16位写入端口0x1f5
shr eax,cl
mov dx,0x1f5
out dx,al
shr eax,cl
and al,0x0f ;lba第24~27位
or al,0xe0 ; 设置7~4位为1110,表示lba模式
mov dx,0x1f6
out dx,al
;第3步:向0x1f7端口写入读命令,0x20
mov dx,0x1f7
mov al,0x20
out dx,al
;第4步:检测硬盘状态
.not_ready:
;同一端口,写时表示写入命令字,读时表示读入硬盘状态
nop
in al,dx
and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙
cmp al,0x08
jnz .not_ready ;若未准备好,继续等。
;第5步:从0x1f0端口读数据
mov ax, di
mov dx, 256
mul dx
mov cx, ax ; di为要读取的扇区数,一个扇区有512字节,每次读入一个字,
; 共需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
mbr.S代码说明:
- 第50 ~ 52 行为函数 rd disk_m_ 16 传递参数。这里选择用ax, bx, ex 寄存器来传递参数。
- 第64 ~ 65 行备份数据。
- 第67 ~ 70 行 先选定一个通道,再往 sector count 寄存器中写扇区数。
- 第74 ~ 95 行是将 LBA 地址写入 LBA 寄存器和 device 寄存器的低位。端口 0x1f3 是寄存器 LBA
low ,端口 0x1f4 是寄存器 LBA mid ,端口 0x1f5 是寄存器 LBA high。 - 第97 ~100 行便是写入命令啦。这里做读操作,所以读扇区的命令是 0x20 。
- 第102 ~ 109 行检测 status 寄存器的 BSY 位。
- 第111 ~ 122 行是从硬盘取数据的过程。
【这部分详细解读请看原书】
loader.S
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
; 输出背景色绿色,前景色红色,并且跳动的字符串"1 MBR"
mov byte [gs:0x20],'2'
mov byte [gs:0x21],0xA4 ; A表示绿色背景闪烁,4表示前景色为红色
mov byte [gs:0x22],' '
mov byte [gs:0x23],0xA4
mov byte [gs:0x24],'L'
mov byte [gs:0x25],0xA4
mov byte [gs:0x26],'O'
mov byte [gs:0x27],0xA4
mov byte [gs:0x28],'A'
mov byte [gs:0x29],0xA4
mov byte [gs:0x2a],'D'
mov byte [gs:0x2b],0xA4
mov byte [gs:0x2c],'E'
mov byte [gs:0x2d],0xA4
mov byte [gs:0x2e],'R'
mov byte [gs:0x2f],0xA4
jmp $ ; 通过死循环使程序悬停在此
代码说明
MBR 与之前的代码较为接近,功能是打印的字符串是“ loader"。
实验操作
1.创建/include/boot.inc文件
(base) user@ubuntu:/home/cooiboi/bochs$ sudo mkdir include
(base) user@ubuntu:/home/cooiboi/bochs$ ls
bin include share
(base) user@ubuntu:/home/cooiboi/bochs$ cd include/
(base) user@ubuntu:/home/cooiboi/bochs/include$ sudo vim boot.inc
2.创建mbr.S并对其进行编译
创建mbr.S
(base) user@ubuntu:/home/cooiboi/bochs$ sudo vim mbr.S
nasm编译
参数-I:表明指定库目录
(base) user@ubuntu:/home/cooiboi/bochs$ sudo nasm -I include/ -o mbr.bin mbr.S
- MBR 写入进磁盘0中
sudo dd if=/home/cooiboi/bochs/mbr.bin of=/home/cooiboi/bochs/hd60M.img bs=512 count=1 conv=notrunc
(base) user@ubuntu:/home/cooiboi/bochs$ sudo dd if=/home/cooiboi/bochs/mbr.bin of=/home/cooiboi/bochs/hd60M.img bs=512 count=1 conv=notrunc
1+0 records in
1+0 records out
512 bytes copied, 0.00026721 s, 1.9 MB/s
4.简单整理了一下文件目录(可以跳过)
笔者这里觉得有点乱,因此创建boot文件夹放入相关编写文件。
# 移动一个文件夹到另一个文件夹下
sudo mv 文件名 目标文件夹路径
(base) user@ubuntu:/home/cooiboi/bochs$ sudo mkdir boot
(base) user@ubuntu:/home/cooiboi/bochs$ sudo mv bochsrc.disk /home/cooiboi/bochs/boot/
(base) user@ubuntu:/home/cooiboi/bochs$ sudo mv mbr.bin /home/cooiboi/bochs/boot/
(base) user@ubuntu:/home/cooiboi/bochs$ sudo mv mbr.S /home/cooiboi/bochs/boot/
(base) user@ubuntu:/home/cooiboi/bochs$ sudo mv hd60M.img /home/cooiboi/bochs/boot/
5.创建loader.S并对其进行编译
(base) user@ubuntu:/home/cooiboi/bochs/boot$ sudo vim loader.S
(base) user@ubuntu:/home/cooiboi/bochs/boot$ cd ..
(base) user@ubuntu:/home/cooiboi/bochs$ sudo nasm -I include/ -o boot/loader.bin boot/loader.S
6.将生成的loader.bin写入硬盘的第二个扇区
sudo dd if=/home/cooiboi/bochs/boot/loader.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=1 seek=2 conv=notrunc
(base) user@ubuntu:/home/cooiboi/bochs/boot$ sudo dd if=/home/cooiboi/bochs/boot/loader.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=1 seek=2 conv=notrunc
0+1 records in
0+1 records out
98 bytes copied, 0.000113617 s, 863 kB/s
7.启动Bochs
sudo bin/bochs -f boot/bochsrc.disk
依次输入6
和c
.
最后,本节的文件结构
【补充】bochsre.disk
中的hd60M.img
写成了绝对地址~
ata0-master: type=disk, path="/home/cooiboi/bochs/boot/hd60M.img", mode=flat,cylinders=121,heads=16,spt=63
参考资料
- 《操作系统真象还原》
- 《操作系统真象还原》第三章 ---- 完善MBR 尝汇编先苦涩后甘甜而再战MBR!(内有闲聊)
- 王道计算机考研 操作系统 磁盘结构