Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

flash-mac.sh: 2-partition parity with Linux flash-helper

Upgrades the macOS flasher from single-partition kernel-direct mode to
the 2-partition layout flash-helper-runner.sh produces on Linux:

1. ACBOOT (FAT32, type "Microsoft Basic Data")
/EFI/BOOT/BOOTX64.EFI = full kernel
/initramfs.cpio.gz, /config.json
PC firmware fallback path.

2. ACEFI (FAT32, type "EFI System Partition")
/EFI/BOOT/BOOTX64.EFI = splash.efi (chains to LOADER.EFI)
/EFI/BOOT/LOADER.EFI = systemd-boot
/EFI/BOOT/KERNEL.EFI = kernel
/loader/entries/ac-native.conf with explicit cmdline
Universal — works on any UEFI that respects ESP type GUID.

Built with sgdisk (gptfdisk) for precise type GUIDs (0700 + ef00) +
newfs_msdos for FAT32 + mount_msdos for staging. No Docker, no /sys/block.

sha256 round-trip verifies every kernel + initramfs copy after writing.

Skipped (follow-up): AC-MAC HFS+ partition for Intel Mac compatibility.
The two FAT32 partitions cover ThinkPads, generic PCs, Apple Silicon
Macs that boot Linux via virt, and most modern UEFI firmware.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+135 -86
+135 -86
fedac/native/scripts/flash-mac.sh
··· 1 1 #!/bin/bash 2 - # flash-mac.sh — macOS-native AC Native OS USB flasher. 2 + # flash-mac.sh — macOS-native AC Native OS USB flasher (no Docker required). 3 3 # 4 - # Mirrors the "kernel-direct" boot mode from scripts/flash-helper-runner.sh 5 - # (Linux/Docker) using only macOS CLI tools — no container required. 6 - # Lays out a single ESP partition with the kernel installed as the standard 7 - # UEFI fallback path (/EFI/BOOT/BOOTX64.EFI) plus the external initramfs at 8 - # the partition root, matching the kernel's CONFIG_CMDLINE which loads 9 - # `initrd=\initramfs.cpio.gz` from the ESP. 4 + # Mirrors the production scripts/flash-helper-runner.sh layout using only 5 + # macOS CLI tools (diskutil, sgdisk, newfs_msdos, mount_msdos, cp, shasum). 6 + # 7 + # Two partitions, matching the Linux flow: 8 + # 1. ACBOOT (FAT32, type "Microsoft Basic Data") 9 + # - kernel-direct boot: /EFI/BOOT/BOOTX64.EFI = full kernel 10 + # - /initramfs.cpio.gz at root 11 + # - /config.json 12 + # - Works on permissive PC firmware that scans non-ESP partitions for 13 + # the standard UEFI fallback path. 14 + # 15 + # 2. ACEFI (FAT32, type "EFI System Partition") 16 + # - splash.efi as /EFI/BOOT/BOOTX64.EFI (chains to LOADER.EFI) 17 + # - systemd-bootx64.efi as /EFI/BOOT/LOADER.EFI 18 + # - kernel as /EFI/BOOT/KERNEL.EFI 19 + # - /initramfs.cpio.gz at root 20 + # - /loader/entries/ac-native.conf with explicit cmdline 21 + # - Universal: works on any UEFI firmware that respects ESP type GUID. 22 + # 23 + # Skipped vs Linux helper: 24 + # - AC-MAC HFS+ partition for Intel Mac compatibility (TODO) 10 25 # 11 26 # Usage: 12 27 # ./flash-mac.sh /dev/diskN [SRC_DIR] 13 28 # 14 29 # SRC_DIR defaults to /tmp/ac-os-pull (where ac-os pull stages downloads). 15 - # It must contain `vmlinuz` and `initramfs.cpio.gz`. 16 - # 17 - # Requires sudo for diskutil destructive ops. 30 + # Must contain `vmlinuz` and `initramfs.cpio.gz`. 18 31 19 32 set -euo pipefail 20 33 21 34 USB_DEV="${1:?usage: $0 /dev/diskN [SRC_DIR]}" 22 35 SRC_DIR="${2:-/tmp/ac-os-pull}" 23 - PART_NAME="${PART_NAME:-ACBOOT}" 36 + 37 + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 38 + REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" 39 + NATIVE_DIR="${REPO_ROOT}/native" 40 + [ -d "${NATIVE_DIR}/boot" ] || NATIVE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" 24 41 25 42 KERNEL="${SRC_DIR}/vmlinuz" 26 43 INITRAMFS="${SRC_DIR}/initramfs.cpio.gz" 44 + SPLASH_EFI="${NATIVE_DIR}/boot/splash.efi" 45 + SDBOOT_EFI="${NATIVE_DIR}/boot/systemd-bootx64.efi" 27 46 28 - log() { echo "[flash-mac] $*"; } 29 - err() { echo "[flash-mac] $*" >&2; } 30 - die() { err "$*"; exit 1; } 47 + log() { echo "[flash-mac] $*"; } 48 + err() { echo "[flash-mac] $*" >&2; } 49 + die() { err "$*"; exit 1; } 31 50 32 51 # --- preflight --- 33 - [ "$(uname)" = "Darwin" ] || die "macOS only — use ac-os flash on Linux." 34 - [ -b "${USB_DEV}" ] || [ -e "${USB_DEV}" ] || die "Not a device: ${USB_DEV}" 35 - [ -f "${KERNEL}" ] || die "Missing kernel: ${KERNEL}" 36 - [ -f "${INITRAMFS}" ] || die "Missing initramfs: ${INITRAMFS}" 52 + [ "$(uname)" = "Darwin" ] || die "macOS only — use ac-os flash on Linux." 53 + command -v sgdisk >/dev/null || die "sgdisk required: brew install gptfdisk" 54 + [ -f "${KERNEL}" ] || die "Missing kernel: ${KERNEL}" 55 + [ -f "${INITRAMFS}" ] || die "Missing initramfs: ${INITRAMFS}" 56 + [ -f "${SPLASH_EFI}" ] || die "Missing splash bootloader: ${SPLASH_EFI}" 57 + [ -f "${SDBOOT_EFI}" ] || die "Missing systemd-boot: ${SDBOOT_EFI}" 37 58 38 - # Refuse to flash internal disks (would brick the Mac). 39 59 INFO=$(diskutil info "${USB_DEV}" 2>/dev/null) || die "diskutil info failed for ${USB_DEV}" 40 60 echo "${INFO}" | grep -q "Removable Media:.*Removable\|Device Location:.*External" \ 41 - || die "${USB_DEV} does not look removable/external. Aborting." 61 + || die "${USB_DEV} is not removable/external. Aborting." 42 62 DEV_NAME=$(echo "${INFO}" | awk -F': +' '/Device \/ Media Name/{print $2}' | head -1) 43 63 DEV_SIZE=$(echo "${INFO}" | awk -F': +' '/Disk Size/{print $2}' | head -1) 64 + DEV_BYTES=$(echo "${INFO}" | awk -F': +' '/Disk Size:/ {print $2}' | grep -oE '\([0-9]+ Bytes\)' | head -1 | tr -dc 0-9) 44 65 45 - KERNEL_SHA=$(shasum -a 256 "${KERNEL}" | awk '{print $1}') 66 + # --- size budgeting (matches Linux helper math) --- 67 + KERNEL_BYTES=$(stat -f%z "${KERNEL}") 68 + INITRD_BYTES=$(stat -f%z "${INITRAMFS}") 69 + KERNEL_SHA=$(shasum -a 256 "${KERNEL}" | awk '{print $1}') 46 70 INITRD_SHA=$(shasum -a 256 "${INITRAMFS}" | awk '{print $1}') 47 - KERNEL_SZ=$(stat -f%z "${KERNEL}") 48 - INITRD_SZ=$(stat -f%z "${INITRAMFS}") 71 + 72 + mb_round_up() { echo $(( ($1 + 1048575) / 1048576 )); } 73 + DISK_MB=$(( DEV_BYTES / 1048576 )) 74 + STAGE_MB=$(( $(mb_round_up "${KERNEL_BYTES}") + $(mb_round_up "${INITRD_BYTES}") + 8 )) 75 + EFI_MB=$(( STAGE_MB + 96 )) 76 + MAIN_MB=$(( DISK_MB - EFI_MB - 64 )) # 64 MB GPT + alignment headroom 77 + 78 + [ "${MAIN_MB}" -ge $(( STAGE_MB + 64 )) ] \ 79 + || die "USB too small (${DISK_MB} MB) for hybrid layout." 49 80 50 81 echo 51 82 log "Target: ${USB_DEV} — ${DEV_NAME} — ${DEV_SIZE}" 52 - log "Kernel: ${KERNEL_SZ} bytes ${KERNEL_SHA:0:16}…" 53 - log "Initramfs: ${INITRD_SZ} bytes ${INITRD_SHA:0:16}…" 83 + log "Kernel: ${KERNEL_BYTES} bytes ${KERNEL_SHA:0:16}…" 84 + log "Initramfs: ${INITRD_BYTES} bytes ${INITRD_SHA:0:16}…" 85 + FREE_MB=$(( DISK_MB - MAIN_MB - EFI_MB - 64 )) 86 + log "Layout: ACBOOT=${MAIN_MB}MB ACEFI=${EFI_MB}MB free=${FREE_MB}MB" 54 87 echo 55 88 read -r -p "Type 'YES' to ERASE ${USB_DEV} and write AC Native OS: " CONFIRM 56 89 [ "${CONFIRM}" = "YES" ] || die "Aborted." 57 90 58 - # --- partition + format --- 91 + # --- wipe + repartition --- 59 92 log "Unmounting…" 60 - diskutil unmountDisk force "${USB_DEV}" >/dev/null 93 + sudo diskutil unmountDisk force "${USB_DEV}" >/dev/null 94 + 95 + log "Zapping GPT + clearing first 16 MiB…" 96 + sudo sgdisk --zap-all "${USB_DEV}" >/dev/null 97 + sudo dd if=/dev/zero of="${USB_DEV}" bs=1m count=16 status=none 98 + 99 + log "Creating GPT layout (ACBOOT + ACEFI)…" 100 + sudo sgdisk \ 101 + --new=1:0:+${MAIN_MB}M --typecode=1:0700 --change-name=1:ACBOOT \ 102 + --new=2:0:0 --typecode=2:ef00 --change-name=2:ACEFI \ 103 + "${USB_DEV}" >/dev/null 104 + 105 + # Re-read partition table so /dev/diskNs* nodes appear. 106 + sudo diskutil unmountDisk force "${USB_DEV}" >/dev/null 2>&1 || true 107 + sleep 1 108 + 109 + P1="${USB_DEV}s1" 110 + P2="${USB_DEV}s2" 111 + RAW1="/dev/r$(basename "${P1}")" 112 + RAW2="/dev/r$(basename "${P2}")" 61 113 62 - log "Erasing + creating GPT/FAT32 …" 63 - # diskutil eraseDisk MS-DOS+GPT creates an EFI helper at s1 (200MB, ESP) 64 - # and FAT32 at s2 (rest). For kernel-direct boot we need a SINGLE ESP that 65 - # fits both kernel + initramfs (~340 MB). Use partitionDisk to lay down a 66 - # single FAT32 spanning the disk, then convert its type GUID to ESP so 67 - # UEFI firmware will scan it for /EFI/BOOT/BOOTX64.EFI. 68 - sudo diskutil partitionDisk "${USB_DEV}" 1 GPT MS-DOS "${PART_NAME}" 100% >/dev/null 114 + log "Formatting FAT32 partitions…" 115 + sudo newfs_msdos -F 32 -v ACBOOT "${RAW1}" >/dev/null 116 + sudo newfs_msdos -F 32 -v ACEFI "${RAW2}" >/dev/null 69 117 70 - # After partitionDisk with 1 partition, layout is: 71 - # ${USB_DEV}s1 — EFI helper (200 MB FAT32, real ESP — too small for initramfs) 72 - # ${USB_DEV}s2 — ${PART_NAME} (30+ GB FAT32, type "Microsoft Basic Data") 73 - # 74 - # Most UEFI firmware will only fall back to /EFI/BOOT/BOOTX64.EFI on a 75 - # partition typed as ESP (GUID c12a7328-f81f-11d2-ba4b-00a0c93ec93b). 76 - # `diskutil` on macOS has no first-class way to change a partition's type 77 - # GUID without reformatting, so we shell out to `sgdisk` (gptfdisk). 78 - DATA_PART="${USB_DEV}s2" 79 - log "Setting ${DATA_PART} type to EFI System Partition…" 80 - diskutil unmount "${DATA_PART}" >/dev/null 2>&1 || true 81 - if command -v sgdisk >/dev/null 2>&1; then 82 - # sgdisk type code "ef00" = EFI System Partition. 83 - sudo sgdisk -t 2:ef00 "${USB_DEV}" >/dev/null 84 - # Re-read the partition table so macOS sees the new type. 85 - sudo /usr/sbin/diskutil unmountDisk force "${USB_DEV}" >/dev/null 2>&1 || true 86 - log " ✓ s2 retyped to ESP via sgdisk" 87 - else 88 - err " sgdisk not found — install with: brew install gptfdisk" 89 - err " Without ESP type, target firmware will probably skip the boot partition." 90 - err " Continuing — verify the boot afterwards." 91 - fi 118 + # --- mount --- 119 + M1=$(mktemp -d /tmp/ac-main.XXXXXX) 120 + M2=$(mktemp -d /tmp/ac-efi.XXXXXX) 121 + trap "sudo umount '${M1}' 2>/dev/null; sudo umount '${M2}' 2>/dev/null; rmdir '${M1}' '${M2}' 2>/dev/null; true" EXIT 92 122 93 - # Re-mount data partition 94 - diskutil mount "${DATA_PART}" >/dev/null 95 - MOUNT="/Volumes/${PART_NAME}" 96 - [ -d "${MOUNT}" ] || die "Expected mount at ${MOUNT}" 123 + log "Mounting partitions…" 124 + sudo mount_msdos "${P1}" "${M1}" 125 + sudo mount_msdos "${P2}" "${M2}" 97 126 98 - # --- lay out kernel-direct boot tree --- 99 - log "Writing boot tree to ${MOUNT}…" 100 - mkdir -p "${MOUNT}/EFI/BOOT" 101 - cp "${KERNEL}" "${MOUNT}/EFI/BOOT/BOOTX64.EFI" 102 - cp "${INITRAMFS}" "${MOUNT}/initramfs.cpio.gz" 127 + # --- layout ACBOOT (kernel-direct + config) --- 128 + log "Writing ACBOOT (kernel-direct boot tree)…" 129 + sudo mkdir -p "${M1}/EFI/BOOT" 130 + sudo cp "${KERNEL}" "${M1}/EFI/BOOT/BOOTX64.EFI" 131 + sudo cp "${INITRAMFS}" "${M1}/initramfs.cpio.gz" 132 + echo '{"handle":"","piece":"notepat","sub":"","email":""}' | sudo tee "${M1}/config.json" >/dev/null 133 + [ -f "${SRC_DIR}/wifi_creds.json" ] && sudo cp "${SRC_DIR}/wifi_creds.json" "${M1}/wifi_creds.json" 103 134 104 - # Default identity / config — overwrite with personal config later if needed. 105 - cat > "${MOUNT}/config.json" <<'EOF' 106 - {"handle":"","piece":"notepat","sub":"","email":""} 135 + # --- layout ACEFI (systemd-boot universal) --- 136 + log "Writing ACEFI (splash → systemd-boot → kernel)…" 137 + sudo mkdir -p "${M2}/EFI/BOOT" "${M2}/loader/entries" 138 + sudo cp "${SPLASH_EFI}" "${M2}/EFI/BOOT/BOOTX64.EFI" 139 + sudo cp "${SDBOOT_EFI}" "${M2}/EFI/BOOT/LOADER.EFI" 140 + sudo cp "${KERNEL}" "${M2}/EFI/BOOT/KERNEL.EFI" 141 + sudo cp "${INITRAMFS}" "${M2}/initramfs.cpio.gz" 142 + sudo tee "${M2}/loader/loader.conf" >/dev/null <<'EOF' 143 + default ac-native.conf 144 + timeout 0 145 + EOF 146 + sudo tee "${M2}/loader/entries/ac-native.conf" >/dev/null <<'EOF' 147 + title AC Native OS 148 + linux /EFI/BOOT/KERNEL.EFI 149 + initrd /initramfs.cpio.gz 150 + options console=tty0 quiet loglevel=3 vt.global_cursor_default=0 init=/init nomodeset efi=noruntime 107 151 EOF 152 + echo '{"handle":"","piece":"notepat","sub":"","email":""}' | sudo tee "${M2}/config.json" >/dev/null 153 + [ -f "${SRC_DIR}/wifi_creds.json" ] && sudo cp "${SRC_DIR}/wifi_creds.json" "${M2}/wifi_creds.json" 108 154 109 - # Optional wifi presets — copy from local file if user has one staged 110 - if [ -f "${SRC_DIR}/wifi_creds.json" ]; then 111 - cp "${SRC_DIR}/wifi_creds.json" "${MOUNT}/wifi_creds.json" 112 - log "Included wifi_creds.json from ${SRC_DIR}" 113 - fi 155 + # --- verify (sha256 round-trip on every kernel + initramfs copy) --- 156 + log "Verifying integrity…" 157 + verify() { 158 + local label="$1" path="$2" expected="$3" 159 + local got 160 + got=$(shasum -a 256 "${path}" | awk '{print $1}') 161 + [ "${got}" = "${expected}" ] || die "${label} sha mismatch (${got} != ${expected})" 162 + log " ✓ ${label} ${got:0:16}…" 163 + } 164 + verify "ACBOOT/EFI/BOOT/BOOTX64.EFI" "${M1}/EFI/BOOT/BOOTX64.EFI" "${KERNEL_SHA}" 165 + verify "ACBOOT/initramfs.cpio.gz" "${M1}/initramfs.cpio.gz" "${INITRD_SHA}" 166 + verify "ACEFI/EFI/BOOT/KERNEL.EFI" "${M2}/EFI/BOOT/KERNEL.EFI" "${KERNEL_SHA}" 167 + verify "ACEFI/initramfs.cpio.gz" "${M2}/initramfs.cpio.gz" "${INITRD_SHA}" 114 168 115 - # --- verify integrity (sha256 round-trip) --- 116 - log "Verifying…" 117 - WK_SHA=$(shasum -a 256 "${MOUNT}/EFI/BOOT/BOOTX64.EFI" | awk '{print $1}') 118 - WI_SHA=$(shasum -a 256 "${MOUNT}/initramfs.cpio.gz" | awk '{print $1}') 119 - [ "${WK_SHA}" = "${KERNEL_SHA}" ] || die "kernel sha mismatch (got ${WK_SHA})" 120 - [ "${WI_SHA}" = "${INITRD_SHA}" ] || die "initramfs sha mismatch (got ${WI_SHA})" 121 - log " ✓ kernel ${WK_SHA:0:16}…" 122 - log " ✓ initramfs ${WI_SHA:0:16}…" 123 - 124 - # --- eject --- 169 + # --- finalize --- 125 170 sync 126 - log "Ejecting ${USB_DEV}…" 127 - diskutil eject "${USB_DEV}" >/dev/null 171 + log "Unmounting + ejecting…" 172 + sudo umount "${M1}" "${M2}" 173 + sudo diskutil eject "${USB_DEV}" >/dev/null 174 + trap - EXIT 175 + rmdir "${M1}" "${M2}" 2>/dev/null || true 128 176 129 - log "Done. USB ready. Boot the AC native device from this drive." 177 + log "Done. USB has both kernel-direct (ACBOOT) + systemd-boot (ACEFI) layouts." 178 + log "Plug into target hardware and boot — UEFI firmware should pick ACEFI (real ESP)."