Skip to main content

Alpine Linux Porting (Part 2)

Original Source: Adapting Alpine Linux for LuckFox Pico – Detailed Steps for Alpine Linux Adaptation

Image Download: AlpineLinux.zip

1. Brief Analysis of Linux Boot Process

To adapt the rootfs, it's essential to understand the common startup process of Linux development boards. We won't delve into the detailed startup process here, as it has been well explained by other experts. In simple terms, after powering on the development board, the bootloader is the first to start, with different boards having different processes. After UBoot finishes its startup, it passes startup parameters to the kernel and jumps to the kernel. The kernel, upon startup, attempts to load the rootfs and looks for the init program in a specific directory. The init program then begins running programs that initialize system services (our image uses OpenRC), and OpenRC is responsible for starting various services.

For example, if a problematic rootfs is passed to the kernel, and the kernel cannot find the init program, an error occurs.

2. Running Alpine Linux Container with Docker

Docker serves as an excellent rootfs extraction tool, widely available for various distribution platforms. It offers fast downloads, version flexibility, and enables the pre-processing of desired software packages and sources on the computer before integrating into the SDK. Without further ado, let's get started.

2.1 Create a temporary 100M file to store the new rootfs

dd if=/dev/zero of=rootfs.ext4 bs=1M count=100

2.2 Format the temporary file

mkfs.ext4 rootfs.ext4

2.3 Mount the temporary file system to /tmp/my-rootfs

mkdir /tmp/my-rootfs sudo mount rootfs.ext4 /tmp/my-rootfs

2.4 Enable virtualization support (if WSL or the computer is restarted, start from this step)

docker run --rm --privileged multiarch/qemu-user-static --reset --persistent yes

2.5 Create a container named armv7alpine

docker run -it \    
--name armv7alpine \
--net=host \
-v /tmp/my-rootfs:/my-rootfs \
arm32v7/alpine

All subsequent operations are performed within this armv7alpine container until the creation of the Alpine root file system compression package is complete.

3. Modify Sources

Due to the slow speed of the official source, it is necessary to replace Alpine Linux's official source with the Aliyun source. After modifying, use the apk update command to test.

sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
apk update

4. Install Boot Services Software and Configure Serial Port

4.1 Install OpenRC

As explained in the first section, understanding the Linux startup process simplifies the installation of OpenRC. Just enter the following command:

apk add openrc

After installing OpenRC, some configurations are still needed. Let's continue.

4.2 Start necessary services

rc-update add devfs boot
rc-update add procfs boot
rc-update add sysfs boot

4.3 Set up automatic login for serial port

Before setting up, confirm the default serial port device used by the kernel. You can use the cat /proc/cmdline command to confirm. Additionally, you can check the default enabled serial ports by entering ls /dev/tty* in the official SDK.

In this case, ttyFIQ0 is used, and other ports are ignored.

4.3.1 Add the serial port to the configuration file responsible for managing which serial port can log in as the root user

echo ttyFIQ0 > /etc/securetty

4.3.2 Modify the /etc/inittab file, which manages the startup services on the serial port

Delete lines starting from tty1 to tty6. This step is crucial; otherwise, the serial port startup will get stuck trying to find ttyX.

Add the following line to allow automatic login via the serial port:

ttyFIQ0::respawn:/sbin/agetty --autologin root ttyFIQ0 vt100

Modified content:

# /etc/inittab

::sysinit:/sbin/openrc sysinit
::sysinit:/sbin/openrc boot
::wait:/sbin/openrc default

# Set up a couple of getty's Delete the lines below this

# Put a agetty on the serial port Modify the line below like this, others unchanged
ttyFIQ0::respawn:/sbin/agetty --autologin root ttyFIQ0 vt100

# Stuff to do for the 3-finger salute
::ctrlaltdel:/sbin/reboot

# Stuff to do before rebooting
::shutdown:/sbin/openrc shutdown

With these changes, the rootfs can now be started. However, network package installation is not yet possible.

5. Configure Network

5.1 Add network interface configuration

Similar to many distributions, to permanently configure network settings, edit the /etc/network/interfaces file. If this file does not exist, create it using the following command:

vi /etc/network/interfaces

Copy the following content into the file, ensuring to replace it with your IP, corresponding subnet, and gateway. It's recommended to use a static IP for easier management.

auto eth0
iface eth0 inet static
address 192.168.50.59
netmask 255.255.255.0
gateway 192.168.50.1

Remember to add the networking service to the default startup queue. If the board lacks an IP, check if the networking service is running correctly.

rc-update add networking default

5.2 Modify Domain Name Resolution Server

Edit the /etc/resolv.conf file and replace the IP address of the DNS server with your local server's IP. Note that this file will be restored after restarting the Docker container or the computer.

# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry# [network]
# generateResolvConf = false
nameserver 202.103.224.68

5.3 Modify Hostname

Edit the /etc/hostname file and replace its content with the hostname, for example, changing it to "Luckfox." The hostname is displayed when accessing the shell via SSH or serial login, serving as an identifier for the host.

6. Install and Configure SSH Service

6.1 Install the openssh package

Simply use the following command to install openssh:

