QOS 开发 1
QOS 开发日记 1: 环境配置与简易 MBR
环境配置
选择 bochs 作为 x86 模拟器。安装:
1 | brew install sdl2 # 依赖 |
选择 nasm 作为汇编器。安装:
1 | brew install nasm |
项目文件结构:
1 | QOS/ |
bochs 配置
配置启动盘
用 bochs 自带的 bximage
即可。根据它的提示输入选项。
硬盘种类选择 flat
,页大小(size of sector)选 512,总大小 60M。
名字就叫 harddisk.img
好了。
配置启动文件
这里有很多坑。。。。直接贴 .bochsrc 罢
1 | # 设置内存为 32 MB |
保存退出,然后运行
1 | bochs -f .bochsrc |
使用配置文件启动模拟器。模拟器启动之后会告诉你串口号,直接 screen
过去即可。
这时进入的是调试器,输入 c
表示 continue
,继续运行。
当然现在运行是报错的:no bootable device
。
MBR 编写
主引导记录(master boot record)是机器启动之后运行的第二块代码(第一块是 BIOS)。
前置芝士
启动操作系统的时候,机器会先运行ROM里的 BIOS,做一些和硬件相关的检查,并建立中断向量表(IVT)。同时,刚开机的时候,x86 架构进入的是实模式,也就是用绝对地址的模式。此时由于地址线只有 20 条,按字节寻址只能包含 1 MB 的地址(从 0x00000
到 0xfffff
)。这 1M 的空间分布是这样的:
起始地址 | 结束地址 | 总大小 | 用途描述 |
---|---|---|---|
0xfffef |
0xfffff |
16B | BIOS 入口 |
0xf0000 |
0xfffef |
65520B | 系统 BIOS |
0xc8000 |
0xeffff |
163840B | 映射硬件适配器的 ROM 或内存映射式 IO |
0xc0000 |
0xc7fff |
32767B | 显示适配器 BIOS |
0xb8000 |
0xbffff |
32768B | 文本显示适配器 |
0xb0000 |
0xb7fff |
32768B | 黑白显示适配器 |
0xa0000 |
0xaffff |
65536B | 彩色显示适配器 |
0x9fc00 |
0x9ffff |
1024B | 扩展 BIOS 数据区(EBDA) |
0x07e00 |
0x9fbff |
622080B | 可用 |
0x07c00 |
0x07dff |
512B | MBR 加载地址 |
0x00500 |
0x07bff |
30464B | 可用 |
0x00400 |
0x004ff |
256B | BIOS 数据区 |
0x00000 |
0x003ff |
1024B | 中断向量表 |
系统使用 16 位的 cs
和 ip
寄存器,利用分段机制进行寻址(一般来说是 cs:ip
)。但是此时寻址都是 20 位的,需要一些处理才能用两个 16 位寄存器表示。处理方式:cs
左移 4 位再加上 ip
,写成 C++
就是:
1 | (cs << 4) + ip |
在启动时,cs:ip
强制初始化为 0xf000:0xfff0
,那么初始的地址就是 0xffff0
。事实上这个位置就是一句 jmp
。
然后在 BIOS 执行的最后,它会检查磁盘的某个特定扇区(0 盘 0 道 1 扇区),如果这个扇区末尾两个字节是 0x55 0xaa
,那么它就认为这个扇区是活动的,然后把它加载到 0x07c00
。我们刚刚设置了扇区大小为 512B。最后的最后 BIOS 执行指令 jmp 0x0000:0x7c00
跳转到那个位置,开始执行。
(由于 x86 平台采用小端序,第 511 字节应当是 0xaa
,512 才是 0x55
。)
简易 MBR
我们现在只用做到清屏、输出字符串、反复 jmp
实现等待即可。
关于系统中断:向寄存器 ah
存入功能号,然后 int 中断号
。注意是向 a
的高位传值。
清屏:功能号 0x06
,中断号 0x10
。
输出:功能号 0x13
,中断号 0x10
。
获取光标位置:中断号 0x03
。
于是我们直接写代码即可。
1 | ; src/mbr.s |
保存然后汇编:
1 | nasm src/mbr.s -o bin/mbr.bin |
然后可以用 xxd
观察 mbr.bin
,发现最后两字节确实是 55aa
:
1 | QOS % xxd bin/mbr.bin |
同样通过 ls
也可以看出 mbr.bin
大小正好是 512B。
汇编之后就是写入 ROM 了。使用 dd
命令:
1 | dd if=bin/mbr.bin of=harddisk.img count=1 bs=512 counv=notrunc |
dd
命令格式:
- of=FILE: 指定被写入的文件为 FILE
- if=FILE: 指定输入文件为 FILE
- bs=BLOCKS 指定块的大小
- count=指定块数
- seek=BLOCKS 指定写入到文件时要跳过几个块
- conv=CONVS 指定转换方式,notrunc 表示不截断文件
写入之后使用同样的命令运行,成功!