2022年5月12日 星期四

用 pascal 寫的 kernel

fig 1. 向 bootboot 致敬
Writing OS (bare metal with FPC)」是我想找用 pascal 寫的 kernel, 意外找到的。

source code: https://gitlab.com/bztsrc/bootboot 放在 gitlab 很少見, 一般都是放在 github, 作者似乎和 ms 有什麼恩怨的樣子。

圖示很有趣, 是一雙短靴 (不是 fig 1 那張照片), 我一開始以為跑到女性服飾的購物網站, 不過 boot 就是這個意思。

結果另外發現作者提供很多語言版本的 kernel, 除了我想要的 pascal 還有 ada ..., 另外還有其他平台的 bootloader/kernel 版本, 例如 Raspberry Pi4 ...

程式分為兩個部份, 一個是 bootloader, 以 x86 來說有提供 legacy bios, uefi; 另外就是 kernel, 這個就有多語言版本。編譯 pascal 寫的 kernel 要安裝 Free Pascal Compiler (fpc)。

以下介紹的 image 開發環境以 x86-64 uefi 為主, 舊的東西就不說了。其他平台我沒力看了。

編譯 bootboot/x86_64-efi 需要安裝 gnu-efi, 否則會看到缺少 efi.h 的錯誤, 這是一個 efi 開發環境, 有 efi runtime, 讓你不用面對 edk2 這個大傢伙, edk2 太複雜, 僅僅開發 bootloader 用 gnu-efi 就可以。

apt-get install gnu-efi

先來看看怎麼執行裡頭的 image, 以 x86_64-efi 的執行方式, 使用 qemu, 先來把這個 disk-x86.img image 執行起來。

先切到 origin/binaries branch:
git checkout origin/binaries -b binaries
cd images
另外需要 OVMF.fd, qemu 才能使用 uefi, 請自行搜尋下載。
qemu-system-x86_64 -bios OVMF.fd -hda disk-x86.img -net none
這樣就會看到一個圖形畫面, 是 kernel 執行的結果 (fig 2), 整個過程是 uefi bootloader 開機執行之後, load BOOTBOOT/INITRD, INITRD 裡頭有 sys/core, core 就是 kernel。

fig 2 預設 image 執行結果


影片是整個執行過程:


由於我已經會用 c 寫 uefi loader, 然後載入 kernel, 但看看他設計的方式也不錯。但讓我真的更感興趣的是用 pascal 寫的 kernel。

再來看看 disk-x86.img, 如何更新編譯後的 kernel? 步驟有點煩雜, 如果對於 uefi, linux initrd 不熟, 可能會難倒你, 需要使用 cpio。我花了一些功夫理解整個 disk-x86.img 的設計。

fig 3. pascal 寫的 kernel

fig 3 是我修改 pascal kernel 多印一個 pascal 字樣, 以示區別。再來介紹如何更新 image, 換上自己編譯的 kernel。

cd bootboot/mykernel/pas
makefile mykernel.x86_64.elf

如果失敗, 可以參考 list 1 的改法。

list 1. fpc 編譯指令
fpc -Px86_64 -Aelf -n -O3 -Xd -CX -XXs -Tlinux kernel.pas
ld -r -b binary -o font.o font.psf
ld -nostdlib -T link.ld kernel.o font.o -o mykernel.x86_64.elf
strip -s -K mmio -K fb -K bootboot -K environment -K initstack mykernel.x86_64.elf
readelf -hls mykernel.x86_64.elf > mykernel.x86_64.txt

Makefile 寫的有些錯誤, 在我的環境無法編譯, list 1 是我修正後的結果。

首先用以下指令把 disk-x86.img image 解出來。
# fdisk -lu disk-x86.img
Disk disk-x86.img: 128 MiB, 134217728 bytes, 262144 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 66AEEDA9-9F6A-2448-34BD-DEE6347D5A4E

Device        Start   End Sectors Size Type
disk-x86.img1   128 32895   32768  16M EFI System

losetup  /dev/loop1 disk-x86.img -o $((128*512))
mount /dev/loop1 a/
list 2 disk-x86.img 目錄結構
root@debian-vm:/home/descent/git/images/img# tree
.
├── BOOTBOOT
│   ├── CONFIG
│   ├── INITRD
│
├── BOOTBOOT.BIN
├── EFI
│   └── BOOT
│       └── BOOTX64.EFI
└── NvVars

整個流程是這樣: uefi 開機之後 load BOOTX64.EFI, BOOTX64.EFI, 會去找 BOOTBOOT/INITRD, INITRD 是一個 cpio 再 gzip 之後的檔案, 類似 linux 的 initrd, 裡頭長這樣:

list 3 BOOTBOOT/INITRD 內容
descent@debian-vm:a$ tree 
.
└── sys
    └─── core

sys/core 就是編譯出來的 pascal kernel。所以 BOOTX64.EFI load BOOTBOOT/INITRD 之後再去執行 sys/core。

解開 BOOTBOOT/INITRD:
mv INITRD INITRD.gz
gzip -d INITRD.gz
mkdir a
cd a
cpio -idv < ../INITRD
就會看到 list 3 的目錄結構, 把編譯好的 pascal kernel 複製到 sys/core 即可, 然後製作 cpio 檔案。
cd a
find . | cpio -ov > /tmp/object.cpio
gzip /tmp/object.cpio
cp /tmp/object.cpio.gz BOOTBOOT/INITRD

再來執行 qemu, 不需要製作 image 的檔案, qemu 可以直結讀取一個目錄當作 image file,
qemu-system-x86_64 -drive if=ide,file=fat:rw:img,index=0,media=disk -bios OVMF.fd -net none

img 就是我解開 disk-x86.img 的目錄。如果不熟 linux 這些 image 指令操作, 相信夠你忙一陣子。

另外也可自己打指令把 uefi loader 執行起來。


另外 disk-x86.img 似乎也同時支援 legacy bios, qemu-system-x86_64 -hda disk-x86.img 也可以看到 kernel 畫面。

至於我有興趣的 pascal kernel, 果然對 pascal 不熟, 有很多東西看不懂, 但大多是語法的部份, 關鍵的地方我已經弄懂, 本來應該自己挑戰一個試試, 不過我得先把時間花在別的地方。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。