Skip to content

Instantly share code, notes, and snippets.

@0x4248
Created January 16, 2024 22:32
Show Gist options
  • Save 0x4248/6e1499eb7a6d79349404780f14fdc38d to your computer and use it in GitHub Desktop.
Save 0x4248/6e1499eb7a6d79349404780f14fdc38d to your computer and use it in GitHub Desktop.
Linux from scratch on a M series Mac

Linux from scratch on a M series Mac (LSFMac)

This is a tutorial on how to build linux and test it using qemu on a M series Mac.

Prerequisites

You will need brew, git ,docker, qemu and a Mac with a M series chip (M1, M2, M3 ...)

Install brew from https://brew.sh/ and then install qemu and docker using brew:

brew install qemu docker git

Test docker using the following command:

docker run hello-world

If it says cant connect to docker daemon, then you need to start docker. I used colima to start docker.

brew install colima
colima start

Now re-run the docker hello world command and it should work.

Docker image

We need to use docker to build the linux from scratch because the build tools that come with Mac are not great for building linux. It also containerises the build so that it doesn't mess up your Mac.

First we need to build the docker image. Go to your documents and make a folder called docker-lfs in here make another folder called Dockerfile and copy the following into it:

FROM debian:latest


RUN apt-get update
RUN apt-get install -y git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison cpio

RUN apt-get install -y qemu-system-aarch64 vim nano
CMD ["/bin/bash"]

This has exactly the right requirements to build linux from scratch. Now we need to build the docker image. Inside the docker-lfs folder run the following command:

docker build --platform linux/arm64 -t linux-dev .

This will now take a while to build the docker image depending on your internet speed and the chip you have.

Downloading the sources

We need to download the sources including the linux kernel and busybox.

cd ~/Documents
git clone https://www.github.com/torvalds/linux
git clone https://www.github.com/mirror/busybox

Tip

In future if the builds fail you can always re-download a stable version of the linux kernel from https://www.kernel.org/ and busybox from https://www.busybox.net/downloads/

99% of the time the builds fail because of a new version of the linux kernel which might have a bug in it. Always check with the stable version of the linux kernel and busybox when encountering a build error.

Starting the docker container

Now we need to start the docker container. Go to the docker-lfs folder and make a run.sh and copy the following into it:

sudo docker run -it --privileged --cap-add SYS_ADMIN --cpus=2 --platform linux/arm64 -v ~/Documents/linux:/linux -v ~/Documents/busybox:/busybox linux-dev

Now run the container

sh run.sh

You are now in the docker container. Please CHECK the following before continuing:

Important

CHECK if you are in arm64 mode by running uname -m or arch it should say arm64 or aarch64 if it doesn't then you are not in arm64 mode and you need to restart the docker container and check if linux/arm64 is on the start.sh.

Important

CHECK if the linux and busybox folders are mounted correctly by running ls /linux and ls /busybox it should list the contents of the folders. If it doesn't then you need to restart the docker container and check if the paths are correct on the start.sh.

Building the linux kernel

Now we need to build the linux kernel. First we need to configure the kernel. Run the following commands:

cd /linux
make menuconfig

You can now configure the linux kernel to your needs. To save and exit press esc twice and press save.

make -j$(nproc)

Important

CHECK Has there been a file created called arch/arm64/boot/Image if not then the build has failed and you need to check the error messages and fix them.

Building busybox

Now we need to build busybox. First we need to configure busybox. Run the following commands:

cd /busybox
make defconfig
make menuconfig

IMPORTANT STEP In the busybox configuration we need to change the Build static binary (no shared libs) to y and then save the configuration. This configuration is stored in settings>build options>build static binary (no shared libs).

Now we can build busybox:

make -j$(nproc)

Now we need to install busybox to our source directory:

make install

Important

CHECK Has there been a folder created called /busybox/_install. This should contain the busybox binary's and the base initramfs. If not then the build has failed and you need to check the error messages and retry this step.

Copying the busybox sources

This might sound odd but becuase our /busybox folder is shared with our Mac we need to copy the busybox sources to a new folder name so we dont have the following error when using mknod later on:

Operation not permitted

To do this run the following commands:

cd /
cp -r /busybox /busybox-2

Creating the initramfs

Now we need to create the initramfs. Run the following commands:

cd /busybox-2/_install
mkdir -p dev
mknod dev/console c 5 1
mknod dev/ram b 1 0

We now need to make a init script which will be the first thing that runs when we boot linux. Run the following command:

nano init

Or if you prefer vim:

vim init

This should put you in a text editor. Copy the following into it:

#!/bin/sh
mkdir /proc /sys /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t tmpfs none /tmp
echo "Welcome to LSFMac"

exec /bin/sh

Now we need to make the init script executable:

chmod a+x init

Now we need to make the initramfs:

find -print0 | cpio -0oH newc | gzip -9 > /initramfs.cpio.gz

In order for us to run it we need to move this initramfs to our mac:

mv /initramfs.cpio.gz /busybox/initramfs.cpio.gz

Now open finder and go to your Documents/busybox folder and you should see a file called initramfs.cpio.gz. Drag it to your user folder and rename it to initramfs.cpio.gz. This will be important later on.

Running the linux kernel

Now we have everything we need to run the linux kernel. Run the following command on your mac not in the docker container:

qemu-system-aarch64 -kernel ~/Documents/linux/arch/arm64/boot/Image -initrd ~/initramfs.cpio.gz \
  --append "root=/dev/ram rw init=/init.sh" -nographic \
  -machine virt \
  -cpu cortex-a57 \
  -m 2G \

This should boot linux and you should see the following:

Welcome to LSFMac
~ # 

Congratulations you have now successfully built linux from scratch on a M series Mac.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment