Embedded Linux Boot time optimization: training and webinar

Bootlin has been helping its customers optimize the boot time of embedded Linux systems for many years, ensuring that these systems meet their startup time requirements. Thanks to this, Bootlin has accumulated a significant experience in this field.

Next week, Bootlin is organizing or participating to two events related to Embedded Linux boot time optimization:

In addition, if you need help and support to optimize the boot time of your Embedded Linux systems, do not hesitate to as we offer engineering services, thanks to which we can take your existing Embedded Linux system, and significantly reduce its startup time to meet your requirements.

Another system update adventure with RAUC, Barebox & Yocto Project

After experiencing both SWupdate and Mender in the past we recently got the opportunity to work with another update framework for embedded systems called RAUC.

This time the choice of RAUC as system upgrade framework was mainly motivated by the Phytec IMX6 board ecosystem which is based on both Barebox and Yocto Project.
Indeed RAUC and Barebox are both developed by Pengutronix and both are designed to provide a complete and homogeneous solution that will be introduced in this post.

Adding RAUC support in Barebox

RAUC relies on the bootchooser mechanism implemented in
Barebox which provides a mean to work with abstract boot targets:

A target can be seen as set of variables and options that will be used by the bootchooser algorithm to choose which target to boot on.
The way those variables are presented and accessed is defined in the Barebox state framework which stores them in persistent memory (EEPROM, NAND/NOR flash, SD/eMMC, etc.).

In our case the state Device Tree below was already described by the Phytec BSP in the board EEPROM:

/ {
        aliases {
                state = &state;
        };

        state: imx6qdl_phytec_boot_state {
                magic = <0x883b86a6>;
                compatible = "barebox,state";
                backend-type = "raw";
                backend = <&backend_update_eeprom>;
                backend-storage-type = "direct";
                backend-stridesize = <54>;

                #address-cells = <1>;
                #size-cells = <1>;
                bootstate {
                        #address-cells = <1>;
                        #size-cells = <1>;
                        last_chosen {
                                reg = <0x0 0x4>;
                                type = "uint32";
                        };
                        system0 {
                                #address-cells = <1>;
                                #size-cells = <1>;
                                remaining_attempts {
                                        reg = <0x4 0x4>;
                                        type = "uint32";
                                        default = <3>;
                                };
                                priority {
                                        reg = <0x8 0x4>;
                                        type = "uint32";
                                        default = <21>;
                                };
                                ok {
                                        reg = <0xc 0x4>;
                                        type = "uint32";
                                        default = <0>;
                                };
                        };
                        system1 {
                                #address-cells = <1>;
                                #size-cells = <1>;
                                remaining_attempts {
                                        reg = <0x10 0x4>;
                                        type = "uint32";
                                        default = <3>;
                                };
                                priority {
                                        reg = <0x14 0x4>;
                                        type = "uint32";
                                        default = <20>;
                                };
                                ok {
                                        reg = <0x18 0x4>;
                                        type = "uint32";
                                        default = <0>;
                                };
                        };
                };
        };
};

&eeprom {
        status = "okay";
        partitions {
                compatible = "fixed-partitions";
                #size-cells = <1>;
                #address-cells = <1>;
                backend_update_eeprom: state@0 {
                        reg = <0x0 0x100>;
                        label = "update-eeprom";
                };
        };
};

Most of the properties are very well documented here.

This Device Tree defines a redundant A/B system upgrade scheme which matches our project requirement, we will just make sure to report remaining_attempts and priority values to the Barebox bootchooser variables that we will add later. For now we have to include the Device Tree in our board Device Tree by adding #include "imx6qdl-phytec-state.dtsi".

As we just added our state backend storage we have to enable both bootchooser and state framework in our barebox configuration:

CONFIG_STATE=y
CONFIG_STATE_DRV=y
CONFIG_CMD_STATE=y
CONFIG_BOOTCHOOSER=y
CONFIG_CMD_BOOTCHOOSER=y

We also have to add the boot targets system0 and system1 (as defined in the state Device Tree) in our default environment.
In Barebox source we create a default environment for our board in arch/arm/<board>/env with two boot entries, respectively arch/arm/<board>/env/boot/system0 and arch/arm/<board>/env/boot/system1.

Both have same contents except for the root filesystem partition name A/B:

#!/bin/sh

[ ! -e /dev/nand0.root.ubi ] && ubiattach /dev/nand0.root

mkdir -p /mnt/nand0.root.ubi.rootfsA
automount -d /mnt/nand0.root.ubi.rootfsA 'mount nand0.root.ubi.rootfsA'

global.bootm.oftree="/mnt/nand0.root.ubi.rootfsA/boot/imx6q-phytec-mira-rdk-nand.dtb"
global.bootm.image="/mnt/nand0.root.ubi.rootfsA/boot/zImage"
global.linux.bootargs.dyn.root="root=ubi0:rootfsA ubi.mtd=root rootfstype=ubifs rw"

