diff --git a/serving/dnsmasq/entrypoint.sh b/serving/dnsmasq/entrypoint.sh index 59a7358..8d0a953 100644 --- a/serving/dnsmasq/entrypoint.sh +++ b/serving/dnsmasq/entrypoint.sh @@ -23,8 +23,12 @@ set -eu mkdir -p /srv/tftp -# User-supplied iPXE binary (ipxe.efi) from the read-only source mount. +# User-supplied iPXE binary (snponly.efi) from the read-only source mount. cp -f /tftp-src/*.efi /srv/tftp/ 2>/dev/null || true +# autoexec.ipxe was an STP-era workaround (ifopen+retry before autoboot); it's no +# longer rendered — PortFast on the switch port is the real fix. Remove any stale +# copy left in a long-lived container's writable layer. +rm -f /srv/tftp/autoexec.ipxe extract_cmdline() { # $1 = UKI path; echoes the cleaned cmdline (empty on failure) uki="$1" @@ -47,49 +51,28 @@ render_boot_ipxe() { echo " hit a dracut shell under Secure Boot OFF (no embedded cmdline)." >&2 echo " Check the /artifacts mount + binutils, or enable Secure Boot." >&2 fi - # Render the SAME boot logic to two files: - # autoexec.ipxe — iPXE fetches + runs this at STARTUP (before autoboot). The - # leading `ifopen net0` brings the link UP on NICs whose NII/UNDI link iPXE - # can't establish during autoboot — e.g. EliteBook 820 G4 / Intel I219-LM, - # which sits at link:down ("No configurable methods") until ifopen. Without - # this, autoboot fails before it can even DHCP for boot.ipxe. - # boot.ipxe — the DHCP-bootfile fallback (tag:ipxe) for firmware that DOES - # autoboot-DHCP fine; harmless to carry the same ifopen. - # Both feed iPXE the UKI's REAL cmdline via imgargs (Secure Boot OFF; see top). - for f in /srv/tftp/autoexec.ipxe /srv/tftp/boot.ipxe; do - cat > "$f" < /srv/tftp/boot.ipxe < iPXE exits -> the -# firmware restarts PXE), so EVERY step that can fail is guarded with '||'. -# -# The link is already up + forwarding here (the TFTP fetch of this script just -# succeeded), so try DHCP FIRST without touching the PHY — on a PortFast/edge -# port this succeeds immediately with no delay. -dhcp net0 && goto have_ip || echo initial DHCP failed; will reset link and retry -# Fallback: reset the link, then retry DHCP long enough to outlast the switch's -# STP forwarding delay (~30s) that the ifopen bounce triggers. A single ifopen -# (held open across the loop) means the PHY bounces ONCE; STP then converges and -# a later DHCP gets through. PROPER fix is PortFast/edge on the switch port so -# there's no delay at all — see serving/tftp/README.md. -ifopen net0 || goto err -set tries 0 -:dhcp_retry -dhcp net0 && goto have_ip || echo DHCP retry \${tries} -inc tries || goto err -iseq \${tries} 30 && goto no_ip || sleep 3 -goto dhcp_retry -:have_ip +dhcp net0 || goto err imgfetch http://${SERVER_IP}/${uki} || goto err imgargs ${uki} ${cmdline} boot ${uki} || goto err -:no_ip -echo No DHCP after ~90s on net0 - enable PortFast/edge on the switch port (STP delay). :err echo Fleet iPXE chain failed (\${errno}) - dropping to iPXE shell shell EOF - done - echo "rendered autoexec.ipxe + boot.ipxe -> ${uki} (cmdline: ${cmdline:-})" + echo "rendered /srv/tftp/boot.ipxe -> ${uki} (cmdline: ${cmdline:-})" } if [ -n "${SERVER_IP:-}" ] && [ -n "${IMAGE_VERSION:-}" ]; then diff --git a/serving/tftp/README.md b/serving/tftp/README.md index ba4ee0f..7147826 100644 --- a/serving/tftp/README.md +++ b/serving/tftp/README.md @@ -11,10 +11,16 @@ Flow for a PXE-only host: ``` firmware PXE (PXEClient) --TFTP--> snponly.efi (iPXE) - iPXE re-DHCPs (user-class "iPXE") --> handed boot.ipxe (TFTP) + iPXE autoboot DHCP (user-class "iPXE") --> handed boot.ipxe (TFTP) boot.ipxe: imgfetch UKI over HTTP -> imgargs -> boot ``` +> **Requires PortFast/edge on the switch port** (see the gotchas below). iPXE's +> autoboot opens the NIC, which bounces the PHY; without PortFast the switch's STP +> forwarding delay (~30 s) drops iPXE's DHCP and the boot fails. PortFast removes +> the delay. (The earlier `autoexec.ipxe` + `ifopen` + DHCP-retry workaround was +> removed once PortFast was identified as the real fix.) + `boot.ipxe` is **rendered at container start** by the dnsmasq entrypoint; only the iPXE binary (`snponly.efi`) is supplied here. The script exists because of a sharp edge: @@ -107,10 +113,16 @@ Hard-won findings from bringing up the first PXE-only physical host: ~30 s to every boot — PortFast removes the delay entirely. - **iPXE aborts the script on any unguarded command failure** (→ iPXE exits → the firmware restarts the whole PXE process, looking like "it doesn't retry"). Every - fallible step in `boot.ipxe`/`autoexec.ipxe` is therefore `||`-guarded. -- **`autoexec.ipxe`** (rendered by the dnsmasq entrypoint) is fetched + run at - iPXE startup, *replacing* the default autoboot; that's where the `ifopen` + - DHCP-retry + `imgargs ` chain lives. + fallible step in `boot.ipxe` is therefore `||`-guarded. +- **`boot.ipxe`** (rendered by the dnsmasq entrypoint, served as the `tag:ipxe` + DHCP bootfile) does `dhcp` + `imgfetch` UKI + `imgargs ` + `boot`. + The cmdline injection is needed only because **Secure Boot is OFF** (the stub + takes the loader's cmdline); under Secure Boot it's a no-op and `boot.ipxe` + could collapse to a plain `chain` of the UKI. +- **Removed STP-era workaround:** an `autoexec.ipxe` that ran `ifopen net0` + a + DHCP-retry loop before autoboot was used to wait out STP. It's gone now that + **PortFast is the fix** — if you ever run on a switch you can't set PortFast on, + that ifopen+retry approach (in git history) is the fallback. ## Note