/DoomLinux

A bash script to build a minimal linux operating system just to play Doom.

Primary LanguageShellMIT LicenseMIT

DoomLinux

A single script to build a minimal live Linux operating system from source code that runs Doom on boot.

./DoomLinux.sh

This command will create an iso of DoomLinux which is bootable from USB stick.

What it does

  • Downloads Linux Kernel 5.4.3 source and compiles it with a minimal configuration
  • Downloads Busybox 1.35.0 source and compiles it statically.
  • Downloads FBDoom and compiles it statically.
  • Creates rootfs for linux kernel.
  • Generates grub configuration
  • Creates a bootable live Linux iso that runs Doom on boot.

Build Dependencies

sudo apt install wget make gawk gcc bc bison flex unzip rsync mtools xorriso libelf-dev libssl-dev grub-common

Explanation

Creating folders and downloading the source codes

We need to create some folders for managing the codes

mkdir -p rootfs
mkdir -p staging
mkdir -p iso/boot
  • rootfs - It is the root file system of DoomLinux.
		rootfs
		├── bin (Busybox and fbdoom binaries)
		├── dev (All the available devices)
		├── mnt (Mount point for temporary external media)
		├── proc (Different information of currently running kernel)
		├── sys (Directory for virtual filesystem)
		└── tmp (Directory for temporary files required during runtime)
  • staging - Here all the source codes are downloaded and compiled.
  • iso - It is the folder structure for grub to make a bootable iso. In boot folder we will place grub.cfg
		iso
		└── boot
		    ├── bzImage (Compiled Linux Kernel)
		    ├── grub
		    │   └── grub.cfg (Grub Configuration)
		    ├── rootfs.gz (Compressed root file system)
		    └── System.map (System map that is compiled with kernel)

Setting variables to make things easy

KERNEL_VERSION=5.4.3
BUSYBOX_VERSION=1.35.0

SOURCE_DIR=$PWD
ROOTFS=$SOURCE_DIR/rootfs
STAGING=$SOURCE_DIR/staging
ISO_DIR=$SOURCE_DIR/iso

We need to download the required source codes in staging folder and extract them.

  • Linux kernel 5.4.3
  • Busybox 1.35.0 - For creating minimum shell environment.
  • FBDoom - A port of Doom original source code for Linux framebuffer.
  • Doom shareware - The shareware version of Doom. You can use other versions if you want.
cd $STAGING
wget -nc -O kernel.tar.xz http://kernel.org/pub/linux/kernel/v5.x/linux-${KERNEL_VERSION}.tar.xz
wget -nc -O busybox.tar.bz2 http://busybox.net/downloads/busybox-${BUSYBOX_VERSION}.tar.bz2
wget -nc -O fbDOOM-master.zip https://github.com/maximevince/fbDOOM/archive/refs/heads/master.zip
wget -nc -O doom1.wad https://distro.ibiblio.org/slitaz/sources/packages/d/doom1.wad

tar -xvf kernel.tar.xz
tar -xvf busybox.tar.bz2
unzip fbDOOM-master.zip

Configuring the kernel and compile it

Change directory to kernel source.

cd $STAGING
cd linux-${KERNEL_VERSION}

Creating a config file for kernel. You can use either of the following

make -j$(nproc) defconfig # Creates a ".config" file with default options from current architecture
make -j$(nproc) menuconfig # Menu-driven user interface for configuring kernel
make -j$(nproc) xconfig # GUI based user interface for configuring kernel

-j$(nproc) flag sets the number of jobs to the number of CPU cores/threads available. It make things compile faster.

Now we will make a couple of changes in the .config file to make our kernel size smaller. It will also reduce the compile time.

Use xz kernel compression instead of gzip

sed -i "s|.*# CONFIG_KERNEL_XZ is not set.*|CONFIG_KERNEL_XZ=y|" .config
sed -i "s|.*CONFIG_KERNEL_GZIP=y.*|# CONFIG_KERNEL_GZIP is not set|" .config

Disable sound drivers.

sed -i "s|.*CONFIG_SOUND=y.*|# CONFIG_SOUND is not set|" .config

Disable network drivers.

sed -i "s|.*CONFIG_NET=y.*|# CONFIG_NET is not set|" .config

Disable EFI stubs.

sed -i "s|.*CONFIG_EFI=y.*|# CONFIG_EFI is not set|" .config 
sed -i "s|.*CONFIG_EFI_STUB=y.*|# CONFIG_EFI_STUB is not set|" .config