In this configuration each partition holds its own Linux kernel image and Device Tree and the creation of the two UBIFS volumes system0/rootfsA and system1/rootfsB will be done by a factory process script.

Now we have to add bootchooser variables associated to both targets in arch/arm/<board>/env/nv directory we add following entries:

├── nv
│   ├── bootchooser.disable_on_zero_attempts
│   ├── bootchooser.reset_attempts
│   ├── bootchooser.reset_priorities
│   ├── bootchooser.retry
│   ├── bootchooser.state_prefix
│   ├── bootchooser.system0.boot
│   ├── bootchooser.system0.default_attempts
│   ├── bootchooser.system0.default_priority
│   ├── bootchooser.system1.boot
│   ├── bootchooser.system1.default_attempts
│   ├── bootchooser.system1.default_priority
│   ├── bootchooser.targets
│   ├── boot.default

The actual boot selection will be done by the bootchooser so we must set boot.default=bootchooser and targets="system0 system1".
We set a higher boot priority for system0 with variable system0.default_priority=21 over system1 with system1.default_priority=20.
We want the board to always boot on a target which means setting disable_on_zero_attempts=0 and finally we must set variable state_prefix="state.bootstate" so that the bootchooser can use our state variables stored in eeprom.

At this point we have completed the Barebox preparation and are ready to test it with RAUC.

From the Linux kernel configuration point of view we only need to add support for the SquashFS filesystem by enabling the CONFIG_SQUASHFS=y option.

Note that during our test we faced an issue with the kernel nvmem subsystem and the EEPROM. It seems that EEPROM partitions are not handled by the nvmem kernel driver because of the current binding that impose a reg property for each subnode to define a range address. The issue produces the following kernel trace at boot:

[    2.065648] nvmem 2-00500: nvmem: invalid reg on /soc/aips-bus@2100000/i2c@21a8000/eeprom@50/partitions

Due to this, from userspace, the barebox-state tool can not read the eeprom:

      root@miraq6-3d:~# barebox-state
      Cannot find backend path in /imx6qdl_phytec_boot_state

Thanks to Ahmad Fatoum we’ve been able to fix this behavior with the patch series he submitted to the Linux kernel at https://lkml.org/lkml/2020/4/28/411.

Yocto integration

Adding RAUC support to our Yocto BSP is quite straightforward, we just add the meta-rauc layer:

$ git clone git://github.com/rauc/meta-rauc
$ bitbake-layers add-layer meta-rauc

Now we create a bundle image recipe recipes-core/bundles/bootlin-bundle.bb for our RAUC updates. The bundle images are the ones that RAUC can deploy on the target for system upgrades.

inherit bundle

RAUC_BUNDLE_COMPATIBLE = "bootlin"
RAUC_BUNDLE_SLOTS = "rootfs"
RAUC_SLOT_rootfs = "bootlin-image"

RAUC_KEY_FILE = "${YOCTOROOT}/keys/bootlindev.key.pem"
RAUC_CERT_FILE = "${YOCTOROOT}/keys/bootlindev.cert.pem"

The bundle image is a SquashFS filesystem composed with the root filesystem image, a manifest and the signature. At this time, RAUC supports two types of bundle formats: plain and a new format called verity that allows authentication of the installed filesystem.

Here the RAUC_SLOT_rootfs must correspond to an existing image in your BSP layer. RAUC allow signing and verifying bundle image with OpenSSL certificates provided by RAUC_CERT_FILE.

Next step, we add a configuration file in recipes-core/rauc/files/system.conf that will define the RAUC configuration on the target:

[system]
compatible=bootlin
bootloader=barebox

[keyring]
path=bootlindev.cert.pem

[slot.rootfs.0]
device=/dev/ubi0_0
type=ubifs
bootname=system0

[slot.rootfs.1]
device=/dev/ubi0_1
type=ubifs
bootname=system1

The bootname for each slot must correspond to those defined in the state Device Tree and bootchooser targets.

After that we append to the rauc recipe using a bbappend such as recipes-core/rauc/rauc_%.bbappend to install our own RAUC configuration:

FILESEXTRAPATHS_prepend := "${THISDIR}/files:"

SRC_URI += "file://system.conf"

We also have to add the rauc client package to our target image with IMAGE_INSTALL += "rauc" and we’re now ready to create our bundle image and test it:

$ bitbake bootlin-bundle

On the target we can start by checking our bundle image:

root@miraq6-3d:~# rauc info bootlin-bundle-miraq6-3d.raucb
rauc-Message: 11:27:53.354: Reading bundle: bootlin-bundle-miraq6-3d.raucb
rauc-Message: 11:27:53.544: Verifying bundle...
Compatible:     'bootlin'
Version:        '1.0'
Description:    'bootlin-bundle version 1.0-r0'
Build:          '20200724101202'
Hooks:          ''
1 Image:
(1)     bootlin-image-miraq6-3d.tar.gz
        Slotclass: rootfs
        Checksum:  4d3be002d7d5f4c8d4cf9d8ca7190a2b09b43ff7300943f8e9cdbcbc43c59508
        Size:      111438905
        Hooks:
0 Files

Certificate Chain:
 0 Subject: /O=rauc Inc./CN=rauc-demo
   Issuer: /O=rauc Inc./CN=rauc-demo
   SPKI sha256: 18:77:3D:06:CE:63:59:AC:CC:41:87:A4:CD:14:E3:52:DA:AB:4D:BE:F5:3B:6C:06:2F:D2:0B:E2:C8:8F:08:0E
   Not Before: Jul 21 07:32:37 2020 GMT
   Not After:  Aug 20 07:32:37 2020 GMT

Note that the target time must be set to allow the signature verification.

If the bundle image is correct we can launch the RAUC update process:

root@miraq6-3d:~# rauc install bootlin-bundle-miraq6-3d.raucb -d
rauc-Message: 11:06:01.505: Debug log domains: 'rauc'
(rauc:464): rauc-DEBUG: 11:06:01.522: install started
(rauc:464): rauc-DEBUG: 11:06:01.522: input bundle: bootlin-bundle-miraq6-3d.raucb
(rauc:464): rauc-DEBUG: 11:06:01.606: Trying to contact rauc service
installing
0% Installing
0% Determining slot states
20% Determining slot states done.
20% Checking bundle
20% Verifying signature
40% Verifying signature done.
40% Checking bundle done.
40% Loading manifest file
60% Loading manifest file done.
60% Determining target install group
80% Determining target install group done.
80% Updating slots
80% Checking slot rootfs.1
90% Checking slot rootfs.1 done.
90% Copying image to rootfs.1
...
100% Copying image to rootfs.1 done.

After reboot we finally check the RAUC boot status:

root@miraq6-3d:~# rauc status
=== System Info ===
Compatible:  bootlin
Variant:
Booted from: rootfs.1 (system1)

=== Bootloader ===
Activated: rootfs.0 (system0)

Note that in this example we only use the RAUC CLI client but you can also use the D-Bus API or the rauc-hawkbit client interfacing with hawkBit
webserver.

Conclusion

This RAUC integration allows us to achieve our system upgrade framework tour and we can say that this project compares very well with other system upgrade frameworks.
Indeed the fact that Barebox and RAUC are both developed by Pengutronix helps a lot for the integration but it is also well supported in U-Boot and doesn’t need much more than a boot script to integrate RAUC on it at least for a simple A/B upgrade strategy.

Initial Allwinner V3 ISP support in mainline Linux

Introduction

Several months ago, Bootlin announced ongoing work on MIPI CSI-2 support for the Allwinner A31/V3 and A83T platforms in mainline Linux, as well as support for the Omnivision OV8865 and OV5648 image sensors. This effort has been a success and while the sensor patches were already integrated in mainline Linux since, the MIPI CSI-2 controller patches are on their way towards inclusion.

Since then we had the opportunity to take things further and start tackling the next steps for advanced camera support in mainline Linux on Allwinner SoCs!

With MIPI CSI-2 support and proper sensor drivers available in V4L2, we were able to capture raw bayer data provided by the sensors. But this data does not constitute the final picture that can be displayed or encoded into a file: a number of enhancement and transformation steps are required to achieve a visually-pleasing result that users typically expect.

These steps are quite calculation-intensive and it does not make sense to implement them with a software pipeline, especially with rates of 25, 30 or even 60 frames per second that are typically expected for video recording.

An open-source and upstream driver for the Allwinner ISP

Allwinner SoCs that support MIPI CSI-2 also include an Image Signal Processor hardware unit, a dedicated accelerator for enhancing and transforming raw data received from sensors.

Allwinner ISP features
Features of the ISP as described in the Allwinner V3 datasheet.

Since support for this ISP was implemented using a non-free blob in Allwinner SDKs, this area remained unsupported in mainline Linux… Until now!

Thanks to some help from Allwinner, we were able to implement a proper V4L2 driver for the Allwinner ISP found in the Allwinner V3, completely open-source, with no binary blob involved. This work was recently submitted upstream, with a first revision totaling more than 8000 new lines of code, which comes together with a significant rework of the Allwinner camera interface driver to make it usable with or without the ISP, and including the new MIPI CSI-2 support which we had submitted previously. We are very happy to keep contributing to advancing fully open-source Allwinner SoCs support in mainline Linux and help tackle some of the remaining areas there!

Our currently proposed driver for the Allwinner ISP only supports a limited set of features: debayering with coefficients and 2D noise filtering. These features were sufficient for our use case, and allowed to offload the computationally intensive debayering process to a dedicated hardware accelerator.

