Linux and SBC devices

The following explains how Linux can be used with SBCs (Single Board Computers).

The boot process of Linux

A simplified representation of the boot process is shown in the figure below:

Linux Boot Diagram

The following briefly explains, in a simplistic manner, the boot process of a device running the Linux operating system.

BIOS/UEFI/PPL

The BIOS (an acronym for 'Basic Input - Output System') initializes the hardware and ensures through a power-on self-test (POST) that all hardware is ready. The main task of the BIOS is to load the bootloader.

UEFI (short for Unified Extensible Firmware Interface) is the newer version that replaces the BIOS with greater capabilities and, among other things, aims to load the bootloader.

Embedded devices such as SBCs, like the Odroid N2, generally do not have a BIOS or UEFI; these are replaced by a microprocessor that has a primary boot program (PPL - Primary Program Loader) in ROM, whose purpose is to search for a secondary boot program (SPL - Secondary Program Loader) called U-Boot SPL whose goal is to initialize the memory so that it can load the U-Boot bootloader.

Some devices still have an SPI memory in which a special distribution Petitboot is installed, which includes the U-Boot distribution that acts as a more advanced bootloader, with multiple boot capabilities.

Bootloader

The bootloader loads the kernel into memory and then starts the kernel with a set of kernel parameters.

Kernel

When the kernel is loaded, it immediately initializes the devices and memory. The main task of the kernel is to load the initialization process (init).

INIT

The init process is the first process that is started; init starts and stops essential processes in the system. This usually represents a symbolic link from the main system initialization service, for example systemd, called init and located in the directory /usr/sbin/ or /sbin/init.

florin@odroid-n2:~$ ps -p 1
    PID TTY          TIME CMD
      1 ?        00:00:04 systemd
florin@odroid-n2:~$ ls -la /sbin/init 
lrwxrwxrwx 1 root root 20 dec  1 15:28 /sbin/init -> /lib/systemd/systemd
florin@odroid-n2:~$ ls -la /usr/sbin/init 
lrwxrwxrwx 1 root root 20 dec  1 15:28 /usr/sbin/init -> /lib/systemd/systemd
florin@odroid-n2:~$ ls -la /sbin
lrwxrwxrwx 1 root root 8 feb  9 04:35 /sbin -> usr/sbin
florin@odroid-n2:~$ ps -ax
    PID TTY      STAT   TIME COMMAND
      1 ?        Ss     0:04 /sbin/init d:
...

The name init comes from SysV init, also known as System V init, which was among the first used in the UNIX-LINUX world.
A non-exhaustive list of such init services is as follows:

  • SysVinit - in this case, /sbin/init will not be a symbolic link;
  • OpenRC - used by Gentoo Linux;
  • Runit - used by Void Linux;
  • Systemd - used by most Linux distributions: Red Hat, Debian, Ubuntu, etc.

Installing the Linux operating system on Odroid N2

For the installation, the Odroid N2 board was used. It has the advantage of being equipped with SPI Flash memory. Inside this memory, there is another mini Linux distribution called Petitboot that will perform the loading process of Linux distributions; it will essentially be the BOOTLOADER in the boot process explained above. It has the advantage of eliminating the need for a U-boot type bootloader on the environment where we want to install Linux and also allows loading multiple kernels and distributions from different media, even over the network.

Preparing the installation environment

Since the board also has USB3 ports, which ensure a higher transfer speed than SDMMC or eMMC devices, one of these USB3 ports will be used with an NVME2 SSD to USB3 adapter. The partitioning used is as follows:

florin@odroid-n2:~$ sudo fdisk -l /dev/sda
Disk /dev/sda: 931,51 GiB, 1000204886016 bytes, 1953525168 sectors
Disk model: X900 1TB        
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 334C2C9E-6346-C54C-AB4D-521EEB3EE059

Device          Start        End   Sectors  Size Type
/dev/sda1        8192     622591    614400  300M Linux filesystem
/dev/sda2      622592  629768191 629145600  300G Linux filesystem
/dev/sda3   629768192 1049198591 419430400  200G Linux filesystem
/dev/sda4  1049198592 1468628991 419430400  200G Linux filesystem
/dev/sda5  1468628992 1888059391 419430400  200G Linux filesystem
/dev/sda6  1888059392 1904836607  16777216    8G Linux swap

In GParted, the partitioning of the SSD is presented in the form of:

GParted - SSD

After partitioning, formatting is performed with mkfs.ext4 for the file partitions and with mkswap for the swap partition. For convenience, GParted can be used.

Six partitions have been created:

  1. /dev/sda1 - 300MiB used for files utilized in the boot process;
  2. /dev/sda2 - 300GiB used for the Armbian distribution;
  3. /dev/sda3 - 200GiB used for the Void Linux distribution;
  4. /dev/sda4 - 200GiB used for the CentOS Stream 10 distribution;
  5. /dev/sda4 - 200GiB used for the EndeavourOS distribution;
  6. /dev/sda5 - 8GiB used for swap.

Installing Armbian

For the installation of the Armbian distribution, version 25.2.1 , Debian 12 variant, with Cinnamon and MESA/VPU extensions and kernel 6.12 was used.
After downloading the file with the archived image, it is extracted:

florin@odroid-n2:~/Descărcări$ unxz Armbian_25.2.1_Odroidn2_bookworm_current_6.12.13_cinnamon-backported-mesa_desktop.img.xz 

Following the extraction, we will have the image of the distribution that is normally written to an eMMC or SDMMC disk, but in this case, it will be mounted in the directory /mnt/armbian:

florin@odroid-n2:~/Descărcări$ sudo mkdir /mnt/armbian
florin@odroid-n2:~/Descărcări$ sudo fdisk -l Armbian_25.2.1_Odroidn2_bookworm_current_6.12.13_cinnamon-backported-mesa_desktop.img 
Disk Armbian_25.2.1_Odroidn2_bookworm_current_6.12.13_cinnamon-backported-mesa_desktop.img: 7,2 GiB, 7730102272 bytes, 15097856 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x3b4ad264

Device                                                                                 Boot Start      End  Sectors  Size Id Type
Armbian_25.2.1_Odroidn2_bookworm_current_6.12.13_cinnamon-backported-mesa_desktop.img1       8192 15097855 15089664  7,2G 83 Linux
florin@odroid-n2:~/Descărcări$ sudo mount -o loop,offset=$((512*8192)) Armbian_25.2.1_Odroidn2_bookworm_current_6.12.13_cinnamon-backported-mesa_desktop.img /mnt/armbian/
florin@odroid-n2:~/Descărcări$ ls /mnt/armbian/
bin  boot  dev  etc  home  initrd.img  initrd.img.old  lib  lost+found  media  mnt  opt  proc  root  run  sbin  selinux  srv  sys  tmp  usr  var  vmlinuz  vmlinuz.old

A working directory, /mnt/work, is created, in which the partition /dev/sda2, created for the purpose of installing the Armbian distribution, is mounted:

florin@odroid-n2:~/Descărcări$ sudo mkdir /mnt/work

Then, the partition /dev/sda2 is mounted in /mnt/work:

florin@odroid-n2:~/Descărcări$ sudo mount /dev/sda2 /mnt/work

A boot directory is created in /mnt/work, where the partition /dev/sda1 will be mounted:

florin@odroid-n2:~/Descărcări$ sudo mkdir /mnt/work/boot

We mount the partition /dev/sda1 in /mnt/work/boot:

florin@odroid-n2:~/Descărcări$ sudo mount /dev/sda1 /mnt/work/boot

After which all directories and files will be copied, preserving attributes, from /mnt/armbian to /mnt/work:

florin@odroid-n2:~/Descărcări$ sudo cp -rp /mnt/armbian/* /mnt/work/

Since Petitboot is used for booting, we will need to create a directory grub in /dev/sda1, which is already mounted at /mnt/work/boot, and in that directory, we will edit a file named grub.cfg, used by Petitboot during the boot process:

florin@odroid-n2:~/Descărcări$ sudo mkdir /mnt/work/boot/grub
florin@odroid-n2:~/Descărcări$ sudo nano /mnt/work/boot/grub/grub.cfg

The file read by Petitboot with the parameters that will be retrieved at boot will be in the form:

#/boot/grub.cfg
set UUID=3131c377-66a0-45d0-bde5-d13295d7fb41
menuentry "Armbian 25.2.1 (6.12.13-current-meson64)" {
  linux       /Image root=UUID=$UUID rootwait rootfstype=ext4 console=ttyAML0,115200 console=tty1 consoleblank=0 coherent_pool=2M
  initrd      /uInitrd
  devicetree  /dtb/amlogic/meson-g12b-odroid-n2.dtb
}

Grub.cfg here does not require the installation of the grub or grub2 bootloader; it is merely a file used by Petitboot that searches the disks for the boot.scr or boot.ini files associated with U-boot and grub.cfg in the grub or grub2 directories to retrieve the boot parameters.

As can be seen, we will need to know the UUID of the partition /dev/sda2 and also that of /dev/sda1 used for /boot, which must also be written in the /etc/fstab file, and this can be obtained with GParted or blkid:

florin@odroid-n2:~$ sudo blkid
/dev/sda1: LABEL="boot" UUID="13c60d7c-1b1c-4d58-b9fe-f9262ebc2174" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="boot" PARTUUID="1a8cf7ef-9bf5-4c56-ac00-d0b95e8b8284"
/dev/sda2: LABEL="armbian" UUID="3131c377-66a0-45d0-bde5-d13295d7fb41" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="armbian" PARTUUID="d7c80cb3-3217-41ba-a5de-b7a5e1d8f3f3"
/dev/sda3: LABEL="voidlinux" UUID="2dc3f400-b644-4998-b77c-665b630bc6f3" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="voidlinux" PARTUUID="efadaf32-c3fd-454f-b13b-98353033e661"
/dev/sda4: LABEL="centos" UUID="11bb4148-3733-422f-a2ae-0ad7c5ceb422" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="centos" PARTUUID="39bce778-f381-4807-8485-d9f72e0c5556"
/dev/sda5: LABEL="archlinux" UUID="9cbc45a5-81d5-49f3-90d8-dce710f90fd1" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="archlinux" PARTUUID="25031d65-5c7d-4e40-bf68-d7fdfb17be19"
/dev/sda6: LABEL="swap" UUID="f8d67fd5-0a7f-45b3-877c-2dcf04aec10a" TYPE="swap" PARTLABEL="swap" PARTUUID="c04500c6-2af8-4b0b-81d0-f73b7ec8c0ed"
/dev/zram1: LABEL="log2ram" UUID="2d080a5b-e763-46ad-8906-3ff9c3f5165b" BLOCK_SIZE="4096" TYPE="ext4"
/dev/loop0: LABEL="armbi_root" UUID="ba88db95-be25-4496-8028-64ba53ef82ac" BLOCK_SIZE="4096" TYPE="ext4"
/dev/zram0: UUID="a5a5ef56-c926-416e-8023-481464490b32" TYPE="swap"

Since Petitboot first reads the boot.scr file, we will need to delete the file created by Armbian, as well as boot.bmp and boot.cmd, which are not useful; the system administrator must also take care of any potential updates to these files that may be restored.
The fstab file will have a content of the form:

#/etc/fstab 
UUID=13c60d7c-1b1c-4d58-b9fe-f9262ebc2174   /boot   ext4    defaults,noatime,commit=120,errors=remount-ro   0 1
UUID=3131c377-66a0-45d0-bde5-d13295d7fb41   /   ext4    defaults,noatime,commit=120,errors=remount-ro   0 1
UUID=f8d67fd5-0a7f-45b3-877c-2dcf04aec10a   none    swap    defaults                    0 0
tmpfs                       /tmp    tmpfs   defaults,nosuid                 0 0

And the directory structure of /boot will be as follows:

florin@odroid-n2:~$ ls /boot/
config-6.12.16-current-meson64      lost+found
dtb                                 System.map-6.12.16-current-meson64
dtb-6.12.16-current-meson64         uInitrd
grub                                uInitrd-6.12.16-current-meson64
Image                               vmlinuz-6.12.16-current-meson64
initrd.img-6.12.16-current-meson64
florin@odroid-n2:~$ ls /boot/grub/
grub.cfg

Finally, after booting, the user will be greeted by the classic installation messages of the Armbian distribution, for entering the root password, creating a user, and selecting the desired language for use. After logging into the system, it will look like this:

After startup, the Petitboot screen will appear as shown in the image below:

Armbian on Odroid N2

Installing CentOS Stream 10

For convenience, a trial and installation iso image of the CentOS Stream 10 distribution is used, which already includes KDE and Gnome, specifically at the time of writing this article CentOS-Stream-Image-MAX-Live.aarch64-10-202501222202.iso:

florin@odroid-n2:~/Descărcări$ ls -lah CentOS-Stream-Image-MAX-Live.aarch64-10-202501222202.iso
-rw-r--r-- 1 florin florin 2,7G mar  1 20:45 CentOS-Stream-Image-MAX-Live.aarch64-10-202501222202.iso

To begin with, it is checked whether the partition /dev/sda4/ where we want to install CentOS is mounted (in use):

florin@odroid-n2:~$ sudo mount | grep sda4
/dev/sda4 on /media/florin/centos type ext4 (rw,nosuid,nodev,relatime,errors=remount-ro,stripe=2,uhelper=udisks2)

As we can see, it is in use, but to mount it in /mnt/work, it will need to be unmounted from /media/florin/centos and mounted in /mnt/work:

florin@odroid-n2:~$ mount | grep sda4
/dev/sda4 on /media/florin/centos type ext4 (rw,nosuid,nodev,relatime,errors=remount-ro,stripe=2,uhelper=udisks2)
florin@odroid-n2:~$ sudo umount /media/florin/centos 
florin@odroid-n2:~$ sudo mount /dev/sda4 /mnt/work/
florin@odroid-n2:~$ mount | grep sda4
/dev/sda4 on /mnt/work type ext4 (rw,relatime,stripe=2)

The rootfs structure will be extracted, just like during the installation of Armbian, but this time from the iso image:

WARNING
The image squasfs.img is of type "erofs" and Armbian and other distributions cannot mount it as there is no kernel support for this. In CentOS Stream 10 x86_84, there is support to extract the rootfs from the iso image:

florin@florin-pc:~/Descărcări$ sudo mount -o loop CentOS-Stream-Image-MAX-Live.aarch64-10-202501222202.iso /mnt/iso/
[sudo] parola pentru florin: 
mount: /mnt/iso: WARNING: source write-protected, mounted read-only.
florin@florin-pc:~/Descărcări$ sudo losetup -Pf /mnt/iso/LiveOS/squashfs.img 
florin@florin-pc:~/Descărcări$ sudo mount /dev/loop1 /mnt/centos/
mount: /mnt/centos: WARNING: source write-protected, mounted read-only.
florin@florin-pc:~/Descărcări$ ls /mnt/centos/
afs  boot  etc   image  lib64  mnt  proc  run   srv  tmp  var
bin  dev   home  lib    media  opt  root  sbin  sys  usr
florin@florin-pc:~/Descărcări$ sudo mount /dev/sdc4 /mnt/work/
florin@florin-pc:~/Descărcări$ sudo cp -rp /mnt/centos/* /mnt/work/
florin@florin-pc:~/Descărcări$ ls /mnt/work/
afs  boot  etc   image  lib64       media  opt   root  sbin  sys  usr
bin  dev   home  lib    lost+found  mnt    proc  run   srv   tmp  var
florin@florin-pc:~/Descărcări$ sudo umount /mnt/work 
florin@florin-pc:~/Descărcări$ sudo umount /mnt/centos 
florin@florin-pc:~/Descărcări$ sudo losetup -d /dev/loop1 
florin@florin-pc:~/Descărcări$ sudo umount /mnt/iso

After obtaining the rootfs, the SSD can be moved from the PC to the Odroid N2 and work can continue in Armbian.

The image used in Live for testing and installation will require us to perform a chroot in it where unnecessary packages will be removed, others will be added, and the user who will use the CentOS Stream 10 distribution that already contains KDE and Gnome pre-installed will be created.

Since we will also be working with chroot on other occasions, it is good to create a script:

#!/bin/bash
#chroot_work.sh
DIR="/mnt/work"
mount -o rbind /dev $DIR/dev
mount -t proc none $DIR/proc
mount -o bind /sys $DIR/sys
mount -o bind /tmp $DIR/tmp
cp /etc/resolv.conf $DIR/etc/
chroot $DIR /bin/bash

It is marked as executable:

florin@odroid-n2:~$ chmod +x chroot_work.sh

It checks if the partition /dev/sda4, where we have established that we will install CentOS Stream 10, is mounted; if it is in use, we unmount it and mount it in the working directory:

florin@odroid-n2:~$ mount | grep /dev/sda4 
florin@odroid-n2:~$ sudo mount /dev/sda4 /mnt/work/
florin@odroid-n2:~$ ls /mnt/work/
afs  boot  etc   image  lib64       media  opt   root  sbin  sys  usr
bin  dev   home  lib    lost+found  mnt    proc  run   srv   tmp  var

After which we run the script for chroot:

florin@odroid-n2:~$ sudo ./chroot_work.sh 
[sudo] parola pentru florin: 
root@odroid-n2:/# 

It is observed that the console name has changed, then the profile corresponding to CentOS is loaded, and for safety, the console name is also changed:

root@odroid-n2:/# source /etc/profile
root@odroid-n2:/# export PS1="(chroot) $PS1"
(chroot) root@odroid-n2:/#

As a result of using the kernel from Armbian, it will be necessary to remove everything related to kernel and grub since these are located in /boot, where later the partition /dev/sda1 will be mounted:

(chroot) root@odroid-n2:/# rpm -qa | grep kernel
kernel-modules-core-6.12.0-43.el10.aarch64
kernel-core-6.12.0-43.el10.aarch64
kernel-modules-6.12.0-43.el10.aarch64
kernel-modules-extra-6.12.0-43.el10.aarch64
kernel-tools-libs-6.12.0-43.el10.aarch64
kernel-tools-6.12.0-43.el10.aarch64
kernel-6.12.0-43.el10.aarch64
(chroot) root@odroid-n2:/# rpm -qa | grep grub
grub2-common-2.12-5.el10.noarch
grub2-tools-minimal-2.12-5.el10.aarch64
grubby-8.40-77.el10.aarch64
grub2-tools-2.12-5.el10.aarch64
grub2-tools-extra-2.12-5.el10.aarch64
grub2-efi-aa64-modules-2.12-5.el10.noarch
grub2-efi-aa64-cdboot-2.12-5.el10.aarch64
grub2-efi-aa64-2.12-5.el10.aarch64
(chroot) root@odroid-n2:/# ls /boot/
config-6.12.0-43.el10.aarch64  initramfs-6.12.0-43.el10.aarch64.img
dtb                            loader
dtb-6.12.0-43.el10.aarch64     symvers-6.12.0-43.el10.aarch64.xz
efi                            System.map-6.12.0-43.el10.aarch64
grub2                          vmlinuz-6.12.0-43.el10.aarch64
(chroot) root@odroid-n2:/#

Later on, perhaps in another article, a custom kernel will be compiled.

(chroot) root@odroid-n2:/# dnf remove kernel-modules-core kernel-core kernel-modules kernel-modules-extra kernel-tools-libs kernel-tools kernel -y 

To remove grub, you will need to comment out the lines (add a hash # in front of the line) in the following files because grub is protected from removal from the system:

(chroot) root@odroid-n2:/# nano /etc/yum/protected.d/grub2-efi-aa64.conf 
(chroot) root@odroid-n2:/# nano /etc/yum/protected.d/grub2-tools-minimal.conf

After making the above modifications, grub can be removed:

(chroot) root@odroid-n2:/# dnf remove grub2-common grub2-tools-minimal grubby grub2-tools grub2-tools-extra grub2-efi-aa64-modules grub2-efi-aa64-cdboot grub2-efi-aa64-cdboot -y

Checking what remains in /boot:

(chroot) root@odroid-n2:/# ls /boot
dtb  efi  loader

The remaining items will be deleted:

(chroot) root@odroid-n2:/# rm -r /boot/*
rm: eliminați '/boot/dtb' de tipul legătură simbolică? y
rm: descindeți în directorul '/boot/efi'? y
rm: descindeți în directorul '/boot/efi/EFI'? y
rm: descindeți în directorul '/boot/efi/EFI/BOOT'? y
rm: eliminați '/boot/efi/EFI/BOOT/fbaa64.efi' de tipul fișier obișnuit? y
rm: eliminați '/boot/efi/EFI/BOOT/BOOTAA64.EFI' de tipul fișier obișnuit? y
rm: eliminați '/boot/efi/EFI/BOOT' de tipul director? y
rm: descindeți în directorul '/boot/efi/EFI/centos'? y
rm: eliminați '/boot/efi/EFI/centos/BOOTAA64.CSV' de tipul fișier obișnuit? y
rm: eliminați '/boot/efi/EFI/centos/shim.efi' de tipul fișier obișnuit? y
rm: eliminați '/boot/efi/EFI/centos/shimaa64-centos.efi' de tipul fișier obișnuit? y
rm: eliminați '/boot/efi/EFI/centos/shimaa64.efi' de tipul fișier obișnuit? y
rm: eliminați '/boot/efi/EFI/centos/mmaa64.efi' de tipul fișier obișnuit? y
rm: eliminați '/boot/efi/EFI/centos' de tipul director? y
rm: eliminați '/boot/efi/EFI' de tipul director? y
rm: eliminați '/boot/efi' de tipul director? y
rm: descindeți în directorul '/boot/loader'? y
rm: descindeți în directorul '/boot/loader/entries'? y
rm: eliminați '/boot/loader/entries/dc9647167e9240308687296b9211acb7-6.12.0-43.el10.aarch64.conf' de tipul fișier obișnuit? y
rm: eliminați '/boot/loader/entries' de tipul director? y
rm: eliminați '/boot/loader' de tipul director? y
(chroot) root@odroid-n2:/# ls /boot/
(chroot) root@odroid-n2:/#  

Next, modify the /etc/fstab file according to the SSD disk:

(chroot) root@odroid-n2:/# nano /etc/fstab

In fstab, edit according to the hardware and the preparations made earlier:

#/etc/fstab
UUID=13c60d7c-1b1c-4d58-b9fe-f9262ebc2174       /boot   ext4    defaults,noatime,commit=120,errors=remount-ro   0 1
UUID=11bb4148-3733-422f-a2ae-0ad7c5ceb422       /       ext4    defaults,noatime,commit=120,errors=remount-ro   0 1
UUID=f8d67fd5-0a7f-45b3-877c-2dcf04aec10a       none    swap    defaults                                        0 0
tmpfs                                           /tmp    tmpfs   defaults,nosuid                                 0 0

You will need to add the directory with the kernel modules from Armbian, specifically from the /usr/lib/modules/ directory to /mnt/work/usr/lib/modules/:

florin@odroid-n2:~$ sudo cp -rp /usr/lib/modules/* /mnt/work/usr/lib/modules/
florin@odroid-n2:~$ sudo cp -r /usr/lib/linux-image-6.12.16-current-meson64 /mnt/work/usr/lib/

Return to chroot and temporarily disable the firewalld service:

(chroot) root@odroid-n2:/# systemctl disable firewalld
Removed '/etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service'.
Removed '/etc/systemd/system/multi-user.target.wants/firewalld.service'.

Then, a user will be added for login:

(chroot) root@odroid-n2:/# useradd -G wheel,dialout,video,audio -s /bin/bash -c 'Florin Tanasă' florin
(chroot) root@odroid-n2:/# ls /home/
florin

A password will be set for the user:

(chroot) root@odroid-n2:/# passwd florin
Parolă nouă: 
Rescrieți parola nouă: 
passwd: parolă actualizată cu succes

The system language can be changed if desired:

(chroot) root@odroid-n2:/# cat /etc/locale.conf
LANG=en_US.UTF-8
(chroot) root@odroid-n2:/# nano /etc/locale.conf 
(chroot) root@odroid-n2:/# cat /etc/locale.conf 
LANG=ro_RO.UTF-8

Then an option with the new distribution will be added to /boot/grub/grub.cfg to be available at boot:

florin@odroid-n2:~$ sudo nano /boot/grub/grub.cfg 

After modification, the grub.cfg file will look like this:

set UUID1=3131c377-66a0-45d0-bde5-d13295d7fb41
set UUID2=11bb4148-3733-422f-a2ae-0ad7c5ceb422

menuentry "Armbian 25.2.1 (6.12.16-current-meson64)" {
  linux       /Image root=UUID=$UUID1 rootwait rootfstype=ext4 console=ttyAML0,115200 console=tty1 consoleblank=0 coherent_pool=2M
  initrd      /uInitrd
  devicetree  /dtb/amlogic/meson-g12b-odroid-n2.dtb
} 
menuentry "CentOS Stream 10 (6.12.16-current-meson64)" {
  linux       /Image root=UUID=$UUID2 rootwait rootfstype=ext4 console=ttyAML0,115200 console=tty1 consoleblank=0 coherent_pool=2M
  initrd      /uInitrd
  devicetree  /dtb/amlogic/meson-g12b-odroid-n2.dtb
}

After rebooting, the user will be able to log into the system using the Desktop Manager sddm:

To use classic Gnome:

Classic Gnome in CentOS Stream 10

To use Gnome:

Gnome in CentOS Stream 10

To use KDE:

KDE in CentOS Stream 10

EndeavourOS Installation

From the category of Arch Linux-based distributions, EndeavorOS will be used, which provides a specially prepared image for the Odroid N2 SBC and also offers a kernel compiled for Amlogic devices.

Downloading the image file

The file with the distribution image is downloaded using the command:

florin@odroid-n2:~/Descărcări$ wget https://github.com/endeavouros-arm/images/releases/download/odroid-n2-image/enosLinuxARM-odroid-n2-latest.img.xz

The downloaded file is extracted:

florin@odroid-n2:~/Descărcări$ unxz enosLinuxARM-odroid-n2-latest.img.xz

Obtaining information related to the image

Next, it is checked whether the image has multiple partitions:

lorin@odroid-n2:~/Descărcări$ sudo fdisk -l enosLinuxARM-odroid-n2-latest.img
Disk enosLinuxARM-odroid-n2-latest.img: 8,5 GiB, 9126805504 bytes, 17825792 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x3b835c24

Device                             Boot   Start      End  Sectors  Size Id Type
enosLinuxARM-odroid-n2-latest.img1         4096  1052671  1048576  512M  c W95 FAT32 (LBA)
enosLinuxARM-odroid-n2-latest.img2      1052672 16777215 15724544  7,5G 83 Linux

From the result, it is found that there are two partitions and information regarding which sector the partitions start from and how many units are in a sector:

  • 512M - used for boot, starting from sector 4096;
  • 7.5G - used for rootfs, starting from sector 1052672;
  • 1 sector has 512 units.

The information above will be useful for mounting in order to extract information.

Adding a new boot partition

Since the distribution image has two partitions, one for boot and one for rootfs, it is necessary to create a new partition for boot. For this purpose, we will use GParted from Armbian installed earlier, and we will make the boot partition larger and of a different type.
We will create a partition named boot_arch of 1G of type linux formatted ext4:

GParted boot partition setup

It will then be finalized by pressing the Apply all operations button:

GParted operation completion

It can be observed that the partition /dev/sda7 has been created, which will be used for /boot, while the partition /dev/sda5, created from the beginning, will be used for rootfs, respectively for /.

Mounting the image and extracting the data

First, we create the directory where we will mount the partitions from within the image in order to extract the data to the SSD partitions:

florin@odroid-n2:~/Descărcări$ sudo mkdir /mnt/endeavouros

We will mount the boot partition from the downloaded image in /mnt/eandeavouros/, then we will mount the partition /dev/sda7 in /mnt/work/, and finally, we will copy the data and unmount the partitions:

florin@odroid-n2:~/Descărcări$ sudo mount -o loop,offset=$((512*4096)) enosLinuxARM-odroid-n2-latest.img /mnt/endeavouros/
florin@odroid-n2:~/Descărcări$ ls /mnt/endeavouros/
boot.ini       dtbs   initramfs-linux.img   sd_fusing.sh
boot.ini.orig  Image  initramfs-linux.uimg  u-boot.bin
florin@odroid-n2:~/Descărcări$ sudo mount /dev/sda7 /mnt/work/
florin@odroid-n2:~/Descărcări$ ls /mnt/work/
lost+found
florin@odroid-n2:~/Descărcări$ sudo cp -rp /mnt/endeavouros/* /mnt/work/
florin@odroid-n2:~/Descărcări$ ls /mnt/work/
boot.ini       dtbs   initramfs-linux.img   lost+found    u-boot.bin
boot.ini.orig  Image  initramfs-linux.uimg  sd_fusing.sh
florin@odroid-n2:~/Descărcări$ sudo umount /mnt/endeavouros 
florin@odroid-n2:~/Descărcări$ sudo umount /mnt/work 

Then the rootfs partition from the downloaded image will be mounted in /mnt/endeavouros/, after which the /dev/sda5 partition will be mounted in /mnt/work, and finally, the data will be copied and the partitions will be unmounted:

florin@odroid-n2:~/Descărcări$ sudo mount -o loop,offset=$((512*1052672)) enosLinuxARM-odroid-n2-latest.img /mnt/endeavouros/
florin@odroid-n2:~/Descărcări$ ls /mnt/endeavouros/
bin   dev  home  lost+found  opt   root  sbin  sys  usr
boot  etc  lib   mnt         proc  run   srv   tmp  var
florin@odroid-n2:~/Descărcări$ sudo mount /dev/sda5 /mnt/work/
florin@odroid-n2:~/Descărcări$ ls /mnt/work/
lost+found
florin@odroid-n2:~/Descărcări$ sudo cp -rp /mnt/endeavouros/* /mnt/work/
florin@odroid-n2:~/Descărcări$ ls /mnt/work/
bin   dev  home  lost+found  opt   root  sbin  sys  usr
boot  etc  lib   mnt         proc  run   srv   tmp  var
florin@odroid-n2:~/Descărcări$ sudo umount /mnt/endeavouros 
florin@odroid-n2:~/Descărcări$ sudo umount /mnt/work 

Using the blkid command, the UUID value of the /dev/sda5 and /dev/sda7 partitions can be identified:

florin@odroid-n2:~/Descărcări$ sudo blkid
/dev/sda1: LABEL="boot" UUID="13c60d7c-1b1c-4d58-b9fe-f9262ebc2174" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="boot" PARTUUID="1a8cf7ef-9bf5-4c56-ac00-d0b95e8b8284"
/dev/sda2: LABEL="armbian" UUID="3131c377-66a0-45d0-bde5-d13295d7fb41" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="armbian" PARTUUID="d7c80cb3-3217-41ba-a5de-b7a5e1d8f3f3"
/dev/sda3: LABEL="voidlinux" UUID="2dc3f400-b644-4998-b77c-665b630bc6f3" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="voidlinux" PARTUUID="efadaf32-c3fd-454f-b13b-98353033e661"
/dev/sda4: LABEL="centos" UUID="11bb4148-3733-422f-a2ae-0ad7c5ceb422" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="centos" PARTUUID="39bce778-f381-4807-8485-d9f72e0c5556"
/dev/sda5: LABEL="archlinux" UUID="9cbc45a5-81d5-49f3-90d8-dce710f90fd1" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="archlinux" PARTUUID="25031d65-5c7d-4e40-bf68-d7fdfb17be19"
/dev/sda6: LABEL="swap" UUID="f8d67fd5-0a7f-45b3-877c-2dcf04aec10a" TYPE="swap" PARTLABEL="swap" PARTUUID="c04500c6-2af8-4b0b-81d0-f73b7ec8c0ed"
/dev/sda7: LABEL="boot_arch" UUID="5c430bbe-17c5-4570-af18-c9f3bef1b22c" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="boot_arch" PARTUUID="c1dbebea-3729-4ab0-9636-7de8ffe3afb8"
/dev/zram1: LABEL="log2ram" UUID="8ff0f59c-0e89-499e-a9c5-0190ae6be290" BLOCK_SIZE="4096" TYPE="ext4"
/dev/zram0: UUID="dc52859c-3820-46b0-86c5-71989bc3abe4" TYPE="swap"

It is observed that the relevant data has the following values:

  • /dev/sda5: LABEL="archlinux" UUID="9cbc45a5-81d5-49f3-90d8-dce710f90fd1";
  • /dev/sda7: LABEL="boot_arch" UUID="5c430bbe-17c5-4570-af18-c9f3bef1b22c".

With this, the parameter for the UUID corresponding to the partition /dev/sda5 for root will be modified in the boot.ini file used during booting, as both will be used in the /etc/fstab file:

florin@odroid-n2:~/Descărcări$ sudo mount /dev/sda7 /mnt/work/
florin@odroid-n2:~/Descărcări$ sudo nano /mnt/work/boot.ini

The file is modified according to our data:

ODROIDN2-UBOOT-CONFIG

# DO Not Edit this line: KERNEL: mainline

setenv board "odroidn2"

# Show logo as soon as possible
showlogo

# System Label
setenv bootlabel "ArchLinux"

# Default Console Device Setting
setenv condev "console=ttyAML0,115200n8 console=tty1"

# Video Output
## preferred_resolution
## set to your preferred resolution using the format:
##    {width}x{height}@{hertz}
##    example: 1920x1080@60
## or
##    Set to an empty string to let the kernel automatically
##    choose a resolution for you.
setenv preferred_resolution ""

## petitboot_edid
## if you experience issues of display not showing any
## image when using petitboot you can use this setting
## to overwrite the default display edid information.
##
## A valid value for this option can be:
##    800x600, 1024x768, 1280x720, 1280x1024, 1600x1200,
##    1680x1050, 1920x1080, 2560x1440, 2880x1800, 3840x2160
## or
##    Set to an empty string to use the display provided
##    edid information.
setenv petitboot_edid ""

## Uncomment to force petitboot to always be set as
## active to always force the edid overwrite.
#setenv petitboot_active "1"

if test "${petitboot_edid}" != "" && test "${petitboot_active}" = "1"; then
    setenv voutput "drm_kms_helper.edid_firmware=HDMI-A-1:edid/${petitboot_edid}.bin"
elif test "${preferred_resolution}" != ""; then
    setenv voutput "video=HDMI-A-1:${preferred_resolution}"
fi

# Boot Args
setenv bootargs "root=UUID=9cbc45a5-81d5-49f3-90d8-dce710f90fd1 rootwait rw fsck.repair=yes"
setenv bootargs "${bootargs} mitigations=off ${condev}"
setenv bootargs "${bootargs} logo=osd0,loaded no_console_suspend"
setenv bootargs "${bootargs} net.ifnames=0 cma=800M"
setenv bootargs "${bootargs} clk_ignore_unused ${voutput}"

# Set load addresses
setenv dtb_loadaddr "0x20000000"
setenv loadaddr "0x1080000"
setenv initrd_loadaddr "0x4080000"

# Load kernel, dtb and initrd
ext4load usb ${devno}:7 ${loadaddr} /Image
if test "${variant}" = "n2_plus"; then
    ext4load usb ${devno}:7 ${dtb_loadaddr} /dtbs/amlogic/meson-g12b-odroid-n2-plus.dtb
else
    ext4load usb ${devno}:7 ${dtb_loadaddr} /dtbs/amlogic/meson-g12b-odroid-n2.dtb
fi
ext4load usb ${devno}:7 ${initrd_loadaddr} /initramfs-linux.uimg

# boot
booti ${loadaddr} ${initrd_loadaddr} ${dtb_loadaddr}

WARNING
The UUID parameter will be given importance, which will inform the kernel where the root partition is located, and the load mmc ${devno}:1 parameter will be replaced with ext4load usb ${devno}:7; essentially, we will inform the bootloader where the boot partition is located from which it will load the kernel (we recall how Linux starts, briefly described at the beginning of the article).

The fstab file will be edited after the /dev/sda7/ partition is unmounted from /mnt/work:

florin@odroid-n2:~$ sudo umount /mnt/work
florin@odroid-n2:~$ sudo mount /dev/sda5 /mnt/work/
florin@odroid-n2:~$ sudo nano /mnt/work/etc/fstab

In the fstab file, the following parameters used by the kernel during boot will be defined for mounting:

# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a device; this may
# be used with UUID= as a more robust way to name devices that works even if
# disks are added and removed. See fstab(5).
#
# <file system>             <mount point>  <type>  <options>  <dump>  <pass>

UUID=5c430bbe-17c5-4570-af18-c9f3bef1b22c       /boot   ext4    defaults       >
UUID=3131c377-66a0-45d0-bde5-d13295d7fb41       /       ext4    defaults,noatim>
UUID=f8d67fd5-0a7f-45b3-877c-2dcf04aec10a       none    swap    defaults       >
tmpfs                                           /tmp    tmpfs   defaults,nosuid>

Finally, after saving, the partition will be unmounted and the SBC Odroid N2 device will be restarted:

florin@odroid-n2:~$ sudo umount /mnt/work 
florin@odroid-n2:~$ sudo reboot

After rebooting, Petitboot will detect the new configuration from /dev/sda7, specifically it will retrieve the data from the previously modified boot.ini file, and after selecting the ArchLinux option from the menu, the kernel will be loaded along with the parameters, and the system will greet us with some prompts for configuration, specifically for the installation of EndevourOS. Finally, after logging in, we will enjoy the new distribution:

EndeavourOS in Odroid N2

Other information related to KDE:

EndeavourOS in Odroid N2

KDE was preferred because it works well in Wayland and its resource consumption is not very significant.

Installing Void Linux

Void Linux is one of the independently developed distributions and does not depend on other distributions, featuring its own package manager called xbps, which is one of the fastest in package management.
For ease of installation, we will use the Live image with XFCE prepared for the pinebook-pro laptop, and later we will modify the boot sequence according to our meson platform, as the kernel is compiled with support for this platform (the image from CentOS Stream 10 did not provide a kernel that offers support for meson).
Next, we will download the image and perform successive mounts until we reach rootfs for the purpose of extracting it into the partitions on the SSD created for this purpose:

florin@odroid-n2:~$ cd Descărcări/
florin@odroid-n2:~/Descărcări$ wget https://repo-default.voidlinux.org/live/current/void-live-aarch64-20250202-xfce.iso
florin@odroid-n2:~/Descărcări$ sudo mount void-live-aarch64-20250202-xfce.iso /mnt/iso/
florin@odroid-n2:~/Descărcări$ sudo mount /mnt/iso/LiveOS/squashfs.img /mnt/work/
florin@odroid-n2:~/Descărcări$ sudo mkdir /mnt/voidlinux
florin@odroid-n2:~/Descărcări$ sudo mount /mnt/work/LiveOS/ext3fs.img /mnt/voidlinux/
florin@odroid-n2:~/Descărcări$ ls /mnt/voidlinux/
bin   dev  home  lib32  lost+found  mnt  proc  run   sys  usr
boot  etc  lib   lib64  media       opt  root  sbin  tmp  var

As with the installation of EndeavourOS, a 1G partition will be created with the ext4 file system for the boot partition:

GParted boot partition settings

After applying the changes to the SSD disk, it will look like the one below:

GParted after the changes

Next, a directory will be created where we will mount the previously created partition /dev/sda8 and copy the data from /mnt/voidlinux/boot:

florin@odroid-n2:~$ sudo mkdir /mnt/boot_void
[sudo] parola pentru florin: 
florin@odroid-n2:~$ mount | grep sda8
florin@odroid-n2:~$ sudo mount /dev/sda8 /mnt/boot_void/
florin@odroid-n2:~$ sudo cp -rp /mnt/voidlinux/boot/* /mnt/boot_void/
florin@odroid-n2:~$ ls /mnt/boot_void/
boot.scr  config-6.12.11_1  initramfs-6.12.11_1.img  vmlinux-6.12.11_1
boot.txt  dtbs              lost+found
florin@odroid-n2:~$ sudo umount /mnt/boot_void

After unmounting the partition /dev/sda8, we will create a directory /mnt/root_void, where we will mount the partition for rootfs created from the beginning /dev/sda3 and where we will copy the relevant rootfs data from the iso image, specifically from /mnt/root_void, after which we will delete the data from /mnt/root_void/boot, and finally, we will mount the boot partition /dev/sda8 in /mnt/root_void/boot:

florin@odroid-n2:~/Descărcări$ cd ~
florin@odroid-n2:~$ sudo mkdir /mnt/root_void
florin@odroid-n2:~$ sudo mount /dev/sda3 /mnt/root_void/
florin@odroid-n2:~$ ls /mnt/root_void/
lost+found
florin@odroid-n2:~$ sudo cp -rp /mnt/voidlinux/* /mnt/root_void/ 
florin@odroid-n2:~$ sudo rm -r /mnt/root_void/boot/*
florin@odroid-n2:~$ ls /mnt/root_void/boot/
florin@odroid-n2:~$ sudo mount /dev/sda8 /mnt/root_void/boot/
florin@odroid-n2:~$ sudo ls /mnt/root_void/boot/
boot.scr  config-6.12.11_1  initramfs-6.12.11_1.img  vmlinux-6.12.11_1
boot.txt  dtbs          lost+found

To determine the partitions, run blkid:

florin@odroid-n2:~$ sudo blkid
/dev/sda1: LABEL="boot" UUID="13c60d7c-1b1c-4d58-b9fe-f9262ebc2174" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="boot" PARTUUID="1a8cf7ef-9bf5-4c56-ac00-d0b95e8b8284"
/dev/sda2: LABEL="armbian" UUID="3131c377-66a0-45d0-bde5-d13295d7fb41" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="armbian" PARTUUID="d7c80cb3-3217-41ba-a5de-b7a5e1d8f3f3"
/dev/sda4: LABEL="centos" UUID="11bb4148-3733-422f-a2ae-0ad7c5ceb422" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="centos" PARTUUID="39bce778-f381-4807-8485-d9f72e0c5556"
/dev/sda5: LABEL="archlinux" UUID="9cbc45a5-81d5-49f3-90d8-dce710f90fd1" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="archlinux" PARTUUID="25031d65-5c7d-4e40-bf68-d7fdfb17be19"
/dev/sda6: LABEL="swap" UUID="f8d67fd5-0a7f-45b3-877c-2dcf04aec10a" TYPE="swap" PARTLABEL="swap" PARTUUID="c04500c6-2af8-4b0b-81d0-f73b7ec8c0ed"
/dev/sda7: LABEL="boot_arch" UUID="5c430bbe-17c5-4570-af18-c9f3bef1b22c" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="boot_arch" PARTUUID="c1dbebea-3729-4ab0-9636-7de8ffe3afb8"
/dev/sda3: LABEL="voidlinux" UUID="f0cc6ce1-37af-4865-9347-24f30edcd042" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="voidlinux" PARTUUID="efadaf32-c3fd-454f-b13b-98353033e661"
/dev/sda8: LABEL="boot_void" UUID="7e62a9a8-46c7-4824-ae08-c2c95151e313" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="boot_void" PARTUUID="f779f881-f461-467b-8803-c10e1313643f"
/dev/loop1: TYPE="squashfs"
/dev/zram1: LABEL="log2ram" UUID="4b9e4e87-e8e5-4837-b56c-e6ef79607e58" BLOCK_SIZE="4096" TYPE="ext4"
/dev/loop2: UUID="3007f301-c2c1-40b3-9dbc-68844f4f1fe6" SEC_TYPE="ext2" BLOCK_SIZE="4096" TYPE="ext3"
/dev/loop0: BLOCK_SIZE="2048" UUID="2025-02-02-00-21-58-00" LABEL="VOID_LIVE" TYPE="iso9660"
/dev/zram0: UUID="4a5c516d-e7e0-4f76-9fb2-4eab60a52d17" TYPE="swap"

WARNING
When reformatting a partition, the UUID is modified.

From which the relevant data is extracted:

  • /dev/sda3: LABEL="voidlinux" UUID="f0cc6ce1-37af-4865-9347-24f30edcd042";
  • /dev/sda8: LABEL="boot_void" UUID="7e62a9a8-46c7-4824-ae08-c2c95151e313"

Data that will be used to edit the fstab file:

florin@odroid-n2:~$ sudo nano /mnt/root_void/etc/fstab 

And the file will look like this:

#
# See fstab(5).
#
# <file system> <dir>   <type>  <options>       <dump>  <pass>
UUID=7e62a9a8-46c7-4824-ae08-c2c95151e313   /boot   ext4    defaults,noatime,commit=120,errors=remount-ro   0 1
UUID=f0cc6ce1-37af-4865-9347-24f30edcd042   /   ext4    defaults,noatime,commit=120,errors=remount-ro   0 1
UUID=f8d67fd5-0a7f-45b3-877c-2dcf04aec10a   none    swap    defaults                    0 0
tmpfs                       /tmp    tmpfs   defaults,nosuid,nodev               0 0

The process will continue by performing a chroot in the directory /mnt/root_void. The previous script can be modified or a new one can be created, in the form:

florin@odroid-n2:~$ cat chroot_work.sh 
#!/bin/bash
#chroot_work.sh
DIR="/mnt/root_void/"
mount -o rbind /dev $DIR/dev
mount -t proc none $DIR/proc
mount -o bind /sys $DIR/sys
mount -o bind /tmp $DIR/tmp
cp /etc/resolv.conf $DIR/etc/
chroot $DIR /bin/bash

Next, the script runs entering chroot mode, the profile is retrieved, the console name is modified, an update and upgrade of packages is performed, the boot startup of the desktop manager - lightdm service will be blocked, and finally, a number of utilities will be installed and the kernel will be uninstalled, etc.:

florin@odroid-n2:~$ sudo ./chroot_work.sh 
bash: warning: setlocale: LC_ALL: cannot change locale (ro_RO.UTF-8)
bash-5.2# source /etc/profile
bash-5.2# export PS1="(chroot) $PS1"
(chroot) bash-5.2# ping google.ro
PING google.ro (142.250.180.227) 56(84) bytes of data.
64 bytes from bud02s34-in-f3.1e100.net (142.250.180.227): icmp_seq=1 ttl=57 time=21.7 ms
64 bytes from bud02s34-in-f3.1e100.net (142.250.180.227): icmp_seq=2 ttl=57 time=21.2 ms
^C
--- google.ro ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 21.220/21.463/21.707/0.243 ms
(chroot) bash-5.2# xbps-install -Su
(chroot) bash-5.2# xbps-query -l | grep linux
ii liblastlog2-2.40.2_1                      Lastlog replacement library from util-linux
ii libsmartcols-2.40.2_1                     Table or Tree library from util-linux
ii linux-6.12_1                              Linux kernel meta package
ii linux-base-2023.05.29_1                   Linux kernel base dependencies
ii linux-firmware-amd-20250211_1             Binary firmware blobs for the Linux kernel - AMD CPU/GPU microcode
ii linux-firmware-broadcom-20250211_1        Binary firmware blobs for the Linux kernel - Broadcom network blobs
ii linux-firmware-network-20250211_1         Binary firmware blobs for the Linux kernel - network
ii linux-firmware-nvidia-20250211_1          Binary firmware blobs for the Linux kernel NVIDIA GPU microcode
ii linux-firmware-qualcomm-20250211_1        Binary firmware blobs for the Linux kernel - Qualcomm SoC blobs
ii linux6.12-6.12.16_1                       Linux kernel and modules (6.12 series)
ii util-linux-2.40.2_1                       Miscellaneous linux utilities
ii util-linux-common-2.40.2_1                Miscellaneous linux utilities - common files
(chroot) bash-5.2# xbps-query -l | grep grub 
ii grub-2.12_2                               GRand Unified Bootloader 2
ii grub-arm64-efi-2.12_2                     GRand Unified Bootloader 2 - arm64 EFI support

(chroot) bash-5.2# xbps-remove grub-arm64-efi-2.12_2 grub-2.12_2

Name           Action    Version           New version            Download size
grub-arm64-efi remove    2.12_2            -                      - 
grub           remove    2.12_2            -                      - 

Size freed on disk:             33MB
Space available on disk:       192GB

Do you want to continue? [Y/n] y
Removing `grub-arm64-efi-2.12_2' ...
Removed `grub-arm64-efi-2.12_2' successfully.
Removing `grub-2.12_2' ...
Removed `grub-2.12_2' successfully.

0 downloaded, 0 installed, 0 updated, 0 configured, 2 removed.

(chroot) bash-5.2# xbps-query -l | grep pinebookpro
ii pinebookpro-base-0.6_1                    Void Linux Pinebook Pro platform package
ii pinebookpro-firmware-0.0.20201114_1       Firmware files for the Pinebook Pro
ii pinebookpro-uboot-2022.04_2               U-Boot for Pinebook Pro
(chroot) bash-5.2# xbps-query -l | grep x13        
ii x13s-base-3_1                             Void Linux Thinkpad X13s platform package

(chroot) bash-5.2# xbps-remove pinebookpro-uboot-2022.04_2 pinebookpro-firmware-0.0.20201114_1 pinebookpro-base-0.6_1

Name                 Action    Version           New version            Download size
pinebookpro-uboot    remove    2022.04_2         -                      - 
pinebookpro-firmware remove    0.0.20201114_1    -                      - 
pinebookpro-base     remove    0.6_1             -                      - 

Size freed on disk:           1792KB
Space available on disk:       192GB

Do you want to continue? [Y/n] y
Removing `pinebookpro-uboot-2022.04_2' ...
Removed `pinebookpro-uboot-2022.04_2' successfully.
Removing `pinebookpro-firmware-0.0.20201114_1' ...
Removed `pinebookpro-firmware-0.0.20201114_1' successfully.
Removing `pinebookpro-base-0.6_1' ...
Updating udev hardware database ...
Removed `pinebookpro-base-0.6_1' successfully.

0 downloaded, 0 installed, 0 updated, 0 configured, 3 removed.

(chroot) bash-5.2# xbps-remove x13s-base-3_1

Name      Action    Version           New version            Download size
x13s-base remove    3_1               -                      - 

Size freed on disk:            1524B
Space available on disk:       192GB

Do you want to continue? [Y/n] y
Removing `x13s-base-3_1' ...
Removed `x13s-base-3_1' successfully.

0 downloaded, 0 installed, 0 updated, 0 configured, 1 removed.

(chroot) bash-5.2# xbps-remove linux6.12-6.12.16_1 linux-6.12_1

Name      Action    Version           New version            Download size
linux6.12 remove    6.12.16_1         -                      - 
linux     remove    6.12_1            -                      - 

Size freed on disk:            231MB
Space available on disk:       192GB

Do you want to continue? [Y/n] y
Removing `linux6.12-6.12.16_1' ...
Executing post-remove kernel hook: 20-initramfs ...
Executing post-remove kernel hook: 50-efibootmgr ...
Removed `linux6.12-6.12.16_1' successfully.
Removing `linux-6.12_1' ...
Removed `linux-6.12_1' successfully.

0 downloaded, 0 installed, 0 updated, 0 configured, 2 removed.
(chroot) bash-5.2# xbps-install nano
(chroot) bash-5.2# xbps-install mc

(chroot) bash-5.2# ls /boot/
boot.scr  boot.txt  config-6.12.11_1  dtbs  initramfs-6.12.11_1.img  lost+found  vmlinux-6.12.11_1
(chroot) bash-5.2# rm -r /boot/dtbs
(chroot) bash-5.2# rm -f /boot/*
rm: cannot remove '/boot/lost+found': Is a directory
(chroot) bash-5.2# ls /boot/
lost+found

Since the kernel and its modules have been uninstalled, the kernel and modules from the EndeavourOS distribution will be used within the rootfs of VoidLinux. For this purpose, a new console is started in which the following commands will be executed:

florin@odroid-n2:~$ sudo mkdir /mnt/boot_endeavouros
florin@odroid-n2:~$ sudo mount /dev/sda7 /mnt/boot_endeavouros/
florin@odroid-n2:~$ ls /mnt/boot_endeavouros/
boot.ini  boot.ini.orig  dtbs  Image  initramfs-linux.img  initramfs-linux.uimg  lost+found  sd_fusing.sh  u-boot.bin
florin@odroid-n2:~$ sudo cp -rp /mnt/boot_endeavouros/dtbs /mnt/root_void/boot/
florin@odroid-n2:~$ sudo cp -fp /mnt/boot_endeavouros/boot.ini /mnt/root_void/boot/
florin@odroid-n2:~$ sudo cp -fp /mnt/boot_endeavouros/Image /mnt/root_void/boot/
florin@odroid-n2:~$ sudo cp -fp /mnt/boot_endeavouros/initramfs-linux.uimg /mnt/root_void/boot/
florin@odroid-n2:~$ ls /mnt/root_void/boot/
boot.ini  dtbs  Image  initramfs-linux.uimg  lost+found
florin@odroid-n2:~$ sudo umount /mnt/boot_endeavouros/
florin@odroid-n2:~$ sudo mount /dev/sda5 /mnt/endeavouros/
florin@odroid-n2:~$ ls /mnt/endeavouros/
bin  boot  dev  etc  home  lib  lost+found  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
florin@odroid-n2:~$ ls /mnt/endeavouros/usr/lib/modules/
6.13.5-1-eos-arm  extramodules-6.13

Return to the chroot console and modify the /boot/boot.ini file according to the new parameters:

(chroot) bash-5.2# nano /boot/boot.ini 

The boot.ini file, after modification, will have a content of the form:

# DO Not Edit this line: KERNEL: mainline

setenv board "odroidn2"

# Show logo as soon as possible
showlogo

# System Label
setenv bootlabel "VoidLinux"

# Default Console Device Setting
setenv condev "console=ttyAML0,115200n8 console=tty1"

# Video Output
## preferred_resolution
## set to your preferred resolution using the format:
##    {width}x{height}@{hertz}
##    example: 1920x1080@60
## or
##    Set to an empty string to let the kernel automatically
##    choose a resolution for you.
setenv preferred_resolution ""

## petitboot_edid
## if you experience issues of display not showing any
## image when using petitboot you can use this setting
## to overwrite the default display edid information.
##
## A valid value for this option can be:
##    800x600, 1024x768, 1280x720, 1280x1024, 1600x1200,
##    1680x1050, 1920x1080, 2560x1440, 2880x1800, 3840x2160
## or
##    Set to an empty string to use the display provided
##    edid information.
setenv petitboot_edid ""

## Uncomment to force petitboot to always be set as
## active to always force the edid overwrite.
#setenv petitboot_active "1"

if test "${petitboot_edid}" != "" && test "${petitboot_active}" = "1"; then
    setenv voutput "drm_kms_helper.edid_firmware=HDMI-A-1:edid/${petitboot_edid}.bin"
elif test "${preferred_resolution}" != ""; then
    setenv voutput "video=HDMI-A-1:${preferred_resolution}"
fi

# Boot Args
setenv bootargs "root=UUID=f0cc6ce1-37af-4865-9347-24f30edcd042 rootwait rw fsck.repair=yes"
setenv bootargs "${bootargs} mitigations=off ${condev}"
setenv bootargs "${bootargs} logo=osd0,loaded no_console_suspend"
setenv bootargs "${bootargs} net.ifnames=0 cma=800M"
setenv bootargs "${bootargs} clk_ignore_unused ${voutput}"

# Set load addresses
setenv dtb_loadaddr "0x20000000"
setenv loadaddr "0x1080000"
setenv initrd_loadaddr "0x4080000"

# Load kernel, dtb and initrd
ext4load usb ${devno}:8 ${loadaddr} /Image
if test "${variant}" = "n2_plus"; then
    ext4load usb ${devno}:8 ${dtb_loadaddr} /dtbs/amlogic/meson-g12b-odroid-n2-plus.dtb
else
    ext4load usb ${devno}:8 ${dtb_loadaddr} /dtbs/amlogic/meson-g12b-odroid-n2.dtb
fi
ext4load usb ${devno}:8 ${initrd_loadaddr} /initramfs-linux.uimg

# boot
booti ${loadaddr} ${initrd_loadaddr} ${dtb_loadaddr}

Upon reboot, Petitboot will take the boot parameters from the modified file.

Then a password is added for the root user, a working user will be created, and the lightdm service is stopped for the first boot:

(chroot) bash-5.2# passwd 
New password: 
Retype new password: 
passwd: password updated successfully
(chroot) bash-5.2#
(chroot) bash-5.2# useradd -G wheel,dialout,video,plugdev -s /bin/bash -c 'Florin Tanasa' florin
(chroot) bash-5.2# ls /home/
florin
(chroot) bash-5.2# passwd florin
New password: 
Retype new password: 
passwd: password updated successfully
(chroot) bash-5.2#
(chroot) bash-5.2# touch /etc/sv/lightdm/down

Finally, the consoles are closed and the system is rebooted.

Upon reboot, Petitboot will detect the boot.ini file on the boot partition and will include the VoidLinux option in the menu with the corresponding boot parameters and arguments:

Petitboot

After booting, the user will be greeted by a console where they will log in as the root user, and then the lightdm service will be started using the command:

sv up lightdm

After executing the command, the lightdm service (Window Manager) will start, and after logging into the VoidLinux system, it will appear as follows (the system in the image was set to use the Romanian language from the console at the first boot):

VoidLinux with XFCE4 on Odroid N2

Conclusions

SBC boards equipped with ARM processors are versatile and can be successfully used for various applications (desktop, various services: web, ftp, ssh, files, etc.), have low power consumption, and are generally fanless, making them practically silent, etc.

As a distribution, VoidLinux is very fast and it is worth compiling a custom kernel for the Odroid N2 board, but it is not for beginners.