Bootlin eligible to French “Crédit Impôt Recherche” tax incentive

Bootlin CIR agreementA number of years ago, the French tax system has created a tax incentive mechanism called Crédit Impôt Recherche (Research Tax Credit) that allows startups and innovative companies to get tax deductions corresponding to a fraction of their research and development costs. This allows French companies to more easily invest in research and development activities.

In 2021, Bootlin has initiated the process to be eligible to this tax incentive mechanism, and we are happy to announce that after studying Bootlin’s expertise, engineering experience and achievements, the French tax administration has confirmed that Bootlin can deliver research and development activities fulfilling the Crédit Impôt Recherche criteria to its customers. This means that Bootlin customers in France can now integrate the cost of Bootlin engineering services that correspond to research and development activities into their Crédit Impôt Recherche and receive a tax incentive corresponding to up to 30% of the cost of our engineering services.

For our customers outside of France, this tax incentive is obviously not available, but the certification of Bootlin by the French tax administration as a company able to deliver research and development activities is another testimonial of our strong technical expertise in our field of Embedded Linux and Linux kernel development.

Bootlin contributions to Linux 5.14 and 5.15

It’s been a while we haven’t posted about Bootlin contributions to the Linux kernel, and in fact missed both the Linux 5.14 and Linux 5.15 releases, which we will cover in this blog post.

Linux 5.14 was released on August 29, 2021. The usual KernelNewbies.org page and the LWN articles on the merge window (part 1 and part 2) provide the best summaries of the new features and hardware support offered by this release.

Linux 5.15 on the other hand was released on November 1, 2021. Here as well, we have a great KernelNewbies.org article and LWN articles on the merge window (part 1 and part 2).

In total for those two releases, Bootlin contributed 79 commits, in various areas:

  • Alexandre Belloni, as the RTC subsystem maintainer, contributed 9 patches improving various aspects of RTC drivers, the RTC subsystem or Device Tree bindings for RTC
  • Clément Léger contributed a small improvement to the at_xdmac driver used on Microchip ARM platforms
  • Hervé Codina enabled Ethernet support on the old ST Spear320 SoC, by leveraging the existing stmmac Ethernet controller driver
  • Maxime Chevallier fixed a small issue with the Ethernet PHY on the i.MX6 Solidrun system-on-module
  • Miquèl Raynal added support for NV-DDR timings in the MTD susbsystem. This allows to improve performance with NAND flash memories that support those timings. Their usage is specifically implemented in the Arasan NAND controller driver, which Miquèl contributed back in Linux 5.8. See our previous blog post on this topic for more details
  • Miquèl Raynal added support for yet another NAND controller driver, the ARM PL35x, which is used for example on Xilinx Zynq 7000. See our previous blog post on this topic.
  • Miquèl Raynal added support for NAND chips with large pages (larger than 4 KB) to the OMAP GPMC driver.
  • Miquèl Raynal made a few fixes to the IIO driver for the max1027 ADC.
  • Paul Kocialkowski contributed a few patches to enable usage of the Hantro video decoder driver on the Rockchip PX30 processor.
  • Thomas Perrot contributed one patch to enable usage of the Flex Timers on i.MX7, and one to fix an issue in the PL022 SPI controller driver.

And now, as usual the complete list of our contributions to Linux 5.14 and 5.15:

The backbone of a Linux Industrial I/O driver

As part of recent projects, we had to dig into the Linux kernel Industrial I/O (IIO) subsystem with the goals of supporting a new ADC and adding new features to an existing driver. These tasks involved quite a few discussions between our engineering team and the IIO maintainers and reviewers. The aim of this blog post is to summarize the substance of these explanations to help others understand how an IIO kernel driver works and interacts with the core IIO subsystem.

Disclaimer: The IIO core is huge and keeps evolving. The aim of this article is not to cover it entirely, but at least explain our knowledge of how to use its basic features for common situations.

What is IIO?

The Industrial I/O subsystem covers any type of device that is commonly called as a “sensor”: ADCs, IMUs, temperature sensors, accelerometers, pressure sensors, potentiometers, light sensors, proximity sensors, etc (as well as few actuators, which I will on purpose disregard in this blog post). All these devices, besides measuring truly different physical components of our three dimensional world, end-up sharing quite a few properties. Any of these sensors must first be configured in order to know what must be measured and possibly how. When adequate, the device must be triggered in order to start converting. When the requested samples are ready, there must be some kind of signaling involved in order for the user to retrieve and process the data.

When thinking about the generic interfaces which could be needed by all these devices, it is quite straightforward to list:

  • The configuration before sampling
  • The triggering mechanism
  • The signaling for an end of conversion situation
  • The reading of the samples
  • The advertisement of the data

Registering an IIO device

The IIO core manipulates struct iio_dev * objects which inherits from struct device. This object should be allocated by the device driver with devm_iio_device_alloc(), providing the size of the driver’s internal structure as second argument. The allocated area dedicated for this internal pointer can be then retrieved with iio_priv().

This iio_dev structure must then be filled with a number of information:

  • The name of the device
  • A set of struct iio_info operations, typically a hook to read one or multiple samples on demand, optionally be able to write to the device, etc.
  • A set of supported modes, such as INDIO_DIRECT_MODE, which is used when samples can be retrieved at any time by the user from sysfs.
  • A scan mask, namely available_scan_masks which defines what are the possible/impossible scan combinations when requesting a read. Typically, a device might be configured to scan all of its internal channels from 1 to N. This can be described with a list of GENMASK(X, 0), with X ranging from 0 to the maximum number of channels. When the user will request a given set of channels, the IIO core will go through all the available masks registered by the driver and pick the first one that contains the desired channels. The selected mask will be available to the driver through the active_scan_mask entry of the iio_dev structure. If ‘anything goes’ and the devices has no restriction regarding which channel(s) can be scanned, this field should be skipped.
  • A definition of all the possible channels, including the type of physical measurements the device is able to perform (IIO_VOLTAGE, IIO_CURRENT, IIO_TEMPERATURE, IIO_STEPS, IIO_ROT, etc), the channel index and the data format.

Here is an example of channel description and below the meaning of these fields.

struct iio_chan_spec chan1 = {
	.type = IIO_VOLTAGE,
	.indexed = 0,
	.channel = index,
	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
	.scan_index = 1,
	.scan_type = {
		.sign = 'u',
		.realbits = 10,
		.storagebits = 16,
		.shift = 2,
		.endianness = IIO_BE,
	},
}
  • .info_mask_separate indicates an entry in sysfs that will be present for all the channels. IIO_CHAN_INFO_RAW is the raw value of the sample.
  • .info_mask_shared_by_type indicates an entry in sysfs that will be sorted by the type. IIO_CHAN_INFO_SCALE means that there will be a common voltage scale sysfs entry shared by all the voltage raw entries. If the device was also able to read a temperature, we would also get a single file indicating the scale for all the temperature samples.
  • The .scan_type field in the example indicates that values are provided as 16-bit big-endian samples that must be shifted by two bits. The full scale range is 0-1023. This conversion only applies to the buffer reading path: raw values directly read from sysfs and returned by the ->read_raw() hook (see below) should be converted by the driver itself.

Once the device fully described (and initialized, of course), the driver must register it with devm_iio_device_register().

Scaling factors

The int (*read_raw)(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) callback will be executed when reading either of the ‘raw value’ or ‘scale’ files from sysfs.

The type of data that must be returned is provided in the mask parameter: IIO_CHAN_INFO_RAW to retrieve the raw measurement or IIO_CHAN_INFO_SCALE to retrieve the scaling parameters, based on the scale information available in the iio_chan_spec structure that describes the channel.

For the IIO_CHAN_INFO_RAW case, most drivers return an IIO_VAL_INT type which can be simply “returned” into the *val argument. It is however possible to return a fixed point number, in this case the logic explained right after applies.

