Minimal Linux Installation on Odroid C2 Single Board Computer

(Experimentation with Crux ARM 64 Bits Distribution)

Thierry Moreau

Document Number C005805

2018/05/16

Copyright (C) 2018 CONNOTECH Experts-conseils inc.

Verbatim redistribution of the present document is authorized.

This document is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Specifications subject to change without notice.

Table of contents

1. Introduction

2. Downloading Software Components

2.1 The ArchLinux Distribution Image

2.2 The Linux Kernel

2.3 Crux-arm 64b Root File System

3. Partition Formatting and Linux Boot Process Installation

3.1 Create a Single ext2 or ext4 Partition

3.2 Fill the ArchLinux Root Partition (Kernel and Linux Distribution Files)

3.3 Arrange u-boot as the Boot Loader

3.4 Testing the boot media

4. Linux Kernel Configuration Build and Replacement

4.1 Preliminary Steps

4.2 Kernel Build Process Overview

4.3 Kernel Build Process Execution

4.4 Kernel Replacement in the ArchLinux Bootable Media

5. Assembling a Complete Crux-arm 64b Root Filesystem

5.1 Core System Components

5.2 Fix for Invisible Console Cursor

5.3 Fix for Blank HDMI Console Display

5.4 Further Customization Opportunities

6. Further Work

7. Conclusion

Document Revision History

C-Number

Date

Explanation

C005805

2018/05/16

Current version

C005805

2018/05/16

Initial release.

1. Introduction

This is a short report on installing the Crux-arm 64 bits Linux distribution on the Odroid C2 single board computer by Hardkernel. My aim is to guide the reader trough the shortest route to get a minimal working Linux installation, from which the reader may pursue her learning and experimentation experience (a minimal Crux installation typically being a basis for some system or O/S customization project).

This report is written with I, we, and you referring, respectively to the truth from my own experience (no pretense to be an expert), to some common understanding that the reader and the author should have at some point in the subject area exposition, and to suggested procedural steps for a reader to go through at its own risk.

Some familiarity with the Crux X86_64 distribution is assumed. The basic tool is a Linux laptop or desktop with an Internet connection and some MicroSD card or Herdkernel eMMC module interfacing mechanism (my laptop has Crux as its single O/S and an integrated SD card reader).

The following minimal hardware is needed:

If you whish a console keyboard and display, you add:

Basically, my Crux-arm 64 bits installation went by downloading u-boot, kernel, and Linux distribution software (system partition images), following the installation instructions for another Linux distribution (ArchLinux) completely tailored for the Odroid C2, and replacing components to achieve a selection of kernel configuration and Linux distribution (Crux) that I am more confident with.

I followed the route where we download some Linux kernel version in source code and we cross-compile it on the X86_64 laptop or desktop before installing it on the boot media for the Odroid C2. This means I had to get a cross-compiler from the X86_64 build environment to the aarch64 target environment (replace aarch64 by arm64 when in the Linux kernel compilation context instead of the gnu autoconf package compilation context). The cross-compiler procurement process is left out of the present document. The alternate route would be to obtain the proper Linux kernel version in binary form for a first Crux-arm 64 bits installation on the Odroid C2, and then rely on native compilers for further kernel configuration, i.e. the same process as with Crux on personal computers.

2. Downloading Software Components

2.1 The ArchLinux Distribution Image

You go to http://archlinuxarm.org, where the the platform ARMv8 is supported with the Amlogic SOC (System On Chip) used by the ODROID-C2 model (https://archlinuxarm.org/platforms/armv8/amlogic/odroid-c2) to find the basic installation information for the ArchLinux bootable system partition on the Odroid C2. You then decide to download http://os.archlinuxarm.org/os/ArchLinuxARM-odroid-c2-latest.tar.gz.

2.2 The Linux Kernel

For the Linux kernel, an ever present challenge is to keep the software up to date with evolving hardware components and interfacing standards. In the case of the Odroid C2, the Amlogic SOC model is the main factor in kernel customization and configuration requirements. Still, a project based on the Odroid C2 may come with kernel adaptation requirements. Our strategy is to start with the Linux kernel version 3.16 (which is officially LTS for long term support) as customized and configured by the Odroid C2 support community (precautiously keep this URL https://forum.odroid.com/viewtopic.php?f=140&t=29735 in file as the Hardkernel web knowledge base is badly indexed). In line with the Crux approach, and by necessity somehow, our strategy involves the kernel (cross-)compilation step. You need this entry-level git command for downloading the required kernel version:

git clone --shallow-since 2018/04/01 --no-checkout --branch odroidc2-v3.16.y https://github.com/hardkernel/linux.git

where

The git magic created a linux directory. Please rename it to my_kernel since the word linux may be ambiguous in my explanations. The my_kernel directory appears empty but consumes a lot of disk space, as shown by the commands:

ls my_kernel
du --summarize --blok-size=M my_kernel

Now a git checkout command gives us the kernel directory tree we wanted:

git -C my_kernel checkout odroidc2-v3.16.y

Note that the kernel updates potentially made in the git remote "origin" from time to time will be part of your cloned my_kernel directory; I leave it to you to learn the git commands required to inspect and reject them if need be.

2.3 Crux-arm 64b Root File System

In order to make my advice more valuable, the Crux root file system tarball for Crux-arm 64 bits is hidden from places where you would look for it: neither at the download page (https://crux-arm.nu/Main/Download) nor at the page "to browse all releases" (http://resources.crux-arm.nu/releases/). It is in the release notes for Crux-arm version 3.3 (https://crux-arm.nu/Documentation/ReleaseNotes3-3), at the bottom of the page under "Development 64b releases" and then at "Generic 64b release", linking to http://resources.crux-arm.nu/files/devel-test/3.3/crux-arm-rootfs-3.3-64b-RC2.tar.xz. You download this file, and you might hope for the MD5 fingerprint 1f510e195928e11173f6a2dc7f4b3dee.

3. Partition Formatting and Linux Boot Process Installation

These instructions require root privilege.

Clear a section at the beginning of the MicroSD memory:

dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=8

3.1 Create a Single ext2 or ext4 Partition

Use the fdisk command or an equivalent utility to create a DOS partition table and a single big Linux partition. The default starting sector should leave enough space for the later u-boot installation. Once done, the command

ls /dev/mmcblk0*

should report

/dev/mmcblk0 /dev/mmcblk0p1

where the latter is the partition device name which will be formatted and will receive the kernel and Linux root file system.

Format the partition /dev/mmcblk0p1 as an ext2 or ext4 file system. The command I used is mkfs.ext4 and the default option values were all right. This may take some time to complete.

3.2 Fill the ArchLinux Root Partition (Kernel and Linux Distribution Files)

First mount the virgin partition at some directory (here /mnt).

mount /dev/mmcblk0p1 /mnt

before you unzip and untar the downloaded ArchLinux tarball, you may have a look at its contents:

gzip -dc [...]/ArchLinuxARM-odroid-c2-latest.tar.gz | tar -tv | less

If you are satisfied, you then proceed with

cd /mnt
gzip -dc [...]/ArchLinuxARM-odroid-c2-latest.tar.gz | tar -x
sync

You may ignore a warning message about unknown metadata extension in the tar, since the ArchLinux installation is not intended for its full functionality and will soon be discarded. Thus, so far we got a partition with a software bundle comprising a boot loader, the kernel, and a Linux distribution all configured for the Odroid C2 hardware, with some required customization already implemented.

3.3 Arrange u-boot as the Boot Loader

While the boot loader files are in the /boot directory in the /dev/mmcblk0p1 partition (still mounted at /mnt), they are not yet properly installed in the enclosing /dev/mmcblk0 boot media. This low-level boot software installation is done by the script /mnt/boot/sd_fusing.h which you may look at and then start as in

cd /mnt/boot
./boot/sd-fusing.h /dev/mmcblk0

Be careful not to specify /dev/mmcblk0p1 as the target boot media (nor any other block device nor any other block device partition).

This installs the last three stages of a four-stage boot chain, comprising:

As hinted in the ArchLinux instructions, I found it convenient to edit /mnt/boot/boot.ini for adjusting the screen resolution for my display, including to comment-out the display-autodetect option.

3.4 Testing the boot media

With the commands:

cd /root
umount /mnt

and then removing the MicroSD card from the SD card reader, we have a boot media for the ArchLinux distribution that we should try as a check for the progress so far. As I write this while replaying the procedure, I indeed get a login prompt on my Odroid C2 console, and I ignore some system error message greeting me after succcessful login as root with root as a password (ArchLinux installation sanity is not an aim by itself).

Shutting down cleanly this boot media is perhaps a good habit:

shutdown -h now

Then remove the MicroSD boot media from the Odroid C2, insert it on the laptop or desktop SD card reader and re-mount it under /mnt.

4. Linux Kernel Configuration Build and Replacement

Except for the installation steps at the end of the process, Linux kernel work does not require root privileges.

4.1 Preliminary Steps

On the laptop or desktop, change the working directory to the directory (named my_kernel) in which the git commands installed the kernel source code package.

Along the PATH, there must be a conventional set of X86_64 native gcc (and binutils) tools, plus some equivalent cross-tools with binary names like aarch64-none-linux-gcc for the gcc (C compiler executable binary file name). In my case, I had my C cross-compiler file at ~/tools_aarch64/bin/aarch64-none-linux-gcc and accordingly, I found useful to type:

export PATH=$PATH:/home/tmoreau/tools_aarch64/bin

Then, the basic command for kernel build process steps is:

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- help

where the make target help specifies the step to be run within the overall build process. It is the Makefile in the current my_kernel directory that drives each required build process step.

The first step is to create a configuration file (named .config). In order to get the kernel configuration that the Odroid C2 community elaborated, you give the command

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- odroidc2_defconfig

This created the .config file from a file (coincidentally) called odroidc2_defconfig, which is part of the device-specific data embedded in the large kernel source package.

Here is a suggestion. Edit the file arch/arm64/configs/odroidc2_defconfig to put your signature-of-the-day string in the configuration setting LOCALVERSION="". You do this a) to later see that your kernel is being run, and b) such that fielded devices with your kernel do not induce support calls handled by someone else. In my own effort, I did:

sed -i -e 's/LOCALVERSION=/LOCALVERSION="-Odroid-C2-demo-by-tmoreau"/' $(find -name odroidc2_defconfig)

and then repeat:

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- odroidc2_defconfig

You see the impact of this configuration setting by:

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- kernelrelease

and, ultimately when the compiled kernel will be running, with the command

uname -s

4.2 Kernel Build Process Overview

In this document section, the /mnt directory is mentionned only as a read-only reference to the bootable ArchLinux installation. The MicroSD partition mounted on /mnt will be modified in later sections.

Before starting the kernel compilation process, we may identify the set of critical output files. I did this in part through the reconciliation of the /mnt/boot directory contents and the output of the above make ... help command. I came to the following list of essential make targets:

The Image and dtbs targets are creating the files arch/arm64/boot/Image and arch/arm64/boot/dts/meson64_odroidc2.dtb, respectively. There are corresponding file names in the /mnt/boot directory (for a kernel version 3.14 that was work-in-progress at the time the ArchLinux distribution was prepared).

The make target modules creates a set of files that are to be transferred to a directory /mnt/lib/modules/<version>/ where <version> is the output string from the above make ... kernelrelease command. This is done with the command

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- INSTALL_MOD_PATH=/<somewhere>/ modules_install

where <somewhere> will be specified below.

Finally, a built kernel may be useless if some firmware files are not available at run time for some peripheral devices (a notable example is a wifi module that needs some closed source firmware in order to ensure compliance to the RF spectrum regulations, the regulating bodies being reluctant to delegate compliance obligations to a bunch of open source volunteers, no matter how technically sound the open source software turns out). These firmware files are to be transferred to a directory /mnt/lib/firmware/ with the command

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- INSTALL_FW_PATH=/<somewhere-else>/ firmware_install

where <somewhere-else> will be specified below.

4.3 Kernel Build Process Execution

With or without the understanding from the previous section, you do:

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- dtbs
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- Image
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- modules

and that's it. In case of difficulty, your first effort sould go into reviewing the prerequisites: the source kernel directory tree, the appropriate cross-compiler tools, the native tools, some environment variables, and the .config file.

4.4 Kernel Replacement in the ArchLinux Bootable Media

The added value in our MicroSD media unit comprises

The kernel files (notably /mnt/boot/Image and /mnt/boot/dtbs/meson64_odroidc2.dtb) are for a kernel version no longer interesting for us, and the remaining of /mnt (outside /mnt/boot, including /mnt/lib/modules and /lib/firmware) is useless since ArchLinux is only a transient installation.

Hence you salvage the valuable /mnt/boot directory contents. First, turn into the root user, since we are about to prepare bootable media contents. Suppose you still are in the my_kernel directory where the new kernel has been built, you do:

