206 lines
7.0 KiB
Markdown
206 lines
7.0 KiB
Markdown
# Phase 18.3: bootable Fruix installer ISO on FreeBSD
|
|
|
|
Date: 2026-04-04
|
|
|
|
## Goal
|
|
|
|
Phase 18.3 extends the Phase 18.2 installer-environment work from a disk-image-style installer into a UEFI-bootable ISO artifact.
|
|
|
|
The intended first ISO is deliberately narrow:
|
|
|
|
- UEFI only
|
|
- serial-console-friendly
|
|
- non-interactive install flow reused from Phase 18.1/18.2
|
|
- target disk installation still performed by the same Fruix-managed in-guest installer logic
|
|
|
|
## Implementation
|
|
|
|
### New API
|
|
|
|
Added in `modules/fruix/system/freebsd.scm`:
|
|
|
|
- `operating-system-installer-iso-spec`
|
|
- `materialize-installer-iso`
|
|
|
|
The system module split done immediately before this phase was also exercised during this work.
|
|
|
|
### New CLI action
|
|
|
|
Added in `scripts/fruix.scm`:
|
|
|
|
- `fruix system installer-iso`
|
|
|
|
This action emits metadata for:
|
|
|
|
- ISO store path
|
|
- ISO image path
|
|
- EFI boot image path
|
|
- installer root image path
|
|
- installer and target closure paths
|
|
- installer state/log paths
|
|
- declared/materialized FreeBSD source metadata
|
|
- store closure counts
|
|
|
|
### ISO boot model
|
|
|
|
The ISO does not try to run the Fruix installer directly from a read-only cd9660 root.
|
|
|
|
Instead it uses a small UEFI El Torito boot image plus an in-memory installer root image:
|
|
|
|
1. a small FAT EFI boot image contains `EFI/BOOT/BOOTX64.EFI`
|
|
2. the ISO root contains real boot assets under `/boot`
|
|
3. the ISO root also contains `/boot/root.img`
|
|
4. `loader.conf` on the ISO is augmented with:
|
|
- `mdroot_load="YES"`
|
|
- `mdroot_type="mfs_root"`
|
|
- `mdroot_name="/boot/root.img"`
|
|
- `vfs.root.mountfrom="ufs:/dev/md0"`
|
|
- `vfs.root.mountfrom.options="rw"`
|
|
|
|
A practical loader detail surfaced during validation:
|
|
|
|
- setting `rootdev` or `currdev` to `md0:` in the ISO loader path is wrong for this loader configuration and caused an early EFI-loader crash before kernel handoff
|
|
- the reliable ISO path is to let loader keep its current device on the CD media, preload `/boot/root.img`, and pass only `vfs.root.mountfrom=ufs:/dev/md0`
|
|
|
|
This preserves the existing Fruix installer environment semantics while avoiding the need to make the whole installer operate directly from a read-only ISO root.
|
|
|
|
### Installer root image contents
|
|
|
|
`materialize-installer-iso` stages the same installer payload model already validated in Phase 18.2:
|
|
|
|
- installer closure
|
|
- target closure
|
|
- target runtime store closure needed for installation/boot
|
|
- staged target rootfs under `/var/lib/fruix/installer/target-rootfs`
|
|
- installer plan and state files under `/var/lib/fruix/installer`
|
|
- installer helper scripts:
|
|
- `/usr/local/libexec/fruix-installer-run`
|
|
- `/usr/local/etc/rc.d/fruix-installer`
|
|
|
|
The ISO root image is then built as a UFS image and embedded as `/boot/root.img`.
|
|
|
|
### Split-regression fixes found during this work
|
|
|
|
While exercising the refactored split modules, two issues surfaced and were fixed:
|
|
|
|
1. `string-hash` name-clash warnings
|
|
- the old helper name collided with Guile/SRFI bindings
|
|
- it was renamed to `sha256-string`
|
|
2. missing `prefix-materializer-version`
|
|
- this constant was accidentally omitted when `modules/fruix/system/freebsd.scm` was split
|
|
- the missing definition was restored in `modules/fruix/system/freebsd/build.scm`
|
|
|
|
## Validation
|
|
|
|
### Completed smoke validation
|
|
|
|
A host-side smoke build was completed successfully for the new ISO builder using a host-staged operating-system definition:
|
|
|
|
- command pattern:
|
|
- `fruix system installer-iso ...`
|
|
- result:
|
|
- successful ISO materialization in a temporary store
|
|
- artifact checks performed:
|
|
- `etdump` reports an EFI El Torito boot entry
|
|
- the ISO contains:
|
|
- `boot/kernel/kernel`
|
|
- `boot/kernel/linker.hints`
|
|
- `boot/loader.conf`
|
|
- `boot/loader.efi`
|
|
- `boot/root.img`
|
|
- `boot/loader.conf` inside the ISO contains the expected `mdroot_*` and `vfs.root.mountfrom` entries
|
|
|
|
Example smoke-build metadata:
|
|
|
|
```text
|
|
action=installer-iso
|
|
iso_volume_label=FRUIX_INSTALLER
|
|
iso_store_path=/tmp/...-fruix-installer-iso-fruix-freebsd-installer
|
|
iso_image=/tmp/...-fruix-installer-iso-fruix-freebsd-installer/installer.iso
|
|
boot_efi_image=/tmp/...-fruix-installer-iso-fruix-freebsd-installer/efiboot.img
|
|
root_image=/tmp/...-fruix-installer-iso-fruix-freebsd-installer/root.img
|
|
installer_closure_path=/tmp/...-fruix-system-fruix-freebsd-installer
|
|
target_closure_path=/tmp/...-fruix-system-fruix-freebsd
|
|
```
|
|
|
|
### End-to-end harness validation
|
|
|
|
Added:
|
|
|
|
- `tests/system/run-phase18-installer-iso.sh`
|
|
|
|
This harness validates the full Phase 18.3 flow:
|
|
|
|
1. build installer ISO
|
|
2. boot it under QEMU/UEFI/TCG
|
|
3. install onto a target disk from inside the booted ISO environment
|
|
4. boot the installed target
|
|
|
|
Passing validation:
|
|
|
|
- `PASS phase18-installer-iso`
|
|
|
|
Validated result summary:
|
|
|
|
```text
|
|
installer_iso_store_path=/frx/store/...-fruix-installer-iso-fruix-freebsd-installer
|
|
installer_iso_image=/frx/store/...-fruix-installer-iso-fruix-freebsd-installer/installer.iso
|
|
installer_boot_efi_image=/frx/store/...-fruix-installer-iso-fruix-freebsd-installer/efiboot.img
|
|
installer_root_image=/frx/store/...-fruix-installer-iso-fruix-freebsd-installer/root.img
|
|
install_target_device=/dev/vtbd0
|
|
freebsd_source_kind=git
|
|
freebsd_source_ref=stable/15
|
|
freebsd_source_commit=332708a606f6bf0841c1d4a74c0d067f5640fe89
|
|
materialized_source_store=/frx/store/459499e0eb29f4c73ad455060dd2502d21fb56f205c0a676831cf723b3a0c378-freebsd-source-stable15-installer-iso-target-source
|
|
installer_state=done
|
|
installer_sshd_status=running
|
|
target_esp_fstype=msdosfs
|
|
target_root_fstype=ufs
|
|
target_shepherd_status=running
|
|
target_sshd_status=running
|
|
installer_iso_boot=ok
|
|
installer_iso_install=ok
|
|
installed_target_boot=ok
|
|
```
|
|
|
|
Notable ISO-specific validation detail:
|
|
|
|
- unlike the disk-image-style installer environment from Phase 18.2, the ISO boots from `cd0`, so the target virtio disk appears as:
|
|
- `/dev/vtbd0`
|
|
- the earlier installer-environment default:
|
|
- `/dev/vtbd1`
|
|
remains correct for the disk-image installer, but not for the ISO path
|
|
|
|
The harness verified all of the following:
|
|
|
|
1. `fruix system installer-iso` produces a bootable ISO artifact in `/frx/store`
|
|
2. the ISO boots successfully under QEMU/UEFI/TCG
|
|
3. the booted installer ISO environment becomes reachable over SSH
|
|
4. `/run/current-system` inside the installer ISO points at the installer closure
|
|
5. the installer rc.d job reaches:
|
|
- `state=done`
|
|
6. the installer log records:
|
|
- `fruix-installer:done`
|
|
7. the installed target disk contains:
|
|
- GPT partitioning
|
|
- EFI filesystem: `msdosfs`
|
|
- root filesystem: `ufs`
|
|
- `EFI/BOOT/BOOTX64.EFI`
|
|
- `/var/lib/fruix/install.scm`
|
|
8. the installed target then boots successfully as its own Fruix system under QEMU/UEFI/TCG
|
|
9. after target boot:
|
|
- `/run/current-system` points at the target closure
|
|
- shepherd is running
|
|
- `sshd` is running
|
|
- activation completed successfully
|
|
|
|
## Result
|
|
|
|
Phase 18.3 is complete.
|
|
|
|
Fruix now has a validated bootable UEFI installer ISO on FreeBSD that can:
|
|
|
|
- boot into a Fruix-managed installer environment from ISO media
|
|
- perform the non-interactive installation flow onto a target disk
|
|
- and boot the installed target successfully
|