boot

文件

  1. boot.s - 引导扇区(汇编)

  2. kernel.c - 内核(C语言)

  3. 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=0x7c00BIOS加载引导扇区的地址

    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

  1. 初始化环境(段寄存器、堆栈)
  2. 显示字符’A’(证明bootloader开始执行)
  3. 读取kernel.bin到内存0x1000:0x0000
  4. 显示字符’B’(证明磁盘读取成功)
  5. 显示字符’J’(准备跳转到kernel)
  6. 远跳转到0x1000:0x0000

阶段3:内核执行(kernel.c)text

  1. 显示字符’KERNEL'
  2. 进入无限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图形 图形窗口 关闭窗口