For the IIO_CHAN_INFO_SCALE case, the return value indicates what type of scaling should be done. In most cases here a fixed point value will be used so *val and *val2 will carry the scaling parameters. Here are two examples:

  1. IIO_TEMP example:
    *val = 1;
    *val2 = 8;
    ret = IIO_VAL_FRACTIONAL;
    

    The full scale sample value should be multiplied by 1/8 in order to get Celcius degrees.

  2. IIO_VOLTAGE example:

    *val = 2500;
    *val2 = 10;
    ret = IIO_VAL_FRACTIONAL_LOG2;
    

    The full scale range is a 10-bit value mapped to a 0-2500mV input level, said otherwise the scaling factor should be 2500 / 1024. The core will automatically do the computation of this factor and return 2,44140625 to the user in order to get milli-Volts.

Sampling

There are two basic common cases here.

In simple situations, a “single” on-demand read was issued by user-space directly by reading /sys/bus/devices/iio:device/in_<type><index>_raw. In this case the ->read_raw() callback should handle basically all the steps necessary to get a measurement, as detailed in our introduction.

However, user-space can also pick a more advanced way of interacting with the measurement device, called triggers.

A trigger is a specific configuration of the device which will sample a number of channels upon a specific event. This event might be the user requesting it from userspace with a so called software trigger, it might also be an external hardware event, or a periodic signal, or an internal continuous read mode… There are many ways of triggering a sensor and they are all covered by the subsystem.

Many devices cannot handle both modes at the same time. The only situation where this might work smoothly is when a device provides a hardware FIFO where you can read from (or a ‘latest value’ register) while not disrupting the FIFO read back. Otherwise, it will be needed, in order to avoid collisions between these two modes, to verify that exclusive access to the device is granted with a call to iio_device_claim_direct_mode() when starting a direct mode operation. As this helper grabs a mutex, it should be only called from process context and always be balanced with a call to iio_device_release_direct_mode().

IIO interoperability model

In the IIO core these four concepts are used:

  • IIO device: the hardware part which produces samples
  • IIO trigger: the signaling capability to request a conversion start
  • IIO buffers: where to store the samples
  • IIO events: threshold detectors

Even though a single hardware device might have hardware support for all these features, they must be described and handled separately so that, when applicable, other IIO devices might use them as well, eg. IIO device 2 could start a conversion upon IIO device 1 trigger state change. In practice it is not always possible but the way the API is built should lead us to keep things well separated anyway.

Registering a trigger

A struct iio_trigger must be allocated by devm_iio_trigger_alloc(), giving the new trigger a name.

The trigger should then receive a set of operations (struct iio_trigger_ops) with at least ->set_trigger_state() implemented, in order to switch on and off the trigger. One can use iio_trigger_set_drvdata() in order to link private data with the trigger and get this pointer back from the trigger callbacks.

Once initialized, devm_iio_trigger_register() will register the IIO trigger. This trigger will appear as a dedicated IIO device in sysfs.

It is likely that an IRQ will need to be registered as part of the trigger initialization step: the driver must be notified somehow that the trigger was toggled. If the asynchronous signaling is tied to a “trigger change” condition, which is the easiest situation, then it is advised to provide iio_trigger_generic_data_rdy_poll() as hard IRQ handler. This helper will just call iio_trigger_poll() and return.

You may of course want to handle more than this but in any case the rule is clear, triggers, buffers and devices should be fully separated. Hence, do not directly handle any data from this handler: an IIO trigger is only supposed to indicate a hardware transition, no more.

The call to iio_trigger_poll() will effectively go through the IIO internal interrupt tree, find the device that is connected to the trigger which fired and call the relevant handler in order to request the waiting device to process the data (which may be identical or different than the triggering device).

In the case of the device being limited to, for instance, an End Of Conversion (EOC) interrupt, you should still consider this signal as being suitable for being registered as a trigger. Yes, this might imply an additional delay between the hardware toggling and the IRQ being fired which is not ideal, but from a software point of view, the split between driver code and core logic will let other IIO devices use this IRQ as a trigger with no additional change needed to your code.

Note: There is one exception here. When a device does not provide any visible per-scan interrupt and the software has only access to some kind of FIFO watermark events, the whole trigger + buffer representation is swapped with a pure buffer-only implementation.

Registering triggered buffers

If the device itself is able to provide fast samples, the driver should also register a buffer, with iio_triggered_buffer_setup(). Both a hard IRQ handler and a threaded IRQ handler can be registered, as well as additional callbacks called before and after enabling and disabling the buffers in order to eg. configure the requested channels based on the current ->active_scan_mask.

Upon a trigger condition, these are the handlers that might be chosen by the core if the trigger is connected to your device!

The hard IRQ handler might be used to eg. save timestamps. The threaded IRQ handler is dedicated to the data processing. Depending of the type of trigger (iio_trigger_using_own()) the driver must decide whether it should start a conversion manually or if the data is waiting somewhere in a hardware FIFO, ready to be retrieved.

The final step is to push the samples into the core’s buffers. This should not be done manually. Let’s say that the user requested channels 0, 1 and 3 while the selected scan mask was including all channels from 0 to 4. Just calling iio_push_to_buffers() is the solution: the core knows that it will receive five samples of 16 bits, it also knows that the user only requested three of them and will automatically pick the right ones.

With all these IIO objects registered, you should be able to properly interact with the core and the other drivers, providing trigger capabilities to third party devices, or benefiting from other’s triggers.

What if my design lacks trigger capabilities?

You can still use triggers by enabling IIO_CONFIGFS (enables the configuration interface) and IIO_SW_TRIGGER. Then, you can either choose to trigger your scans from userspace with a simple file write, thanks to CONFIG_IIO_SYSFS_TRIGGER, or leverage timers to get periodic scans with CONFIG_IIO_HRTIMER_TRIGGER.

As an example, here is how to create a sysfs trigger:

# echo 0 > /sys/bus/iio/devices/iio_sysfs_trigger/add_trigger
# cat /sys/bus/iio/devices/iio_sysfs_trigger/trigger0/name
sysfstrig0

And here is how to create a timer based software trigger:

# mkdir -p /config
# mount -t configfs none /config
# mkdir /config/iio/triggers/hrtimer/my_5ms_trigger
# cat /sys/bus/iio/devices/trigger0/name
my_5ms_trigger
# echo 200 > /sys/bus/iio/devices/trigger0/sampling_frequency

How to use triggers and buffers from userspace

Just for the reference, linking an IIO trigger to an IIO device is as simple as:

# cat /sys/bus/iio/devices/trigger0/name > /sys/bus/iio/devices/iio:device0/trigger/current_trigger

The next step is to configure the channels that should be scanned:

# echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_en
# echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage1_en
# echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage3_en

Starting the sampling process is managed with:

# echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable

In the case of a sysfs software trigger, it is the user’s responsibility to timely run:

# echo 1 > /sys/bus/iio/devices/trigger0/trigger_now

The samples are available to be read in /dev/iio\:device0.

The decoding process before the scaling operation must be performed by the userspace, following the content of:

# cat /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_type
be:u12/16>>2 

Which in this case would mean that each sample is 16 bits wide, values should be considered big-endian, shifted twice before being considered as an unsigned 12-bit value.

# od -t x1 /dev/iio\:device0
0000000 08 06

Should be interpreted as 0x806 >> 2 = 0x201, which should be then multipled by the scaling factor in order to get the final mV value.

Conclusion

While contributing to this (relatively new) subsystem, we discovered a number of interesting features and design choices which would really benefit from a much tougher in-kernel documentation as most of the available information explains how to use IIO (with libiio or configfs) more than how to write a decent and properly shaped IIO device driver. As the subsystem is still pretty recent, it is valid to look at existing drivers to make design choices, but that is not a magic solution as no device never fully matches any software API anyway, and sensors unfortunately do not escape from that sticky rule.

We want to warmly thank Jonathan Cameron, IIO founder and maintainer, for his precious feedback on the mailing list, as well as his valuable review and contribution to this blog post.

We hope this article will help you go through this API and if it does, please mind letting us know by dropping a comment in the section down below!

OP-TEE gains a clock framework contributed by Bootlin

Introduction

OP-TEE logoOP-TEE is a popular open-source reference implementation of a Trusted Execution Environment that relis on the Arm Trustzone technology. While working on the OP-TEE port for an ARM 32-bit system-on-chip, the Microchip SAMA5D2, we needed to add support for the complete clock tree of this SoC. OP-TEE did not have any generic clock support at all and we felt the need to add such a framework. Thanks to this framework, support the 10+ clocks of the Microchip SAMA5D2 was easily imported from Linux with less work than a complete rewrite of the clock tree. Using generic subsystems allows to lower the maintenance cost and easily add new clocks.

In this blog post, we will describe in more details this clock framework, and the contributions we are doing to the OP-TEE project.

Clock framework

The clock framework that we contributed to OP-TEE allows to register clocks and represent a full clock tree with parents. Device Tree support has been added to allows parsing the clocks and their relationships from Device Tree. It provides a consumer API that allows device drivers to query clocks from their Device Tree node, enable or disable them, and get or set the needed clock rates.

assigned-clock-parents and assigned-clock-rates Device Tree properties are also supported and will apply the clock parents and rates described in these properties. A fixed-clock driver matching the "fixed-clock" compatible string has also been added since this one is often present in SoC Device Trees.

Peripheral drivers in OP-TEE can now use the functions provided by the clock framework to get clocks from the Device Tree using clk_dt_get_by_name() and then enable/disable them at will with clk_enable() and clk_disable() . Rates can also be set and retrieved using clk_set_rate() and clk_get_rate().

The pull request was made on OP-TEE github and contained the following commits, which have now been merged in the official upstream OP-TEE project:

Future work

With this clock framework in place, we are soon going to contribute support for the Microchip SAMA5D2, which will make use of the new clock framework. Some other platforms will also gain cleaner clock support thanks to this framework: for example, the existing STM32MP1 clock support is expected to be migrated to this clock framework.

In addition, based on this clock framework, SCMI (System Control and Management Interface) clock support has also been added. While OP-TEE already has support for exposing SCMI clocks to clients, the actual callbacks have to be implemented by platform-specific code. This additional support will allow exposing clocks registered within the clock framework to a SCMI client without any custom platform code. A Device Tree description will allow matching SCMI clock identifiers with clocks provided by clock drivers.

We have already submitted a pull request for this support, which is currently under review: Provide plat_scmi_clock_* using clock framework.

New training course: Real-Time Linux with PREEMPT_RT

In the field of embedded systems, a number of applications need real-time guarantees, and the Linux ecosystem has been offering for a long time a number of solutions to address those needs, either by improving the Linux kernel itself using the PREEMPT_RT approach, or by using a co-kernel approach such as the one offered by Xenomai. Bootlin training’s portfolio already has an initial coverage of these topics in our Embedded Linux system development course.

Today, we are happy to announce a brand new Real-Time Linux with PREEMPT_RT, which is specifically focused on the PREEMPT_RT solution. This solution made a vast amount of progress in recent times in terms of integration into the official Linux kernel, which makes it even more relevant for a number of projects which need real-time guarantees.

The main topics covered by the course are:

  • What is a real-time and deterministic operating system
  • How to configure, build and setup a PREEMPT_RT enabled Linux kernel
  • How to identify and benchmark the hardware platform in terms of real-time characteristics
  • How to configure and tune the Linux kernel and the system for deterministic behavior
  • How to develop and debug real-time user-space Linux applications as well as analyze latencies

The course is illustrated by practical labs or demonstrations made on the BeagleBone Black platform. It has been developed and is taught by Bootlin engineer Maxime Chevallier, who is an experienced embedded Linux and Linux kernel engineer and trainer.

As usual our training materials are fully open-source, including the ones for this brand new session. You can read the Slides and Practical lab instructions. Bootlin is one of the very few companies delivering training courses to make its training materials open-source: by choosing to work with Bootlin for your trainings, you support our work on developing and publishing freely available training materials.

If you’re interested in getting this new Real-Time Linux with PREEMPT_RT training course, you have three options:

  • Public on-line sessions, opened to individual registration. The course lasts 3 sessions of 4 hours. We have scheduled a first session on January 19-21, 2022, and registration is open, for 399 EUR at the Early bird rate, of 449 EUR at the Regular rate.
  • Dedicated on-line sessions, which we organize at the date/time of your choice. The course also lasts 3 sessions of 4 hours. Contact us to request a quote.
  • Dedicated on-site sessions, where our trainer travels to your location to deliver the training course. In this case, the course lasts two full days. Contact us to request a quote.

