Why “can it run DOOM” is a real engineering test
DOOM has become the informal maturity test for every platform since 1993, and the joke hides a serious point: DOOM is someone else’s software. It was written a third of a century ago with no knowledge of EuroOS. It cannot be tweaked into cooperating; the operating system either provides what a real interactive program needs, or the port fails.
A demo written for your OS proves little; it will politely avoid everything that does not work. DOOM does the opposite. In one binary it exercises process loading, memory management, file I/O, timing, input and graphics, at interactive rates, all at once. That is why we treated it not as a stunt but as a system-level integration test with a 30-year-old test author.
doom1.wad, compiled as a static musl binary. Not a re-implementation, not a video, not a mock. Our entire contribution on the DOOM side is a ~150-line platform backend.What EuroOS had to provide
EuroOS is written from scratch in Rust: its own UEFI boot chain, kernel, drivers, filesystems and desktop. Unmodified Linux binaries run on a compatibility bridge: a Linux-ABI syscall layer on top of native EuroOS machinery. DOOM forced that bridge to grow up:
A real process, safely mapped
DOOM needs ~10 MiB of working memory. The kernel now spawns preemptively scheduled apps in 32 MiB address-space arenas: W^X code pages (write-xor-execute), non-executable heap and stack, its own page tables. These are the same hardening rules our native processes live under.
The file I/O a real C library actually uses
DOOM reads its 4 MiB WAD data file through musl’s stdio. That meant implementing not just open/read/lseek/close, but the calls a libc really makes: readv (scatter-gather reads; musl’s buffered reads use nothing else), fstat (stdio sizes its buffers from it) and openat. If your ABI only covers the calls you expected, real software finds the rest within seconds.
Graphics and input for userspace
Two new syscalls: fb_present hands the kernel a finished 640×400 frame, which is scaled, centered and written directly to the physical framebuffer in the app’s own syscall context; getkey delivers raw make/break key events. No X11, no Wayland, no GPU stack: one clean ownership hand-off between the desktop and a fullscreen app.
Time
clock_gettime backed by the kernel tick, and a nanosleep that yields the CPU through the scheduler. A game is a hard real-time-ish consumer of both; getting them subtly wrong shows up instantly as a frozen or racing game loop.
The bugs DOOM found for us
This is the part we care most about. Getting DOOM running surfaced four real defects in EuroOS, every one of them a bug that any serious third-party workload would eventually have hit:
- Missing
readv. musl’s stdio reads exclusively through it. Without it, DOOM read garbage and rejected its own WAD file. A whole class of “ported software mysteriously can’t read files” fixed in one syscall. - A global pipe table colliding with file descriptors. A boot demo left a stale pipe on fd 3; DOOM’s WAD reads were silently routed into the pipe. Real files now take precedence, and per-process fd isolation moved up our roadmap.
- Boot-time integrity scrubbing made large files unaffordable. Embedding the 4 MiB WAD in the RAM filesystem sent boot time past four minutes under emulation. The WAD is now served straight from the kernel image.
- Present-path starvation. A busy game plus background tasks starved the desktop loop that used to blit app frames; the title screen froze even though DOOM was rendering happily. Frames are now painted from the presenting syscall itself, so the app’s own CPU time pays for its own pixels.
Then it became a diagnostic instrument
To test DOOM we built an automated harness that boots the OS hundreds of times, drives the keyboard and screenshots the result. That harness turned a vague annoyance (“sometimes a boot hangs”) into a measured, reproducible failure rate. And a measured failure is a fixable one.
We sampled the instruction pointer of a hung boot through the hypervisor: thirty samples, all at the same instruction: the spin loop of the kernel heap lock, with interrupts disabled. Root cause: an interrupt handler (our USB input harvester) could allocate memory and log to the serial console; if that interrupt preempted a task that was already holding the heap or console lock, the handler spun forever with interrupts off. A silent, total hang whose location varied with timing: the classic lock-in-interrupt-context bug. It had been hiding since the USB interrupt work landed, and struck roughly every other boot under slow emulation.
The fix is the textbook one, applied as a rule rather than a patch: any lock that an interrupt handler may take is only ever held with interrupts disabled. The same audit fixed a second, subtler defect: keyboard scancodes were dropped instead of deadlocked on the same kind of contention, the occasional “first keystroke after boot goes missing”. Same root class, both directions: one hung, one lost data.
What this says about EuroOS
EuroOS is not a Linux distribution, and DOOM does not change that. Its identity is the native platform: EuroGuard capabilities, EuroIPC, signed updates, sealed keys, the sovereign European stack. The Linux ABI is a bridge, but a bridge is only worth having if real traffic crosses it. DOOM is that traffic: proof that unmodified, decades-old, third-party software runs on a kernel whose every line we wrote ourselves.
Practically, everything DOOM forced into existence is now platform capability, not game code: big hardened process arenas, honest libc-grade file I/O, a userspace framebuffer path, raw input delivery, real timing syscalls, and an input/interrupt layer that survived a hostile audit. The next ported application starts from all of it.
Honest footnotes, as always
- Verified today: boot → shell →
doom→ title screen → keyboard menu navigation → in-game E1M1 with live HUD, on the stock EuroOS image under QEMU. Screenshots above are from those runs. - Our CI environment emulates the CPU instruction-by-instruction (no hardware virtualization), roughly 60× slower than real hardware, so DOOM’s startup takes minutes there and the frame rate is low. That is the harness, not the port; on virtualized or native hardware the same binary has dramatically more headroom.
- Sound and music are disabled (
-nosound -nomusic) because EuroOS has no audio stack yet. - It is the shareware WAD (episode 1), run with the doomgeneric port layer of the GPL engine source.
- One fullscreen app owns the screen at a time today; windowed apps use the existing EuroOS desktop instead.