This is a tutorial on how to build linux and test it using qemu on a M series Mac.
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 gitTest docker using the following command:
docker run hello-worldIf it says cant connect to docker daemon, then you need to start docker. I used colima to start docker.
brew install colima
colima startNow re-run the docker hello world command and it should work.
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.
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/busyboxTip
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.
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-devNow run the container
sh run.shYou 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.
Now we need to build the linux kernel. First we need to configure the kernel. Run the following commands:
cd /linux
make menuconfigYou 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.
Now we need to build busybox. First we need to configure busybox. Run the following commands:
cd /busybox
make defconfig
make menuconfigIMPORTANT 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 installImportant
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.
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 permittedTo do this run the following commands:
cd /
cp -r /busybox /busybox-2Now 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 0We now need to make a init script which will be the first thing that runs when we boot linux. Run the following command:
nano initOr if you prefer vim:
vim initThis 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/shNow we need to make the init script executable:
chmod a+x initNow we need to make the initramfs:
find -print0 | cpio -0oH newc | gzip -9 > /initramfs.cpio.gzIn order for us to run it we need to move this initramfs to our mac:
mv /initramfs.cpio.gz /busybox/initramfs.cpio.gzNow 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.
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.
Nice gist. I think you meant “LFSMac”. I had to run "colima start -V ~/repos/busybox -V ~/repos/linux” to be able to write to /linux and /busybox. Make menuconfig didn’t work OOTB, so now I'm figuring out what to turn off in the blizzard of kernel options. Turning off all the CPU architectures except Arm64 also didn’t work using the latest kernel. I’m on an M3 Mac.
I hope to use this Linux VM to teach Linux From Scratch.
I really like Colima, btw. Thanks for introducing it to me.
Edit: Put “int ” before “main” in check-lxdialog to fix “make menuconfig”.