1.. _binary_descriptors:
2
3Binary Descriptors
4##################
5
6Binary Descriptors are constant data objects storing information about the binary executable.
7Unlike "regular" constants, binary descriptors are linked to a known offset in the binary, making
8them accessible to other programs, such as a different image running on the same device or a host tool.
9A few examples of constants that would make useful binary descriptors are: kernel version, app version,
10build time, compiler version, environment variables, compiling host name, etc.
11
12Binary descriptors are created by using the ``DEFINE_BINDESC_*`` macros. For example:
13
14.. code-block:: c
15
16   #include <zephyr/bindesc.h>
17
18   BINDESC_STR_DEFINE(my_string, 2, "Hello world!"); // Unique ID is 2
19
20``my_string`` could then be accessed using:
21
22.. code-block:: c
23
24   printk("my_string: %s\n", BINDESC_GET_STR(my_string));
25
26But it could also be retrieved by ``west bindesc``:
27
28.. code-block:: bash
29
30   $ west bindesc custom_search STR 2 build/zephyr/zephyr.bin
31   "Hello world!"
32
33Internals
34*********
35Binary descriptors are implemented with a TLV (tag, length, value) header linked
36to a known offset in the binary image. This offset may vary between architectures,
37but generally the descriptors are linked as close to the beginning of the image as
38possible. In architectures where the image must begin with a vector table (such as
39ARM), the descriptors are linked right after the vector table. The reset vector points
40to the beginning of the text section, which is after the descriptors. In architectures
41where the image must begin with executable code (e.g. x86), a jump instruction is injected at
42the beginning of the image, in order to skip over the binary descriptors, which are right
43after the jump instruction.
44
45Each tag is a 16 bit unsigned integer, where the most significant nibble (4 bits) is the type
46(currently uint, string or bytes), and the rest is the ID. The ID is globally unique to each
47descriptor. For example, the ID of the app version string is ``0x800``, and a string
48is denoted by 0x1, making the app version tag ``0x1800``. The length is a 16 bit
49number equal to the length of the data in bytes. The data is the actual descriptor
50value. All binary descriptor numbers (magic, tags, uints) are laid out in memory
51in the endianness native to the SoC. ``west bindesc`` assumes little endian by default,
52so if the image belongs to a big endian SoC, the appropriate flag should be given to the
53tool.
54
55The binary descriptor header starts with the magic number ``0xb9863e5a7ea46046``. It's followed
56by the TLVs, and ends with the ``DESCRIPTORS_END`` (``0xffff``) tag. The tags are
57always aligned to 32 bits. If the value of the previous descriptor had a non-aligned
58length, zero padding will be added to ensure that the current tag is aligned.
59
60Putting it all together, here is what the example above would look like in memory
61(of a little endian SoC):
62
63.. code-block::
64
65    46 60 a4 7e 5a 3e 86 b9 02 10  0d 00  48 65 6c 6c 6f 20 77 6f 72 6c 64 21 00 00 00 00 ff ff 00 00
66   |         magic         | tag |length| H  e  l  l  o     w  o  r  l  d  !    |   pad  |    end    |
67
68Usage
69*****
70Binary descriptors are always created by the ``BINDESC_*_DEFINE`` macros. As shown in
71the example above, a descriptor can be generated from any string or integer, with any
72ID. However, it is recommended to comply with the standard tags defined in
73``include/zephyr/bindesc.h``, as that would have the following benefits:
74
75 1. The ``west bindesc`` tool would be able to recognize what the descriptor means and
76    print a meaningful tag
77 2. It would enforce consistency between various apps from various sources
78 3. It allows upstream-ability of descriptor generation (see Standard Descriptors)
79
80To define a descriptor with a standard tag, just use the tags included from ``bindesc.h``:
81
82.. code-block:: c
83
84   #include <zephyr/bindesc.h>
85
86   BINDESC_STR_DEFINE(app_version, BINDESC_ID_APP_VERSION_STRING, "1.2.3");
87
88Standard Descriptors
89====================
90Some descriptors might be trivial to implement, and could therefore be implemented
91in a standard way in upstream Zephyr. These could then be enabled via Kconfig, instead
92of requiring every user to reimplement them. These include build times, kernel version,
93and host info. For example, to add the build date and time as a string, the following
94configs should be enabled:
95
96.. code-block:: kconfig
97
98   # Enable binary descriptors
99   CONFIG_BINDESC=y
100
101   # Enable definition of binary descriptors
102   CONFIG_BINDESC_DEFINE=y
103
104   # Enable default build time binary descriptors
105   CONFIG_BINDESC_DEFINE_BUILD_TIME=y
106   CONFIG_BINDESC_BUILD_DATE_TIME_STRING=y
107
108To avoid collisions with user defined descriptors, the standard descriptors were allotted
109the range between ``0x800-0xfff``. This leaves ``0x000-0x7ff`` to users.
110For more information read the ``help`` sections of these Kconfig symbols.
111By convention, each Kconfig symbol corresponds to a binary descriptor whose
112name is the Kconfig name (with ``CONFIG_BINDESC_`` removed) in lower case. For example,
113``CONFIG_BINDESC_KERNEL_VERSION_STRING`` creates a descriptor that can be
114accessed using ``BINDESC_GET_STR(kernel_version_string)``.
115
116Reading Descriptors
117===================
118It's also possible to read and parse binary descriptors from an application.
119This can be useful both for an image trying to read its own descriptors, and for
120an image trying to read another image's descriptors. Reading can be performed through
121one of three backends:
122
123 #. RAM - assuming the descriptors have been copied to RAM (e.g. by a bootloader), they
124    can be read from the buffer they reside in.
125
126 #. Memory mapped flash - If the flash where the image to be read resides in flash and is
127    accessible through the program's address space, it can be read directly from flash.
128    This option uses the least amount of RAM, but will not work if the flash is not memory mapped,
129    and is not recommended to read a bootloader's descriptors for security concerns.
130
131 #. Flash - Using an internal buffer, the descriptors are read one by one using the flash API,
132    and given to the user while they're in the buffer.
133
134To enable reading descriptors, enable :kconfig:option:`CONFIG_BINDESC_READ`. The three backends are
135enabled by these Kconfig symbols, respectively: :kconfig:option:`CONFIG_BINDESC_READ_RAM`,
136:kconfig:option:`CONFIG_BINDESC_READ_MEMORY_MAPPED_FLASH`, and :kconfig:option:`CONFIG_BINDESC_READ_FLASH`.
137
138To read the descriptors, a handle to the descriptors should first be initialized:
139
140.. code-block:: c
141
142   struct bindesc_handle handle;
143
144   /* Assume buffer holds a copy of the descriptors */
145   bindesc_open_ram(&handle, buffer);
146
147The ``bindesc_open_*`` functions are the only functions concerned with the backend used.
148The rest of the API is agnostic to where the data is. After the handle has been initialized,
149it can be used with the rest of the API:
150
151.. code-block:: c
152
153   char *version;
154   bindesc_find_str(&handle, BINDESC_ID_KERNEL_VERSION_STRING, &version);
155   printk("Kernel version: %s\n", version);
156
157west bindesc tool
158=================
159``west`` is able to parse and display binary descriptors from a given executable image.
160
161For more information refer to ``west bindesc --help`` or the :ref:`documentation<west-bindesc>`.
162
163API Reference
164*************
165
166.. doxygengroup:: bindesc_define
167
168.. doxygengroup:: bindesc_read
169