Disable kernel debug.

sed -i "s/^CONFIG_DEBUG_KERNEL.*/\\# CONFIG_DEBUG_KERNEL is not set/" .config

Optimize for size.

sed -i "s|.*CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y.*|# CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE is not set|" .config
sed -i "s|.*# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set.*|CONFIG_CC_OPTIMIZE_FOR_SIZE=y|" .config

Change host name

sed -i "s|.*CONFIG_DEFAULT_HOSTNAME=*|CONFIG_DEFAULT_HOSTNAME=\"DoomLinux\"|" .config

Enable Bochs dispi vga interface for QEMU

sed -i "s|.*# CONFIG_DRM_BOCHS is not set*|CONFIG_DRM_BOCHS=y|" .config

Now compile the kernel and copy the binaries.

make -j$(nproc) bzImage
cp arch/x86/boot/bzImage $SOURCE_DIR/iso/boot/bzImage
cp System.map $SOURCE_DIR/iso/boot/System.map

Configuring busybox and compile it

cd  $STAGING
cd busybox-${BUSYBOX_VERSION}
make defconfig
LDFLAGS="--static" make busybox install -j$(nproc)
cd _install
cp -r ./ $ROOTFS/
cd $ROOTFS
rm -f linuxrc

These commands will statically compile busybox. The default installation folder for busybox is _install. We will copy the compiled binaries from there to our rootfs.

Compile FBDoom statically

cd $STAGING
cd fbDOOM-master/fbdoom
sed -i "s|CFLAGS+=-ggdb3 -Os|CFLAGS+=-ggdb3 -Os -static|" Makefile
sed -i "s|ifneq (\$(NOSDL),1)|ifeq (\$(LINK_SDL),1)|" Makefile
make -j$(nproc)
cp fbdoom $ROOTFS/bin/fbdoom
cp $STAGING/doom1.wad $ROOTFS/bin/doom1.wad

We need to statically compile FBDoom to work it in our system with minimal dependencies. The above commands will do that for us and it will also copy the doom1.wad in our root folder.

Archive rootfs

We will create additional folders so that Linux kernel can use them on runtime.

cd $ROOTFS
mkdir -p dev proc sys mnt tmp

Now we will create a init file for our kernel.

echo '#!/bin/sh' > init

Suppress all messages from the kernel except panic messages.

echo 'dmesg -n 1' >> init

Mount dev folder to devtmpfs

echo 'mount -t devtmpfs none /dev' >> init

Mount proc folder to proc

echo 'mount -t proc none /proc' >> init

Mount sys folder to sysfs

echo 'mount -t sysfs none /sys' >> init

Run doom right after booting the kernel. After that we will run busybox with cttyhack to stop kernel panic if we want to exit busybox. It will open another shell instead.

echo 'fbdoom -iwad /bin/doom1.wad' >> init
echo 'setsid cttyhack /bin/sh' >> init

We must have to make the init file executable.

chmod +x init

Now archive the rootfs with cpio.

cd $ROOTFS
find . | cpio -R root:root -H newc -o | gzip > $SOURCE_DIR/iso/boot/rootfs.gz

Using GRUB bootloader to boot DoomLinux

Create a grub configuration file in iso/boot directory.

cd $SOURCE_DIR/iso/boot
mkdir -p grub
cd grub
cat > grub.cfg << EOF
set default=0
set timeout=30

# Menu Colours
set menu_color_normal=white/black
set menu_color_highlight=white/green

root (hd0,0)

menuentry "DoomLinux" {      
  linux  /boot/bzImage
  initrd /boot/rootfs.gz
}
EOF

These are the location of our compiled kernel and archived rootfs.

  linux  /boot/bzImage
  initrd /boot/rootfs.gz

Finally create DoomLinux bootable iso

cd $SOURCE_DIR
grub-mkrescue --compress=xz -o DoomLinux.iso iso

You can now write the iso in your USB stick and play Doom.

Compiled size

The final iso should be around 20 MB in size depending on the architecture. For my x86_64 CPU the compiled kernel size is 4.1 MB and the iso is 17.9 MB.

Acknowledgements

Run

Real Hardware

Write the iso image on USB stick and boot it from BIOS menu.

QEMU

To run on QEMU :

qemu-system-x86_64 DoomLinux.iso

DoomLinux in Action

DoomLinux

Disclaimer

This project is made just for those who wants to learn how basic linux systems works. Under no circumstances shall the author be liable for any damage.

Licence

Licensed under the MIT License.