Skip to content

Instantly share code, notes, and snippets.

@tirzasrwn
Last active March 30, 2022 04:49
Show Gist options
  • Save tirzasrwn/0601d3c408f2f5aa887ad7511a12f60a to your computer and use it in GitHub Desktop.
Save tirzasrwn/0601d3c408f2f5aa887ad7511a12f60a to your computer and use it in GitHub Desktop.
Learning embedded Linux note

Note

This is a note from this book. I'm using Ubuntu 16.04 for this.

Install require packages

sudo apt-get install autoconf automake bison bzip2 cmake flex g++ gawk gcc gettext git gperf help2man libncurses5-dev libstdc++6 libtool libtool-bin make patch python3-dev rsync texinfo unzip wget xz-utils u-boot-tools

Install crosstool-NG

git clone https://github.com/crosstool-ng/crosstool-ng.git
cd crosstool-ng
git checkout crosstool-ng-1.24.0 # Can skip this command
./bootstrap
./configure --prefix=${PWD}
make
make install

Building a toolchain for QEMU

bin/ct-ng distclean
bin/ct-ng list-samples # To see sample config.
bin/ct-ng arm-unknown-linux-gnueabi
bin/ct-ng menuconfig
# In Paths and misc options, disable Render the toolchain read-only(CT_PREFIX_DIR_RO).
bin/ct-ng build # This process can take forever, just take your coffee or doin somethin else while waiting.
bit/ct-ng distclean # You can delete build cache by this command to free some space.

Add path for arm toolchain bin directory

printf "\n\n# add PATH for arm toolchain bin directory\nexport PATH=$PATH:${HOME}/x-tools/arm-unknown-linux-gnueabi/bin/\n" >> ${HOME}/.profile
sudo reboot # rebooting for the changes to take effect

Building a kernel for QEMU

# Go to https://www.kernel.org/. Select version and copy tarball link. In my case is https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.28.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.28.tar.xz
tar xfv linux-5.15.28.tar.xz
cd linux-5.15.28
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- mrproper
make ARCH=arm versatile_defconfig
make -j4 ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- zImage
make -j4 ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- modules
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- dtbs

Install QEMU

sudo apt install qemu

Booting QEMU I - Without initramfs

mkdir qemu
cd qemu
# Copy this following files to qemu folder.
# zImage: <your-kernel-folder>/arch/arm/boot
# versatile-pb.dtb: <your-kernel-folder>/arch/arm/boot/dts
QEMU_AUDIO_DRV=none qemu-system-arm -m 256M -nographic -M versatilepb -kernel zImage -append "console=ttyAMA0,115200" -dtb versatile-pb.dtb
# To exit from QEMU, press Ctrl + A and then x (two separate keystrokes).

Building root filesystem

mkdir rootfs
cd rootfs
mkdir bin dev etc home lib proc sbin sys tmp usr var
mkdir usr/bin usr/lib usr/sbin
mkdir -p var/log
tree -d # To see the directory hierarchy.

Building Busybox for QEMU

# Go to https://www.busybox.net/downloads/. Select version and copy tarball link. In my case is https://www.busybox.net/downloads/busybox-1.35.0.tar.bz2
wget https://www.busybox.net/downloads/busybox-1.35.0.tar.bz2
tar xfv busybox-1.35.0.tar.bz2
cd busybox-1.35.0
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- disclean
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- defconfig
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi-
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- CONFIG_PREFIX=<your-rootfs-directory> install

Copy shared library

export SYSROOT=$(arm-unknown-linux-gnueabi-gcc --print-sysroot)
cp -Pv $SYSROOT/lib/* <your-rootfs-directory>/lib/
cd <your-rootfs-directory>/lib/ && rm -v *.a

Configure rootfs directory

cd <your-rootfs-directory>
sudo chown -R root:root *
sudo mknod -m 666 dev/null c 1 3
sudo mknod -m 600 dev/console c 5 1

Creating a boot initramfs

cd <your-rootfs-directory>
find . | cpio -H newc -ov --owner root:root >../initramfs.cpio
cd ..
gzip initramfs.cpio
cp initramfs.cpio.gz <your-qemu-folder>
mkimage -A arm -O linux -T ramdisk -d initramfs.cpio.gz uRamdisk
cp uRamdisk <your-qemu-folder>

Booting QEMU II - With initramfs

cd <your-qemu-folder>
QEMU_AUDIO_DRV=none qemu-system-arm -m 256M -nographic -M versatilepb -kernel zImage -append "console=ttyAMA0,115200 rdinit=/bin/sh" -dtb versatile-pb.dtb -initrd initramfs.cpio.gz
# You should get a root shell with the prompt / #.

Building an initramfs into the kernel image

cd <your-qemu-folder>
gzip -d initramfs.cpio.gz
cd <your-kernel-source-folder>
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- mrproper
make ARCH=arm versatile_defconfig
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- menuconfig
# General setup | Initramfs source file(s) | Paste your initramfs.cpio location and save.
yes 'N' | make -j4 ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- zImage # Rebuild the kernel.
make -j4 ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- modules
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- dtbs
# Remember to regenerate the cpio file each time you change the contents of the root filesystem and then rebuild the kernel.

Booting QEMU III - Without initramfs and rootfs inside zImage

cd <your-qemu-folder>
# Copy this following new files after rebuild to qemu folder.
# zImage: <your-kernel-folder>/arch/arm/boot
# versatile-pb.dtb: <your-kernel-folder>/arch/arm/boot/dts
QEMU_AUDIO_DRV=none qemu-system-arm -m 256M -nographic -M versatilepb -kernel zImage -append "console=ttyAMA0 rdinit=/bin/sh" -dtb versatile-pb.dtb
# You should get a root shell with the prompt / #.
# Exit qemu: Ctrl + A -> C -> quit

The init program

cd <your-rootfs-folder>
sudo mkdir -p etc/init.d/
sudo nano etc/inittab
# Paste this:
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/ash
::respawn:/sbin/syslogd -n
# Save
sudo nano etc/init.d/rcS
# Paste this:
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
# Save
sudo chmod +x etc/init.d/rcS
# Creating a boot initramfs
# Booting QEMU II - With initramfs with changing the -append parameter like this:
# -append "console=ttyAMA0 rdinit=/sbin/init"

Configuring user accounts

cd <your-rootfs-folder>
sudo nano etc/passwd
# Paste this:
root:x:0:0:root:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/false
# Save
sudo nano etc/group
# Paste this
root:x:0:
daemon:x:1:
# Save
sudo nano etc/shadow
# Paste this:
root::10933:0:99999:7:::
daemon:*:10933:0:99999:7:::
# Save
sudo nano etc/inittab
# Replace etc/inittab with this:
::sysinit:/etc/init.d/rcS
::respawn:/sbin/getty 115200 console
# Save
# Creating a boot initramfs
# Booting QEMU II - With initramfs with changing the -append parameter like this:
# -append "console=ttyAMA0 rdinit=/sbin/init"

A better way of managing device nodes - using mdev

Add this in /etc/init.d/rcS

mount -t devtmpfs devtmpfs /dev
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

Create file /etc/mdev.conf

cd <your-rootfs-folder>
sudo nano etc/mdev.conf
# Paste this:
null root:root 666
random root:root 444
urandom root:root 444
# Save.

Configuring the network - static IP

cd <your-rootfs-folder>
sudo mkdir etc/network
sudo mkdir etc/network/if-pre-up.d
sudo mkdir etc/network/if-up.d
sudo mkdir var/run
sudo nano etc/network/interfaces
# Paste this:
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
  address 192.168.1.101
  netmask 255.255.255.0
  network 192.168.1.0
# Save.
sudo nano etc/nsswitch.conf
# Paste this:
passwd:		files
group:		files
shadow:		files
hosts:		files dns
networks:	files
protocols:	files
services:	files
# Save.
sudo echo "127.0.0.1 localhost" > etc/hosts

Mounting the root filesystem using NFS

On Ubuntu install NFS server

sudo apt install nfs-kernel-server

Add this in /etc/exports Ubutu

<your-rootfs-directory-full-path> *(rw,sync,no_subtree_check,no_root_squash)

Restart NFS Server

sudo systemctl restart nfs-kernel-server

Install this for tunctl command

sudo apt install uml-utilities

Make this executable for mounting rootfs folder using NFS

cd ~
nano runme.sh
# Paste this:
#!/bin/bash
KERNEL=<your-qemu-folder>/zImage
DTB=<your-qemu-folder>/versatile-pb.dtb
ROOTDIR=<your-rootfs-directory>
HOST_IP=192.168.1.1
TARGET_IP=192.168.1.101
NET_NUMBER=192.168.1.0
NET_MASK=255.255.255.0

sudo tunctl -u $(whoami) -t tap0
sudo ifconfig tap0 ${HOST_IP}
sudo route add -net ${NET_NUMBER} netmask ${NET_MASK} dev tap0
sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"

QEMU_AUDIO_DRV=none qemu-system-arm -m 256M -nographic -M versatilepb -kernel ${KERNEL} -append "console=ttyAMA0,115200 root=/dev/nfs rw nfsroot=${HOST_IP}:${ROOTDIR} ip=${TARGET_IP}" -dtb ${DTB} -net nic -net tap,ifname=tap0,script=no
# Save.
chmod +x runme.sh
sudo ./runme.sh

Fix telnetd

When running telnetd -F then try to connect, I got error like this:

/ # telnetd -F
telnetd: can't find free pty

To fix that, add this line in etc/init.d/rcS

mkdir /dev/pts
mount -t devpts none /dev/pts

Install mosquitto

wget https://mosquitto.org/files/source/mosquitto-1.6.9.tar.gz
tar xfv mosquitto-1.6.9.tar.gz
cd mosquitto-2.0.9
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- WITH_TLS=no With_DOCS=no
sudo make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- WITH_TLS=no WITH_DOCS=no DESTDIR=<your-rootfs-folder> install

Configure .profile

cd <your-rootfs-folder>
sudo nano root/.profile
# Copy this:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
export PATH=$PATH:/usr/local/bin:/usr/local/sbin
# Save.

Building everything above using buildroot

wget https://buildroot.org/downloads/buildroot-2022.02.tar.gz
tar xfv buildroot-2022.02.tar.gz
cd buildroot-2022.02
make list-defconfigs # List config sample in folder configs or we can see by this command.
make qemu_arm_versatile_defconfig
make menuconfig # For set some configuration.
make
qemu-system-arm -M versatilepb -m 256 -kernel output/images/zImage -dtb output/images/versatile-pb.dtb -drive file=output/images/rootfs.ext2,if=scsi,format=raw -append "root=/dev/sda console=ttyAMA0,115200" -serial stdio -net nic,model=rtl8139 -net user
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment