The amd64 images

EB corbos Linux doesn’t support any amd64 hardware at the moment, but we provide some QEMU amd64 images. Using amd64 for development may help to make your development flow much smoother since you don’t have to handle the tricky aspects of cross-building.

For amd64/qemu we provide example images for EB corbos Linux (EBcL) and for Ubuntu Jammy. The difference between EBcl and Jammy is, that EBcL provides some additional components, like the crinit init-manager and the elos logging and event framework, and that EBcL provides a qualified security maintenance release every three months, while Jammy is proving updates continuously, using less strict qualification and documentation.

The amd64 Jammy images

In images/amd64/qemu/jammy you can find five basic example images demonstrating how to use the EB corbos Linux SDK. This folder contains the common configuration shared by all the examples, and makes use of the QEMU images/qemu*.mk include makefiles.

# Kernel package to use
kernel: linux-image-generic
# Apt repositories to use
apt_repos:
  - apt_repo: http://archive.ubuntu.com/ubuntu
    distro: jammy
    components:
      - main
      - universe
  - apt_repo: http://archive.ubuntu.com/ubuntu
    distro: jammy-security
    components:
      - main
      - universe
# CPU architecture
arch: 'amd64'

All examples make use of the kernel “linux-image-generic”. This is a meta-package and always takes the latest available Ubuntu Jammy package. The Canonical Ubuntu apt repositories are used to build the examples.

# Partition layout of the image
# For more details see https://elektrobit.github.io/embdgen/index.html
image:
  type: gpt
  boot_partition: root

  parts:
    - name: root
      type: partition
      fstype: ext4
      size: 2 GB
      content:
        type: ext4
        content:
          type: archive
          archive: build/ubuntu.config.tar

All examples make use of a very simple image consisting of a gpt partition table and a single ext4 root partition with a size of 2 GB.

# Derive values from base.yaml - relative path
base: base.yaml
# Root device to mount
root_device: /dev/vda1
# List of kernel modules
modules:
  - kernel/drivers/block/virtio_blk.ko # virtio_blk is needed for QEMU

Also the initrd.img is shared by all examples. It first loads the virt-IO block driver and then mounts /dev/vda1 as the root filesystem.

# Derive values from base.yaml - relative path
base: base.yaml
# Download dependencies of the kernel package - necessary if meta-package is specified
download_deps: true
# Files to copy from the packages
files:
  - boot/vmlinuz*
  - boot/config*
# Do not pack the files as tar - we need to provide the kernel binary to QEMU
tar: false

The boot.yaml is also not image specific. It’s used to download and extract the kernel binary. In addition, the kernel config is extracted.

# Derive the base configuration
base: base.yaml
# Reset the kernel - should not be installed
kernel: null
# Name of the archive.
name: ubuntu
# Packages to install in the root tarball
packages:
  - systemd
  - udev        # udev will create the device node for ttyS0
  - util-linux
# Scripts to configure the root tarball
scripts:
  - name: config_root.sh # Name of the script, relative path to this file
    env: chroot # Type of execution environment - chfake means fakechroot

The root.yaml shares the common parts of the root filesystem configuration of all these example images. All examples use “ubuntu” as name, by default have a minimal root filesystem only consisting of debootstrap and systemd, udev, and util-linux additionally installed, and use the config_root.sh as configuration, which links systemd as /sbin/init.

The amd64 Jammy berrymill image

At the moment, the EBcL SDK makes use of two more generic Linux root filesystem builders, elbe and kiwi-ng. The default is elbe, because it provides a much better build speed, but also the previously used kiwi-ng is still supported. Future EBcL major release lines may drop both and come with a more embedded optimized solution, so ideally you make use of the root.yaml instead of using an own elbe or kiwi-ng XML image description.

The amd64/qemu/jammy/berrymill image makes use of the above mentioned configurations, and extends it with an own root.yaml and a specific Makefile.

# Config to use as a base
base: ../root.yaml
# Add the EB corbos Linux apt repo to provide the boostrap package
use_ebcl_apt: true
# Overwrite the image builder type - ensure kiwi is used
type: kiwi
# Pattern to match the result file
result_pattern: '*.tar.xz'

This root.yaml inherits the root.yaml from the partent folder, described above, and adds the EBcL apt repository, which provides the required kiwi-ng bootstrap package, set the build type to “kiwi” and updates the build result search pattern to “*.tar.xz”, since there is no way to disable the result compression with kiwi-ng.

# Makefile for ammy QEMU amd64 image using kiwi

# Arch for sysroot extraction
arch = x86_64

#---------------------
# Image specifications
#---------------------

# Specification of the partition layout of the image.raw
partition_layout = ../image.yaml
# Specification of the root filesystem content and configuration
root_filesystem_spec = root.yaml
# Specification of the initrd.img
initrd_spec = ../initrd.yaml
# Specification of the kernel
boot_spec = ../boot.yaml

#-------------------------
# Additional configuration
#-------------------------

# Config script for root filesystem
config_root = ../config_root.sh


#--------------------
# Generated artifacts
#--------------------

# Disc image
disc_image = $(result_folder)/image.raw

# Base root tarball
base_tarball = $(result_folder)/ubuntu.tar.xz

# Configured root tarball
root_tarball = $(result_folder)/ubuntu.config.tar

# Generated initrd.img
initrd_img = $(result_folder)/initrd.img

# Kernel image
kernel = $(result_folder)/vmlinuz

# Sysroot tarball
sysroot_tarball = $(result_folder)/ubuntu_sysroot.tar


#-------------------
# Run the QEMU image
#-------------------

# QEMU kernel command line
kernel_cmdline_append = "rw"


# for building
include ../../../../qemu.mk

# for running QEMU
include ../../../../qemu_x86_64.mk

The Makefile point make to the right specification files, sets the flag to mount the root filesystem as writable, and includes the base makefiles describing how to build an QEMU image and how to run the build results using QEMU.

The amd64 Jammy debootstrap image

In general, kiwi-ng can also build images using debootstrap instead of a pre-built bootstrap package. This brings the limitation that only one apt repository is supported, which needs to provide a proper main component, and that a debootstrap script must be available in the build VM for the selected distribution. The EBcL SDK can make use of this for Ubuntu Jammy builds, and the image amd64/qemu/jammy/debootstrap is a proof of concept showing how to do it.

# CPU architecture
arch: amd64
# Name of tarball
name: ubuntu
# APT repo for kiwi build
apt_repos:
  - apt_repo: http://archive.ubuntu.com/ubuntu
    distro: jammy
    components:
      - main
      - universe
# Use debootstrap instead of bootstrap package
# This allows us to use only one apt repo.
use_bootstrap_package: false
# Select required bootstrap packages
bootstrap:
  - apt
# Packages to install in the root tarball
packages:
  - systemd
  - udev
  - util-linux
# Overwrite the image builder type - ensure kiwi is used
type: kiwi
# Pattern to match the result file
result_pattern: '*.tar.xz'
# Scripts to configure the root tarball
scripts:
  - name: ../config_root.sh # Name of the script, relative path to this file
    env: chroot # Type of execution environment

The root.yaml configures the Ubuntu Jammy apt repository as the single apt repository to use, and sets the “use_bootstrap_package” to false, which will result in a kiwi-ng build not relying on the EBcL bootstrap package.

The amd64 Jammy elbe image

The images/amd64/qemu/jammy/elbe image makes use of the elbe root filesystem builder. The only difference to the shared configuration is that elbe is explicitly selected.

# Config to use as a base
base: ../root.yaml
# Overwrite the image builder type - ensure elbe is used
type: elbe

The Makefile is similar to the one above.

The amd64 Jammy kernel source image

The amd64/qemu/jammy/kernel_src image is a proof of concept showing how to make use of local compiled kernels with EBcL builds. The boot.yaml is used to get the kernel configuration of the Ubuntu Jammy kernel. The initrd.yaml extends the shared initrd.yaml with the line “modules_folder: $$RESULTS$$”. The parameter “modules_folder” can be used to provide kernel modules from the host environment, and the string “$$RESULTS$$” will be replaced with the path to the build folder.

The Makefile extends the default QEMU makefile with a bunch of new make targets.

#--------------------------
# Image build configuration
#--------------------------

$(source):
	@echo "Get kernel sources..."
	mkdir -p kernel
	cd kernel && apt -y source linux
	sudo apt -y build-dep linux
	cd $(kernel_dir) && chmod +x scripts/*.sh

$(kconfig): $(boot_spec) $(source)
	@echo "Get kernel config as $(kconfig)..."
	mkdir -p $(result_folder)
	set -o pipefail && boot_generator $(boot_spec) $(result_folder) 2>&1 | tee $(kconfig).log
	@echo "Renaming $(result_folder)/config-* as $(kconfig)..."
	mv $(result_folder)/config-* $(kconfig)
	@echo "Copying $(kconfig) to $(kernel_dir)..."
	cp $(kconfig) $(kernel_dir)/.config
	@echo "Set all not defined values of the kernel config to defaults..."
	cd $(kernel_dir) && yes "" | $(MAKE) $(kernel_make_args) olddefconfig
	@echo "Copying modified config as olddefconfig..."
	cp $(kernel_dir)/.config $(result_folder)/olddefconfig

$(kernel): $(kconfig) $(source)
	@echo "Get kernel binary..."
	cd $(kernel_dir) && yes "" | $(MAKE) -j 16 bzImage
	cd $(kernel_dir) && INSTALL_PATH=../../$(result_folder) $(MAKE) install
	cp -v $(result_folder)/vmlinuz-* $(kernel)
	@echo "Results were written to $(kernel)"

$(modules): $(kernel)
	@echo "Get virtio driver..."
	cd $(kernel_dir) && $(MAKE) -j 16 modules
	cd $(kernel_dir) && chmod +x debian/scripts/sign-module
	mkdir -p $(result_folder)
	cd $(kernel_dir) && INSTALL_MOD_PATH=../../$(result_folder) $(MAKE) modules_install

$(initrd_img): $(initrd_spec) $(modules)
	@echo "Build initrd.img..."
	mkdir -p $(result_folder)
	set -o pipefail && initrd_generator $(initrd_spec) $(result_folder) 2>&1 | tee $(initrd_img).log

#--------------------
# Helper make targets
#--------------------

# Rebuild the kernel binary
.PHONY: rebuild_kernel
rebuild_kernel:
	mkdir -p $(result_folder)
	cd $(kernel_dir) && yes "" | $(MAKE) -j 16 bzImage
	cd $(kernel_dir) && INSTALL_PATH=../../$(result_folder) $(MAKE) install
	cp -v $(result_folder)/vmlinuz-* $(kernel)
	@echo "Results were written to $(kernel)"

# Rebuild the kernel modules
.PHONY: rebuild_modules 
rebuild_modules: kernel
	mkdir -p $(result_folder)
	cd $(kernel_dir) && $(MAKE) modules -j 16
	cd $(kernel_dir) && chmod +x debian/scripts/sign-module
	rm -rf build/lib
	cd $(kernel_dir) && INSTALL_MOD_PATH=../../$(result_folder) $(MAKE) modules_install

The “$(source)” is responsible for fetching the kernel sources using apt, and installing the kernel build dependencies. The “$(kconfig)” target gets the default config for the used kernel package and adds it to the kernel source tree. The “$(kernel)” target describes how to compile the kernel and get the kernel binary. The “$(modules)” describes how to build and install the modules to the results folder. The new make for the inird.img adds the dependency to the locally built kernel modules.

Overall, these new rules describe how to fetch the kernel sources and build the kernel binary and modules. These binaries are then picked up by the default QEMU build flow and make rules.

The amd64 Jammy kiwi image

The EBcL SDK makes by default use of berrymill for kiwi-ng builds, but it also supports using kiwi-ng directly. The image description in amd64/qemu/jammy/kiwi is a proof of concept how to use kiwi-ng without berrymill. Setting the flag “use_berrymill” to false does the trick. This build variant has some limitations compared to the berrymill build. Derived images are not supported, and the current implementation doesn’t use apt repository authentication.

The amd64 EB corbos Linux images

EB corbos Linux (EBcL) is an embedded Linux distribution targeting automotive and other industrial embedded Linux solutions. The main differences between EBcL and Ubuntu are the release and qualification handling, and some additional components added by EBcL which allow building more lightweight and better performing embedded images.

# Kernel package to use
kernel: linux-image-generic
use_ebcl_apt: true
# Additional apt repos
apt_repos:
  # Get latest security fixes
  - apt_repo: http://archive.ubuntu.com/ubuntu
    distro: jammy-security
    components:
      - main
      - universe
# CPU architecture
arch: 'amd64'

Again, the base.yaml is used to define the kernel package, the apt repos and the CPU architecture. The EBcL repo can be added using the “use_ebcl_apt” flag. For experimenting and if we want the latest security patches without qualification, we can add the Ubuntu Jammy repositories.

The boot.yaml is not different to the one used for the Jammy images, and just extracts the kernel binary and configuration form the given kernel package. The image.yaml and the initrd.yaml are also identical to the ones used with the Jammy images.

The amd64 EB corbos Linux systemd images

EBcL supports the systemd init-manager and if startup time and the resource footprint are not too critical, it’s a quite good choice because all of the Ubuntu packages are fully compatible with it, and all services come with their configs for systemd. To run systemd without providing the init-manager using the kernel command line, we can link it as /sbin/init. This is done using the config_root.sh script.

The amd64 EB corbos Linux systemd berrymill image

The amd64/qemu/ebcl/systemd/berrymill defines a QEMU image using berrymill and kiwi-ng for building the root filesystem. This root filesystem is a very minimal one, only providing systemd, udev and the default command line tools.

The amd64 EB corbos Linux systemd elbe image

The amd64/qemu/ebcl/systemd/berrymill defines a QEMU image using elbe for building the root filesystem. This root filesystem is a very minimal one, only providing systemd, udev and the default command line tools.

The amd64 EB corbos Linux crinit images

EBcL adds crinit init-manger, as an alternative to systemd. Crinit is a much more lightweight init-manager, compared with systemd, and tailored ro embedded. Since all the hardware and use-cases are very well known in advance for an embedded system, many dynamic configuration and detection features of systemd can be skipped, which results in a faster and much more lightweight solution. The drawback of using crinit is that the Ubuntu packages are not prepared for crinit, and all service and startup configuration needs to be done by the user.

The necessary minimal configuration to use crinit is contained in images/amd64/qemu/ebcl/crinit/crinit_config, and this folder is copied as overlay to the root filesystem using the root.yaml. The script config_root.sh ensures that the sbin/init script, provided in the overlay, is executable. Instead of systemd, crinit and its command line client crinit-ctl is installed.

Let’s take a closer look at the crinit_config overlay. The sbin/init mounts the /proc filesystem and then runs the crinit init-manager. The /etc folder contains a minimal crinit configuration. The file /etc/crinit/default.series is the main configuration file, and the folder /etc/crinit/crinit.d contains the services we want to run. The task /etc/crinit/crinit.d/agetty-ttyS0.crinit runs agetty on the serial console ttyS0, so that we can login using the QEMU serial console. The task /etc/crinit/crinit.d/earlysetup.crinit sets the hostname, so that we get proper logs. The task /etc/crinit/crinit.d/mount.crinit takes care of mounting the additional filesystems.

The amd64 EB corbos Linux crinit berrymill image

The amd64/qemu/ebcl/crinit/berrymill defines a QEMU image using berrymill and kiwi-ng for building the root filesystem. This root filesystem is a very minimal one, only providing crinit.

The amd64 EB corbos Linux crinit elbe image

The amd64/qemu/ebcl/crinit/elbe defines a QEMU image using elbe for building the root filesystem. This root filesystem is a very minimal one, only providing crinit.

The amd64 EB corbos Linux server images

The previous images were all very minimal images, only providing enough to boot and login to the system. For developing an embedded system this is the right place to start development, but for exploring and playing with the system it’s too less. The server images provide a more complete user experience and add logging, network, apt and ssh.

The amd64 EB corbos Linux server crinit image

The crinit variant of the server image is contained in images/amd64/qemu/ebcl/server. In addition to crinit, it provides the elos logging and event manager, which is a lightweight replacement of journald and dbus, which allows automatic log evaluation and event handling. To manage the network interfaces, netifd form the OpenWRT world is used. It’s a very powerful and nevertheless lightweight network manager used in many router solutions. Als NTP client ntpdate is used. To allow remote login openssh-server is added. The image also contains apt to allow easy installation of additional packages, and the typical Linux tools and editors for playing and exploring.

The root_common.yaml is the shared root specification of all the EBcL server variants. It defines the name, the architecture and the common tools and services, like openssh-server. The root.yaml extends the package list with the crinit and elos specific packages, and defines the overlay for the crinit configuration and the config script for the crinit variant. This config_root.sh sets a machine ID, required by elos, and generates a /etc/hosts file.

Let’s take a look at the server configuration. In addition to the /usr/sbin/init, which runs crinit, a ntp_time.sh is provided. This ntp_time.sh does a one-shot NTP time update, as soon as the network is up, to avoid issues with apt and other time sensitive services. The /etc/apt folder provides the apt repository configuration for EBcL and Ubuntu Jammy. The file /etc/config/network/network is evaluated by netifd to bring up the network interfaces. This configuration makes use of an static IPv6 and a dynamic IPv4 configuration. The crinit tasks are extended with tasks to run elos, bring up the network, run the SSH service, and trigger the NTP time update. The file /etc/elos/elosd.json contains some basic elos configuration, to use it as syslog demon. The conifg /etc/ssh/sshd_config.d/10-root-login.conf enables SSH login is root. The config /etc/gai.conf ensures that IPv4 DNS is preferred over IPv6. The other config files just set some reasonable defaults.

The amd64 EB corbos Linux server systemd image

The folder images/amd64/qemu/ebcl/server/systemd contains a variant of the EBcL server image using systemd as init manager. It’s mainly provided as a reference, to compare the configuration and performance.