Firmware information

The firmware information module (fw_info) provides externally readable metadata about a firmware image. This information is located at a specific offset in the image. In addition, the module provides code to read such information.

Purpose

The purpose of the firmware information is to allow other images such as bootloaders, or infrastructure such as firmware servers, to gain information about the firmware image.

The firmware information structure has a 12-byte magic header and a verified binary layout to ensure that the format is portable and identifiable. It must be located at one of the following offsets from the start of the image: 0x0, 0x200, 0x400, 0x800, 0x1000. The reason that the structure is not located at 0x00 is that this can be problematic in some use cases, such as when the vector table must be located at 0x00.

These rules make it simple to retrieve the information by checking for each possible offset (0x0, 0x200, 0x400, 0x800, 0x1000) if the first 12 bytes match the magic value. If they do, the information can be retrieved according to the definition in fw_info.

Information structure

The information structure contains a minimal set of information that allows other code to reason about the firmware with no other information available. It includes the following information:

  • The size of the firmware image.

  • The single, monotonically increasing version number of the image.

  • The address through which to boot into the firmware (the vector table address). This address is not necessarily the start of the image.

  • A value that can be modified in place to invalidate the firmware. See CONFIG_FW_INFO_VALID_VAL. If this option is set to any other value than CONFIG_FW_INFO_VALID_VAL (for example, by the bootloader), the image can quickly be established as invalid. The bootloader sets this option to 0 when the image fails validation, so that there is no need to perform a costly validation on every boot. If the firmware is write-protected before being booted by the bootloader, only the bootloader can invalidate it.

At the end of the information structure, there is a variable-size list containing:

  • External APIs

  • External API requests

External APIs

The firmware information structure allows for exchange of arbitrary tagged and versioned interfaces called external APIs (EXT_APIs).

An EXT_API structure is a structure consisting of a header followed by arbitrary data. The header consists of the following information:

  • EXT_API ID (uniquely identifies the EXT_API)

  • EXT_API version (single, monotonically increasing version number for the EXT_API with this ID)

  • EXT_API flags (32 individual bits for indicating the particulars of the EXT_API)

  • EXT_API length (length of the following data)

The EXT_API structures of the image are placed at the end of the information structure, immediately before its EXT_API requests.

An EXT_API request structure is a structure consisting of a description of the requested EXT_API (in the form of an EXT_API header), plus a few more pieces of data. The structure also contains the location of a RAM buffer into which a pointer to a matching EXT_API can be placed before booting. The RAM buffer must not be initialized or otherwise touched when booting the image, to ensure that the value written there before booting is preserved. The EXT_API_REQ helper macro uses the __noinit decorator provided by Zephyr to achieve this.

The EXT_API system allows a bootloader to parse the image’s requests and find matches for these requests by parsing its own (the bootloader’s) EXT_APIs or EXT_APIs of other images. After finding the matches, the bootloader populates the RAM buffers with pointers to the other images’ EXT_APIs. In this way, the bootloader will be aware of whether all images have access to their dependencies.

Each image can provide multiple EXT_APIs and make multiple requests. It is also possible for images to search other images directly for matching EXT_APIs, without using EXT_API requests.

Typically, the data of an EXT_API structure contains a function pointer (or a list of function pointers), but it could consist of anything. The data should be considered read-only though.

Binary compatibility

When exposing an interface between images that have not been compiled and linked together, you must take extra care to ensure binary-compatibility between how the interface is implemented and how it is used. The data placed into EXT_APIs must always have the same format for a given ID/version/flags combination.

To facilitate this, the fw_info.h file contains a number of static asserts to ensure that the memory mapping of structures is the same every time the code is compiled. There are a number of things that can change from compilation to compilation, depending on compiler options or just chance interactions with the rest of the code.

Some things to look out for are:

  • Struct padding: The space between members of a struct can be different.

  • Flag ordering: When splitting an integer into smaller chunks (with :), like flags, the order in which they are mapped to memory can be different.

  • Function ABI: The way the function uses registers and stack can be different.

  • Size of certain types: The size of chars and enums can differ depending on compiler flags.

  • Floating point ABI: The way floating point numbers are processed can be different (hard/soft/softfp).

Usage

To locate and verify firmware information structures, use fw_info_find() and fw_info_check(), respectively.

To find an EXT_API with a given version and flags, call fw_info_ext_api_find(). This function calls ext_api_in under the hood, checks the EXT_API’s version against the allowed range, and checks that it has all the flags set.

To populate an image’s ext_api_in (before booting the image), the booting image should call fw_info_ext_api_provide() with the other image’s firmware information structure. Note that if the booting (current) firmware image and the booted image’s RAM overlap, fw_info_ext_api_provide() will corrupt the current firmware’s RAM. This is ok if it is done immediately before booting the other image, thus after it has performed its last RAM access.

Creating EXT_APIs

To create an EXT_API, complete the following steps:

  1. Create a unique ID for the EXT_API:

    #define MY_EXT_API_ID 0xBEEF
    
  2. Create Kconfig entries using Kconfig.template.fw_info_ext_api:

    EXT_API = MY
    flags = 0
    ver = 1
    source "${ZEPHYR_BASE}/../nrf/subsys/fw_info/Kconfig.template.fw_info_ext_api"
    
  3. Declare a new struct type that starts with the fw_info_ext_api struct:

    typedef int (*my_ext_api_foo_t)(bool arg1, int *arg2);
    
    struct my_ext_api {
      struct fw_info_ext_api header;
      struct {
              /* Actual EXT_API/data goes here. */
              my_ext_api_foo_t my_foo;
      } ext_api;
    };
    
  4. Use the EXT_API macro to initialize the EXT_API struct in an arbitrary location. EXT_API will automatically include the EXT_API in the list at the end of the firmware information structure.

    #ifdef CONFIG_MY_EXT_API_ENABLED
    EXT_API(struct my_ext_api, my_ext_api) = {
      .header = FW_INFO_EXT_API_INIT(MY_EXT_API_ID,
                              CONFIG_MY_EXT_API_FLAGS,
                              CONFIG_MY_EXT_API_VER,
                              sizeof(struct my_ext_api)),
      .ext_api = {
              /* EXT_API initialization goes here. */
              .my_foo = my_foo_impl,
      }
    };
    #endif
    
  5. Enable the EXT_API in Kconfig:

    CONFIG_MY_EXT_API_ENABLED=y
    

Creating EXT_API requests

To create an EXT_API request, complete the following steps:

  1. Assuming that the ID and the Kconfig entries are already created (see Creating EXT_APIs), use the EXT_API_REQ macro to create a request structure:

    EXT_API_REQ(MY, 1, struct my_ext_api, my);
    
  2. Use the EXT_API through the name given to EXT_API_REQ:

    my->ext_api.my_foo(my_arg1, my_arg2);
    
  3. Request the EXT_API in Kconfig:

    CONFIG_MY_EXT_API_REQUIRED=y
    

API documentation

Header file: include/fw_info.h
Source files: subsys/fw_info/
group fw_info

Defines

MAGIC_LEN_WORDS
EXT_API(ext_api_name, type, name)
EXT_API_REQ(name, req, type, var_name)
FW_INFO_OFFSET0
FW_INFO_OFFSET1
FW_INFO_OFFSET2
FW_INFO_OFFSET3
FW_INFO_OFFSET4
FW_INFO_OFFSET_COUNT
FW_INFO_VECTOR_OFFSET
FW_INFO_CURRENT_OFFSET

Typedefs

typedef bool (*fw_info_ext_api_provide_t)(const struct fw_info *fwinfo, bool provide)

Functions

const struct fw_info_ext_api *fw_info_ext_api_check(uint32_t ext_api_addr)
const struct fw_info *fw_info_check(uint32_t fw_info_addr)
const struct fw_info *fw_info_find(uint32_t firmware_address)

Search for the firmware_info structure inside the firmware.

Return

A pointer to the fw_info struct if found. Otherwise NULL.

Parameters
  • [in] firmware_address: The start of the image. The function will search at the allowed offsets from firmware_address.

bool fw_info_ext_api_provide(const struct fw_info *fwinfo, bool provide)

Expose EXT_APIs to another firmware

Populate the other firmware’s ext_api_in with EXT_APIs from other images.

Note

This is should be called immediately before booting the other firmware since it will likely corrupt the memory of the running firmware.

Return

Whether requirements could be satisified.

Parameters
  • [in] fwinfo: Pointer to the other firmware’s information structure.

  • [in] provide: If true, populate ext_api_in. If false, only check whether requirements can be satisfied.

void fw_info_invalidate(const struct fw_info *fw_info)

Invalidate an image by manipulating its fw_info.

Invalidation happens by setting the valid value to 0x0.

Note

This function needs to have CONFIG_NRF_NVMC enabled.

Parameters
  • fw_info: The info structure to invalidate. This memory will be modified directly in flash.

Variables

const uint32_t fw_info_allowed_offsets[] = {0x0, 0x200, 0x400, 0x800, 0x1000}
struct fw_info_ext_api
#include <fw_info.h>

This struct serves as a header before an EXT_API.

This header contains basic metadata about the EXT_API: A unique identifier, version and flags to be used for compatibility matching. The payload that comes after this header is completely implementation dependent for each EXT_API.

Note

How to use the EXT_API, such as the signatures of all the functions in the list must be unambiguous for an ID/version combination.

struct fw_info_ext_api_request
#include <fw_info.h>

A struct that is used to request an EXT_API.

It contains a pointer to a non-initialized pointer that a previous boot stage will populate with a pointer to a compliant EXT_API. The EXT_API could be located in the boot stage itself, or in a third image. An EXT_API fulfills a request if the ID matches, all flags in the request are set in the EXT_API, and the version falls between the minimum and maximum (inclusive).

The request is placed in the list of requests in the fw_info struct.

Note

If required is true, the image making the request will not function unless it has access to the EXT_API.

struct fw_info
#include <fw_info.h>

The top level firmware info struct.

This is a data structure that is placed at a specific offset inside a firmware image so it can be consistently read by external parties. The specific offset makes it easy to find, and the magic value at the start guarantees that it contains data of a specific format.

struct ext_api_provide_ext_api
#include <fw_info.h>

Structure describing the EXT_API_PROVIDE EXT_API.