diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 3eca87c..e20e6ec 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -100,6 +100,6 @@ Commit: Per `docs/PLAN_4.md`, the next planned step is: -- **Phase 18.3** — produce a bootable UEFI installer ISO +- **Phase 19.1** — define and document the canonical Fruix deployment workflow for rebuild, image generation, installation, and rollback -That should build on the now-validated installer environment rather than replacing it. +Phase 18.3 is now complete: Fruix can build, boot, install from, and validate a bootable UEFI installer ISO on FreeBSD. diff --git a/docs/reports/phase18-installer-iso-freebsd.md b/docs/reports/phase18-installer-iso-freebsd.md index b6ebf39..feba27e 100644 --- a/docs/reports/phase18-installer-iso-freebsd.md +++ b/docs/reports/phase18-installer-iso-freebsd.md @@ -52,12 +52,16 @@ Instead it uses a small UEFI El Torito boot image plus an in-memory installer ro 3. the ISO root also contains `/boot/root.img` 4. `loader.conf` on the ISO is augmented with: - `mdroot_load="YES"` - - `mdroot_type="md_image"` + - `mdroot_type="mfs_root"` - `mdroot_name="/boot/root.img"` - - `rootdev="ufs:/dev/md0"` - `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 @@ -66,7 +70,7 @@ This preserves the existing Fruix installer environment semantics while avoiding - installer closure - target closure -- target store 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: @@ -86,7 +90,7 @@ While exercising the refactored split modules, two issues surfaced and were fixe - 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` -## Current validation status +## Validation ### Completed smoke validation @@ -119,21 +123,83 @@ installer_closure_path=/tmp/...-fruix-system-fruix-freebsd-installer target_closure_path=/tmp/...-fruix-system-fruix-freebsd ``` -### Validation harness added +### End-to-end harness validation Added: - `tests/system/run-phase18-installer-iso.sh` -This harness is intended to validate the full Phase 18.3 flow: +This harness validates the full Phase 18.3 flow: 1. build installer ISO 2. boot it under QEMU/UEFI/TCG -3. install onto a second disk from inside the booted ISO environment +3. install onto a target disk from inside the booted ISO environment 4. boot the installed target -## Status +Passing validation: -Phase 18.3 implementation is now in place, with successful build-smoke validation and a dedicated end-to-end harness added. +- `PASS phase18-installer-iso` -The remaining step is full end-to-end boot/install validation of the ISO path under QEMU/UEFI/TCG and, if practical, the broader validated virtualization path. +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 diff --git a/modules/fruix/system/freebsd/media.scm b/modules/fruix/system/freebsd/media.scm index 3fdccbe..a6704a4 100644 --- a/modules/fruix/system/freebsd/media.scm +++ b/modules/fruix/system/freebsd/media.scm @@ -464,7 +464,7 @@ (define* (operating-system-installer-iso-spec os #:key - (install-target-device "/dev/vtbd1") + (install-target-device "/dev/vtbd0") (installer-host-name (string-append (operating-system-host-name os) "-installer")) (root-size #f) @@ -494,7 +494,7 @@ (define image-builder-version "2") (define install-builder-version "1") (define installer-image-builder-version "1") -(define installer-iso-builder-version "1") +(define installer-iso-builder-version "2") (define (operating-system-install-metadata-object install-spec closure-path store-items) `((install-version . ,install-builder-version) @@ -1091,14 +1091,21 @@ (substring sanitized 0 32) sanitized))) +(define (source-store-item? item) + (string-contains (path-basename item) "-freebsd-source-")) + +(define (runtime-store-items items) + (filter (lambda (item) + (not (source-store-item? item))) + items)) + (define (write-installer-iso-loader-conf source-path destination) (let* ((mode (stat:perms (stat source-path))) (base (call-with-input-file source-path get-string-all)) (extra (string-append "mdroot_load=\"YES\"\n" - "mdroot_type=\"md_image\"\n" + "mdroot_type=\"mfs_root\"\n" "mdroot_name=\"/boot/root.img\"\n" - "rootdev=\"ufs:/dev/md0\"\n" "vfs.root.mountfrom=\"ufs:/dev/md0\"\n" "vfs.root.mountfrom.options=\"rw\"\n"))) (write-file destination @@ -1110,6 +1117,14 @@ extra)) (chmod destination mode))) +(define (rewrite-installer-iso-fstab image-rootfs installer-closure-path) + (let ((fstab-path (string-append image-rootfs "/frx/store/" + (path-basename installer-closure-path) + "/etc/fstab"))) + (rewrite-text-file fstab-path + '(("/dev/gpt/fruix-installer-root\t/\tufs" + . "/dev/md0\t/\tufs"))))) + (define* (make-ufs-image output-path source-root label #:key size) (apply run-command (append (list "makefs" "-t" "ffs" "-T" "0" "-B" "little") @@ -1167,7 +1182,7 @@ (guile-prefix "/tmp/guile-freebsd-validate-install") (guile-extra-prefix "/tmp/guile-gnutls-freebsd-validate-install") (shepherd-prefix "/tmp/shepherd-freebsd-validate-install") - (install-target-device "/dev/vtbd1") + (install-target-device "/dev/vtbd0") (root-size #f) (installer-host-name (string-append (operating-system-host-name os) "-installer")) @@ -1191,9 +1206,11 @@ #:shepherd-prefix shepherd-prefix)) (target-closure-path (assoc-ref target-closure 'closure-path)) (installer-closure-path (assoc-ref installer-closure 'closure-path)) - (target-store-items (store-reference-closure (list target-closure-path))) - (installer-store-items (store-reference-closure (list installer-closure-path))) - (combined-store-items (delete-duplicates (append installer-store-items target-store-items))) + (target-closure-store-items (store-reference-closure (list target-closure-path))) + (target-runtime-store-items (runtime-store-items target-closure-store-items)) + (installer-store-items (runtime-store-items + (store-reference-closure (list installer-closure-path)))) + (combined-store-items (delete-duplicates (append installer-store-items target-runtime-store-items))) (sanitized-iso-volume-label (sanitize-iso-volume-label iso-volume-label)) (installer-iso-spec (operating-system-installer-iso-spec os #:install-target-device install-target-device @@ -1207,7 +1224,7 @@ (target-install-spec (assoc-ref installer-iso-spec 'target-install)) (install-metadata (operating-system-install-metadata-object target-install-spec target-closure-path - target-store-items)) + target-closure-store-items)) (installer-plan-directory "/var/lib/fruix/installer") (installer-state-path (string-append installer-plan-directory "/state")) (installer-log-path "/var/log/fruix-installer.log") @@ -1223,7 +1240,7 @@ "\ncombined-store-items=\n" (string-join combined-store-items "\n") "\ntarget-store-items=\n" - (string-join target-store-items "\n") + (string-join target-closure-store-items "\n") "\ninstall-metadata=\n" (object->string install-metadata) "\n")) @@ -1257,7 +1274,7 @@ (copy-tree-contents target-rootfs (string-append plan-root "/target-rootfs")) (copy-store-items-into-rootfs image-rootfs store-dir combined-store-items) (write-file (string-append plan-root "/store-items") - (string-append (string-join (map path-basename target-store-items) "\n") "\n")) + (string-append (string-join (map path-basename target-runtime-store-items) "\n") "\n")) (write-file (string-append plan-root "/install.scm") (object->string install-metadata)) (copy-regular-file (string-append target-closure-path "/boot/loader.efi") @@ -1276,6 +1293,7 @@ (render-installer-rc-script installer-plan-directory)) (chmod (string-append image-rootfs "/usr/local/libexec/fruix-installer-run") #o555) (chmod (string-append image-rootfs "/usr/local/etc/rc.d/fruix-installer") #o555) + (rewrite-installer-iso-fstab image-rootfs installer-closure-path) (make-ufs-image temp-root image-rootfs installer-root-partition-label #:size root-size) (populate-installer-iso-boot-tree installer-closure-path iso-root temp-root) (make-efi-boot-image (resolved-path (string-append installer-closure-path "/boot/loader.efi")) temp-esp) @@ -1334,5 +1352,5 @@ (host-base-provenance-file . ,(assoc-ref target-closure 'host-base-provenance-file)) (store-layout-file . ,(assoc-ref target-closure 'store-layout-file)) (store-items . ,combined-store-items) - (target-store-items . ,target-store-items) + (target-store-items . ,target-closure-store-items) (installer-store-items . ,installer-store-items)))) diff --git a/scripts/fruix.scm b/scripts/fruix.scm index 11ca552..1ff8ead 100644 --- a/scripts/fruix.scm +++ b/scripts/fruix.scm @@ -635,7 +635,7 @@ Common options:\n\ #:guile-prefix guile-prefix #:guile-extra-prefix guile-extra-prefix #:shepherd-prefix shepherd-prefix - #:install-target-device (or install-target-device "/dev/vtbd1") + #:install-target-device (or install-target-device "/dev/vtbd0") #:root-size root-size))) ((string=? action "install") (unless target diff --git a/tests/system/run-phase18-installer-iso.sh b/tests/system/run-phase18-installer-iso.sh index 18f9d5a..14da9af 100755 --- a/tests/system/run-phase18-installer-iso.sh +++ b/tests/system/run-phase18-installer-iso.sh @@ -9,7 +9,7 @@ system_name=${SYSTEM_NAME:-phase18-target-operating-system} store_dir=${STORE_DIR:-/frx/store} installer_root_size=${INSTALLER_ROOT_SIZE:-} target_disk_capacity=${TARGET_DISK_CAPACITY:-12g} -install_target_device=${INSTALL_TARGET_DEVICE:-/dev/vtbd1} +install_target_device=${INSTALL_TARGET_DEVICE:-/dev/vtbd0} qemu_smp=${QEMU_SMP:-2} installer_memory=${INSTALLER_MEMORY:-6144} installer_ssh_port=${INSTALLER_SSH_PORT:-10027}