Maintaining Yocto Project Documentation

For many years, Bootlin has been a strong user and a contributor to the Yocto Project, delivering numerous customized embedded Linux distributions and Board Support Packages based on Yocto Project and OpenEmbedded to its customers, for a wide range of hardware platforms and architectures.

In 2021, we have been able to bring this engagement further, as Bootlin engineer Michael Opdenacker has been given the opportunity to work as a maintainer for Yocto Project’s documentation, thanks to funding from the Linux Foundation. Since the mourning of Scott Rifenbark, the former maintainer, in early 2020, the project was in need for someone to fill this role.

Yocto Project developers and contributors did their best in the meantime though, in particular by migrating the documentation sources from the DocBook format to reStructuredText, to generate documentation using Sphinx. This migration was also done by the Linux kernel community, as reStructuredText seems to make it easier for developers to contribute to documentation.

For Michael Opdenacker, the interest of getting back to Yocto Project and OpenEmbedded was strong: he was one of the early users, over 15 years ago, of BitBake and OpenEmbedded. Many things have changed since Michael was generating his own OPIE and GPE Palmtop Environment images for handheld devices back in 2004 and 2005.

Indeed, since that time, under the Yocto Project umbrella, BitBake and OpenEmbedded have formed what’s probably the smartest, most versatile and most powerful embedded Linux build system. This build system takes a pretty steep learning curve though, and that’s why maintaining high quality documentation was a key focus from the start.

In addition to acting as a maintainer (reviewing and merging patches from contributors, encouraging contributions, managing documentation bugs, keeping the documentation current with the evolution of the code), we have so far also managed to:

  • Create brand new documentation, thanks to studying the source code and to knowledge from core developers. In particular, we documented vulnerability (CVE) management and drafted a first description of Hash Equivalence.
  • Implement a new infrastructure to generate diagrams from SVG sources. Currently, most graphics are bitmaps and do not render well in PDF and EPUB output. The next step is to migrate all diagrams to SVG.
  • Implement styling improvements, trying to eliminate unnecessarily complex sentences and make the documents easier to read.
  • Propose a “Documentation standards” document in markdown format, together with a diagram template with reusable shapes, text boxes and clipart, so that new documentation and diagrams are created in a way that’s consistent with existing documents.
  • Find bugs and improvement opportunities, and implement some of them, thanks to testing the software and reading the code.

Here are our contributions to the project so far, as of Oct. 20, 2021:

Yocto Project is a very welcoming open-source project, in which core developers are keen to help users and contributors. This makes it a real pleasure to participate to this project, in addition to the satisfaction to contribute to the progress of Embedded Linux.

If you are interested in studying Yocto Project and Bitbake’s documentation and contributing to it, you will be most welcome! The best is to get in touch with our community through the docs mailing list or chat with us on IRC. See our mailing lists and chat page for details.

If you’re interested in using Yocto Project for your embedded Linux projects, we also recommend you to check out our Yocto Project and OpenEmbedded system development training course, and its freely available training materials. We recently announced new dates for our upcoming online training classes on the Yocto Project:

Bootlin at the SIDO event in Paris, November 10

The SIDO is a large event dedicated to IoT, AI, robotics in Paris, and it takes place next to the Open Source Experience event, which as the name suggest is dedicated to all things related to open-source. For Bootlin whose activity is precisely at the junction between embedded systems/IoT and open-source, being present at this combined event made complete sense.

Therefore, Bootlin CEO Thomas Petazzoni will be present at SIDO on November 10, 2021. If you’re interested in discussing with Bootlin about:

  • Engineering services, and how Bootlin can help you with Embedded Linux development, Linux kernel development and upstreaming, boot time optimization, real-time, Yocto, Buildroot, and anything related to Embedded Linux;
  • Training services, and how Bootlin can help your company, team and engineers grow their skills in the field of Embedded Linux;
  • Career opportunities, both full-time positions and internships

Then feel free to contact us to schedule a meeting!

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!