Zircon Device Index (MDI)

Introduction

The Zircon Device Index (MDI) is a read-only binary data structure passed from the bootloader that contains configuration information for the kernel and various drivers in zircon. For platforms that use it, the MDI binary data is included as a section in the bootdata.bin file. The MDI binary is generated by the mdigen tool from one or more MDI source files. The mdigen tool also generates a header file containing C preprocessor defines for the MDI ids defined in the MDI sources.

The MDI data is structured as a tree of nodes, can be thought of as ID/value pairs. IDs are 32 bit integers that encode both the name and type of the node. MDI node values can be one of the following:

  • uint8 - an 8-bit unsigned integer
  • int32 - a 32-bit signed integer
  • uint32 - a 32-bit unsigned integer
  • uint64 - a 64-bit unsigned integer
  • boolean - uint8 that is either true or false
  • string - a zero-terminated array of uint8s representing an ASCII string
  • list - an ordered list of MDI nodes
  • array - an ordered list of directly indexable values of the same type. Allowed array types are uint8, int32, uint32, uint64 and boolean.

Syntax

MDI has a very simple syntax with a small number of tokens and reserved words. The syntax is so simple that no extra symbols are needed to determine the boundary between top-level statements or elements within lists. The only exception is that commas are needed between elements in arrays.

MDI supports C/C++ style comments. Whitespace is not significant, other than the fact that comments beginning with // are terminated by the end of the line.

Integers can be expressed as literal values, either in decimal, octal or hexadecimal form. Octal integer literals start with a leading 0 and hexadecimal literals start with a leading 0x or 0X

MDI also supports integer expressions. The arithmetic operators +, -, *, /, % are supported, as well as the bitwise operators ~, |, &, ^, << and >>. The syntax and precedence rules are the same as in C.

MDI source files can contain three types of top-level statements:

  • Includes
  • ID Definitions
  • Constant Definitions
  • Node Definitions

Includes

Includes are statements that include other MDI source files, similar to #include in C or C++. Include statements begin with the include keyword, followed by a double quoted path to another MDI file to include. The path is relative to the current working directory of the mdigen tool. For example:

include "kernel/include/mdi/kernel-defs.mdi"

At this point the mdigen will process the MDI source code in the file kernel/include/mdi/kernel-defs.mdi before continuing to process the current source file.

ID Definitions

ID definitions define the IDs that can be used for nodes in the tree. ID definitions consist of a type, one or more dot-separated identifiers, a C symbol name and an integer value. When the MDI binary is generated, the IDs are written as 32-bit integers containing both the ID definition's type and integer value. The dot-separated list of identifiers are used to match a paths to nodes in the MDI source file.

For example, the following are ID definitions:

int32  foo      MDI_FOO     1
list   bar      MDI_BAR     2
string bar.baz  MDI_BAR_BAZ 3

IDs of type array must also specify the array element type for the ID:

array[boolean]  boolean-array   4

Identifiers can contain alphanumeric characters and the '-' and '_' characters, and must start with a letter.

The mdigen uses the C symbol names to generate a C header file to be included by C/C++ code that uses the MDI. The example above will result in the following code in the C header file:

#define MDI_FOO                                            0x00800001
#define MDI_BAR                                            0x08000002
#define MDI_BAR_BAZ                                        0x09000003

The purpose of using integer IDs rather than arbitrary string names for nodes is to:

  1. provide build time error checking when compiling MDI files, and
  2. provide better efficiency when traversing the MDI binary at runtime

Constant Definitions

Integer constants can be defined for integer literals or expressions. For example:

const ONE = 1
const THREE = 1 + 2

Node Definitions

Node definitions define a top-level node in the MDI node tree. All top-level nodes are implicitly added to the MDI root node, which is an unnamed list that does not actually appear in the MDI source code. Node definitions are of the form <identifier> = <value>, where value can be an integer literal, expression, constant, a boolean or string literal, a list or an array. List values begin and end with '{' and '}', while array values begin and end with '[' and ']'

For example, the following MDI node definitions can be written using the ID definitions in the examples above:

foo = 1

bar = {
    baz = "Hi there"
}

boolean-array = [ true, false, true ]

Compiling this code will generate an MDI binary a root node with three children: foo, bar and bool-array.

Binary Format

An MDI binary begins with a bootdata_t header (see system/public/zircon/boot/bootdata.h) The bootdata_t.type field is set to BOOTDATA_TYPE_MDI.

Following the bootdata_t header are a sequence of mdi_node_t structs. These structs are aligned to 8 byte boundaries. The mdi_node_t struct contains:

  • the node's ID
  • total length of the node, including subnodes
  • the node's value (for nodes of type uint8, int32, uint32, uint64 and boolean
  • for strings, the string's length
  • for lists, the number of immediate child nodes
  • for arrays, the number of array elements

List nodes are immediately followed by the children of the list. Strings and arrays are immediately followed by the string value or array elements.

IDs contain the node type in the high bits and the ID's integer value in the low bits. See the header file system/public/zircon/mdi.h for more details.

mdigen

The mdigen tool generates MDI binaries from one or more MDI source files, with paths to the source files provided via mdigen's command line. mdigen can also take the following additional options:

  • -o <path to output MDI binary>
  • -h <path to output C header file containing MDI ID definitions>

When invoking mdigen, you must specify either a -o or -h option, or both.

MDI Library

Zircon contains a library for reading the MDI binary format The library can be used both in the kernel and userspace. The header file for the MDI library is at system/ulib/mdi/include/mdi/mdi.h.

When using the MDI library, one must first call mdi_init() to get a reference to the root node in the MDI tree. The remaining library functions can be used to read the value of a node, access the ith element of an array node, or traverse the children of list nodes.

The MDI library does not allocate memory or depend on any other libraries, so it can be used within any context in the zircon kernel or userspace.