apk add openssh

6.2 Configure SSH

Open the /etc/ssh/sshd_config file and modify lines 32 and 57 as follows:

# Authentication:

#LoginGraceTime 2m
PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

#PubkeyAuthentication yes

# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication yes

6.3 Add sshd to the default runlevel queue for automatic startup

rc-update add sshd default

7. Configure Boot-Time ntpd for Network Time Synchronization

7.1 Create a local startup service script

Create the /etc/local.d/crond.start file with the following content. This startup script performs three tasks: ntp network time synchronization, starting crond scheduled tasks, and turning off the board's LED.

#!/bin/bash

ntpd -d -q && crond
echo 0 > /sys/class/leds/workbr/ightness

Save the script and give it executable permissions.

chmod a+x /etc/local.d/crond.start

7.2 Configure ntp server

Create the /etc/ntp.conf file and add ntp servers. Here, Aliyun's time servers are used. The file content is as follows:

erver ntp.aliyun.com iburst
server ntp0.aliyun.com iburst
server ntp1.aliyun.com iburst
server ntp2.aliyun.com iburst

7.3 Add local to the default runlevel queue

rc-update add local default

7.4 Set the timezone

Alpine Linux does not include multi-language and timezone support by default. Install the tzdata package, copy the Shanghai timezone data, and then remove the package to save space.

apk add tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone && apk del tzdata

At this point, the boot-time synchronization service is completed. However, if the board needs to be turned on for an extended period, and accurate time is required throughout, follow the next steps to add scheduled ntp time synchronization configuration.

7.5 Set up crond scheduled tasks

Note: This step should be done after burning to the board, as doing it now won't be effective. The exact reason has not been investigated and analyzed.

7.5.1 Create the /var/spool/cron/crontabs directory to store scheduling configurations

mkdir -p /var/spool/cron/crontabs

7.5.2 Edit scheduled tasks using the crontab -e command

crontab -e

Copy the following content and save:

# do daily/weekly/monthly maintenance
# min hour day month weekday command
0 */6 * * * ntpd -d -q

8. Install Required Software Packages

At this point, the customization of the rootfs is mostly completed. Install some needed software packages into the rootfs based on your requirements. It is recommended to install util-linux, btop, and bash.

apk add util-linux
apk add sqlite btop
apk add bash bash-completion

Modify the default shell for the root user to bash. Open the /etc/passwd file and change the ash at the end of the root line to bash, as shown below:

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin

9. Package the Rootfs and Integrate it into the SDK for Image Building

9.1 Package the rootfs

Execute the following commands in the / directory:

for d in bin etc lib root sbin usr; do tar c "$d" | tar x -C /my-rootfs; done
for dir in dev proc run sys var; do mkdir /my-rootfs/${dir}; done
cd /my-rootfs/ && tar czf alpine.tar.gz *

Once completed, you'll see an alpine.tar.gz file in the /tmp/my-rootfs directory. Copy this file to the sysdrv/custom_rootfs directory under Luckfox's official SDK (create the directory if necessary).

9.2 Modify the build.sh file in the SDK root directory

Open the SDK's build.sh file and find the function __PACKAGE_ROOTFS() around line 1044. Replace the content before build_get_sdk_version with the following:

function __PACKAGE_ROOTFS()
{
local rootfs_tarball _target_dir _install_dir

if [ -f $rootfs_tarball ]; then
if [ -z $RK_CUSTOM_ROOTFS ]; then
rootfs_tarball="$RK_PROJECT_PATH_SYSDRV/rootfs_${RK_LIBC_TPYE}_${RK_CHIP}.tar"
tar xf $rootfs_tarball -C $RK_PROJECT_OUTPUT
else
rootfs_tarball="$RK_CUSTOM_ROOTFS"
if [ ! -d $RK_PROJECT_PACKAGE_ROOTFS_DIR ]; then
mkdir $RK_PROJECT_PACKAGE_ROOTFS_DIR
fi
tar xf $rootfs_tarball -C $RK_PROJECT_PACKAGE_ROOTFS_DIR
fi
else
msg_error "Not found rootfs tarball: $rootfs_tarball"
exit 1
fi

build_get_sdk_version

9.3 Modify the .BoardConfig.mk file in the SDK root directory

Add the following content to the end of the file:

# Configure custom image directory
export RK_CUSTOM_ROOTFS=../sysdrv/custom_rootfs/alpine.tar.gz

Now, execute the following commands to delete the output/out/rootfs_uclibc_rv1106 directory:

rm -rf output/out/rootfs_uclibc_rv1106 This directory is a temporary rootfs generated by the build system. Deleting it ensures that our rootfs takes effect.

Then, rerun the build.sh command to generate the image. After the image is generated, burn it to the Nand Flash using the standard procedure. The TF card image handling is still under investigation; after burning, the root partition size cannot be expanded.

./build.sh

Cleaning Up Temporary File System

If you rarely use it in the future and don't want to tinker with the file system yourself, you can follow these steps.

Close the Docker container:

docker stop armv7alpine

Unmount:

sudo umount /tmp/my-rootfs

Delete files:

rm rootfs.ext4

Reference Documents