cd ..
mkdir boot
cp --recursive /mnt/boot/* boot/
rm boot/Image
rm boot/dtbs/meson64_odroidc2.dtb

Note: among the salvaged files, the file boot/initramfs-linux.img happens to be compatible with either kernel versions and also with either Linux distributions (ArchLinux or Crux-arm 64). This might be by chance; I should know better. In any event, the following commands in a temporary working directory allow the investigation of the initramfs contents:

gzip -dc /mnt/boot/initramfs-linux.img | cpio --extract

Actually you may skip the rest of this document section. It merely provides a test that the kernel just build is working.

cd my_kernel
cp arch/arm64/boot/Image /mnt/boot/
cp arch/arm64/boot/dts/meson64_odroidc2.dtb /mnt/boot/dtbs/
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- INSTALL_MOD_PATH=/mnt/ modules_install
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- INSTALL_FW_PATH=/mnt/lib/firmware/ firmware_install
cd ..

You may now un-mount the MicroSD boot media and try it to check that the new kernel is installed (uname -s command) and working for ArchLinux.

5. Assembling a Complete Crux-arm 64b Root Filesystem

5.1 Core System Components

We are now in a position to prepare the definitive contents of the Crux distribution root file system. You may do this on a working directory my_clfs, located in the same directory as my_kernel and the salvage directory boot:

mkdir my_clfs
cd my_clfs

The Crux-arm 64b root filesystem tarball obviously goes there:

xz -dc [...]/crux-arm-rootfs-3.3-64b-RC2.tar.xz | tar -x

The Crux arm documentation stated that the system boot sequence was out of scope, hence the my_clfs/boot directory should be empty, and filled by the salvage directory from ArchLinux:

ls boot
cp --archive --verbose ../boot/* boot/

The next thing to put in our root filesystem assembly is the set of kernel critical files, with the same set of commands that targeted the MicroSD media.

cd ../my_kernel

Note that I prefer to specify absolute file names to the kernel make process, hence the reliance on the bash $OLDPWD shell variable expansion to point to directories and files in the my_clfs directory. The following echo command lets you see if it works as intended:

echo make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- INSTALL_MOD_PATH=$OLDPWD modules_install

Then, the kernel installation commands are:

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- INSTALL_MOD_PATH=$OLDPWD modules_install
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux- INSTALL_FW_PATH=$OLDPWD/lib/firmware firmware_install
cp --verbose arch/arm64/boot/Image $OLDPWD/boot
cp --verbose arch/arm64/boot/dts/meson64_odroidc2.dtb $OLDPWD/boot/dtbs
cd $OLDPWD

There are two recommended fixes for the installation usability.

5.2 Fix for Invisible Console Cursor

The first installation fix has to do with an otherwise invisible cursor on the hdmi console output. This fix works by changing two settings in the terminfo terminal capability database for the linux console profile, defining a software cursor instead of a hardware cursor. Back to the my_clfs directory, the commands to extract the terminal profile, changing the settings, and putting back the terminal profile in its stardard place are as follows:

infocmp -I -1 -A usr/share/terminfo linux >~/tmp_terminfo.txt
sed -i -e '/cnorm=/s/0c/112c/' -e '/cvvis=/s/8c/48;0;64c/' ~/tmp_terminfo.txt
tic -o usr/share/terminfo ~/tmp_terminfo.txt

The shell commands for a normal cursor, a very visible cursor, and an invisible one are, respectively:

tput cnorm
tput cvvis
tput civis

For a normal cursor to be requested at the start of any bash login session, you may also do:

echo tput cnorm>>etc/profile

5.3 Fix for Blank HDMI Console Display

The second installation fix in our Crux root file system assembly is a workaround utility program to be run at boot time for the following difficulty with the Odroid C2 hdmi console display output: for unknown reason, the kernel initializes the hdmi output in a blank screen state that is exited only if the kernel receives two requests: first to blank the (already blanked) screen and then to unblank the screen. This sequence happens if a connected keyboard is idle for 15 minutes and then touched by the user. I wrote a small C program that makes the two kernel requests without wait. This program needs to be (cross-)compiled for the Arm 64 bits architecture, and called somewhere in our Crux installation startup sequence.

cat <<EOF_EOF >root/unblank.c
/* unbklank.c -- ...
 *
 * Contributed in May 2018 by Thierry Moreau
 * as a workaround for a blank screen when booting
 * the Crux-arm 64b Linux distribution on the
 * Hardkernel Odroid C2 single board computer.
 *
 * Public domain file.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/vt.h>
#include <linux/tiocl.h>
#include <errno.h>

static void usage(const char *prgm)
{
  printf("Usage %s <tty name>\n\n"
      "Attempts to blank and un-blank screen on console <tty name>.\n",prgm);
}

static int blank_code=TIOCL_BLANKSCREEN;
static int unblank_code=TIOCL_UNBLANKSCREEN;

int main(int argc, char *argv[])
{
  int td;
  int st;
  int r=EXIT_SUCCESS;
  if (argc!=2 || strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 ) {
    usage(argv[0]);
    return EXIT_FAILURE;
  }
  td=open(argv[1],O_RDWR|O_NOCTTY);
  if (td<0) {
    printf("Open error %d on \"%s\"\n\n",errno,argv[1]);
    usage(argv[0]);
    return EXIT_FAILURE;
  }
  if (!isatty(td)) {
    printf("Not a TTY: \"%s\"\n\n",argv[1]);
    usage(argv[0]);
    r=EXIT_FAILURE;
  }
  else {
    errno=0;
    st=ioctl(td,TIOCLINUX,&blank_code);
    if (st<0) {
      printf("Error %d attempting to blank screen \"%s\"\n\n",errno,argv[1]);
      r=EXIT_FAILURE;
    }
    errno=0;
    st=ioctl(td,TIOCLINUX,&unblank_code);
    if (st<0) {
      printf("Error %d attempting to unblank screen \"%s\"\n\n",errno,argv[1]);
      r=EXIT_FAILURE;
    }
  }
  close(td);
  return r;
}
EOF_EOF
cd root
aarch64-none-linux-gcc -o unblank unblank.c
cd ..
echo /root/unblank /dev/tty0 >>etc/rc.local

5.4 Further Customization Opportunities

That's it for the essential Crux root filesystem assembly in my_clfs.

You may now customize the Crux installation to your local preferences, by editing files like etc/rc.conf, etc/rc.d/net, etc/ssh/sshd_config. It may be convenient to rely on the bootable ArchLinux installation to gather system configuration details, e.g. that the Odroid C2 Ethernet port is eth0.

If not already done, make sure the MicroSD boot media is mounted on /mnt. We are ready now to erase the whole root file system and replace its contents with the directory structure we just assembled in my_clfs. You are on your own for commands to erase a complete partition on the MicroSD device, and not on your laptop hard disk which you did not back up for a while. Then, having the empty ext2 or ext4 MicroSD partition mounted on /mnt, you may do:

cp --archive [...]/my_clfs/* /mnt
sync

You may now further customize the Crux installation to your preferences, this time in /mnt applicable to this sepcific MicroSD boot media. For instance, I assign a fixed IPv4 address to each media unit and this is reflected in /mnt/etc/rc.d/net and /mnt/etc/ssh/sshd_config so that the unique ssh authentication public key (created upon first run of /etc/rc.d/sshd, triggered by the /mnt/etc/rc.conf configuration) is bound to a unique host IPv4 address (in my laptop ~/.ssh/known_hosts file).

Then, after un-mounting /mnt, you have a bootable Crux-arm 64b for the Odroid C2.

6. Further Work

Before re-distributing the software to third parties, attention should be paid to licensing requirements, notably that the spirit of the GPL commands the availability of software in source form for the third parties who receives the software in binary form, including when embedded in a system.

Re-building u-boot files from u-boot source code.

Re-building the file boot/initramfs-linux.img from its source code.

Adding the X-11 support.

7. Conclusion

A basic Crux arm 64b implementation is available on the Odroid C2, presumably useful for anyone interested in a simple Linux distribution scheme as a base of customization of Odroid C2 platform for projects not involving X-11 (for now).

Somehow paradoxically, the Odroid C2 attractiveness as a low cost 64 bits server device lies on its market success as a video engine (the Amlogic SOC offering appears to be propelling this success).

The closed source software components (mainly the Amlogic SOC boot ROM, BL1, and BL2 above, plus the Arm Mali 450 GPU drivers that would be needed for X-11 support) look like a necessary evil since totally open source state-of-the-art boards are typically short-lived in the market.