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 three offsets from the start of the image: 0x200, 0x400, or 0x800. 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 (0x200, 0x400, 0x800) 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.

Additionally, there is information for exchanging arbitrary data:

  • Application Binary Interface (ABI) getter (abi_out)
  • Pointer to ABI getter (abi_in)

ABIs

The firmware information structure allows for exchange of arbitrary tagged and versioned interfaces called Application Binary Interfaces (ABIs).

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

  • ABI ID (uniquely identifies the ABI)
  • ABI version (single, monotonically increasing version number for the ABI with this ID)
  • ABI flags (32 individual bits for indicating the particulars of the ABI)
  • ABI length (length of the following data)

To retrieve an ABI, a firmware image calls another firmware image’s ABI getter. Every image must provide an ABI getter (abi_out) that other images can use to retrieve its ABIs. This ABI getter is a function pointer that retrieves ABI structs (or rather pointers to ABI structs).

In addition, every image can access other ABIs through a second ABI getter (abi_in). This ABI getter must be provided when booting the image. In other words, an image should expect its abi_in ABI getter to be filled at the time it boots, and will not touch it during booting. After booting, an image can call abi_in to retrieve ABIs from the image or images that booted it without knowing where they are located.

Each image can provide multiple ABIs. An ABI getter function takes an index, and each index from 0 to n must return a different ABI, given that the image provides n ABIs with the same ID.

Typically, the actual ABI will be a function pointer (or a list of function pointers), but the data can be anything, though it should be considered read-only. The reason for making the ABI getters function pointers instead of pointing to a list of ABIs is to allow dynamic creation of ABI lists without using RAM.

Usage

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

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

To populate an image’s abi_in (before booting the image), the booting image should call fw_info_abi_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_abi_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 ABIs

To create an ABI, complete the following steps:

  1. Declare a new struct type that starts with the fw_info_abi struct:

    struct my_abi {
         struct fw_info_abi header;
         struct {
                 /* Actual ABI/data goes here. */
         } abi;
    };
    
  2. Use the __ext_abi macro to initialize the ABI struct in an arbitrary location. __ext_abi will automatically include the ABI in the list provided via fw_info_abi_provide().

    __ext_abi(struct my_abi, my_abi) = {
         .header = FW_INFO_ABI_INIT(MY_ABI_ID,
                                 CONFIG_MY_ABI_FLAGS,
                                 CONFIG_MY_ABI_VER,
                                 sizeof(struct my_abi)),
         .abi = {
                 /* ABI initialization goes here. */
         }
    };
    
  3. To include function pointers in your ABI, call the EXT_ABI_FUNCTION macro to forward-declare the function and create a typedef for the function pointer:

    EXT_ABI_FUNCTION(int, my_abi_foo, bool arg1, int *arg2);
    

API documentation

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

Defines

MAGIC_LEN_WORDS
OFFSET_CHECK_EXT_ABI(type, member, value)
FW_INFO_ABI_INIT(id, flags, version, total_size)
EXT_ABI_FUNCTION(retval, name, ...)
FW_INFO_OFFSET0
FW_INFO_OFFSET1
FW_INFO_OFFSET2
FW_INFO_OFFSET_COUNT
VECTOR_OFFSET
CURRENT_OFFSET

Typedefs

typedef int (*fw_info_abi_getter)(u32_t id, u32_t index, const struct fw_info_abi **abi)

Function that returns an ABI.

Parameters
  • id: Which ABI to get.
  • index: If there are multiple ABIs available with the same ID, retrieve the different ones with this.
  • abi: Pointer to the abi with the given id and index.
Return Value
  • 0: Success.
  • -ENOENT: id not found.
  • -EBADF: index too large.
  • -EFAULT: abi was NULL.

Functions

static bool memeq_32(const void *expected, const void *actual, u32_t len)
static bool memeq_8(const void *expected, const void *actual, u32_t len)
static bool memeq(const void *expected, const void *actual, u32_t len)
static const struct fw_info *fw_info_check(u32_t fw_info_addr)
static const struct fw_info *fw_info_find(u32_t firmware_address)
static bool fw_info_abi_check(const struct fw_info_abi *abi_info)
void fw_info_abi_provide(const struct fw_info *fw_info)

Expose ABIs to another firmware

Populate the other firmware’s abi_in with a internal ABI getter function which serves all ABIs created with __ext_abi.

Note
This is should be called immediately before booting the other firmware since it will likely corrupt the memory of the running firmware.
Parameters
  • fw_info: Pointer to the other firmware’s information structure.

const struct fw_info_abi *fw_info_abi_get(u32_t id, u32_t index)

Get a single ABI.

Return
The ABI, or NULL, if it wasn’t found.
Parameters
  • id: Which ABI to get.
  • index: If there are multiple ABIs available with the same ID, retrieve the different ones with this.

const struct fw_info_abi *fw_info_abi_find(u32_t id, u32_t flags, u32_t min_version, u32_t max_version)

Find an ABI based on a version range.

Return
The ABI, or NULL if none was found.
Parameters
  • id: The ID of the ABI to find.
  • flags: The required flags of the ABI to find. The returned ABI may have other flags set as well.
  • min_version: The minimum acceptable ABI version.
  • max_version: One more than the maximum acceptable ABI version.

Variables

const u32_t allowed_offsets[]

= { 0x200 , 0x400 ,

0x800 }


struct fw_info
#include <fw_info.h>

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 fw_info_abi
#include <fw_info.h>

This struct is meant to serve as a header before a list of function pointers (or something else) that constitute the actual ABI. How to use the ABI, such as the signatures of all the functions in the list must be unambiguous for an ID/version combination.