Kernel of Doom - A Tiny Linux Kernel to Boot into Doom
好的,我现在要帮用户总结这篇文章的内容,控制在100字以内。首先,我需要通读文章,理解其主要内容和目的。 文章主要讲述了作者将Doom游戏移植到一个极简Linux内核的过程。他使用了Linux 6.18.1内核,并通过配置tinyconfig和kvm_guest.config来构建最小化内核。同时,他还修改了initramfs以包含Doom-ASCII和必要的WAD文件。最终,这个定制的内核能够在QEMU环境中启动并运行Doom。 接下来,我需要提取关键点:极简内核、Doom移植、QEMU环境、压缩大小等。然后,用简洁的语言把这些点整合起来,确保不超过100字。 可能的结构是:描述项目目标(移植Doom到极简Linux内核),方法(配置和修改),结果(成功运行在QEMU中)以及最终的大小统计。 最后,检查语言是否流畅,信息是否准确,并确保符合用户的要求。 </think> 这篇文章介绍了一个项目:将 Doom 游戏移植到一个极简 Linux 内核中。作者通过配置 Linux 6.18.1 内核并构建最小化 initramfs,成功在 QEMU 环境中运行 Doom-ASCII。最终的压缩内核和 initramfs 总大小不到 8 兆字节。 2025-12-20 14:22:22 Author: www.archcloudlabs.com(查看原文) 阅读量:0 收藏

About The Project

I love Doom. It’s one of my favorite video game franchises, and the community around Doom that ports this game to run on bizarre hardware is nothing short of genius. The process of porting Doom to run on something it wasn’t designed for is quintessential hacking. You’re reverse engineering hardware and software, building custom loaders, writing file format parsers, debugging weird machines, and everything in between. The ports are an art unto itself. They include running Doom on a heat pump, a Smart Planter, and even the Flipper Zero. This blog post walks through building a tiny Linux kernel whose only job is to boot Doom. While I fully admit this is not as nearly as impressive as having rats run doom, it’s the start of Arch Cloud Labs running Doom on weird devices.

doom_coffee.png

Building A Tiny Kernel

I am leveraging kernel 6.18.1 for this blog post, and it can be obtained from kernel.org. The Linux kernel’s Makefile contains numerous build targets to quickly get up and running with the latest build. These options can be seen when executing make help from the root directory of the Kernel source code. Of interest to us for “Kernel of Doom” are tinyconfig and kvm_guest.config. As the descriptions show below, tinyconfig will build the tiniest kernel possible and kvm_guest.config will enable specific QEMU necessary configurations to boot our kernel images with QEMU. This combined with a minimal initramfs will allow for the Linux kernel to kick off our init process which will be doom-ascii.

➜  linux-6.18.1 make help
Cleaning targets:
  clean		  - Remove most generated files but keep the config and
                    enough build support to build external modules
  mrproper	  - Remove all generated files + config + various backup files
  distclean	  - mrproper + remove editor backup and patch files

Configuration targets:
  config	  - Update current config utilising a line-oriented program
  nconfig         - Update current config utilising a ncurses menu based program
  menuconfig	  - Update current config utilising a menu based program
  xconfig	  - Update current config utilising a Qt based front-end
  gconfig	  - Update current config utilising a GTK+ based front-end
  oldconfig	  - Update current config utilising a provided .config as base
  localmodconfig  - Update current config disabling modules not loaded
                    except those preserved by LMC_KEEP environment variable
  localyesconfig  - Update current config converting local mods to core
                    except those preserved by LMC_KEEP environment variable
  defconfig	  - New config with default from ARCH supplied defconfig
  savedefconfig   - Save current config as ./defconfig (minimal config)
  allnoconfig	  - New config where all options are answered with no
  allyesconfig	  - New config where all options are accepted with yes
  allmodconfig	  - New config selecting modules when possible
  alldefconfig    - New config with all symbols set to default
  randconfig	  - New config with random answer to all options
  yes2modconfig	  - Change answers from yes to mod if possible
  mod2yesconfig	  - Change answers from mod to yes if possible
  mod2noconfig	  - Change answers from mod to no if possible
  listnewconfig   - List new options
  helpnewconfig   - List new options and help text
  olddefconfig	  - Same as oldconfig but sets new symbols to their
                    default value without prompting
  tinyconfig	  - Configure the tiniest possible kernel
  testconfig	  - Run Kconfig unit tests (requires python3 and pytest)

Configuration topic targets:
  debug.config              - Debugging for CI systems and finding regressions
  hardening.config          - Basic kernel hardening options
  kvm_guest.config          - Bootable as a KVM guest
  nopm.config               - Disable Power Management
  rust.config               - Enable Rust
  x86_debug.config          - Debugging options for tip tree testing
  xen.config                - Bootable as a Xen guest

...truncated....

