fix(build): make the packed image actually bootable

There were three independent reasons a fresh build produced an image that
OVMF would not boot. All three are fixed here; verified by booting the
resulting ISO/deshab-dev.img under QEMU/OVMF through to FirstInit.

1. ESP rejected by OVMF (CODE/linux/pack_system_image.sh)
   The vfat formatter was invoked with an explicit -s 8, forcing 4KiB clusters.
   On the 256MiB ESP that yields exactly 65535 clusters - the FAT32 0xFFF5
   boundary - and OVMF's FatPkg refuses to mount the volume. Symptom: BdsDxe
   logs "failed to load ... Not Found", the ESP shows up as BLKn with no FS in
   the UEFI Shell, and the firmware falls through to the internal shell.
   The sidecar FAT32 image (make_sata_sidecar.sh) mounts fine because it calls
   the formatter without -s. Drop -s here too and let the tool pick geometry.
   Do NOT add -f 1: OVMF mounts nfat=1 and nfat=2 alike, it was not the cause.

2. Limine dropped into a panic (CODE/linux/pack_system_image.sh)
   limine.conf declares the Linux-compat guest modules. build.sh uncomments
   them once Linux has been built, but nothing ever comments them back, so the
   file says "load boot():/boot/linux-initrd.img" while only linux-bzImage
   exists on disk - Limine panics on the missing module and never reaches the
   kernel. Filter the guest module_path lines against SYSTEM/boot/ at pack
   time and write the result to the ESP. Both the root /limine.conf and
   /limine/limine.conf are written, since Limine 10 prefers the latter. The
   tracked source file is left untouched.

3. Nine drivers were never compiled (build.ps1)
   Commit 055c521 untracked the .drv binaries on the grounds that they are
   regenerable, but build.ps1 only ever invoked Build-DkmDriver for 8 of the
   17 drivers listed in manifest.json. timer/acpi/pci/ahci/bootfs/vfs/fat32/
   devfs/ps2kbd had sources under CODE/DKM/ and no build step, so the image
   shipped without them and Limine failed on the first one. Add the missing
   compile steps, grouped by load stage. All 17 now match manifest.json.

Also in this commit (environment/tooling hygiene):
- check_env.sh: stop hardcoding /mnt/d/Code/Deshab (the original author's
  machine); derive the repo root from the script's own location. It was
  reporting a bogus "wsl_mount" failure on every other checkout and sending
  people to install packages that were already present. Also check and
  advertise the image-packing tools (dosfstools/e2fsprogs/fdisk/rsync), which
  were missing from the dependency list even though pack_system_image.sh
  hard-requires them.
- build_initrd.sh: same hardcoded-path fix.
- README.md: relative path instead of the stale absolute one.

Verified: ./build.bat -Variant dev under WSL2/Ubuntu, then QEMU/OVMF boots
through BdsDxe -> Limine -> UTSM -> DKM (17/17) -> DSK -> FirstInit, with the
PS/2 mouse initialising cleanly.
This commit is contained in:
boonxiong2
2026-10-05 14:03:39 +08:00
parent ee8b607f9a
commit 1c99c77b3f
5 changed files with 109 additions and 20 deletions
+2 -2
View File
@@ -24,8 +24,8 @@ the UTSM hypervisor (VMX root).
### 1. From WSL
```bash
# Navigate to the project (adjust path for your WSL mount)
cd /mnt/d/Code/Deshab/CODE/linux
# Navigate to the project from your repo root in WSL
cd CODE/linux
# Make the build script executable
chmod +x build.sh
+3 -2
View File
@@ -2,8 +2,9 @@
# Build initramfs only (assumes bzImage already built)
set -e
SCRIPT_DIR="/mnt/d/Code/Deshab/CODE/linux"
PROJECT_ROOT="/mnt/d/Code/Deshab"
# 按脚本自身位置推导,不认死盘符路径。
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
OUTPUT_DIR="$PROJECT_ROOT/SYSTEM/boot"
INITRAMFS_DIR="$SCRIPT_DIR/initramfs"
LINUX_SRC="${LINUX_SRC:-$HOME/linux-6.6}"
+25 -8
View File
@@ -5,7 +5,7 @@
# 用途: 在 WSL/Linux 中运行,验证构建 Linux 6.6 LTS bzImage 所需依赖
#
# 使用:
# ! wsl bash /mnt/d/Code/Deshab/CODE/linux/check_env.sh
# ! wsl bash CODE/linux/check_env.sh
#
# 退出码:
# 0 = 所有依赖已满足
@@ -16,6 +16,10 @@ set -e
echo "[check] Linux 内核构建环境检查"
echo "[check] ==========================="
# 仓库根目录:按脚本自身位置推导,不依赖当前工作目录,也不认死任何盘符路径。
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
echo "[check] 仓库根: $REPO_ROOT"
# 1. 检查操作系统
if [ -f /etc/os-release ]; then
. /etc/os-release
@@ -40,6 +44,18 @@ for pkg in build-essential bc flex bison libelf-dev libssl-dev cpio gzip git; do
fi
done
# 2b. 检查镜像打包工具(pack_system_image.sh 硬依赖,不在上面那组内核依赖里)
echo "[check]"
echo "[check] 检查镜像打包工具..."
for pkg in dosfstools e2fsprogs fdisk rsync; do
if dpkg -s "$pkg" >/dev/null 2>&1; then
echo "[check] ✓ $pkg"
else
echo "[check] ✗ $pkg (缺失)"
MISSING+=("$pkg")
fi
done
# 3. 检查 gcc 版本
echo "[check]"
echo "[check] 工具链版本:"
@@ -60,7 +76,7 @@ fi
# 4. 检查磁盘空间(需要 ~3GB)
echo "[check]"
PROJECT_ROOT="/mnt/d/Code/Deshab"
PROJECT_ROOT="$REPO_ROOT"
if [ -d "$PROJECT_ROOT" ]; then
AVAILABLE_KB=$(df "$PROJECT_ROOT" | awk 'NR==2 {print $4}')
AVAILABLE_GB=$((AVAILABLE_KB / 1024 / 1024))
@@ -73,13 +89,13 @@ if [ -d "$PROJECT_ROOT" ]; then
fi
fi
# 5. 检查访问 Windows D: 盘
# 5. 检查 WSL 能否访问当前仓库(不认死盘符路径)
echo "[check]"
if [ -d "/mnt/d/Code/Deshab" ]; then
echo "[check] ✓ WSL 可访问 /mnt/d/Code/Deshab"
if [ -d "$REPO_ROOT/CODE/linux" ] && [ -d "$REPO_ROOT/SYSTEM" ]; then
echo "[check] ✓ WSL 可访问当前仓库 ($REPO_ROOT)"
else
echo "[check] ✗ WSL 无法访问 /mnt/d/Code/Deshab"
echo "[check] 请确认在 WSL 中执行,且 D: 盘已挂载"
echo "[check] ✗ WSL 无法访问当前仓库 ($REPO_ROOT)"
echo "[check] 请从仓库内运行本脚本,且 Windows 盘已挂载到 /mnt/<盘符>"
MISSING+=("wsl_mount")
fi
@@ -107,11 +123,12 @@ if [ ${#MISSING[@]} -gt 0 ]; then
echo "[check] 请运行以下命令安装依赖:"
echo "[check] sudo apt update"
echo "[check] sudo apt install -y build-essential bc flex bison libelf-dev libssl-dev cpio gzip git"
echo "[check] sudo apt install -y dosfstools e2fsprogs fdisk rsync"
exit 1
fi
echo "[check] ✓ 所有依赖已满足,可以构建 Linux 内核"
echo "[check]"
echo "[check] 下一步执行:"
echo "[check] cd /mnt/d/Code/Deshab/CODE/linux && chmod +x build.sh && ./build.sh"
echo "[check] cd $REPO_ROOT/CODE/linux && chmod +x build.sh && ./build.sh"
exit 0
+59 -8
View File
@@ -167,13 +167,17 @@ if [[ -z "$p1" || -z "$p2" ]]; then
exit 1
fi
# 4KiB clusters on a ~5GiB ESP; 32KiB if the ESP is 4GiB or larger.
fat_cluster_sectors=8
if [[ "$esp_bytes" -ge $((4096 * 1024 * 1024)) ]]; then
fat_cluster_sectors=64
fi
mkfs.vfat -F 32 -n DESHABBOOT -s "$fat_cluster_sectors" "$p1" >/dev/null
# 簇大小交给 mkfs.vfat 自动选择。
#
# 历史坑一:这里曾经强制 -s 8(4KiB 簇)。256MiB 的 ESP 恰好被算成 65535
# 个簇(FAT32 的 0xFFF5 边界),该几何下 OVMF(EDK2 FatPkg) 拒挂该卷 ——
# 现象是 BdsDxe "Not Found",Shell 里分区只显示 BLKn、没有对应 FS。
# 对照实验:sidecar(CODE/linux/make_sata_sidecar.sh)用不带 -s 的
# mkfs.vfat 就一切正常,所以这里也不再手工指定簇大小。
#
# 历史坑二:不要加 -f 1。UEFI 规范建议 ESP 用单 FAT 表,但实测 OVMF 对
# nfat=1/2 都能挂,加 -f 1 并不能解决上面的问题,反而偏离 sidecar 的行为。
mkfs.vfat -F 32 -n DESHABBOOT "$p1" >/dev/null
# DSK 内嵌 ext4 读写驱动支持范围:4K block / 256B inode / extent 树,
# 不支持 journal、metadata_csum、64bit(遇到即 panic FS-E28)——
# 因此这里必须显式关闭这三个 feature。
@@ -190,7 +194,54 @@ echo "[pack] copying p1 FAT32 ESP (EFI/ limine/ boot/ driver/ system/)"
# vfat has no Unix owners/perms; -a would fail with rsync code 23.
rsync -rltD --info=progress2 "$SYSTEM/EFI/" "$MNT_ESP/EFI/"
rsync -rltD --info=progress2 "$SYSTEM/limine/" "$MNT_ESP/limine/"
cp -f "$SYSTEM/limine/limine.conf" "$MNT_ESP/limine.conf"
# limine.conf 的 Linux 兼容层 module_path 段必须与 SYSTEM/boot/ 的实际产物一致。
# CODE/linux/build.sh 建完 Linux 内核会自动取消那几行注释,但没有任何地方会关回去;
# 而 055c521 之后 .drv / Linux 产物都不入库,于是「注释开着但文件不存在」会让
# Limine 直接 panic(Failed to open module ...),系统根本进不去。
# 这里按实际存在的文件过滤后再写进 ESP —— 源文件 SYSTEM/limine/limine.conf 保持不动。
#
# 注意:Limine 10 优先读 ESP 上的 /limine/limine.conf(目录形式),其次才是
# 根目录 /limine.conf。上面 rsync 已经把未过滤的原版拷进 /limine/ 了,所以
# 两处都必须写过滤后的副本,否则改根目录那份不起作用。
filter_limine_conf() {
local dst="$1"
python3 - "$SYSTEM/limine/limine.conf" "$SYSTEM/boot" "$dst" <<'PYEOF'
import os, re, sys
src, bootdir, dst = sys.argv[1], sys.argv[2], sys.argv[3]
# Linux/guest 内核段的 module_path(其它 module_path 靠路径前缀区分)
GUEST_PREFIXES = ('/boot/linux-', '/boot/xj380.krl')
lines = open(src, 'r', encoding='utf-8').read().splitlines(keepends=True)
out, dropped, kept = [], [], []
for line in lines:
m = re.match(r'^(\s*)module_path:\s*boot\(\):(\S+)\s*$', line)
if m and m.group(2).startswith(GUEST_PREFIXES):
host = os.path.join(bootdir, m.group(2).split('/boot/', 1)[1])
if os.path.exists(host):
kept.append(m.group(2))
out.append(line)
else:
# 产物不存在 → 注释掉,避免 Limine 加载失败 panic
out.append('%s# [pack] module missing on disk, disabled: %s' % (m.group(1), line))
dropped.append(m.group(2))
else:
out.append(line)
open(dst, 'w', encoding='utf-8').write(''.join(out))
for p in kept:
print('[pack] limine module kept: %s' % p)
for p in dropped:
print('[pack] limine module DISABLED (file absent): %s' % p)
PYEOF
}
mkdir -p "$MNT_ESP/limine"
filter_limine_conf "$MNT_ESP/limine.conf" # 根目录副本
filter_limine_conf "$MNT_ESP/limine/limine.conf" # Limine 10 实际优先读的那份
rsync -rltD --info=progress2 "$SYSTEM/boot/" "$MNT_ESP/boot/"
rsync -rltD --info=progress2 "$SYSTEM/driver/" "$MNT_ESP/driver/"
rsync -rltD --info=progress2 "$SYSTEM/system/" "$MNT_ESP/system/"
+20
View File
@@ -961,20 +961,40 @@ foreach ($tool in $toolApps) {
Write-Host '[build] Building DKM framebuffer driver...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\console_fb\console_fb.c') (Join-Path $SystemDir 'driver\console\console_fb.drv')
# stage0: platform + bus — timer 提供 PIT 校准,acpi 枚举 RSDP/XSDT,pci 扫描配置空间。
# 这三个驱动 manifest.json 里是必需的;055c521 把它们从 git 移除后此处必须重新编译,
# 否则 limine 会 panic「Failed to open module boot():/driver/platform/timer.drv」。
Write-Host '[build] Building DKM platform/bus drivers...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\timer\timer.c') (Join-Path $SystemDir 'driver\platform\timer.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\acpi\acpi.c') (Join-Path $SystemDir 'driver\platform\acpi.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\pci\pci.c') (Join-Path $SystemDir 'driver\bus\pci.drv')
# B7: apic.drv 纳入构建 (原 platform/apic.drv 为预置二进制, 不随 apic.c 更新)
Write-Host '[build] Building DKM platform apic driver...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\apic\apic.c') (Join-Path $SystemDir 'driver\platform\apic.drv')
Write-Host '[build] Building DKM storage drivers...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\ahci\ahci.c') (Join-Path $SystemDir 'driver\block\ahci.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\nvme\nvme.c') (Join-Path $SystemDir 'driver\block\nvme.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\ehci\ehci.c') (Join-Path $SystemDir 'driver\block\ehci.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\xhci\xhci.c') (Join-Path $SystemDir 'driver\block\xhci.drv')
# stage2: filesystem — bootfs 挂 boot module,vfs 汇总,fat32 只读 FAT32,devfs 暴露 /dev。
Write-Host '[build] Building DKM filesystem drivers...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\bootfs\bootfs.c') (Join-Path $SystemDir 'driver\fs\bootfs.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\vfs\vfs.c') (Join-Path $SystemDir 'driver\fs\vfs.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\fat32\fat32.c') (Join-Path $SystemDir 'driver\fs\fat32.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\devfs\devfs.c') (Join-Path $SystemDir 'driver\fs\devfs.drv')
Write-Host '[build] Building DKM network drivers...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\e1000\e1000.c') (Join-Path $SystemDir 'driver\net\e1000.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\virtio_net\virtio_net.c') (Join-Path $SystemDir 'driver\net\virtio_net.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\ath9k\ath9k.c') (Join-Path $SystemDir 'driver\net\ath9k.drv')
# stage3: input — ps2kbd 注册 IRQ1。
Write-Host '[build] Building DKM input drivers...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\ps2kbd\ps2kbd.c') (Join-Path $SystemDir 'driver\input\ps2kbd.drv')
Write-Host "[build] Output: $Output"
Write-Host "[build] DSK Output: $DskOutput"
Write-Host "[build] Variant: $Variant"