As the driver for now relies on a specific user-space API that does not yet cover all aspects of the ISP, the driver was submitted to the Linux kernel staging area and will probably stay there until all ISP features are properly described.

Our work on this advanced camera support, including the ISP driver, has been described in the talk we have given earlier this week at the Embedded Linux Conference, for which the slides are already available.

Advanced camera support on Allwinner SoCs with Mainline Linux

Additional features and future work

The Allwinner ISP supports a lot more features beyond just debayering and noise filtering. For example, it supports statistics to implement 3A algorithms (auto-focus, auto-exposition and auto-white-balance) which are necessary to avoid manual configuration of scene-specific parameters. These could typically be implemented in libcamera, the community free software project that supports complex image capture pipelines and ISPs.

As a result Bootlin would be very interested to continue this work and bring this driver to a more advanced state. So if you have a project that could help move this topic forward, do not hesitate to contact us about it!

Online Embedded Linux system development course in new time zones

Since April 2020, we are offering our training courses online, both in public sessions available to individual registration and in dedicated sessions for specific customers.

So far, our public sessions have always been organized from 2 PM to 6 PM Paris time, which was a good fit for our customers in Europe and in the US East Coast, but not so much for our customers in the US West Coast, in the Middle East and Asia.

Therefore, we are happy to announce that we have opened two sessions of our Embedded Linux system development course at different times, to suit the needs of customers in different parts of the world:

  • An Embedded Linux system development course will start on November 22, spread over 7 sessions of 4 hours organized from 09:00 to 13:00 Paris time (UTC+1), which is 13:30 to 17:30 in India, and 16:00 to 20:00 in China. This time is best for our customers in Europe, Middle East and Asia. Registration is possible directly online or by contacting us to get a quotation. The trainer for this course will be Grégory Clement.
  • An Embedded Linux system development course will start on November 29, spread over 7 sessions of 4 hours organized from 18:00 to 22:00 Paris time (UTC+1), which is 09:00 to 13:00 in the US West Coast, and 12:00 to 16:00 in the US East Coast. Registration is possible directly online or by contacting us to get a quotation. The trainer for this course will be Michael Opdenacker.

In both cases, the course is offered at 829 EUR per participant in the early bird rate (valid for registrations at least one month prior to the course starting date), or otherwise at 929 EUR.

Of course, like for all our training courses, the training materials are fully open, so that you can verify that the course suits your needs. See Embedded Linux system development training page for the complete agenda, slides and lab instructions.

If there is sufficient interest in these new time zones, we will consider offering our other courses at similar times in the future.

Bootlin contributions to Linux 5.13

After finally publishing about our Linux 5.12 contributions and even though Linux 5.14 was just released yesterday, it’s hopefully still time to talk about our contributions to Linux 5.13. Check out the LWN articles about the merge window to get the bigger picture about this release: part 1 and part 2.

In terms of Bootlin contributions, this was a much more quiet release than Linux 5.12, with just 28 contributions. The main highlights are:

  • The usual round of RTC subsystem updates from its maintainer Alexandre Belloni
  • A large amount of improvements in the MTD subsystem by its co-maintainer Miquèl Raynal, continuing his effort to improve the ECC handling in the MTD subsystem. See Miquèl’s talk at ELCE 2020 for more details on this effort: slides and video.
  • A small fix for an annoying regression in the musb USB gadget controller driver.

Even though we contributed just 28 commits to this release, as maintainers, some of us also reviewed and merged code from other contributors: Miquèl Raynal as the MTD co-maintainer merged 63 patches, Alexandre Belloni merged 22 patches, and Grégory Clement 6 patches.

Here are the details of our contributions to Linux 5.13:

Mainline Linux support for the ARM Primecell PL35X NAND controller

It has been more than 7 years since the first draft of a Linux kernel driver for the ARM Primecell PL35X NAND controller was posted on a public mailing list. Maybe because of the lack of time, each new version was delayed so much that it actually needed another iteration just to catch up with the latest internal API changes in the MTD subsystem (quite a number of them happened in the last 2-3 years). The NAND controller itself is part of an ARM Primecell Static Memory bus Controller (SMC) which increased the overall complexity. Finally, the way the commands and data are shared with the memory controller is very specific to the SMC. All these technical points probably played against Xilinx engineers, and Bootlin was contracted in 2021 to finalize the work of getting the ARM Primecell PL35X NAND controller driver in the upstream Linux kernel.

Static Memory Controller principles

SMC diagram from the TRM

The SMC can interface with two different memory types: NAND or SRAM/NOR. As it features two memory slots, this means that it can drive two memories, but they must be of the same type. When handling NAND devices, a hardware ECC engine is available to perform on-the-fly correction.

As only a single type of memory device can be plugged in at a time (either two SRAM/NORs or two NANDs), we don’t need to share a lot of controls with the SRAM/NOR controllers. So in the end the memory bus driver is almost an empty envelope that relies on the child controller driver to do the job.

Interactions with a memory device

On the CPU side, the controller has two main interfaces: APB and AXI.

The APB interface works like any regular interface: the CPU sees registers that it can access with diverse read and write operations, which will effectively read and write the content of the 32-bit registers located at the desired addresses. This is how the driver configures the device type, the timings, the possible ECC configuration and so forth. All the initial SMC configuration is done through the APB interface.

The AXI interface does not quite work like this. Instead of featuring a set of registers at a fixed address in which the content of the command, address and data cycles would be written in order to be forwarded to the memory device, the AXI interface needs to reserve a notable range in the addressable space. In particular, the offset targetted by the AXI write depend on the type of action that must be performed and the content of the action:

  • When requesting the controller to send command and address cycles to the memory device, the datasheet refers to it as the “command phase”.
  • When doing I/Os, eg. actually reading from/writing to the memory device, the datasheet calls this the “data phase”.

Both the command and data phase use regular AXI read/writes, but the offsets and values are different than usual.

Command phase

When the driver wants to send command cycles, it must perform one or two register writes. The address of the write operation in the AXI address space must target a specific offset. This offset indicates a number of information:

  • A specific bit is set to tell the SMC that it must enter a command phase.
  • Part of the offset are made of the shifted values of the different command opcodes for the memory device.
  • Part of the offset encodes the number of address cycles to perform on the NAND bus.

The payload of the AXI write contains the value of the address cycles that should be forwarded to the memory device. If there are more than 4 address cycles (which is quite common today), then a second AXI write containing the remaining address cycles as payload must happen at the same offset as before.

/*
 * Define the offset in the AXI address space where to write with:
 * - the bit indicating the command phase
 * - the number of address cycles
 * - the command opcode
 */
cmd_addr = PL35X_SMC_CMD_PHASE |
           PL35X_SMC_CMD_PHASE_NADDRS(naddr_cycles) |
           PL35X_SMC_CMD_PHASE_CMD0(NAND_CMD_XXXX);

/* Define the payload with the address bytes */
for (i = 0, row = 0; row < nrows; i++, row++) {
        [...]
        if (row < 4)
                addr1 |= PL35X_SMC_CMD_PHASE_ADDR(row, addr);
        else
                addr2 |= PL35X_SMC_CMD_PHASE_ADDR(row - 4, addr);
}

/* Send the command and address cycles */
writel(addr1, nfc->io_regs + cmd_addr);
if (naddr_cycles > 4)
        writel(addr2, nfc->io_regs + cmd_addr);

Data phase

The data phase is a bit easier to understand: several AXI reads or writes will be performed at a specific offset. The payload matches our expectations: it is actually the data that we want to read from or write to the device. However, the offset in the AXI address space is again a bit counter-intuitive:

  • It contains a specific bit such as the command phase to inform the controller that the data phase must be entered.
  • It also contains shifted values of different flags regarding the ECC configuration. The thing is, this offset will change at the end of the I/O operation because the last chunk of data must always be handled differently because of the ECC calculations that must be manually started. We end up reading or writing physically contiguous data by accessing two completely different offsets.
/* I/O transfers: simple case */
for (i = 0; i < buf_end; i++) {
        data_phase_addr = PL35X_SMC_DATA_PHASE;
        if (i + 1 == buf_end)
                data_phase_addr += PL35X_SMC_DATA_PHASE_ECC_LAST;

        writel(buf32[i], nfc->io_regs + data_phase_addr);
}

But what happens if a command cycle must be sent at the end of a data transfer (typical case of a PAGE_WRITE)? While it would certainly be more logical to perform an additional command phase AXI write, it was certainly more optimized to merge data and command phase on the last access. And here is how it looks like:

/* I/O transfers: less straightforward situation */
for (i = 0; i < buf_end; i++) {
        data_phase_addr = PL35X_SMC_DATA_PHASE;
        if (i + 1 == buf_end)
                data_phase_addr +=
                    PL35X_SMC_DATA_PHASE_ECC_LAST |
                    PL35X_SMC_CMD_PHASE_CMD1(NAND_CMD_PAGEPROG) |
                    PL35X_SMC_CMD_PHASE_CMD1_VALID);

        writel(buf32[i], nfc->io_regs + data_phase_addr);
}

Of course, nothing highly unreadable, but at the very least, these accesses are quite uncommon.

A memory bus driver and a NAND controller driver

As explained earlier, this SMC controller can support different types of memories, and this has called for a Device Tree representation where the SMC controller is one node, and the memories connected to it are represented as sub-node. So, the Device Tree representation of the SMC controller, used with its NAND controller looks like this:

    smcc: memory-controller@e000e000 {
      compatible = "arm,pl353-smc-r2p1", "arm,primecell";
      reg = <0xe000e000 0x0001000>;
      clock-names = "memclk", "apb_pclk";
      clocks = <&clkc 11>, <&clkc 44>;
      ranges = <0x0 0x0 0xe1000000 0x1000000 /* Nand CS region */
                0x1 0x0 0xe2000000 0x2000000 /* SRAM/NOR CS0 region */
                0x2 0x0 0xe4000000 0x2000000>; /* SRAM/NOR CS1 region */
      #address-cells = <2>;
      #size-cells = <1>;

      nfc0: nand-controller@0,0 {
        compatible = "arm,pl353-nand-r2p1";
        reg = <0 0 0x1000000>;
        #address-cells = <1>;
        #size-cells = <0>;
      };
    };

So, we first have a node for the SMC controller itself, memory-controller@e000e000, which will allow probing the memory bus driver located at drivers/memory/pl353-smc.c. This driver is very simple: it enables the clocks necessary for the SMC to work, and then it probes the first child device that matches either the cfi-flash or arm,pl353-nand-r2p1 compatible strings. In the latter case (which is illustrated in our example), the NAND controller driver at drivers/mtd/nand/raw/pl35x-nand-controller.c will be probed, and where the two memory areas (accessed through APB and AXI) will be mapped, and accessed to program the NAND controller.

Now in the mainline Linux kernel

Starting from the latest version posted by Xilinx, Miquèl Raynal, Bootlin’s NAND/MTD expert, performed a massive cleanup of the memory bus driver and the NAND controller driver, rewrote entirely the binding file (in YAML schema!) and three versions later, with the support of Xilinx engineers and the acknowledgements of Rob Herring and Krzysztof Kozlowski, managed to finally close the story. The driver is now part of Linux 5.14-rc, and will therefore be in the final Linux 5.14 release in a few weeks!

Embedded Linux Conference 2021 schedule published, 4 talks from Bootlin

The schedule for the Embedded Linux Conference 2021 has been published and features 4 talks proposed by Bootlin !

This year, the ELC will take place in Seatle but will be organised as a hybrid virtual/physical event  due to the pandemic.  As usual the ELC will have a really interesting schedule with 46 talks covering a wide range of topics: build system, kernel graphics, boot process, security, etc.

See below the details of Bootlin talks that will be presented as virtual talks.

Advanced Camera Support on Allwinner SoCs with Mainline Linux – Paul Kocialkowski, Bootlin

Capturing pixels with a camera involves a number of steps, from the ADC reading the photosites in the image sensor to the final pixel values that are ready for encode/display, with various processing and transmission taking place along the way. While simple cases put most of the heavy lifting on the image sensor’s side (through its embedded processor) and use a simple parallel bus for transmission, advanced cases require more work to be done outside of the sensor. In addition, modern high-speed transmission buses also bring-in more complexity. This talk will present how support for such an advanced use case was integrated into the mainline Linux kernel, using the Media and V4L2 APIs. It involves supporting a sensor using the raw Bayer RGB format, transmission over the MIPI CSI-2 bus as well as support for the Image Signal Processor (ISP) found on Allwinner platforms. A specific focus will be set on this ISP, with details about the features it implements as well as the internal and userspace APIs that are used to support it. The integration between all of the involved components will also be highlighted.

Talk given by Paul Kocialkowski, at 4:50 PM PDT on September 27, 2021. See this talk in the schedule.

Embedded Linux Nuggets found in Buildroot Package Eldorado

To this date, Buildroot supports more than 2,500 packages, selected for the ability to run them on embedded Linux systems. We’ve gone exploring this Eldorado, and came back with multiple nuggets of all shapes and colors. Join this playful presentation and as if you were still a new comer to the embedded Linux community, discover lesser known tools and resources that can add to the functionality of your systems or make your life as a developer easier and more fun. Whenever possible, each resource will be shown through a quick demonstration or video capture. During this talk, I’ll also open an Etherpad for all participants to share their favorite solutions with the rest of the audience, especially the ones that deserve to be better known, and could be worth supporting in Buildroot too. We will close the session by an open review and discussion based on the nuggets shared by the audience.

Michael Opdenacker

Talk given by Michael Opdenacker, at 12:00 PM PDT on September 28, 2021. See this talk in the schedule.

I3C in Tomorrow’s Design

I3C is the new bus specification by the MIPI Alliance. While being compatible with I2C devices, this bus brings a colorful set of new features such as dynamic address assignment, in-band interrupts, hot-join, master handover and many others. It was improved once again recently with the 1.1 version of the specification which brought timer based sampling synchronization and targeted reset. All this make the I3C bus a good candidate for a number of new situations compared to its I2C cousin. It is then more and more being included in new hardware designs. With this talk we would like to propose a reminder of the various components and concepts of this relatively new bus. We will then detail how it is implemented in the Linux kernel with a short guided tour in the I3C core. Since the previous talk on I3C in 2018 by Boris Brezillon, I3C has now become a reality and starts to become available in real hardware designs. This talk will recap the basics of I3C as well as add details of the 1.1 specification and improvements in the Linux support.

Miquèl Raynal

Talk given by Miquèl Raynal, at 4:00 PM PDT on September 28, 2021. See this talk in the schedule.

OP-TEE: When Linux Loses Control

OP-TEE is an open-source Trusted Execution Environment designed to be executed in a secure context as a companion to a non secure Linux system. But what happens to the peripherals control since OP-TEE can forbid the non-secure OS to access them ? When running with a TEE, Linux isn’t in charge anymore of some critical peripherals and relies on the TEE to access and configure them. There are multiple protocols and methods to access these peripherals that are supported by Linux (SCMI, PSCI, SMC). Supporting them for a SoC requires understanding the various interactions between the systems and how to modify them to fit that new control scheme. Additionally, the configuration must be passed from OP-TEE to Linux to allow a seamless integration. This talk will cover the boot process to start a secure system and the modifications needed to run Linux when OP-TEE is in charge of some peripherals. The work that has been done for a specific SoC will be described to have a tangible real-world use-case.

Clément Léger

Talk given by Clément Léger, at 12:00 PM PDT on September 29, 2021. See this talk in the schedule.

GPIO Aggregator, a virtual gpio chip

GPIOs are obviously widely used in embedded systems, and many of them are typically driven directly by Linux kernel drivers for interrupt lines, reset lines, or other control lines used to connect with various peripherals. However, a number of GPIOs are sometimes directly driven by user-space applications. Historically, the Linux kernel has provided a sysfs interface, in /sys/class/gpio to allow such direct control. But in recent years, this sysfs interface has been superseded by a new user-space interface based on /dev/gpiochip* character devices.

This new interface has numerous advantages over the previous /sys/class/gpio interface. However, one drawback is that it creates one device file per GPIO chip, which means that access rights are defined per GPIO chip, and not per GPIOs.

For this reason, in Linux 5.8, Geert Uytterhoeven has contributed the GPIO aggregator mechanism. It allows to group a number of GPIOs into a virtual GPIO chip, visible as an additional /dev/gpiochip*. Its documentation can be found in Documentation/admin-guide/gpio/gpio-aggregator.rst.

The list of GPIOs part of this new virtual GPIO chip is defined in the Device Tree. One other interesting thing is that, as any GPIO controler, the lines can be named, and then queried by user-space applications based on their name, using the libgpiod library.

Configuration and Device Tree description

To have GPIO Aggregator support in your kernel, simply configure

CONFIG_GPIO_AGGREGATOR=y

Add a gpio-aggregator node in your Device Tree source. For instance, the following DTS snippet declares an aggregator with several GPIO lines:

gpio-aggregator {
    pinctrl-names = "default";
    pinctrl-0 = <&gpio_pins>;
    compatible = "gpio-aggregator";

    gpios = <&gpio3 4 GPIO_ACTIVE_HIGH>,
            <&gpio2 4 GPIO_ACTIVE_HIGH>,
            <&gpio1 28 GPIO_ACTIVE_HIGH>,
            <&gpio1 29 GPIO_ACTIVE_HIGH>,
            <&gpio2 0 GPIO_ACTIVE_HIGH>,
            <&gpio2 1 GPIO_ACTIVE_HIGH>,
            <&gpio3 8 GPIO_ACTIVE_HIGH>;

    gpio-line-names = "line_a", "line_b", "line_c", "line_d",
            "line_e", "line_f", "line_g";
};

In this example, line 4 of gpio controller gpio3 is used and is named “line_a”, line 4 of gpio controller gpio2 is used and is named “line_b”, and so on up to line 8 of gpio controler gpio3.

Usage from user-space

From userspace we can see the GPIO chip and its aggregated lines:

# gpioinfo
...
gpiochip6 - 7 lines:
    line 0: "line_a" unused input active-high
    line 1: "line_b" unused input active-high
    line 2: "line_c" unused input active-high
    line 3: "line_d" unused input active-high
    line 4: "line_e" unused input active-high
    line 5: "line_f" unused input active-high
    line 6: "line_g" unused input active-high
...

We can search a gpio chip and a line number by the line name:

# gpiofind 'line_b'
gpiochip6 1

We can access a GPIO line by its name

# gpioget $(gpiofind 'line_b')
1
#
# gpioset $(gpiofind 'line_e')=1
# gpioset $(gpiofind 'line_e')=0

We can change the GPIO chip device file ownership to allow user or group to access the attached lines:

