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:

  • External API (EXT_API) getter (ext_api_out)

  • Pointer to EXT_API getter (ext_api_in)

EXT_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)

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

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

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

Typically, the actual EXT_API 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 EXT_API getters function pointers instead of pointing to a list of EXT_APIs is to allow dynamic creation of EXT_API lists without using RAM.

Usage

To locate and verify firmware info 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. Declare a new struct type that starts with the fw_info_ext_api struct:

    struct my_ext_api {
         struct fw_info_ext_api header;
         struct {
                 /* Actual EXT_API/data goes here. */
         } ext_api;
    };
    
  2. 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 provided via fw_info_ext_api_provide().

    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. */
         }
    };
    
  3. To include function pointers in your EXT_API, call the EXT_API_FUNCTION macro to forward-declare the function and create a typedef for the function pointer:

    EXT_API_FUNCTION(int, my_ext_api_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_API(type, member, value)
EXT_API(type, name)
FW_INFO_EXT_API_INIT(id, flags, version, total_size)
EXT_API_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_ext_api_getter)(u32_t id, u32_t index, const struct fw_info_ext_api **ext_api)

Function that returns an EXT_API.

Parameters
  • [in] id: Which EXT_API to get.

  • [in] index: If there are multiple EXT_APIs available with the same ID, retrieve the different ones with this.

  • [out] ext_api: Pointer to the ext_api with the given id and index.

Return Value
  • 0: Success.

  • -ENOENT: id not found.

  • -EBADF: index too large.

  • -EFAULT: ext_api was NULL.

Functions

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)

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.

static bool fw_info_ext_api_check(const struct fw_info_ext_api *ext_api_info)
void fw_info_ext_api_provide(const struct fw_info *fwinfo)

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.

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

const struct fw_info_ext_api *fw_info_ext_api_get(u32_t id, u32_t index)

Get a single EXT_API.

Return

The EXT_API, or NULL, if it wasn’t found.

Parameters
  • [in] id: Which EXT_API to get.

  • [in] index: If there are multiple EXT_APIs available with the same ID, retrieve the different ones with this.

const struct fw_info_ext_api *fw_info_ext_api_find(u32_t id, u32_t flags, u32_t min_version, u32_t max_version)

Find an EXT_API based on a version range.

Return

The EXT_API, or NULL if none was found.

Parameters
  • [in] id: The ID of the EXT_API to find.

  • [in] flags: The required flags of the EXT_API to find. The returned EXT_API may have other flags set as well.

  • [in] min_version: The minimum acceptable EXT_API version.

  • [in] max_version: One more than the maximum acceptable EXT_API version.

Variables

const u32_t allowed_offsets[] = {0x200, , }
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_ext_api
#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 EXT_API. 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.