在 x86 架構中,CPU 上電後會從固定地址 reset vector (0xFFFF0) 開始執行。BIOS 在這裡會放置一條 far jump 指令,將控制權轉交給真正的 BIOS 入口 (通常位於 0xF000:0x0000,也就是實體位址 0xF0000),再展開後續的初始化流程。
今天的目標是,在 Hypervisor 中掛載一顆 最小化 BIOS 韌體:
在我們的 Hypervisor 環境中,系統的記憶體布局如下:
0x00000 -------------------------
Interrupt Vector Table (IVT, 256 個 entry)
每個 entry 4 bytes → offset:segment
0x07C00 -------------------------
Boot 入口 (Boot Sector, 512 bytes)
BIOS 最後會跳到這裡開始執行開機程式
0xF0000 -------------------------
BIOS 韌體入口 (初始化邏輯)
- 建立 IVT
- 跳往 boot sector
0xFFFF0 -------------------------
Reset Vector
jmp far F000:0000 ; 將控制權交給 BIOS 韌體入口
0xFFFE -------------------------
0xAA55 簽名 (BIOS ROM header)
標記這是一段有效的 BIOS ROM
目前 bios.bin 的設計相當單純,主要分為幾個部分:
2.BIOS 入口 (0xF0000)
這裡會先關閉中斷確保初始化過程不會被任何事情打擾,並清空暫存器。接著建置 IVT,在0x0~0x3FF 配置好 IVT。接著透過 out 命令向 Host 輸出 [BIOS] reset complete banner,驗證 BIOS 已掛載並正常運行。
最後開啟中斷,讓系統進入正常中斷處理階段,並交付控制權給 0x7C00 的 Bootloader。
3.中斷向量表 (IVT)
IVT 位於 Real mode 的 0x0000–0x03FF,共有 256 個 entry,每個 entry 4 bytes (offset + segment)。我們將常見的幾個中斷號掛上測試用的 ISR:
4.ISR(中斷服務程式)
由於目前尚未模擬真實裝置,這些 ISR 只做一件事:
透過 out dx, al 將訊息送到 0xE9 debug port,由 Hypervisor/host 端處理。
如:
調用 INT 10h → 輸出 [BIOS] INT 10h
.code16
.intel_syntax noprefix
.equ ROM_SEGMENT, 0xF000
/* ROM 起始地址 */
.equ DEBUG_PORT, 0x00E9
.section .text
.globl _start
_start = bios_reset_entry
.org 0x0000
bios_default_isr:
push ax
push dx
mov al, '!'
call bios_putc
pop dx
pop ax
iret
/* 未實現的中斷服務程式一律打印 ! */
.org 0x0100
bios_reset_entry:
cli
cld
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x8000
call setup_ivt
mov ax, cs
mov ds, ax
mov es, ax
mov si, offset bios_banner
call bios_puts
mov ax, 0
mov ds, ax
mov es, ax
sti
jmp far ptr 0x0000:0x7C00
/* 執行 boot loader */
setup_ivt:
push ax
push ds
push bx
xor ax, ax
mov ds, ax
mov bx, ROM_SEGMENT
mov word ptr ds:[0x0000], offset bios_default_isr
mov word ptr ds:[0x0002], bx
mov word ptr ds:[0x10 * 4], offset bios_video_handler
mov word ptr ds:[0x10 * 4 + 2], bx
mov word ptr ds:[0x13 * 4], offset bios_disk_handler
mov word ptr ds:[0x13 * 4 + 2], bx
mov word ptr ds:[0x16 * 4], offset bios_keyboard_handler
mov word ptr ds:[0x16 * 4 + 2], bx
mov word ptr ds:[0x1A * 4], offset bios_timer_handler
mov word ptr ds:[0x1A * 4 + 2], bx
pop bx
pop ds
pop ax
ret
bios_putc:
push dx
mov dx, DEBUG_PORT
out dx, al
pop dx
ret
bios_puts:
push ax
push si
pushf
cld
1:
lodsb
test al, al
jz 2f
call bios_putc
jmp 1b
2:
popf
pop si
pop ax
ret
bios_video_handler:
push ax
push dx
push ds
push cs
pop ds
mov si, offset bios_msg_int10
call bios_puts
pop ds
pop dx
pop ax
iret
bios_disk_handler:
push ax
push dx
push ds
push cs
pop ds
mov si, offset bios_msg_int13
call bios_puts
pop ds
pop dx
pop ax
iret
bios_keyboard_handler:
push ax
push dx
push ds
push cs
pop ds
mov si, offset bios_msg_int16
call bios_puts
pop ds
pop dx
pop ax
iret
bios_timer_handler:
push ax
push dx
push ds
push cs
pop ds
mov si, offset bios_msg_int1a
call bios_puts
pop ds
pop dx
pop ax
iret
bios_banner:
.ascii "[BIOS] reset complete\r\n"
.byte 0
bios_msg_int10:
.ascii "[BIOS] INT 10h\r\n"
.byte 0
bios_msg_int13:
.ascii "[BIOS] INT 13h\r\n"
.byte 0
bios_msg_int16:
.ascii "[BIOS] INT 16h\r\n"
.byte 0
bios_msg_int1a:
.ascii "[BIOS] INT 1Ah\r\n"
.byte 0
.org 0xFFF0
bios_reset_vector:
jmp far ptr ROM_SEGMENT:0x0100
.byte 0x00, 0x00, 0x00
.org 0xFFFE
bios_signature:
.word 0xAA55