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 integerint32
- a 32-bit signed integeruint32
- a 32-bit unsigned integeruint64
- a 64-bit unsigned integerboolean
- uint8 that is eithertrue
orfalse
string
- a zero-terminated array of uint8s representing an ASCII stringlist
- an ordered list of MDI nodesarray
- an ordered list of directly indexable values of the same type. Allowed array types are uint8, int32, uint32, uint64 and boolean.
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 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 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:
- provide build time error checking when compiling MDI files, and
- provide better efficiency when traversing the MDI binary at runtime
Integer constants can be defined for integer literals or expressions. For example:
const ONE = 1
const THREE = 1 + 2
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
.
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
andboolean
- 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.
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.
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.