Skip to content

Restore the ten options f61a90d97 dropped, and gate against an eleventh - #5

Merged
mastacontrola merged 4 commits into
masterfrom
bios-restore-framebuffer-console
Aug 8, 2026
Merged

Restore the ten options f61a90d97 dropped, and gate against an eleventh#5
mastacontrola merged 4 commits into
masterfrom
bios-restore-framebuffer-console

Conversation

@mastacontrola

Copy link
Copy Markdown
Member

What

Three commits:

  1. Restore CONSOLE_FRAMEBUFFER on BIOS builds. This is what the forum report is about — the boot menu renders as unstyled text on a black screen on every legacy-BIOS client.
  2. Restore the five remaining options an audit turned up, of which DOWNLOAD_PROTO_HTTPS on BIOS is serious breakage.
  3. Add a CI gate so the next one cannot ship unnoticed.

Why the menu was plain text

CONSOLE_FRAMEBUFFER is what pulls in vesafb and fbcon, the only graphical console a BIOS build can have — EFI gets its own through efifb, which is why no amount of UEFI testing was ever going to catch this.

It fails silently rather than falling back. bootmenu.class.php emits

console --picture <booturl>/ipxe/bg.png --left 100 --right 80 && goto console_set || goto alt_console

and alt_console exists to catch exactly this. It never fires: with no console implementing .configure, console_configure() has nothing to hand the pixel buffer to and returns success, so the command reports success having drawn nothing. The picture is fetched over HTTP, PNG-decoded, and dropped.

The HTTPS one

Without DOWNLOAD_PROTO_HTTPS there is no https_uri_opener in the binary at all, so a legacy-BIOS client cannot fetch an https:// URL by any route.

FOG hands it one on every -S/--force-https install — the installer writes chain ${httpproto}://$ipaddress${webroot}service/ipxe/boot.php##params into default.ipxe. So on an HTTPS server every BIOS client chainloads default.ipxe over TFTP, fails to open the first https:// URL, and lands in ipxescript's "Chainloading failed" reboot loop.

Those installs are also the only ones that build from this tree instead of taking a release asset, because CERT=/TRUST= bake a per-server CA in — which is how it rode in under a commit titled "Fix iPXE compilation error for HTTPS FOG Installs".

The full audit

Comparing effective macro state per platform against the pre-f61a90d97 headers, rather than diffing the text, finds every remaining loss at once:

Option Platform Impact
CONSOLE_FRAMEBUFFER BIOS Boot menu is unstyled text
DOWNLOAD_PROTO_HTTPS BIOS HTTPS installs unbootable
DOWNLOAD_PROTO_FTP both Parity only
DOWNLOAD_PROTO_NFS both Parity only
NET_PROTO_LLDP BIOS Parity only
PXE_CMD BIOS Parity only

After this, effective option state matches the pre-drift headers exactly on both platforms. HTTP_AUTH_NTLM and NET_PROTO_IPV6 stay off — upstream's #undef agrees with FOG's own header on both.

The gate

tools/check-linked-objects.sh reads the linker map files after buildipxe.sh and compares the objects pulled into each shipped binary against a committed baseline. CI runs it between the build and the release step, so a tag that would publish drifted binaries fails before publishing.

The linked object list rather than the headers, deliberately: an option can be lost by a #define becoming a comment or by an #undef in a platform block hundreds of lines away, so no textual header diff tells you what the binary contains — which is how these survived people reading the headers. GH-958 was the inverse case, where the #define was absent so REQUIRE_OBJECT never fired and no console driver linked at all; an option-state audit would have called that header correct.

A diff is not automatically a bug. Bumping IPXEVER or enabling an option on purpose both move the baseline; the check exists so a human sees the move and commits it in the same PR.

Verification

  • Full buildipxe.sh completes cleanly across BIOS, i386-efi, x86_64-efi and arm64-efi.
  • nm on the linked BIOS binary: fbcon 0 → 18 symbols, vesafb 0 → 17. Registered URI openers go from {http, mtftp, tcp, tftm, tftp, udp, xfer} to that set plus https, ftp, nfs.
  • Booted on a real legacy-BIOS PXE client: screen mode goes from 720x400 VGA text to 800x600 VESA with the FOG background rendered.
  • Gate proven by reintroducing the framebuffer regression in a build tree — reports vesafb.o, fbcon.o, utf8.o removed and exits 1.
  • undionly.kkpxe grows 91,079 → 101,190 bytes, almost all of it TLS.

Not verified: an end-to-end boot against an actual HTTPS FOG install (the HTTPS fix is proven at the binary level), and physical hardware rather than a VM.

🤖 Generated with Claude Code

The boot menu renders as unstyled text on black on every legacy-BIOS
client: no background picture, no colours, just the bare item list.
Reported on the forums for dev-branch with undionly.kpxe.

Same cause and same shape as the command trim list this repository
already overrides in general.h. Upstream's console.h carries its own
"not historically included in BIOS builds" block, and it arrived here
with the same wholesale header copy in fogproject f61a90d97 (2026-01-26)
that brought the command one. FOG's console.h had a plain
"#define CONSOLE_FRAMEBUFFER" and no PLATFORM_pcbios block at all before
that commit. Restoring the eight commands in #3 fixed the trim list one
file over but left this one, so CONSOLE_CMD came back while the console
it drives did not.

CONSOLE_FRAMEBUFFER is what pulls in vesafb and fbcon, the only
graphical console a BIOS build can have -- EFI gets its own through
efifb, which is why no amount of UEFI testing was ever going to catch
this.