# ls -al /dev/gpiochip*
crw------- 1 root root 254, 0 Jan 1 00:00 /dev/gpiochip0
crw------- 1 root root 254, 1 Jan 1 00:00 /dev/gpiochip1
crw------- 1 root root 254, 2 Jan 1 00:00 /dev/gpiochip2
crw------- 1 root root 254, 3 Jan 1 00:00 /dev/gpiochip3
crw------- 1 root root 254, 4 Jan 1 00:00 /dev/gpiochip4
crw------- 1 root root 254, 5 Jan 1 00:00 /dev/gpiochip5
crw------- 1 root root 254, 6 Jan 1 00:00 /dev/gpiochip6
#
# chown root:users /dev/gpiochip6
# chmod 660 /dev/gpiochip6
# ls -al /dev/gpiochip*
crw------- 1 root root 254, 0 Jan 1 00:00 /dev/gpiochip0
crw------- 1 root root 254, 1 Jan 1 00:00 /dev/gpiochip1
crw------- 1 root root 254, 2 Jan 1 00:00 /dev/gpiochip2
crw------- 1 root root 254, 3 Jan 1 00:00 /dev/gpiochip3
crw------- 1 root root 254, 4 Jan 1 00:00 /dev/gpiochip4
crw------- 1 root root 254, 5 Jan 1 00:00 /dev/gpiochip5
crw-rw---- 1 root users 254, 6 Jan 1 00:00 /dev/gpiochip6
#

The GPIO chip created by the aggregator can be retrieved from sysfs:

# ls -1 /sys/bus/platform/devices/gpio-aggregator/
driver
driver_override
gpio
gpiochip6
modalias
of_node
power
subsystem
uevent
# 
# cat /sys/bus/platform/devices/gpio-aggregator/gpiochip6/dev
254:6
#

Conclusion

A GPIO Aggregator can be used to group a subset of GPIO lines, name them, access them by their names and manage access control to the virtual gpio chip created by the aggregator. On an embedded system, this can simplify the management and usage of individual GPIO lines.

Bootlin contributions to Linux 5.12

Yes, Linux 5.13 was released yesterday, but we never published the blog post detailing our contributions to Linux 5.12, so let’s do this now! First of all the usual links to the excellent LWN.net articles on the 5.12 merge window: part 1 and part 2.

LWN.net also published an article with Linux 5.12 development statistics, and two Bootlin engineers made their way to the statistics: Alexandre Belloni in the list of top contributors by number of changesets, with 69 commits, and Paul Kocialkowski in the list of top contributors by number of changed lines, with over 6000 lines changed.

Here are the highlights of our contributions:

  • Addition of a new driver for the Silvaco I3C master controller. This was contributed by Miquèl Raynal, who became the maintainer for this driver. Bootlin has pioneered support for I3C in Linux, by introducing the complete drivers/i3c subsystem a few years ago, together with the first controller driver, for a Cadence IP, see our blog post from 2018.
  • Addition of two new camera sensor drivers, one for the Omnivision OV5648 and another for the Omnivision OV8865. These were contributed by Paul Kocialkowski.
  • Implementation of mqprio support in the Marvell Ethernet controller driver mvneta, see this commit. As explained in the tc-mqprio man page, the MQPRIO qdisc is a simple queuing discipline that allows mapping traffic flows to hardware queue ranges using priorities and a configurable priority to traffic class mapping. This was contributed by Maxime Chevallier
  • Improvements in the IIO driver for the ms58xx family of sensors, contributed by Alexandre Belloni.
  • The final removal of the atmel_tclib code, which has been replaced by proper drivers for the TCB timers on Atmel/Microchip ARM platforms over the past few releases, also by Alexandre Belloni.
  • As usual, a large amount of fixes and improvements in the RTC subsystem, by its maintainer Alexandre Belloni.

Here is the detailed list of our contributions to this release:

Slides and videos of Bootlin talks at Live Embedded Event #2

The second edition of Live Embedded Event took place on June 3rd, exactly 6 months after the first edition. Even though there were a few issues with the online platform, it was once again great to learn new things about embedded, and share some of the work we’ve been doing at Bootlin on various topics. For the next edition, we plan to switch to a different online platform, hopefully providing a better experience.

But in the mean time, all videos of the event have been posted on the Youtube Channel of the event. The talks from Bootlin have been posted on Bootlin’s Youtube Channel.

Indeed, in addition to being part of the organization committee, Bootlin prepared and delivered 5 talks as part of Live Embedded Event, covering different topics we have worked on in the recent months for our customers.

Understanding U-Boot Falcon Mode and adding support for new boards, Michael Opdenacker

Slides [PDF]

Introduction to RAUC, Kamel Bouhara

Slides [PDF]

Security vulnerability tracking tools in Buildroot, Thomas Petazzoni

Slides [PDF]

Secure boot in embedded Linux systems, Thomas Perrot

Slides [PDF]

Device Tree overlays and U-boot extension board management, Köry Maincent

Slides [PDF]