From 3472afea3240933a10379231bdffe66260538296 Mon Sep 17 00:00:00 2001 From: Mikhail Chusavitin Date: Wed, 1 Apr 2026 08:33:36 +0300 Subject: [PATCH] fix(iso): make memtest non-blocking by default --- .../2026-04-01-memtest-build-strategy.md | 55 ++++++ bible-local/decisions/README.md | 1 + bible-local/docs/iso-build-rules.md | 25 ++- iso/builder/build.sh | 181 ++++++++++++++---- .../hooks/normal/9100-memtest.hook.binary | 139 ++++++++++++++ 5 files changed, 363 insertions(+), 38 deletions(-) create mode 100644 bible-local/decisions/2026-04-01-memtest-build-strategy.md create mode 100755 iso/builder/config/hooks/normal/9100-memtest.hook.binary diff --git a/bible-local/decisions/2026-04-01-memtest-build-strategy.md b/bible-local/decisions/2026-04-01-memtest-build-strategy.md new file mode 100644 index 0000000..da0a89c --- /dev/null +++ b/bible-local/decisions/2026-04-01-memtest-build-strategy.md @@ -0,0 +1,55 @@ +# Decision: Treat memtest as explicit ISO content, not as trusted live-build magic + +**Date:** 2026-04-01 +**Status:** active + +## Context + +We have already iterated on `memtest` multiple times and kept cycling between the same ideas. +The commit history shows several distinct attempts: + +- `f91bce8` — fixed Bookworm memtest file names to `memtest86+x64.bin` / `memtest86+x64.efi` +- `5857805` — added a binary hook to copy memtest files from the build tree into the ISO root +- `f96b149` — added fallback extraction from the cached `.deb` when `chroot/boot/` stayed empty +- `d43a9ae` — removed the custom hook and switched back to live-build built-in memtest integration +- `60cb8f8` — restored explicit memtest menu entries and added ISO validation +- `3dbc218` / `3869788` — added archived build logs and better memtest diagnostics + +Current evidence from the archived `easy-bee-nvidia-v3.14-amd64` logs dated 2026-04-01: + +- `lb binary_memtest` does run and installs `memtest86+` +- but the final ISO still does **not** contain `boot/memtest86+x64.bin` +- the final ISO also does **not** contain memtest menu entries in `boot/grub/grub.cfg` or `isolinux/live.cfg` + +So the assumption "live-build built-in memtest integration is enough on this stack" is currently false for this project until proven otherwise by a real built ISO. + +## Decision + +For `bee`, memtest must be treated as an explicit ISO artifact with explicit post-build validation. + +Project rules from now on: + +- Do **not** trust `--memtest memtest86+` by itself. +- A memtest implementation is considered valid only if the produced ISO actually contains: + - `boot/memtest86+x64.bin` + - `boot/memtest86+x64.efi` + - a GRUB menu entry + - an isolinux menu entry +- If live-build built-in integration does not produce those artifacts, use an explicit project-owned mechanism such as: + - a binary hook copying files into `binary/boot/` + - extraction from the cached `memtest86+` `.deb` + - another deterministic build-time copy step +- Do **not** remove such explicit logic later unless a fresh real ISO build proves that built-in integration alone produces all required files and menu entries. + +Current implementation direction: + +- keep the live-build memtest stage enabled if it helps package acquisition +- but enforce memtest explicitly in a project-owned binary hook +- patch the generated `binary/boot/grub/grub.cfg` and `binary/isolinux/live.cfg` directly in the binary stage if memtest entries are missing + +## Consequences + +- Future memtest changes must begin by reading this ADR and the commits listed above. +- We should stop re-introducing "prefer built-in live-build memtest" as a default assumption without new evidence. +- Memtest validation in `build.sh` is not optional; it is the acceptance gate that prevents another silent regression. +- If we change memtest strategy again, we must update this ADR with the exact build evidence that justified the change. diff --git a/bible-local/decisions/README.md b/bible-local/decisions/README.md index 72a2042..38b7485 100644 --- a/bible-local/decisions/README.md +++ b/bible-local/decisions/README.md @@ -5,3 +5,4 @@ One file per decision, named `YYYY-MM-DD-short-topic.md`. | Date | Decision | Status | |---|---|---| | 2026-03-05 | Use NVIDIA proprietary driver | active | +| 2026-04-01 | Treat memtest as explicit ISO content | active | diff --git a/bible-local/docs/iso-build-rules.md b/bible-local/docs/iso-build-rules.md index 8318698..1ef1144 100644 --- a/bible-local/docs/iso-build-rules.md +++ b/bible-local/docs/iso-build-rules.md @@ -17,6 +17,25 @@ This applies to: ## Memtest rule -Prefer live-build's built-in memtest integration over custom hooks or hardcoded -bootloader paths. If you ever need to reference memtest files manually, verify -the exact package file list first for the target Debian release. +Do not assume live-build's built-in memtest integration is sufficient for `bee`. +We already tried that path and regressed again on 2026-04-01: `lb binary_memtest` +ran, but the final ISO still lacked memtest binaries and menu entries. + +For this project, memtest is accepted only when the produced ISO actually +contains all of the following: + +- `boot/memtest86+x64.bin` +- `boot/memtest86+x64.efi` +- a memtest entry in `boot/grub/grub.cfg` +- a memtest entry in `isolinux/live.cfg` + +Rules: + +- Keep explicit post-build memtest validation in `build.sh`. +- If built-in integration does not produce the artifacts above, use a + deterministic project-owned copy/extract step instead of hoping live-build + will "start working". +- Do not switch back to built-in-only memtest without fresh build evidence from + a real ISO. +- If you reference memtest files manually, verify the exact package file list + first for the target Debian release. diff --git a/iso/builder/build.sh b/iso/builder/build.sh index 400a991..4a6ebe0 100755 --- a/iso/builder/build.sh +++ b/iso/builder/build.sh @@ -38,6 +38,7 @@ export BEE_GPU_VENDOR . "${BUILDER_DIR}/VERSIONS" export PATH="$PATH:/usr/local/go/bin" +: "${BEE_REQUIRE_MEMTEST:=0}" # Allow git to read the bind-mounted repo (different UID inside container). git config --global safe.directory "${REPO_ROOT}" @@ -177,6 +178,16 @@ dump_memtest_debug() { fi done + echo "-- source binary hooks --" + for hook in \ + "${BUILDER_DIR}/config/hooks/normal/9100-memtest.hook.binary"; do + if [ -f "$hook" ]; then + echo " hook: $hook" + else + echo " (missing $hook)" + fi + done + if [ -n "$lb_dir" ] && [ -d "$lb_dir" ]; then echo "-- live-build workdir package lists --" for pkg in \ @@ -203,6 +214,20 @@ dump_memtest_debug() { echo " (missing $lb_dir/binary/boot)" fi + echo "-- live-build binary grub cfg --" + if [ -f "$lb_dir/binary/boot/grub/grub.cfg" ]; then + grep -n 'Memory Test\|memtest' "$lb_dir/binary/boot/grub/grub.cfg" || echo " (no memtest lines)" + else + echo " (missing $lb_dir/binary/boot/grub/grub.cfg)" + fi + + echo "-- live-build binary isolinux cfg --" + if [ -f "$lb_dir/binary/isolinux/live.cfg" ]; then + grep -n 'Memory Test\|memtest' "$lb_dir/binary/isolinux/live.cfg" || echo " (no memtest lines)" + else + echo " (missing $lb_dir/binary/isolinux/live.cfg)" + fi + echo "-- live-build package cache --" if [ -d "$lb_dir/cache/packages.chroot" ]; then find "$lb_dir/cache/packages.chroot" -maxdepth 1 -name 'memtest86+*.deb' -print | sed 's/^/ /' || true @@ -235,45 +260,75 @@ dump_memtest_debug() { memtest_fail() { msg="$1" iso_path="${2:-}" - echo "ERROR: ${msg}" >&2 + level="WARNING" + if [ "${BEE_REQUIRE_MEMTEST:-0}" = "1" ]; then + level="ERROR" + fi + echo "${level}: ${msg}" >&2 dump_memtest_debug "failure" "${LB_DIR:-}" "$iso_path" >&2 - exit 1 + if [ "${BEE_REQUIRE_MEMTEST:-0}" = "1" ]; then + exit 1 + fi + return 0 } validate_iso_memtest() { iso_path="$1" echo "=== validating memtest in ISO ===" - [ -f "$iso_path" ] || memtest_fail "ISO not found for validation: $iso_path" "$iso_path" - require_iso_reader "$iso_path" + [ -f "$iso_path" ] || { + memtest_fail "ISO not found for validation: $iso_path" "$iso_path" + return 0 + } + require_iso_reader "$iso_path" || return 0 iso_list_files "$iso_path" | grep -q '^boot/memtest86+x64\.bin$' || { memtest_fail "memtest BIOS binary missing in ISO: boot/memtest86+x64.bin" "$iso_path" + return 0 } iso_list_files "$iso_path" | grep -q '^boot/memtest86+x64\.efi$' || { memtest_fail "memtest EFI binary missing in ISO: boot/memtest86+x64.efi" "$iso_path" + return 0 } grub_cfg="$(mktemp)" isolinux_cfg="$(mktemp)" - iso_extract_file "$iso_path" boot/grub/grub.cfg > "$grub_cfg" || memtest_fail "failed to extract boot/grub/grub.cfg from ISO" "$iso_path" - iso_extract_file "$iso_path" isolinux/live.cfg > "$isolinux_cfg" || memtest_fail "failed to extract isolinux/live.cfg from ISO" "$iso_path" + iso_extract_file "$iso_path" boot/grub/grub.cfg > "$grub_cfg" || { + memtest_fail "failed to extract boot/grub/grub.cfg from ISO" "$iso_path" + rm -f "$grub_cfg" "$isolinux_cfg" + return 0 + } + iso_extract_file "$iso_path" isolinux/live.cfg > "$isolinux_cfg" || { + memtest_fail "failed to extract isolinux/live.cfg from ISO" "$iso_path" + rm -f "$grub_cfg" "$isolinux_cfg" + return 0 + } grep -q 'Memory Test (memtest86+)' "$grub_cfg" || { memtest_fail "GRUB menu entry for memtest is missing" "$iso_path" + rm -f "$grub_cfg" "$isolinux_cfg" + return 0 } grep -q '/boot/memtest86+x64\.efi' "$grub_cfg" || { memtest_fail "GRUB memtest EFI path is missing" "$iso_path" + rm -f "$grub_cfg" "$isolinux_cfg" + return 0 } grep -q '/boot/memtest86+x64\.bin' "$grub_cfg" || { memtest_fail "GRUB memtest BIOS path is missing" "$iso_path" + rm -f "$grub_cfg" "$isolinux_cfg" + return 0 } grep -q 'Memory Test (memtest86+)' "$isolinux_cfg" || { memtest_fail "isolinux menu entry for memtest is missing" "$iso_path" + rm -f "$grub_cfg" "$isolinux_cfg" + return 0 } grep -q '/boot/memtest86+x64\.bin' "$isolinux_cfg" || { memtest_fail "isolinux memtest path is missing" "$iso_path" + rm -f "$grub_cfg" "$isolinux_cfg" + return 0 } rm -f "$grub_cfg" "$isolinux_cfg" @@ -292,6 +347,10 @@ cleanup_build_log() { status="${1:-$?}" trap - EXIT INT TERM HUP + if [ "${STEP_LOG_ACTIVE:-0}" = "1" ]; then + cleanup_step_log "${status}" || true + fi + if [ "${BUILD_LOG_ACTIVE:-0}" = "1" ]; then BUILD_LOG_ACTIVE=0 exec 1>&3 2>&4 @@ -335,6 +394,63 @@ start_build_log() { echo "=== build log archive: ${LOG_ARCHIVE} ===" } +cleanup_step_log() { + status="${1:-$?}" + + if [ "${STEP_LOG_ACTIVE:-0}" = "1" ]; then + STEP_LOG_ACTIVE=0 + exec 1>&5 2>&6 + exec 5>&- 6>&- + if [ -n "${STEP_TEE_PID:-}" ]; then + wait "${STEP_TEE_PID}" 2>/dev/null || true + fi + rm -f "${STEP_LOG_PIPE}" + fi + + return "${status}" +} + +run_step() { + step_name="$1" + step_slug="$2" + shift 2 + + step_log="${LOG_DIR}/${step_slug}.log" + echo "" + echo "=== step: ${step_name} ===" + echo "=== step log: ${step_log} ===" + + STEP_LOG_PIPE="$(mktemp -u "${TMPDIR:-/tmp}/bee-step-log.XXXXXX")" + mkfifo "${STEP_LOG_PIPE}" + + exec 5>&1 6>&2 + tee "${step_log}" < "${STEP_LOG_PIPE}" >&5 & + STEP_TEE_PID=$! + exec > "${STEP_LOG_PIPE}" 2>&1 + STEP_LOG_ACTIVE=1 + + set +e + "$@" + step_status=$? + set -e + + cleanup_step_log "${step_status}" + if [ "${step_status}" -ne 0 ]; then + echo "ERROR: step failed: ${step_name} (see ${step_log})" >&2 + exit "${step_status}" + fi + + echo "=== step OK: ${step_name} ===" +} + +run_step_sh() { + step_name="$1" + step_slug="$2" + step_script="$3" + + run_step "${step_name}" "${step_slug}" sh -c "${step_script}" +} + start_build_log # Auto-detect kernel ABI: refresh apt index, then query current linux-image-amd64 dependency. @@ -370,8 +486,8 @@ echo "Debian: ${DEBIAN_VERSION}, Kernel ABI: ${DEBIAN_KERNEL_ABI}, Go: ${GO_VERS echo "Audit version: ${AUDIT_VERSION_EFFECTIVE}, ISO version: ${ISO_VERSION_EFFECTIVE}" echo "" -echo "=== syncing git submodules ===" -git -C "${REPO_ROOT}" submodule update --init --recursive +run_step "sync git submodules" "05-git-submodules" \ + git -C "${REPO_ROOT}" submodule update --init --recursive # --- compile bee binary (static, Linux amd64) --- # Shared between variants — built once, reused on second pass. @@ -383,13 +499,13 @@ if [ -f "$BEE_BIN" ]; then fi if [ "$NEED_BUILD" = "1" ]; then - echo "=== building bee binary ===" - cd "${REPO_ROOT}/audit" - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \ - go build \ - -ldflags "-s -w -X main.Version=${AUDIT_VERSION_EFFECTIVE}" \ - -o "$BEE_BIN" \ - ./cmd/bee + run_step_sh "build bee binary" "10-build-bee" \ + "cd '${REPO_ROOT}/audit' && \ + env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \ + go build \ + -ldflags '-s -w -X main.Version=${AUDIT_VERSION_EFFECTIVE}' \ + -o '${BEE_BIN}' \ + ./cmd/bee" echo "binary: $BEE_BIN" if command -v stat >/dev/null 2>&1; then BEE_SIZE_BYTES="$(stat -c '%s' "$BEE_BIN" 2>/dev/null || stat -f '%z' "$BEE_BIN")" @@ -408,9 +524,8 @@ fi # --- NVIDIA-only build steps --- GPU_BURN_WORKER_BIN="${DIST_DIR}/bee-gpu-burn-worker-linux-amd64" if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then - echo "" - echo "=== downloading cuBLAS/cuBLASLt/cudart ${NCCL_CUDA_VERSION} userspace ===" - sh "${BUILDER_DIR}/build-cublas.sh" \ + run_step "download cuBLAS/cuBLASLt/cudart ${NCCL_CUDA_VERSION} userspace" "20-cublas" \ + sh "${BUILDER_DIR}/build-cublas.sh" \ "${CUBLAS_VERSION}" \ "${CUDA_USERSPACE_VERSION}" \ "${NCCL_CUDA_VERSION}" \ @@ -424,8 +539,8 @@ if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then fi if [ "$GPU_STRESS_NEED_BUILD" = "1" ]; then - echo "=== building bee-gpu-burn worker ===" - gcc -O2 -s -Wall -Wextra \ + run_step "build bee-gpu-burn worker" "21-gpu-burn-worker" \ + gcc -O2 -s -Wall -Wextra \ -I"${CUBLAS_CACHE}/include" \ -o "$GPU_BURN_WORKER_BIN" \ "${BUILDER_DIR}/bee-gpu-stress.c" \ @@ -546,9 +661,8 @@ done # --- NVIDIA kernel modules and userspace libs --- if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then - echo "" - echo "=== building NVIDIA ${NVIDIA_DRIVER_VERSION} modules ===" - sh "${BUILDER_DIR}/build-nvidia-module.sh" "${NVIDIA_DRIVER_VERSION}" "${DIST_DIR}" "${DEBIAN_KERNEL_ABI}" + run_step "build NVIDIA ${NVIDIA_DRIVER_VERSION} modules" "40-nvidia-module" \ + sh "${BUILDER_DIR}/build-nvidia-module.sh" "${NVIDIA_DRIVER_VERSION}" "${DIST_DIR}" "${DEBIAN_KERNEL_ABI}" KVER="${DEBIAN_KERNEL_ABI}-amd64" NVIDIA_CACHE="${DIST_DIR}/nvidia-${NVIDIA_DRIVER_VERSION}-${KVER}" @@ -576,9 +690,8 @@ if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then fi # --- build / download NCCL --- - echo "" - echo "=== downloading NCCL ${NCCL_VERSION}+cuda${NCCL_CUDA_VERSION} ===" - sh "${BUILDER_DIR}/build-nccl.sh" "${NCCL_VERSION}" "${NCCL_CUDA_VERSION}" "${DIST_DIR}" "${NCCL_SHA256:-}" + run_step "download NCCL ${NCCL_VERSION}+cuda${NCCL_CUDA_VERSION}" "50-nccl" \ + sh "${BUILDER_DIR}/build-nccl.sh" "${NCCL_VERSION}" "${NCCL_CUDA_VERSION}" "${DIST_DIR}" "${NCCL_SHA256:-}" NCCL_CACHE="${DIST_DIR}/nccl-${NCCL_VERSION}+cuda${NCCL_CUDA_VERSION}" @@ -591,9 +704,8 @@ if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then echo "=== cuBLAS: $(ls "${CUBLAS_CACHE}/lib/" | wc -l) files injected into /usr/lib/ ===" # --- build nccl-tests --- - echo "" - echo "=== building nccl-tests ${NCCL_TESTS_VERSION} ===" - sh "${BUILDER_DIR}/build-nccl-tests.sh" \ + run_step "build nccl-tests ${NCCL_TESTS_VERSION}" "60-nccl-tests" \ + sh "${BUILDER_DIR}/build-nccl-tests.sh" \ "${NCCL_TESTS_VERSION}" \ "${NCCL_VERSION}" \ "${NCCL_CUDA_VERSION}" \ @@ -607,9 +719,8 @@ if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then cp "${NCCL_TESTS_CACHE}/lib/"* "${OVERLAY_STAGE_DIR}/usr/lib/" 2>/dev/null || true echo "=== all_reduce_perf injected ===" - echo "" - echo "=== building john jumbo ${JOHN_JUMBO_COMMIT} ===" - sh "${BUILDER_DIR}/build-john.sh" "${JOHN_JUMBO_COMMIT}" "${DIST_DIR}" + run_step "build john jumbo ${JOHN_JUMBO_COMMIT}" "70-john" \ + sh "${BUILDER_DIR}/build-john.sh" "${JOHN_JUMBO_COMMIT}" "${DIST_DIR}" JOHN_CACHE="${DIST_DIR}/john-${JOHN_JUMBO_COMMIT}" mkdir -p "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/john" rsync -a --delete "${JOHN_CACHE}/run/" "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/john/run/" @@ -730,10 +841,10 @@ BEE_GPU_VENDOR_UPPER="$(echo "${BEE_GPU_VENDOR}" | tr 'a-z' 'A-Z')" export BEE_GPU_VENDOR_UPPER cd "${LB_DIR}" -lb clean 2>&1 | tail -3 -lb config 2>&1 | tail -5 +run_step_sh "live-build clean" "80-lb-clean" "lb clean 2>&1 | tail -3" +run_step_sh "live-build config" "81-lb-config" "lb config 2>&1 | tail -5" dump_memtest_debug "pre-build" "${LB_DIR}" -lb build 2>&1 +run_step_sh "live-build build" "90-lb-build" "lb build 2>&1" # --- persist deb package cache back to shared location --- # This allows the second variant to reuse all downloaded packages. diff --git a/iso/builder/config/hooks/normal/9100-memtest.hook.binary b/iso/builder/config/hooks/normal/9100-memtest.hook.binary new file mode 100755 index 0000000..15d8ab5 --- /dev/null +++ b/iso/builder/config/hooks/normal/9100-memtest.hook.binary @@ -0,0 +1,139 @@ +#!/bin/sh +# Ensure memtest is present in the final ISO even if live-build's built-in +# memtest stage does not copy the binaries or expose menu entries. +set -e + +: "${BEE_REQUIRE_MEMTEST:=0}" + +MEMTEST_FILES="memtest86+x64.bin memtest86+x64.efi" +BINARY_BOOT_DIR="binary/boot" +GRUB_CFG="binary/boot/grub/grub.cfg" +ISOLINUX_CFG="binary/isolinux/live.cfg" + +log() { + echo "memtest hook: $*" +} + +fail_or_warn() { + msg="$1" + if [ "${BEE_REQUIRE_MEMTEST}" = "1" ]; then + log "ERROR: ${msg}" + exit 1 + fi + log "WARNING: ${msg}" + return 0 +} + +copy_memtest_file() { + src="$1" + base="$(basename "$src")" + dst="${BINARY_BOOT_DIR}/${base}" + + [ -f "$src" ] || return 1 + mkdir -p "${BINARY_BOOT_DIR}" + cp "$src" "$dst" + log "copied ${base} from ${src}" +} + +extract_memtest_from_deb() { + deb="$1" + tmpdir="$(mktemp -d)" + + log "extracting memtest payload from ${deb}" + dpkg-deb -x "$deb" "$tmpdir" + for f in ${MEMTEST_FILES}; do + if [ -f "${tmpdir}/boot/${f}" ]; then + copy_memtest_file "${tmpdir}/boot/${f}" + fi + done + rm -rf "$tmpdir" +} + +ensure_memtest_binaries() { + missing=0 + for f in ${MEMTEST_FILES}; do + [ -f "${BINARY_BOOT_DIR}/${f}" ] || missing=1 + done + [ "$missing" -eq 1 ] || return 0 + + for root in chroot/boot /boot; do + for f in ${MEMTEST_FILES}; do + [ -f "${BINARY_BOOT_DIR}/${f}" ] || copy_memtest_file "${root}/${f}" || true + done + done + + missing=0 + for f in ${MEMTEST_FILES}; do + [ -f "${BINARY_BOOT_DIR}/${f}" ] || missing=1 + done + [ "$missing" -eq 1 ] || return 0 + + for root in cache chroot/var/cache/apt/archives /var/cache/apt/archives; do + [ -d "$root" ] || continue + deb="$(find "$root" -type f \( -name 'memtest86+_*.deb' -o -name 'memtest86+*.deb' \) 2>/dev/null | head -1)" + [ -n "$deb" ] || continue + extract_memtest_from_deb "$deb" + break + done + + missing=0 + for f in ${MEMTEST_FILES}; do + if [ ! -f "${BINARY_BOOT_DIR}/${f}" ]; then + fail_or_warn "missing ${BINARY_BOOT_DIR}/${f}" + missing=1 + fi + done + [ "$missing" -eq 0 ] || return 0 +} + +ensure_grub_entry() { + [ -f "$GRUB_CFG" ] || { + fail_or_warn "missing ${GRUB_CFG}" + return 0 + } + + grep -q '### BEE MEMTEST ###' "$GRUB_CFG" && return 0 + + cat >> "$GRUB_CFG" <<'EOF' + +### BEE MEMTEST ### +if [ "${grub_platform}" = "efi" ]; then + menuentry "Memory Test (memtest86+)" { + chainloader /boot/memtest86+x64.efi + } +else + menuentry "Memory Test (memtest86+)" { + linux16 /boot/memtest86+x64.bin + } +fi +### /BEE MEMTEST ### +EOF + + log "appended memtest entry to ${GRUB_CFG}" +} + +ensure_isolinux_entry() { + [ -f "$ISOLINUX_CFG" ] || { + fail_or_warn "missing ${ISOLINUX_CFG}" + return 0 + } + + grep -q '### BEE MEMTEST ###' "$ISOLINUX_CFG" && return 0 + + cat >> "$ISOLINUX_CFG" <<'EOF' + +# ### BEE MEMTEST ### +label memtest + menu label ^Memory Test (memtest86+) + linux /boot/memtest86+x64.bin +# ### /BEE MEMTEST ### +EOF + + log "appended memtest entry to ${ISOLINUX_CFG}" +} + +log "ensuring memtest binaries and menu entries in binary image" +ensure_memtest_binaries +ensure_grub_entry +ensure_isolinux_entry +log "memtest assets ready"