前幾天,我已經完成 BIOS 初始化、磁碟 I/O 模擬等。今天的目標,是讓虛擬機真正載入 GRUB,讓開機流程第一次跨過韌體這一層,進入可互動的 bootloader。
為了讓 Hypervisor 能啟動 GRUB,我們需要一顆可以開機的映像檔。
映像檔必須同時包含:
IMG=build/grub-demo.img
SIZE=64M
MNT=/mnt/grubdemo
# 建立空白磁碟與分割
mkdir -p "$(dirname "$IMG")" && sudo truncate -s "$SIZE" "$IMG"
sudo parted -s "$IMG" mklabel msdos
sudo parted -s "$IMG" mkpart primary ext2 1MiB 100%
# 格式化並掛載
LOOP=$(sudo losetup --find --show --partscan "$IMG")
sudo mkfs.ext2 -F "${LOOP}p1"
sudo mkdir -p "$MNT" && sudo mount "${LOOP}p1" "$MNT"
# 放入最小 grub.cfg
sudo mkdir -p "$MNT/boot/grub"
sudo tee "$MNT/boot/grub/grub.cfg" >/dev/null <<'EOF'
set timeout=10
set default=0
menuentry "GRUB demo" {
echo "Hello from GRUB running on our hypervisor!"
sleep --interruptible 5
}
EOF
# 安裝 GRUB 到 MBR
sudo grub-install --target=i386-pc \
--boot-directory="$MNT/boot" \
--modules="normal biosdisk part_msdos ext2" \
"$LOOP"
sync && sudo umount "$MNT" && sudo losetup -d "$LOOP"
echo "GRUB demo image ready at $IMG"
老實說,這段原本我打算自己搞。看了一些資料感覺有點繁瑣,這裡就用 codex 幫我生成,效果還不錯。
接著重複之前的工作,不過我們把載入的檔案從 boot.bin 改成 grub.img,也就是透過 grub-install 製作的 GRUB 映像檔。以下是輸出結果:
BIOS 完成初始化後,透過虛擬化的 ATA 模組發出 read LBA=0 請求,成功讀取 MBR。接著控制權交給 GRUB,這時它嘗試向某些 I/O port 寫出除錯訊息。
目前 Host 僅實作少數必要的 port,所以當 GRUB 調用尚未實作的 port,例如這裡的 0x00DB 時,Hypervisor 會在終端輸出:
[host] unhandled IO port 0x00db dir=out size=1 count=1
並停止 Guest,這個 log 用於提醒這是沒有實現的 I/O 操作。接下來我們的任務是根據 log 一步一步擴充這些 I/O 操作,讓 GRUB 真的可以跑起來。