Skip to content

Instantly share code, notes, and snippets.

@javamak
Forked from bluedragon1221/min-linux.md
Last active January 18, 2025 21:21
Show Gist options
  • Save javamak/710f90a5005c65bcfec2e8a24a452a73 to your computer and use it in GitHub Desktop.
Save javamak/710f90a5005c65bcfec2e8a24a452a73 to your computer and use it in GitHub Desktop.

Revisions

  1. javamak revised this gist Jan 18, 2025. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions min-linux.md
    Original file line number Diff line number Diff line change
    @@ -81,6 +81,8 @@ make -j$(nproc)
    ```
    It shouldn't take nearly as long, but you never know...

    Running install at this stage throws some exception. So need to disable CONFIG_TC to n in the .config file

    Since busybox will go into our initramfs, let's make a folder for it in `/boot-files`:
    ```sh
    mkdir /boot-files/initramfs
  2. @bluedragon1221 bluedragon1221 revised this gist Sep 2, 2024. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion min-linux.md
    Original file line number Diff line number Diff line change
    @@ -134,7 +134,7 @@ To format it, we'll need `dosfstools`:
    apt install dosfstools
    ```

    The to format it:
    Then to format it:
    ```sh
    mkfs -t fat boot
    ```
  3. @bluedragon1221 bluedragon1221 revised this gist Sep 1, 2024. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion min-linux.md
    Original file line number Diff line number Diff line change
    @@ -45,7 +45,7 @@ make menuconfig
    ```
    Then arrow over to `< Exit >`, and save the configuration.

    So, with out further adue, let's build the kernel.
    So, without further ado, let's build the kernel.
    ```sh
    make -j$(nproc)
    ```
  4. @bluedragon1221 bluedragon1221 created this gist Jan 17, 2024.
    197 changes: 197 additions & 0 deletions min-linux.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,197 @@
    https://www.youtube.com/watch?v=QlzoegSuIzg

    ## The Three Parts
    To build a minimal linux distro, we need three parts:
    1. The Kernel
    2. Userspace (busybox)
    3. Bootloader (syslinux)

    When the system boots, it loads the kernel, which loads busybox.
    ```
    Bootloader -> Kernel -> Userspace
    ```

    ## Create a Container
    It's bad to [install locallly](https://www.youtube.com/watch?v=J0NuOlA2xDc), so we'll do everything in an Ubuntu container.
    ```sh
    docker run --privileged -it ubuntu
    ```
    You'll notice that `--privileged` flag. It is required for mounting some stuff later on.

    ### Setup the Container
    Start by updating the system:
    ```sh
    apt update
    ```

    Then we need to install some packages.
    ```sh
    apt install bzip2 git vim make gcc libncurses-dev flex bison bc cpio libelf-dev libssl-dev
    ```

    ## Build Linux from Source
    First, we need to get a kernel.

    We'll start by cloning linux. We'll use the GitHub mirror, because why not?
    ```sh
    git clone --depth 1 https://github.com/torvalds/linux.git
    cd linux
    ```
    `--depth 1` just means that it won't clone the entire git history. (That would be significantly longer)

    Next, we need to make the kernel config. There's a lot of options, and I'm sure you can find some other tutorial for making a kernel config. For this, we'll just use the default options.
    ```sh
    make menuconfig
    ```
    Then arrow over to `< Exit >`, and save the configuration.

    So, with out further adue, let's build the kernel.
    ```sh
    make -j$(nproc)
    ```
    This will take a LONG time...

    At the end, you should see a line that says:
    ```txt
    Kernel at arch/x86/boot/bzImage is ready
    ```

    This is our kernel binary. Since we'll need it later, let's copy it to another place.
    ```sh
    mkdir /boot-files
    cp arch/x86/boot/bzImage /boot-files/
    ```

    ## Build the Userspace (Busybox)
    Busybox is a bundle of many basic linux utilities, for example `ls`, `grep`, and `vi` are all included.

    First, let's clone the sources (this process will feel very similar to the Kernel)
    ```sh
    git clone --depth 1 https://git.busybox.net/busybox
    cd busybox
    ```

    Now, we can edit the busybox config. In this one, we will need to change one option. Go to `Settings --->` and toggle `[ ] Build static binary (no shared libraries)`. This will make our build simpler; not depending on any shared libraries.

    Then exit, and save changes, just like in the kernel.

    Now we'll build it, just like the kernel:
    ```sh
    make -j$(nproc)
    ```
    It shouldn't take nearly as long, but you never know...

    Since busybox will go into our initramfs, let's make a folder for it in `/boot-files`:
    ```sh
    mkdir /boot-files/initramfs
    ```

    Now, we'll install busybox to that directory:
    ```
    make CONFIG_PREFIX=/boot-files/initramfs install
    ```
    This will basically just create a bunch of symlinks in that folder

    Also, we can remove `linuxrc`, because we don't need it.
    ```sh
    rm /boot-files/initramfs/linuxrc
    ```

    ### Create an init script
    The kernel needs something to execute. It looks in `/init` for this file. On a normal linux system, this is a systemd binary, but here, it's just a shell script.
    ```sh
    cd /boot-files/initramfs
    echo << EOF > ./init
    #!/bin/sh
    /bin/sh
    EOF
    chmod +x ./init
    ```
    This short little script will launch a shell as soon as we boot into the system.

    ## Create the Initramfs
    An initramfs is a cpio archive. This archive needs to contain all of the files in `initramfs/`. First, let's create a list of all of those files:
    ```sh
    find .
    ```

    Then pass it to `cpio` to create the archive:
    ```sh
    find . | cpio -o -H newc > ../init.cpio
    ```
    `-o` creates a new archive, and `-H newc` specifies the type of the archive.

    ## Create the "filesystem"
    We don't want to create a whole partition for this device, and, luckily, we don't have to. We'll create an empty file and format it with fat.
    ```sh
    dd if=/dev/zero of=./boot bs=1M count=50
    ```
    Hopefully you can spare 50M...

    To format it, we'll need `dosfstools`:
    ```sh
    apt install dosfstools
    ```

    The to format it:
    ```sh
    mkfs -t fat boot
    ```

    ### Install the bootloader
    We'll need the `syslinux` package:
    ```sh
    apt install syslinux
    ```

    Then we can install syslinux with:
    ```sh
    syslinux ./boot
    ```

    ### Copy files to the image
    We need to mount that image (Here's why our container needed root privileges). Create the mountpoint, then mount it there.
    ```sh
    mkdir m
    mount boot m
    ```

    Then move the kernel binary and the initramfs to the image:
    ```sh
    cp {bzImage,init.cpio} m/
    ```

    Now we can umount the filesystem:
    ```sh
    umount m
    ```

    ## Test it
    We can test the image using qemu. Assuming you don't want to install qemu on your container, we'll copy the files to the host system.
    ```sh
    docker cp {container_name}:/boot-files ~/boot-files
    ```

    Now we can run `./boot` with qemu:
    ```sh
    qemu-system-x86_64 -drive format=raw,file=./boot -display gtk
    ```

    We'll get a prompt that says `boot: `. Type,
    ```sh
    /bzImage -initrd=/init.cpio
    ```

    Just wait for it to boot up, then you'll be greeted with a shell prompt!
    ```
    ~ #
    ```

    ## Bonus: Installing binaries
    You can install any software that you want to the system using the following procedure:
    1. Build the software, with `CONFIG_PREFIX=/boot-files/initramfs`
    2. Remount `./boot` to `m`
    3. Delete the old `init.cpio` and `m/init.cpio`
    4. Create a new one, and copy it to `m/`
    5. Umount `./m`