Tidy up pxe flow

This commit is contained in:
Audiobuzz
2026-06-20 09:19:01 +00:00
parent fb69068980
commit 33583bb4d8
2 changed files with 36 additions and 41 deletions
+19 -36
View File
@@ -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" <<EOF
# boot.ipxe is fetched as the DHCP bootfile (dnsmasq tag:ipxe) AFTER iPXE's
# normal autoboot DHCP has already configured the link, so it just fetches and
# boots the UKI. `imgargs` feeds the UKI's REAL cmdline (Secure Boot OFF — see
# top); without it iPXE would pass the image name as the cmdline and dracut
# would drop to an emergency shell. (every fallible step is '||'-guarded — a
# bare failure aborts the iPXE script.)
#
# REQUIRES PortFast/edge on the switch port: iPXE's autoboot opens the NIC,
# which bounces the PHY; without PortFast the switch's STP forwarding delay
# (~30s) drops the DHCP and the boot fails. (We removed the old ifopen+retry
# workaround now that PortFast is the documented requirement — serving/tftp/README.)
cat > /srv/tftp/boot.ipxe <<EOF
#!ipxe
# NOTE: in iPXE a bare failing command aborts the script (-> 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:-<MISSING>})"
echo "rendered /srv/tftp/boot.ipxe -> ${uki} (cmdline: ${cmdline:-<MISSING>})"
}
if [ -n "${SERVER_IP:-}" ] && [ -n "${IMAGE_VERSION:-}" ]; then
+17 -5
View File
@@ -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 <real cmdline> -> 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 <real cmdline>` 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 <real cmdline>` + `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