boot
文件
-
boot.s - 引导扇区(汇编)
-
kernel.c - 内核(C语言)
-
Makefile - 构建脚本
代码
boot.s
.intel_syntax noprefix
.global _start
.code16
.section .text
_start:
cli # 关闭中断,防止引导过程中被打断
xor ax, ax
mov ds, ax # 数据段寄存器DS=0
mov es, ax # 附加段寄存器ES=0
mov ss, ax # 堆栈段寄存器SS=0
mov sp, 0x7c00 # 堆栈指针SP=0x7c00(BIOS加载引导扇区的地址)
mov ah, 0x0e # BIOS TTY模式,准备显示字符
mov al, 'A' # 显示字符'A'
int 0x10 # 调用BIOS中断,显示字符
mov ax, 0x1000 # 目标段地址0x1000(加载内核到0x1000:0x0000)
mov es, ax
mov bx, 0x0000 # ES:BX = 0x1000:0x0000,内核加载到这里
mov dh, 8 # 读取8个扇区(每扇区512字节,8*512=4096字节)
call read_kernel # 调用读取内核函数
# 检查读取是否成功
jc .error # 如果CF=1,说明读取失败
mov ah, 0x0e # 显示成功标志
mov al, 'B' # 显示字符'B'
int 0x10
# 远跳转到0x1000:0x0000,进入内核
mov ah, 0x0e # 显示跳转标志
mov al, 'J' # 显示字符'J'
int 0x10
jmp 0x1000:0x0000
.error:
mov ah, 0x0e # 显示错误标志
mov al, 'E' # 显示字符'E'
int 0x10
.halt:
hlt # 停机
jmp .halt # 死循环
# 读取内核函数,使用BIOS int 0x13磁盘服务
read_kernel:
mov ah, 0x02 # 功能号:读扇区
mov al, dh # 读取的扇区数
mov ch, 0x00 # 柱面号
mov cl, 0x02 # 起始扇区号(第2扇区,1是引导扇区)
mov dh, 0x00 # 磁头号
mov dl, 0x00 # 驱动器号(软盘A)
int 0x13 # BIOS磁盘中断
ret
.org 510
.byte 0x55
.byte 0xAA # 引导扇区魔数,BIOS识别用
kernel.c
__attribute__((naked)) void _start() {
__asm__ volatile (
".intel_syntax noprefix\n" // 使用Intel语法
// "mov al, 0x00\n"
// "mov ah, 0x00\n"
// "int 0x10\n" // BIOS视频中断
"mov ah, 0x0E\n" // 功能号:显示字符
"mov al, 'K'\n" // 字符'K'
"int 0x10\n" // BIOS视频中断
"mov ah, 0x0E\n" // 功能号:显示字符
"mov al, 'E'\n" // 字符'E'
"int 0x10\n" // BIOS视频中断
"mov ah, 0x0E\n" // 功能号:显示字符
"mov al, 'R'\n" // 字符'R'
"int 0x10\n" // BIOS视频中断
"mov ah, 0x0E\n" // 功能号:显示字符
"mov al, 'N'\n" // 字符'N'
"int 0x10\n" // BIOS视频中断
"mov ah, 0x0E\n" // 功能号:显示字符
"mov al, 'E'\n" // 字符'E'
"int 0x10\n" // BIOS视频中断
"mov ah, 0x0E\n" // 功能号:显示字符
"mov al, 'L'\n" // 字符'L'
"int 0x10\n" // BIOS视频中断
"int 0x10\n" // BIOS视频中断
// "mov AH, 0x0E\n" // 功能号:显示字符
// "mov BH, 0\n" // 页号=0(显存页)
// "mov BL, 0x0F\n" // 颜色属性=0x0F(白色字符,黑色背景)
// "int 0x10\n" // 额外的BIOS中断调用,强制刷新输出缓冲区
"1: hlt\n" // 停机
"jmp 1b\n" // 无限循环
".att_syntax\n" // 切换回AT&T语法(GCC默认)
);
/* AT&T语法版本:
__asm__ volatile (
"mov $0x0E, %%ah\n" // 功能号:显示字符
"mov $0x5A, %%al\n" // 字符'Z'
"int $0x10\n" // BIOS视频中断
"1: hlt\n" // 停机
"jmp 1b\n" // 无限循环
);
*/
}
Makefile
AS=i686-linux-gnu-as
CC=i686-linux-gnu-gcc
LD=i686-linux-gnu-ld
all: os.img
boot.bin: boot.s
$(AS) --32 boot.s -o boot.o
ld -Ttext 0x7c00 --oformat binary -e _start -melf_i386 boot.o -o boot.bin
kernel.bin: kernel.c
$(CC) -ffreestanding -fno-pic -nostdlib -Wall -march=i386 -fno-stack-protector -fcf-protection=none -c kernel.c -o kernel.o
$(LD) -Ttext 0x1000 --oformat binary -melf_i386 kernel.o -o kernel.bin
os.img: boot.bin kernel.bin
dd if=/dev/zero of=os.img bs=512 count=20
dd if=boot.bin of=os.img conv=notrunc
dd if=kernel.bin of=os.img seek=1 conv=notrunc
run:
@echo "启动QEMU curses模式"
@echo "退出方法:Ctrl+A 然后按 X,或者 Ctrl+C"
qemu-system-i386 -fda os.img -boot a -display curses -monitor stdio
run-console:
@echo "启动QEMU(控制台模式),按 Ctrl+A 然后按 X 退出"
qemu-system-i386 -fda os.img -boot a -nographic
run-debug:
qemu-system-i386 -fda os.img -boot a -display curses -d int -D qemu.log
run-vga:
qemu-system-i386 -fda os.img -boot a -vga std
clean:
rm -f *.o *.bin *.img *.log
启动流程
阶段1:BIOS启动
BIOS → 加载引导扇区到内存0x7c00 → 跳转到0x7c00执行
阶段2:引导扇区执行(boot.s)text
- 初始化环境(段寄存器、堆栈)
- 显示字符’A’(证明bootloader开始执行)
- 读取kernel.bin到内存0x1000:0x0000
- 显示字符’B’(证明磁盘读取成功)
- 显示字符’J’(准备跳转到kernel)
- 远跳转到0x1000:0x0000
阶段3:内核执行(kernel.c)text
- 显示字符’KERNEL'
- 进入无限hlt循环
构建
1. 编译引导扇区
i686-linux-gnu-as --32 boot.s -o boot.o
ld -Ttext 0x7c00 --oformat binary -e _start -melf_i386 boot.o -o boot.bin
2. 编译内核
i686-linux-gnu-gcc -ffreestanding -fno-pic -nostdlib -Wall -march=i386 -fno-stack-protector -fcf-protection=none -c kernel.c -o kernel.o
i686-linux-gnu-ld -Ttext 0x1000 --oformat binary -melf_i386 kernel.o -o kernel.bin
3. 制作软盘镜像
dd if=/dev/zero of=os.img bs=512 count=20 # 创建空白镜像
dd if=boot.bin of=os.img conv=notrunc # 写入引导扇区
dd if=kernel.bin of=os.img seek=1 conv=notrunc # 写入内核
运行方式
命令 | 模式 | 特点 | 退出方式 |
---|---|---|---|
make run | curses窗口 | 独立窗口,图形界面 | Ctrl+A, X 或 Ctrl+C |
make run-console | 控制台模式 | 无窗口,直接输出 | Ctrl+A, X |
make run-vga | VGA图形 | 图形窗口 | 关闭窗口 |