前言
在概述,我想我已经介绍过我开始这一些列博文的原因。 我不能担保我所进行的所有试验性操作都是对的,但是,至少这些在我所描述的环境下成功的运行了起来,并帮助我触及我始终敬畏而又敬而远之的硬件&OS
在《Linux内核完全注释》一书第三章——内核编程语言和环境,描述了用 as86 汇编语言构建 boot 引导程序,并最终通过 Bochs 仿真器在模拟开机通电后,BIOS Load boot 程序,通过显示器输出 Loading System... 。
这些,将是这里所要描述的主要内容。
boot.s 汇编程序
这里没法多说,都是书中的源码,只是供未读过书的读者参考,特意摘录了下来(不过,就不加注释了)
boot.s 源代码
.global begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
BOOTSEG=0x07c0
entry start
start:
jmpi go,BOOTSEG
go: mov ax,cs
mov ds,ax
mov es,ax
mov [msg+17],ah
mov cx,#20
mov dx,#0x1004
mov bx,#0x000c
mov bp,#msg
mov ax,#0x1301
int 0x10
loop1: jmp loop1
msg: .ascii "Loading System..."
.byte 13,10
.org 510
.word 0xAA55
.text
endtext:
.data
enddata:
.bss
endbss:
这段程序的主要的执行流程将是:
- 通过 BIOS 加载这段 boot 引导程序
- 红色字体打印 Loading System... 并响铃
- 指令自循环 (
loop1 jmp loop1
) ,将始终展示上述字样,并不接收命令
下面就该把这个汇编程序 编译 + 链接 成 boot 引导程序。
虽然在 macOS 上确实找到了一个 as86 汇编器(来自 nasm 的一个编译选项,但我不知道为什么始终无法正常编译,也可能这个根本就不是我想要的)。
通过 docker 容器部署的 Ubuntu,可以很容易地得到一个 as86 汇编程序。
apt-get install bin86 # as86, ld86 都在这个包里提供了
# 这句需要在宿主机上执行
docker cp boot.s linux:/root/boot.s # 这里 linux 是我 docker 部署的 Ubuntu 的容器的名称。 如果之前 boot.s 在宿主机上,可以这样拷贝到容器中
as86 -0 -a -o boot.o boot.s # 编译
ld86 -0 -s -o execfile boot.o # 链接
# 当然,到这里为止,其实都可以理解成在将汇编代码编译链接成可执行程序 (和 boot 引导程序没有太大关系,唯一有关系的就是这段汇编语言是以引导为目的写的)
dd bs=32 if=execfile of=boot skip=1 # 这里去掉可执行程序的前 32 字节,形成刚好 512 字节的 boot 引导程序
用仿真器启动引导程序
事实上,这部分内容,我始终没有搞清楚 磁盘映像文件 和 boot 引导程序 间的关系(当然还有 floppy 和 ata0~3)
在上一节成功拿到 512B 的 boot 引导程序之后,直接使用这个貌似我也运行成功了(根本就不需要创建磁盘映像文件并将引导程序写入映像文件中,不过,也许只是因为这个引导程序太简单了,根本就不需要其他程序的配合。它最后就是一个死循环 )
总之,先按照最简单的来吧。
把 boot 引导程序拷贝到宿主机上(貌似频繁地在两个OS上交互文件,这个很无奈,docker容器中的进程难以启动显示程序)
- 在宿主机新建一个目录 linux-boot
- 拷贝 boot 引导程序到宿主机上
docker cp linux:/root/boot linux-boot/
- 在 linux-boot 下建立 bochsrc 文件(这将是整个仿真器的配置,用于模拟组装机器涉及的 CPU, 内存, BIOS, 显示器等)
- 这里使用的配置文件如下:
# You may now use double quotes around pathnames, in case
# your pathname includes spaces.
cpu: model=pentium, count=1, ips=50000000, reset_on_triple_fault=1, ignore_bad_msrs=1, msrs="msrs.def"
cpu: cpuid_limit_winnt=0
memory: guest=512, host=256
romimage: file=$BXSHARE/BIOS-bochs-legacy, options=fastboot # BIOS 配置
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
mouse: enabled=0
pci: enabled=1, chipset=i440fx
private_colormap: enabled=0
floppya: 1\_44="./boot", status=inserted # 装载一个软盘 A ,这里用当前目录下的 boot (boot 引导程序,事实上书中说要用 .img 磁盘映像文件,但这里不影响实际效果)
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9
boot: a # 配置引导程序所在的磁盘
floppy_bootsig_check: disabled=0
log: bochsout.txt
panic: action=ask
error: action=report
info: action=report
debug: action=ignore, pci=report # report BX_DEBUG from module 'pci'
debugger_log: -
parport1: enabled=1, file="parport.out"
speaker: enabled=1, mode=sound
- 当前目录 linux-boot 下,键入命令
bochs
- 由于读取到 bochsrc 配置文件的存在,menu 的默认选项为 6 (开始模拟机器)
You can also start bochs with the -q option to skip these menus.
1. Restore factory default configuration
2. Read options from...
3. Edit options
4. Save options to...
5. Restore the Bochs state from...
6. Begin simulation
7. Quit now
Please choose one: [6]
- 直接开始运行机器,键入命令
c
(这部分在 概述 有过了描述,以后将不再描述)
Please choose one: [6]
00000000000i[ ] lt_dlhandle is 0x7f85d0405ff0
00000000000i[PLUGIN] loaded plugin libbx_sdl2.so
00000000000i[ ] installing sdl2 module as the Bochs GUI
00000000000i[SDL2 ] maximum host resolution: x=2880 y=1800
00000000000i[ ] using log file bochsout.txt
Next at t=0
(0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b ; ea5be000f0
<bochs:1> c
- 观察仿真器的表现
Oh, YES! 成功输出了 Loading System... (不过响铃没有听到,可能与我没有配置 sound 有关)
- 关机
无需多言,右上角模拟的就是关机实体按键
__ __
/ _| __ _ _ __ __ _ / _| ___ _ __ __ _
| |_ / _` | '_ \ / _` | |_ / _ \ '_ \ / _` |
| _| (_| | | | | (_| | _| __/ | | | (_| |
|_| \__,_|_| |_|\__, |_| \___|_| |_|\__, |
|___/ |___/