[{"content":"","date":"8 April 2023","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"Here are all the technologies that I use in my projects.\nProgramming Languages # Databases # Tools # Libraries \u0026amp; Frameworks # Self Hosted # Other # All trademarks and logos are the property of their respective owners. They are used here for informational purposes only.\n","date":"8 April 2023","externalUrl":null,"permalink":"/technologies/","section":"Home","summary":"Here are all the technologies that I use in my projects.\nProgramming Languages # Databases # Tools # Libraries \u0026 Frameworks # Self Hosted # Other # All trademarks and logos are the property of their respective owners. They are used here for informational purposes only.\n","title":"Technologies I use","type":"page"},{"content":" Introduction # In a previous article I explained the Linux boot process. The last step of the Linux boot process is the execution of init system\u0026rsquo;s binary. But what if we don\u0026rsquo;t want to have a conventional init system? In this article, we are going to write our own /init script.\nThe Linux Boot Process Explained 5 April 2023\u0026middot;Updated: 22 February 2026\u0026middot;8 mins Maciej Opaliński Linux Compiling the Linux kernel # First, we need to compile the Linux kernel. To do this, we need to obtain the Linux source code. Let\u0026rsquo;s clone the git repository.\ngit clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git # clone the git repository cd linux/ Next, let\u0026rsquo;s choose a specific Linux version. You can see a list of versions using git tag command. I chose v6.1.\ngit checkout v6.1 Next, let\u0026rsquo;s start the build process.\nWarning! Linux compilation is a lengthy and very resource intensive process. make mrproper make defconfig # use the default config make -j\u0026#34;$(nproc)\u0026#34; # compile with multiple jobs nproc command returns the number of your CPU threads. To make the compilation faster, we use all the threads.\nOn my machine it took almost 5 minutes to finish.\nIt finished with a following message:\nKernel: arch/x86/boot/bzImage is ready At this point we are pretty much ready to boot our kernel using QEMU, but the kernel is going to panic, because it can\u0026rsquo;t find the /init executable in the initramfs. Let\u0026rsquo;s create it!\nCreating the /init executable # All we need is a single statically linked ELF file. Here is an example C program:\n// init.c #include \u0026lt;stdio.h\u0026gt; int main() { printf(\u0026#34;\\n\\n\\nHello, world!\\n\\n\\n\\n\u0026#34;); while (1); } Notice the infinite while loop at the end of the main function. We can\u0026rsquo;t exit the program, because the kernel will also panic as it does not expect the init process to quit.\nWe compile it using the following command:\ngcc -static init.c -o init The executable is ready to be put into the initramfs and executed by the kernel.\nCreating the initramfs # To create the initramfs, use the following commands:\nmkdir initramfs/ cp init initramfs/ cd initramfs/ find . | cpio -oH newc | gzip \u0026gt; ../root.cpio.gz ROOTFS_PATH=\u0026#34;$(pwd)/../root.cpio.gz\u0026#34; We define a ROOTFS_PATH variable, because we will use the path to the archive later.\nOur initramfs is ready.\nBooting the system using QEMU # Now, we can boot our \u0026ldquo;system\u0026rdquo; using QEMU. Go back to the Linux kernel repository.\nqemu-system-x86_64 -kernel arch/x86/boot/bzImage -initrd \u0026#34;$ROOTFS_PATH\u0026#34; After running the command, we can see that /init has been executed successfully.\nCreating an ISO image # To create an ISO image that you can run in any other virtualization software or even a real hardware, run the following command:\nmake isoimage FDINITRD=\u0026#34;$ROOTFS_PATH\u0026#34; FDARGS=\u0026#34;initrd=root.cpio.gz\u0026#34; After finishing, the generated ISO image is located in arch/x86/boot/image.iso.\nInstalling BusyBox # We successfully booted a minimal Linux system with just a kernel and /init executable. It is not very useful though. Let\u0026rsquo;s make it more interesting by installing BusyBox.\nBusyBox is a software suite that provides several Unix utilities in a single executable file. It provides the utilities we commonly know and use, like: ls, find, tree, touch, rm and many more. BusyBox offers more than 480 commands.\nFirst, we need to clone, configure and compile BusyBox.\ngit clone https://github.com/mirror/busybox.git cd busybox/ make defconfig echo CONFIG_STATIC=y \u0026gt;\u0026gt; .config make -j\u0026#34;$(nproc)\u0026#34; make CONFIG_PREFIX=\u0026#34;$PWD/initramfs\u0026#34; install This will generate many files inside initramfs/ directory.\nNext, we add the /init script inside the initramfs.\ncd initramfs/ touch init chmod +x init Paste the following contents into the /init script.\n#!/bin/sh # mount required directories mount -t proc proc /proc mount -t sysfs none /sys mount -t devtmpfs udev /dev # launch interactive shell /bin/sh # if shell exits, poweroff to prevent kernel panic poweroff -f Finally, let\u0026rsquo;s pack the initramfs.\nfind . | cpio -oH newc | gzip \u0026gt; ../root.cpio.gz ROOTFS_PATH=\u0026#34;$(pwd)/../root.cpio.gz\u0026#34; Go back to the Linux kernel repository and again, create the ISO using the following command:\nmake isoimage FDINITRD=\u0026#34;$ROOTFS_PATH\u0026#34; FDARGS=\u0026#34;initrd=root.cpio.gz\u0026#34; Now, the ISO image that was generated in arch/x86/boot/image.iso is basically the most minimal Linux distro. It contains only a Linux kernel and some basic utilities.\nMake a bootable USB # To run it on a real hardware, you need to make a bootable USB. Insert your empty USB drive and make sure to check its device path using something like fdisk -l or lsblk. My USB drive is /dev/sdc. To flash the USB drive with the ISO image, run the following command.\nsudo dd if=arch/x86/boot/image.iso of=/dev/sdc Now you can reboot your computer, go into your motherboard\u0026rsquo;s boot menu and boot the system from the USB drive.\nVideo # Your browser cannot play this video. Download video.\nMinimal Linux Distribution with BusyBox ","date":"6 April 2023","externalUrl":null,"permalink":"/posts/booting-linux-without-an-init-system/","section":"Posts","summary":"Introduction # In a previous article I explained the Linux boot process. The last step of the Linux boot process is the execution of init system’s binary. But what if we don’t want to have a conventional init system? In this article, we are going to write our own /init script.\n","title":"Booting Linux without an init system","type":"posts"},{"content":"","date":"6 April 2023","externalUrl":null,"permalink":"/tags/linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"6 April 2023","externalUrl":null,"permalink":"/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"6 April 2023","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":" Introduction # If you are reading this, you probably used Linux before. No matter if you are running some Linux distribution as a daily driver or not, you might use an Android phone that is using the Linux kernel. You also requested this website content from a server running Linux. But did you ever wonder how Linux works? In this article, I am going to explain one of the most fundamental aspects of Linux - its boot process.\nBIOS/UEFI Firmware # When you power on your computer, the first thing that happens is the BIOS or UEFI firmware (depending on your system) initializes the hardware and performs a Power-On Self-Test to ensure that all components are functioning correctly. The BIOS/UEFI firmware then looks for a boot device, typically a HDD or SDD, where the operating system is installed.\nBIOS vs UEFI # I found a great comparison of BIOS and UEFI posted by u/AiwendilH on r/linux. Below is an excerpt from the comment explaining how BIOS and UEFI work:\nBIOS (Basic Input/Output System) # \u0026ldquo;Let\u0026rsquo;s look at the first sector of the first hard disk and boot the kernel saved in it.\u0026rdquo;\n\u0026ldquo;We are not in the 80s anymore\u0026hellip; no kernel fits in the boot sector nowadays.\u0026rdquo;\n\u0026ldquo;Then, let\u0026rsquo;s try booting a tiny kernel that loads the real kernel\u0026hellip; we can call it a boot manager.\u0026rdquo;\n\u0026ldquo;Nice, this works\u0026hellip; but what if we want to boot another OS?\u0026rdquo;\n\u0026ldquo;We already have the boot manager, let\u0026rsquo;s just add a menu to it to choose what kernel it should boot.\u0026rdquo;\n\u0026ldquo;Mhhh, but those kernels are on different filesystems like ext2 or FAT.\u0026rdquo;\n\u0026ldquo;Well\u0026hellip; let\u0026rsquo;s add filesystem drivers to our boot manager.\u0026rdquo;\n\u0026ldquo;But now the boot manager is too big to fit in the boot sector\u0026hellip; we are back at square one.\u0026rdquo;\n\u0026ldquo;Let\u0026rsquo;s use the empty sectors before the first partition as well\u0026hellip; we assume that those are unused and only needed for partition alignment\u0026hellip; that gives us some more space.\u0026rdquo;\n\u0026ndash; u/AiwendilH on r/linux\nThis is not a good solution. The boot manager falls apart in certain conditions. For more information, read the entire comment thread.\nAlso I\u0026rsquo;m pretty sure that no kernel ever fit in the 512 byte boot sector, so a true \u0026ldquo;BIOS boot\u0026rdquo; has not been around for years on any modern machines. So we might as well already move to UEFI.\nUEFI (Unified Extensible Firmware Interface) # \u0026ldquo;Okay, the BIOS was bad, let\u0026rsquo;s make it better.\u0026rdquo;\n\u0026ldquo;We support loading kernels from partitions right away\u0026hellip; let\u0026rsquo;s go with something simple for the filesystem format\u0026hellip; like FAT32.\u0026rdquo;\n\u0026ndash; u/AiwendilH on r/linux\nBasically, UEFI is more modern, faster and less problematic to setup. Just use UEFI.\nThere is much more going into UEFI, for example Secure Boot that was quite controversial. The original comment has some factual errors explaining how Secure Boot was introduced and how it\u0026rsquo;s used. If you want to know more, please read the entire comment thread.\nBootloader # The bootloader is responsible for loading the kernel into memory. The most common bootloader is GRUB (GRand Unified Bootloader). It provides a menu of boot options if multiple operating systems or kernels are installed. The bootloader also loads any necessary drivers for hardware components that are required to access the boot device.\nYou can use GRUB to choose which operating system to boot (Linux, Windows, etc.) and which kernel to load into memory. When you upgrade the kernel, most Linux distributions don\u0026rsquo;t actually remove the old kernel. This is done in case the newly upgraded kernel fails to boot, then you can just go into advanced options in GRUB and choose a different kernel that works.\nLinux Kernel # Once the bootloader has loaded, it locates the Linux kernel and loads it into memory. The kernel is the core component of the operating system. It is responsible for managing system resources such as CPU, memory, input/output (I/O) devices and many more. The kernel also initializes system services and device drivers.\nInitramfs # After loading the kernel, the system then loads the initramfs (initial RAM filesystem). The initramfs is a temporary filesystem used to initialize and load other device drivers, optionally handle decryption and finally mount the actual root file system. The first program that is executed by the Linux kernel after loading initramfs is the /init executable.\nWe can explore the initramfs of our Linux installation. It is usually found in the /boot directory.\nIn my Arch Linux install, I have the following files in the /boot directory:\n$ tree /boot /boot ├── efi │ ├── EFI │ │ ├── Boot │ │ │ ├── bootx64.efi │ │ │ ├── fbx64.efi │ │ │ └── mmx64.efi │ │ ├── grub_uefi │ │ │ └── grubx64.efi │ │ └── Microsoft │ │ ├── Boot │ │ (...) ├── grub │ (...) │ ├── grub.cfg │ (...) ├── initramfs-linux-fallback.img ├── initramfs-linux.img (...) └── vmlinuz-linux I replaced some unimportant files with (...).\nLet\u0026rsquo;s explain what every file and directory is for:\nefi/ is the directory to which my EFI partition mounts. I\u0026rsquo;m running UEFI, so GRUB bootloader lives there along with the default Windows Boot Manager; grub/ is the directory in which GRUB keeps its config files, like grub.cfg; initramfs-linux.img and initramfs-linux-fallback.img are the initramfs images themselves; vmlinuz-linux is the compressed Linux kernel, there instead could be vmlinux-linux which is uncompressed Linux kernel. Let\u0026rsquo;s explore the initramfs image!\nExploring initramfs # The simplest method of extracting the initramfs image is to use the lsinitcpio tool from mkinitcpio package.\nmkdir initramfs/ cd initramfs/ sudo cp /boot/initramfs-linux.img ./ # working on a copy sudo chown $USER:$USER ./initramfs-linux.img # to avoid extracting with sudo lsinitcpio -x initramfs-linux.img # extract to the current directory, run without the -x flag to just list the files First thing that the Linux kernel executes is the /init script, so let\u0026rsquo;s have a look at it.\n#!/usr/bin/ash # SPDX-License-Identifier: GPL-2.0-only export PATH=\u0026#39;/usr/local/sbin:/usr/local/bin:/usr/bin\u0026#39; # !!! redacted !!! mount_handler=default_mount_handler init=/sbin/init # !!! redacted !!! . /init_functions mount_setup # parse the kernel command line parse_cmdline \u0026lt;/proc/cmdline # !!! redacted !!! # Mount root at /new_root \u0026#34;$mount_handler\u0026#34; /new_root # !!! redacted !!! exec env -i \\ \u0026#34;TERM=$TERM\u0026#34; \\ /usr/bin/switch_root /new_root \u0026#34;$init\u0026#34; \u0026#34;$@\u0026#34; # vim: set ft=sh ts=4 sw=4 et: I redacted some of the less important parts of the script. Let\u0026rsquo;s analyze the script line by line.\nFirst, we are exporting a PATH environmental variable with default locations of all executables. We need it for our shell to locate the executables without typing out their entire paths by ourselves.\nThen, we are defining some other variables that will be used later in the script.\nNext, we evaluate the /init_functions script. The script is much longer and contains many functions used in the /init script, such as: default_mount_handler, parse_cmdline, etc.\nAfter that, we call mount_setup from the mentioned /init_functions script. Here is how the function looks:\nmount_setup() { mount -t proc proc /proc -o nosuid,noexec,nodev mount -t sysfs sys /sys -o nosuid,noexec,nodev mount -t devtmpfs dev /dev -o mode=0755,nosuid mount -t tmpfs run /run -o nosuid,nodev,mode=0755 mkdir -m755 /run/initramfs if [ -e /sys/firmware/efi ]; then mount -t efivarfs efivarfs /sys/firmware/efi/efivars -o nosuid,nodev,noexec fi # Setup /dev symlinks if [ -e /proc/kcore ]; then ln -sfT /proc/kcore /dev/core fi ln -sfT /proc/self/fd /dev/fd ln -sfT /proc/self/fd/0 /dev/stdin ln -sfT /proc/self/fd/1 /dev/stdout ln -sfT /proc/self/fd/2 /dev/stderr } It basically mount all required system directories like /proc, /sys, etc.\nNext step is quite interesting, because we are calling parse_cmdline function. The function makes sure that the Linux boot params are easily accessible as shell variables from inside the script. This is done for convenience purposes. I won\u0026rsquo;t show the entire function, as it is rather complicated. After verifying that the shell variable is valid it just calls eval in the following way:\neval \u0026#34;$key\u0026#34;=\u0026#39;${value:-y}\u0026#39; Finally, we can mount our real root filesystem, where all our persistent files are stored. We call $mount_handler, which was previously defined as default_mount_handler. Here is the function definition:\ndefault_mount_handler() { msg \u0026#34;:: mounting \u0026#39;$root\u0026#39; on real root\u0026#34; if ! mount -t \u0026#34;${rootfstype:-auto}\u0026#34; -o \u0026#34;${rwopt:-ro}${rootflags:+,$rootflags}\u0026#34; \u0026#34;$root\u0026#34; \u0026#34;$1\u0026#34;; then echo \u0026#34;You are now being dropped into an emergency shell.\u0026#34; # shellcheck disable=SC2119 launch_interactive_shell msg \u0026#34;Trying to continue (this will most likely fail) ...\u0026#34; fi } The function tries to mount the root filesystem to /new_root and will drop th euser into an emergency shell if something goes wrong.\nWhere does the $root variable come from? It comes from the previous step when we called parse_cmdline.\nThe final step in the /init script is to execute /usr/bin/switch_root to obviously chroot into the /new_root directory that contains our actual files and programs. The next argument is the path to the executable to be launched next ($init, previously defined as /sbin/init). This is the path from our actual root filesystem.\n$ ls -la /sbin/init lrwxrwxrwx 1 root root 22 Mar 29 20:41 /sbin/init -\u0026gt; ../lib/systemd/systemd As we can see, in my case, the /sbin/init is linked to /lib/systemd/systemd as I am running systemd as my init system.\nThis was just an example /init script from my Arch Linux install. Yours might be different. You can even write your own /init! More on that in an upcoming article!\nInit System # Once the Linux kernel executes /sbin/init, the init system takes over. The init system is responsible for starting system services, such as networking, logging as well as running any startup scripts required by the system. The most popular init systems used in Linux are: systemd, OpenRC, runit.\nConclusion # In conclusion, the Linux boot process is a complex sequence of steps that starts with the BIOS/UEFI firmware initializing the hardware and performing a Power-On Self-Test (POST), followed by the bootloader loading the Linux kernel into memory. The kernel initializes system resources, loads necessary device drivers, and hands over control to the init system, which starts system services and user applications. Understanding the Linux boot process is essential for troubleshooting and diagnosing problems with the operating system.\n","date":"5 April 2023","externalUrl":null,"permalink":"/posts/the-linux-boot-process-explained/","section":"Posts","summary":"Introduction # If you are reading this, you probably used Linux before. No matter if you are running some Linux distribution as a daily driver or not, you might use an Android phone that is using the Linux kernel. You also requested this website content from a server running Linux. But did you ever wonder how Linux works? In this article, I am going to explain one of the most fundamental aspects of Linux - its boot process.\n","title":"The Linux Boot Process Explained","type":"posts"},{"content":"I am a software engineer with a passion for web development and embedded programming. I also specialize in system administration and DevOps. I have experience with a wide range of technologies.\nI am a big fan of free and open-source software and I try to contribute to the community as much as I can. I almost exclusively use free and open-source software on a daily basis. I am a big fan of Linux and I use it as my primary operating system.\nI am interested in electronics. I have experience with designing, building, reverse engineering electronic circuits and manufacturing PCBs.\n","date":"1 January 2023","externalUrl":null,"permalink":"/about-me/","section":"Home","summary":"I am a software engineer with a passion for web development and embedded programming. I also specialize in system administration and DevOps. I have experience with a wide range of technologies.\n","title":"About Me","type":"page"},{"content":" Email # The best way to contact me is by email: maciej@mopalinski.com.\nGPG # All legitimate emails from me will be signed with my GPG key. It is advised to encrypt your message with my GPG key prior to sending.\nKey ID: F34F8711606DA21A\nFingerprint: 3C02 2F9B DBD6 59B6 E0CD C654 F34F 8711 606D A21A\nYou can download my GPG public key here or just run the following command to import it into your GPG key chain.\ncurl -sL https://mopalinski.com/mopalinski.gpg | gpg --import ","date":"1 January 2023","externalUrl":null,"permalink":"/contact/","section":"Home","summary":"Email # The best way to contact me is by email: maciej@mopalinski.com.\nGPG # All legitimate emails from me will be signed with my GPG key. It is advised to encrypt your message with my GPG key prior to sending.\n","title":"Contact","type":"page"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"Welcome to my personal webpage. Check out some of my posts!\nTechnologies I use 8 April 2023\u0026middot;Updated: 22 February 2026\u0026middot;1 min Maciej Opaliński ","externalUrl":null,"permalink":"/","section":"Home","summary":"Welcome to my personal webpage. Check out some of my posts!\nTechnologies I use 8 April 2023·Updated: 22 February 2026·1 min Maciej Opaliński ","title":"Home","type":"page"},{"content":"","externalUrl":null,"permalink":"/authors/maciej.opalinski/","section":"Authors","summary":"","title":"Maciej Opaliński","type":"authors"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]