bootloader.asm:
; ==============================
; bootloader.asm - 完整可运行的最小引导程序
; 编译: nasm -f bin bootloader.asm -o bootloader.bin
; ==============================
section .text
global _start
bits 16
_start:
; 初始化寄存器
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0xFFFE
; 打印 'S' 表示开始
mov ah, 0x0E
mov al, 'S'
int 0x10
; 加载内核到 0x1000:0x0000 (物理地址 0x10000)
mov ah, 0x02 ; BIOS 磁盘读取功能
mov al, 0x10 ; 读取1个扇区
mov ch, 0x00 ; 柱面0
mov cl, 0x02 ; 扇区2(LBA 1)
mov dh, 0x00 ; 磁头0
mov dl, 0x80 ; 第一块硬盘
mov bx, 0x1000 ; ES:BX = 0x1000:0000
mov es, bx
xor bx, bx
int 0x13
jc .disk_error
; 显示 'O' 表示成功
mov al, 'O'
mov ah, 0x0E
int 0x10
jmp enter_protected_mode
.disk_error:
mov al, 'X'
mov ah, 0x0E
int 0x10
hlt
.hang:
hlt
jmp .hang
; ========================================
; 进入保护模式部分
; ========================================
enter_protected_mode:
cli
lgdt [gdt_descriptor] ; ✅ 加载 GDT
mov eax, cr0
or eax, 1 ; ✅ 设置 PE 位
mov cr0, eax
mov dword [0xB8000], 0x0F52 ; 白字黑底的 'R'
jmp CODE_SEG:flush ; ✅ 远跳转刷新流水线(far jump)
mov dword [0xB8000], 0x0F52 ; 白字黑底的 'R'
mov dword [0xB8000], 0x0F52 ; 白字黑底的 'R'
mov dword [0xB8000], 0x0F52 ; 白字黑底的 'R'
bits 32
flush:
; 设置段寄存器为数据段选择子(DATA_SEG)
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
; 设置栈指针(可选)
mov ebp, 0x90000
mov esp, ebp
; ✅ 写显存显示 'R'
mov dword [0xB8000], 0x0F52 ; 白字黑底的 'R'
; ❌ 不要再使用 int 0x10,BIOS 中断只在实模式有效!
; ✅ 跳转到内核入口地址(物理地址 0x10000)
jmp CODE_SEG:0x10000
; ========================================
; GDT 定义
; ========================================
gdt_start:
dq 0x0 ; Null段
gdt_code:
dw 0xFFFF ; Limit (low)
dw 0x0000 ; Base (low)
db 0x00 ; Base (middle)
db 0x9A ; Present, DPL=0, S=1, Executable, Readable
db 0xCF ; Granularity, 32-bit
db 0x00 ; Base (high)
gdt_data:
dw 0xFFFF ; Limit (low)
dw 0x0000 ; Base (low)
db 0x00 ; Base (middle)
db 0x92 ; Present, DPL=0, S=1, Writable
db 0xCF ; Granularity, 32-bit
db 0x00 ; Base (high)
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
; ========================================
; 引导扇区结束
; ========================================
times 510 - ($ - $$) db 0
dw 0xAA55
kernel_entry.axm:
; ==============================
; kernel_entry.asm
; 功能: 32位内核入口,设置保护模式环境并调用kernel_main
; 编译: nasm -f elf32 kernel_entry.asm -o kernel_entry.o
; ==============================
bits 32
section .text
global start_pm
extern kernel_main ; 声明外部C函数
start_pm:
; 打印 'R' 到屏幕左上角
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
mov dword [0xB8000], 0x0F52 ; 绿色背景白字的 'R'
; 设置段寄存器
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; 设置栈指针
mov ebp, 0x90000
mov esp, ebp
; 调用C内核入口
call kernel_main
; 如果返回,则挂起
hang:
cli
hlt
jmp hang
; GDT符号(需与bootloader.asm一致)
CODE_SEG equ 0x08
DATA_SEG equ 0x10
linker.ld:
/* 内核链接器脚本 */
OUTPUT_FORMAT("elf32-i386")
ENTRY(start_pm)
SECTIONS {
/* 内核加载地址为0x10000 (与bootloader中的0x1000:0x0000对应) */
. = 0x10000;
.text : {
*(.text)
}
.data : {
*(.data)
*(.rodata*)
}
.bss : {
*(COMMON)
*(.bss)
}
/* 确保内核大小不超过8KB (16个扇区) */
/DISCARD/ : {
*(.note*)
*(.comment*)
}
}
Makefile:
# Makefile for MyOS (兼容 Windows, WSL 和 Ubuntu 18.04.6 LTS)
# 工具定义
ifeq ($(OS),Windows_NT)
# Windows 设置
NASM := nasm
GCC := gcc
LD := ld
RM := del /Q
MKDIR := mkdir
QEMU := qemu-system-x86_64
OBJCOPY := objcopy
else
# Linux/WSL 设置
NASM := nasm
GCC := gcc
LD := ld
RM := rm -f
MKDIR := mkdir -p
QEMU := qemu-system-x86_64
OBJCOPY := objcopy
endif
# 编译选项
NASMFLAGS := -f elf32
GCCFLAGS := -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \
-nostartfiles -nodefaultlibs -Wall -Wextra -c \
-Ikernel/include
LDFLAGS := -m elf_i386 -T linker.ld
# 目标文件
BOOTLOADER := boot/bootloader.bin
KERNEL_ENTRY_OBJ := boot/kernel_entry.o
# 新增:添加 process_switch.o
KERNEL_OBJS := kernel/main.o kernel/memory_manager.o kernel/process.o kernel/vga.o kernel/process_switch.o
KERNEL_BIN := kernel/kernel.bin
KERNEL_ELF := kernel/kernel.elf
OS_IMAGE := myos.img
# 默认目标
all: $(OS_IMAGE)
# 构建引导扇区
$(BOOTLOADER): boot/bootloader.asm
$(NASM) -f bin $< -o $@
# 构建内核入口
$(KERNEL_ENTRY_OBJ): boot/kernel_entry.asm
$(NASM) $(NASMFLAGS) $< -o $@
# 构建C内核对象文件
kernel/%.o: kernel/%.c
$(GCC) $(GCCFLAGS) $< -o $@
# 特别处理 .S 汇编文件(注意使用 $(CC) 来启用预处理器)
kernel/%.o: kernel/%.S
$(GCC) $(GCCFLAGS) $< -o $@
# 链接内核
$(KERNEL_ELF): $(KERNEL_ENTRY_OBJ) $(KERNEL_OBJS)
$(LD) $(LDFLAGS) $^ -o $@
# 生成纯二进制内核
$(KERNEL_BIN): $(KERNEL_ELF)
$(OBJCOPY) -O binary $< $@
# 创建OS镜像
$(OS_IMAGE): $(BOOTLOADER) $(KERNEL_BIN)
@echo "Creating OS image..."
@dd if=/dev/zero of=$@ bs=512 count=2880 2>/dev/null
@dd if=$(BOOTLOADER) of=$@ conv=notrunc 2>/dev/null
@dd if=$(KERNEL_BIN) of=$@ seek=1 conv=notrunc 2>/dev/null
# 清理
clean:
$(RM) $(BOOTLOADER) $(KERNEL_ENTRY_OBJ) $(KERNEL_OBJS) $(KERNEL_ELF) $(KERNEL_BIN) $(OS_IMAGE)
# 运行QEMU
run: $(OS_IMAGE)
$(QEMU) -drive format=raw,file=$<
这些程序在QEMU中运行时,为什么会导致不断地运行到设置PE位附近时重新开始运行
最新发布