An issue with tinyconfig is that it does not enable CONFIG_PRINTK which means no output is written to the QEMU console. It’s essential for doom-ascii that this is enabled. Also, tinyconfig enables a networking stack and other features that are not particularly necessary for the goals of Kernel of Doom. Ultimately the two Makefile targets give an excellent basis to start configuration modifications are, but they still include features that are not necessary for building the tiniest kernel image possible. Removing subsystems that are not necessary for the goal of booting Doom can greatly reduce the overall size of the kernel. To reduce size, I disabled CONFIG_NET to remove all networking functionality and modified the initramfs support to exclude compression algorithms I wasn’t using.

    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
    ()    Initramfs source file(s)                                    
    [*]   Support initial ramdisk/ramfs compressed using gzip         
    [*]   Support initial ramdisk/ramfs compressed using bzip2        
    [*]   Support initial ramdisk/ramfs compressed usiining LZMA      
    [ ]   Support initial ramdisk/ramfs compressed using XZ           
    [ ]   Support initial ramdisk/ramfs compressed using LZO          
    [ ]   Support initial ramdisk/ramfs compressed using LZ4          
    [ ]   Support initial ramdisk/ramfs compressed using ZSTD         

After applying the previously mentioned changes, a bzip compressed kernel image comes in at a whopping 1.1 megabytes! With the kernel at a reasonably small size, the next object to tackle is the initramfs.

➜ kernel-of-doom ls -lah linux-6.18.1/arch/x86/boot/bzImage
-rw-r--r-- 1 dllcoolj dllcoolj 1.1M Dec 20 21:04 linux-6.18.1/arch/x86/boot/bzImage

Building The Initramfs

Per the Linux kernel documentation, the Linux kernel will execute a binary called init after unpacking the initramfs. For Kernel of Doom, this binary can just be doom-ascii. Doom-ASCII is a port of Doom that runs entirely in the terminal. Only one small changes was necessary to the Makefile to generate a static binary to run as a standalone utility for the initramfs.

diff --git a/Makefile b/Makefile
index d1dbb72..af4dbc1 100644
--- a/Makefile
+++ b/Makefile
@@ -59,7 +59,7 @@ APPDIR = $(OBJDIR)/io.github.wojciech_graj.doom_ascii.AppDir
 OUTDIR = game
 APPOUTDIR = appimage
 
-CFLAGS += -O3 -flto -Wall -D_DEFAULT_SOURCE -DVERSION=$(VERSION) -std=c99 #-DSNDSERV -DUSEASM
+CFLAGS += -static -O3 -flto -Wall -D_DEFAULT_SOURCE -DVERSION=$(VERSION) -std=c99 #-DSNDSERV -DUSEASM

For the custom initramfs it is nothing more than that a directory with the doom-ascii binary in a file called init, and a Doom game file (WAD). The WAD file provides game data to actually run Doom. This was obtained by purchasing “Doom II” on Steam and copying the “WAD” file from the game installation. This minimal initramfs file system looks as follows:

➜  kernel-of-doom ls -lah initramfs 
total 16M
drwxr-xr-x 1 dllcoolj dllcoolj   62 Dec 20 21:13 .
drwxr-xr-x 1 dllcoolj dllcoolj  242 Dec 20 20:47 ..
-rwxr-xr-x 1 dllcoolj dllcoolj   92 Dec 15 20:15 build-initramfs.sh
-rwxr-xr-x 1 dllcoolj dllcoolj  14M Dec 18 21:05 doom2.wad
-rwxr-xr-x 1 dllcoolj dllcoolj 1.5M Dec 18 21:21 init

The build-initramfs.sh file is simply a helper file to rapidly build a new initramfs for testing. The contents are as follows:

$> find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz

The compressed initramfs comes in at 6.6 megabytes, the largest file being the Doom2.wad at 14 megabytes. The Doom Wiki states that a WAD file is:

WAD (which, according to the Doom Bible, is an acronym for “Where’s All the Data?"[1]) is the file format used by Doom and all Doom-engine-based games for storing data

The WAD files are necessary for Doom to get up and running, and doom-ascii requires this be passed via a command line argument or be in the same directory doom-ascii is executing in. To further reduce this size, a modification to doom-ascii to take a compressed WAD file via command line would be ideal. This is left as an exercise to the reader should they want to re-implement this.

Booting into Doom

With the kernel and initramfs built, the last step is to boot the QEMU environment. The command line below shows how to boot a custom x86_64 kernel with an initramfs and have the console redirect to serial out.

$> qemu-system-x86_64 -kernel ./arch/x86_64/boot/bzImage \
    -initrd ./initramfs.cpio.gz \
    -nographic -append "console=ttyS0"

Behold! a successful minimal kernel that boots directly into Doom! Is it hard to read? Yes. Is it awesome? Also, yes.

doom_logo.png doom_2.png

The total size, to include uncompressed files is as follows:

  • vmlinux: 11M
  • bzImage: 1.1M
  • initramfs.cpio.gz: 6.6M
  • initramfs uncompressed: 15.5M (Doom WAD & Doom-ASCII)

Beyond The Project

The total size of the compressed kernel and initramfs with the necessary Doom artifacts was less than 8 megabytes in size. I’m sure there are ways to optimize beyond what I’ve shown here and I will leave that as a friendly challenge to the reader of this blog post. I would imagine a ARM kernel would be smaller, and the ability to read in a compressed WAD file or even a reduced WAD in size would result in something even smaller. I hope you enjoyed reading this, and I hope this inspires you to do something similar!


文章来源: https://www.archcloudlabs.com/projects/kernel_of_doom/
如有侵权请联系:admin#unsafe.sh