It fails silently rather than falling back. bootmenu.class.php emits

  console --picture <booturl>/ipxe/bg.png --left 100 --right 80 \
      && goto console_set || goto alt_console

and alt_console exists to catch exactly this. It never fires: with no
console implementing .configure, console_configure() has nothing to hand
the pixel buffer to and returns success, so the command reports success
having drawn nothing. The picture is fetched over HTTP, PNG-decoded, and
dropped.

Only CONSOLE_FRAMEBUFFER is restored. FOG has never used the syslog
consoles, so leaving those undefined keeps the BIOS binary's growth to
what the boot menu actually needs: undionly.kkpxe goes from 91079 to
93499 bytes, +2.7%.

Kept as an explicit override after upstream's block, matching general.h,
so the next refresh onto a new upstream tag diffs cleanly and this reads
as a FOG choice rather than more drift.

Verified on a legacy-BIOS PXE client (VirtualBox, vboxvga, FOG 1.6):
nm on the linked binary goes from 0 to 18 fbcon and 0 to 17 vesafb
symbols, and the client's screen mode goes from 720x400 VGA text to
800x600 VESA graphics with the FOG background rendered.
An audit of all six headers that commit rewrote, comparing effective
macro state per platform against the pre-drift FOG headers rather than
diffing the text, turns up five more losses. HTTPS is the serious one.

  DOWNLOAD_PROTO_HTTPS (BIOS)  -- no https_uri_opener in the binary, so a
    legacy-BIOS client cannot fetch an https:// URL at all. Every
    -S/--force-https FOG install writes exactly such a URL into
    default.ipxe, so every BIOS client on an HTTPS server dies at the
    first chain and reboot-loops on "Chainloading failed". Those installs
    are also the only ones that build from this tree instead of taking a
    release asset, which is why the breakage rode in under a commit
    titled "Fix iPXE compilation error for HTTPS FOG Installs".

  DOWNLOAD_PROTO_FTP, DOWNLOAD_PROTO_NFS (both platforms)
  NET_PROTO_LLDP (BIOS), PXE_CMD (BIOS)
    -- nothing in FOG emits ftp://, nfs://, startpxe or stoppxe, so these
    are parity rather than breakage. Restored on the same reasoning as
    the six diagnostic commands in the trim-list override: FOG shipped
    them for years, no one chose to drop them, and a silently narrowed
    feature set is not a decision anybody made.

HTTP_AUTH_NTLM and NET_PROTO_IPV6 stay off. Upstream's undefs agree with
FOG's own header on both, so there is nothing to restore.

After this the effective option set for both platforms matches the
pre-f61a90d97 headers exactly, with no remaining losses on either.
undionly.kkpxe grows from 93499 to 101190 bytes, almost all of it TLS.

Verified by nm on the linked BIOS binary: the registered URI openers go
from {http, mtftp, tcp, tftm, tftp, udp, xfer} to that set plus https,
ftp and nfs.
Ten iPXE options were lost to fogproject f61a90d97 copying upstream's
config headers in wholesale. They surfaced one at a time over six
releases, each found by a user hitting a symptom rather than by anyone
looking: a blank screen, a boot that dies on "params: command not found",
a menu with no background, and -- still unreleased until now -- no HTTPS
at all on legacy BIOS. Nothing in the repository could tell you the
feature set had changed, so nothing did.

This makes it a standing property instead. check-linked-objects.sh reads
the linker's map files after buildipxe.sh and compares the set of objects
pulled into each shipped binary against a committed baseline. CI runs it
between the build and the release step, so a tag that would publish
drifted binaries fails before it publishes anything.

The linked object list rather than the headers, for two reasons. An
option can be lost by a #define becoming a comment or by an #undef in a
platform block hundreds of lines away, so no textual diff of a header
tells you what the binary ends up containing -- which is exactly how
these went unnoticed while people read the headers. And GH-958 was the
inverse case: the #define was absent, so REQUIRE_OBJECT never fired and
no console driver linked at all; an option-state audit would have called
that header correct. The map file cannot be fooled by either, and it is
produced by the real build rather than by a harness here that would
reimplement the build's include paths and then rot.

Three baselines, one per build tree, because they genuinely can disagree:
BIOS and EFI are separate clones with separate headers, and arm64 is a
separate compile whose object set differs by architecture. One target per
tree is enough -- every binary in a tree links from the same blib.a
against the same headers.

A diff is not automatically a bug. Bumping IPXEVER or enabling an option
on purpose both move the baseline; the check exists so a human sees the
move and commits it deliberately in the same pull request.

Verified by reintroducing the framebuffer regression in a build tree and
rerunning: the check reports vesafb.o, fbcon.o and utf8.o removed and
exits 1. Restored, it passes at 340/341/338 objects.
The first CI run of the check failed against baselines generated on a
Fedora box, reporting nearly every object as both removed and added.
Nothing had drifted: glibc's default collation ignores punctuation on its
first pass, so en_US.UTF-8 and C disagree about whether "ipstat_cmd.o"
sorts before or after "ipstat.o", and the sorted baseline is compared
with diff.

A check that fails because of the developer's locale is worse than no
check -- it trains people to ignore it. LC_ALL=C makes byte order the
contract everywhere.

Baselines regenerated; the object sets are byte-identical when re-sorted,
so this commit changes ordering only. Counts stay 340/341/338.
@mastacontrola
mastacontrola merged commit c3e2a7c into master Aug 8, 2026
1 check passed
@mastacontrola
mastacontrola deleted the bios-restore-framebuffer-console branch August 8, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant