]>
Commit | Line | Data |
---|---|---|
bf7fd50b SG |
1 | # Copyright (c) 2016 Google, Inc |
2 | # | |
3 | # SPDX-License-Identifier: GPL-2.0+ | |
4 | # | |
5 | ||
6 | Introduction | |
7 | ------------ | |
8 | ||
9 | Firmware often consists of several components which must be packaged together. | |
10 | For example, we may have SPL, U-Boot, a device tree and an environment area | |
11 | grouped together and placed in MMC flash. When the system starts, it must be | |
12 | able to find these pieces. | |
13 | ||
14 | So far U-Boot has not provided a way to handle creating such images in a | |
15 | general way. Each SoC does what it needs to build an image, often packing or | |
16 | concatenating images in the U-Boot build system. | |
17 | ||
18 | Binman aims to provide a mechanism for building images, from simple | |
19 | SPL + U-Boot combinations, to more complex arrangements with many parts. | |
20 | ||
21 | ||
22 | What it does | |
23 | ------------ | |
24 | ||
25 | Binman reads your board's device tree and finds a node which describes the | |
26 | required image layout. It uses this to work out what to place where. The | |
27 | output file normally contains the device tree, so it is in principle possible | |
28 | to read an image and extract its constituent parts. | |
29 | ||
30 | ||
31 | Features | |
32 | -------- | |
33 | ||
34 | So far binman is pretty simple. It supports binary blobs, such as 'u-boot', | |
35 | 'spl' and 'fdt'. It supports empty entries (such as setting to 0xff). It can | |
36 | place entries at a fixed location in the image, or fit them together with | |
37 | suitable padding and alignment. It provides a way to process binaries before | |
38 | they are included, by adding a Python plug-in. The device tree is available | |
39 | to U-Boot at run-time so that the images can be interpreted. | |
40 | ||
41 | Binman does not yet update the device tree with the final location of | |
42 | everything when it is done. A simple C structure could be generated for | |
43 | constrained environments like SPL (using dtoc) but this is also not | |
44 | implemented. | |
45 | ||
46 | Binman can also support incorporating filesystems in the image if required. | |
47 | For example x86 platforms may use CBFS in some cases. | |
48 | ||
49 | Binman is intended for use with U-Boot but is designed to be general enough | |
50 | to be useful in other image-packaging situations. | |
51 | ||
52 | ||
53 | Motivation | |
54 | ---------- | |
55 | ||
56 | Packaging of firmware is quite a different task from building the various | |
57 | parts. In many cases the various binaries which go into the image come from | |
58 | separate build systems. For example, ARM Trusted Firmware is used on ARMv8 | |
59 | devices but is not built in the U-Boot tree. If a Linux kernel is included | |
60 | in the firmware image, it is built elsewhere. | |
61 | ||
62 | It is of course possible to add more and more build rules to the U-Boot | |
63 | build system to cover these cases. It can shell out to other Makefiles and | |
64 | build scripts. But it seems better to create a clear divide between building | |
65 | software and packaging it. | |
66 | ||
67 | At present this is handled by manual instructions, different for each board, | |
68 | on how to create images that will boot. By turning these instructions into a | |
69 | standard format, we can support making valid images for any board without | |
70 | manual effort, lots of READMEs, etc. | |
71 | ||
72 | Benefits: | |
73 | - Each binary can have its own build system and tool chain without creating | |
74 | any dependencies between them | |
75 | - Avoids the need for a single-shot build: individual parts can be updated | |
76 | and brought in as needed | |
77 | - Provides for a standard image description available in the build and at | |
78 | run-time | |
79 | - SoC-specific image-signing tools can be accomodated | |
80 | - Avoids cluttering the U-Boot build system with image-building code | |
81 | - The image description is automatically available at run-time in U-Boot, | |
82 | SPL. It can be made available to other software also | |
83 | - The image description is easily readable (it's a text file in device-tree | |
84 | format) and permits flexible packing of binaries | |
85 | ||
86 | ||
87 | Terminology | |
88 | ----------- | |
89 | ||
90 | Binman uses the following terms: | |
91 | ||
92 | - image - an output file containing a firmware image | |
93 | - binary - an input binary that goes into the image | |
94 | ||
95 | ||
96 | Relationship to FIT | |
97 | ------------------- | |
98 | ||
99 | FIT is U-Boot's official image format. It supports multiple binaries with | |
100 | load / execution addresses, compression. It also supports verification | |
101 | through hashing and RSA signatures. | |
102 | ||
103 | FIT was originally designed to support booting a Linux kernel (with an | |
104 | optional ramdisk) and device tree chosen from various options in the FIT. | |
105 | Now that U-Boot supports configuration via device tree, it is possible to | |
106 | load U-Boot from a FIT, with the device tree chosen by SPL. | |
107 | ||
108 | Binman considers FIT to be one of the binaries it can place in the image. | |
109 | ||
110 | Where possible it is best to put as much as possible in the FIT, with binman | |
111 | used to deal with cases not covered by FIT. Examples include initial | |
112 | execution (since FIT itself does not have an executable header) and dealing | |
113 | with device boundaries, such as the read-only/read-write separation in SPI | |
114 | flash. | |
115 | ||
116 | For U-Boot, binman should not be used to create ad-hoc images in place of | |
117 | FIT. | |
118 | ||
119 | ||
120 | Relationship to mkimage | |
121 | ----------------------- | |
122 | ||
123 | The mkimage tool provides a means to create a FIT. Traditionally it has | |
124 | needed an image description file: a device tree, like binman, but in a | |
125 | different format. More recently it has started to support a '-f auto' mode | |
126 | which can generate that automatically. | |
127 | ||
128 | More relevant to binman, mkimage also permits creation of many SoC-specific | |
129 | image types. These can be listed by running 'mkimage -T list'. Examples | |
130 | include 'rksd', the Rockchip SD/MMC boot format. The mkimage tool is often | |
131 | called from the U-Boot build system for this reason. | |
132 | ||
133 | Binman considers the output files created by mkimage to be binary blobs | |
134 | which it can place in an image. Binman does not replace the mkimage tool or | |
135 | this purpose. It would be possible in some situtions to create a new entry | |
136 | type for the images in mkimage, but this would not add functionality. It | |
137 | seems better to use the mkiamge tool to generate binaries and avoid blurring | |
138 | the boundaries between building input files (mkimage) and packaging then | |
139 | into a final image (binman). | |
140 | ||
141 | ||
142 | Example use of binman in U-Boot | |
143 | ------------------------------- | |
144 | ||
145 | Binman aims to replace some of the ad-hoc image creation in the U-Boot | |
146 | build system. | |
147 | ||
148 | Consider sunxi. It has the following steps: | |
149 | ||
150 | 1. It uses a custom mksunxiboot tool to build an SPL image called | |
151 | sunxi-spl.bin. This should probably move into mkimage. | |
152 | ||
153 | 2. It uses mkimage to package U-Boot into a legacy image file (so that it can | |
154 | hold the load and execution address) called u-boot.img. | |
155 | ||
156 | 3. It builds a final output image called u-boot-sunxi-with-spl.bin which | |
157 | consists of sunxi-spl.bin, some padding and u-boot.img. | |
158 | ||
159 | Binman is intended to replace the last step. The U-Boot build system builds | |
160 | u-boot.bin and sunxi-spl.bin. Binman can then take over creation of | |
161 | sunxi-spl.bin (by calling mksunxiboot, or hopefully one day mkimage). In any | |
162 | case, it would then create the image from the component parts. | |
163 | ||
164 | This simplifies the U-Boot Makefile somewhat, since various pieces of logic | |
165 | can be replaced by a call to binman. | |
166 | ||
167 | ||
168 | Example use of binman for x86 | |
169 | ----------------------------- | |
170 | ||
171 | In most cases x86 images have a lot of binary blobs, 'black-box' code | |
172 | provided by Intel which must be run for the platform to work. Typically | |
173 | these blobs are not relocatable and must be placed at fixed areas in the | |
174 | firmare image. | |
175 | ||
176 | Currently this is handled by ifdtool, which places microcode, FSP, MRC, VGA | |
177 | BIOS, reference code and Intel ME binaries into a u-boot.rom file. | |
178 | ||
179 | Binman is intended to replace all of this, with ifdtool left to handle only | |
180 | the configuration of the Intel-format descriptor. | |
181 | ||
182 | ||
183 | Running binman | |
184 | -------------- | |
185 | ||
186 | Type: | |
187 | ||
188 | binman -b <board_name> | |
189 | ||
190 | to build an image for a board. The board name is the same name used when | |
191 | configuring U-Boot (e.g. for sandbox_defconfig the board name is 'sandbox'). | |
192 | Binman assumes that the input files for the build are in ../b/<board_name>. | |
193 | ||
194 | Or you can specify this explicitly: | |
195 | ||
196 | binman -I <build_path> | |
197 | ||
198 | where <build_path> is the build directory containing the output of the U-Boot | |
199 | build. | |
200 | ||
201 | (Future work will make this more configurable) | |
202 | ||
203 | In either case, binman picks up the device tree file (u-boot.dtb) and looks | |
204 | for its instructions in the 'binman' node. | |
205 | ||
206 | Binman has a few other options which you can see by running 'binman -h'. | |
207 | ||
208 | ||
9c0a8b1f SG |
209 | Enabling binman for a board |
210 | --------------------------- | |
211 | ||
212 | At present binman is invoked from a rule in the main Makefile. Typically you | |
213 | will have a rule like: | |
214 | ||
215 | ifneq ($(CONFIG_ARCH_<something>),) | |
216 | u-boot-<your_suffix>.bin: <input_file_1> <input_file_2> checkbinman FORCE | |
217 | $(call if_changed,binman) | |
218 | endif | |
219 | ||
220 | This assumes that u-boot-<your_suffix>.bin is a target, and is the final file | |
221 | that you need to produce. You can make it a target by adding it to ALL-y | |
222 | either in the main Makefile or in a config.mk file in your arch subdirectory. | |
223 | ||
224 | Once binman is executed it will pick up its instructions from a device-tree | |
225 | file, typically <soc>-u-boot.dtsi, where <soc> is your CONFIG_SYS_SOC value. | |
226 | You can use other, more specific CONFIG options - see 'Automatic .dtsi | |
227 | inclusion' below. | |
228 | ||
229 | ||
bf7fd50b SG |
230 | Image description format |
231 | ------------------------ | |
232 | ||
233 | The binman node is called 'binman'. An example image description is shown | |
234 | below: | |
235 | ||
236 | binman { | |
237 | filename = "u-boot-sunxi-with-spl.bin"; | |
238 | pad-byte = <0xff>; | |
239 | blob { | |
240 | filename = "spl/sunxi-spl.bin"; | |
241 | }; | |
242 | u-boot { | |
243 | pos = <CONFIG_SPL_PAD_TO>; | |
244 | }; | |
245 | }; | |
246 | ||
247 | ||
248 | This requests binman to create an image file called u-boot-sunxi-with-spl.bin | |
249 | consisting of a specially formatted SPL (spl/sunxi-spl.bin, built by the | |
250 | normal U-Boot Makefile), some 0xff padding, and a U-Boot legacy image. The | |
251 | padding comes from the fact that the second binary is placed at | |
252 | CONFIG_SPL_PAD_TO. If that line were omitted then the U-Boot binary would | |
253 | immediately follow the SPL binary. | |
254 | ||
255 | The binman node describes an image. The sub-nodes describe entries in the | |
256 | image. Each entry represents a region within the overall image. The name of | |
257 | the entry (blob, u-boot) tells binman what to put there. For 'blob' we must | |
258 | provide a filename. For 'u-boot', binman knows that this means 'u-boot.bin'. | |
259 | ||
260 | Entries are normally placed into the image sequentially, one after the other. | |
261 | The image size is the total size of all entries. As you can see, you can | |
262 | specify the start position of an entry using the 'pos' property. | |
263 | ||
264 | Note that due to a device tree requirement, all entries must have a unique | |
265 | name. If you want to put the same binary in the image multiple times, you can | |
266 | use any unique name, with the 'type' property providing the type. | |
267 | ||
268 | The attributes supported for entries are described below. | |
269 | ||
270 | pos: | |
271 | This sets the position of an entry within the image. The first byte | |
272 | of the image is normally at position 0. If 'pos' is not provided, | |
273 | binman sets it to the end of the previous region, or the start of | |
274 | the image's entry area (normally 0) if there is no previous region. | |
275 | ||
276 | align: | |
277 | This sets the alignment of the entry. The entry position is adjusted | |
278 | so that the entry starts on an aligned boundary within the image. For | |
279 | example 'align = <16>' means that the entry will start on a 16-byte | |
280 | boundary. Alignment shold be a power of 2. If 'align' is not | |
281 | provided, no alignment is performed. | |
282 | ||
283 | size: | |
284 | This sets the size of the entry. The contents will be padded out to | |
285 | this size. If this is not provided, it will be set to the size of the | |
286 | contents. | |
287 | ||
288 | pad-before: | |
289 | Padding before the contents of the entry. Normally this is 0, meaning | |
290 | that the contents start at the beginning of the entry. This can be | |
291 | offset the entry contents a little. Defaults to 0. | |
292 | ||
293 | pad-after: | |
294 | Padding after the contents of the entry. Normally this is 0, meaning | |
295 | that the entry ends at the last byte of content (unless adjusted by | |
296 | other properties). This allows room to be created in the image for | |
297 | this entry to expand later. Defaults to 0. | |
298 | ||
299 | align-size: | |
300 | This sets the alignment of the entry size. For example, to ensure | |
301 | that the size of an entry is a multiple of 64 bytes, set this to 64. | |
302 | If 'align-size' is not provided, no alignment is performed. | |
303 | ||
304 | align-end: | |
305 | This sets the alignment of the end of an entry. Some entries require | |
306 | that they end on an alignment boundary, regardless of where they | |
307 | start. If 'align-end' is not provided, no alignment is performed. | |
308 | ||
309 | Note: This is not yet implemented in binman. | |
310 | ||
311 | filename: | |
312 | For 'blob' types this provides the filename containing the binary to | |
313 | put into the entry. If binman knows about the entry type (like | |
314 | u-boot-bin), then there is no need to specify this. | |
315 | ||
316 | type: | |
317 | Sets the type of an entry. This defaults to the entry name, but it is | |
318 | possible to use any name, and then add (for example) 'type = "u-boot"' | |
319 | to specify the type. | |
320 | ||
321 | ||
322 | The attributes supported for images are described below. Several are similar | |
323 | to those for entries. | |
324 | ||
325 | size: | |
326 | Sets the image size in bytes, for example 'size = <0x100000>' for a | |
327 | 1MB image. | |
328 | ||
329 | align-size: | |
330 | This sets the alignment of the image size. For example, to ensure | |
331 | that the image ends on a 512-byte boundary, use 'align-size = <512>'. | |
332 | If 'align-size' is not provided, no alignment is performed. | |
333 | ||
334 | pad-before: | |
335 | This sets the padding before the image entries. The first entry will | |
336 | be positionad after the padding. This defaults to 0. | |
337 | ||
338 | pad-after: | |
339 | This sets the padding after the image entries. The padding will be | |
340 | placed after the last entry. This defaults to 0. | |
341 | ||
342 | pad-byte: | |
343 | This specifies the pad byte to use when padding in the image. It | |
344 | defaults to 0. To use 0xff, you would add 'pad-byte = <0xff>'. | |
345 | ||
346 | filename: | |
347 | This specifies the image filename. It defaults to 'image.bin'. | |
348 | ||
349 | sort-by-pos: | |
350 | This causes binman to reorder the entries as needed to make sure they | |
351 | are in increasing positional order. This can be used when your entry | |
352 | order may not match the positional order. A common situation is where | |
353 | the 'pos' properties are set by CONFIG options, so their ordering is | |
354 | not known a priori. | |
355 | ||
356 | This is a boolean property so needs no value. To enable it, add a | |
357 | line 'sort-by-pos;' to your description. | |
358 | ||
359 | multiple-images: | |
360 | Normally only a single image is generated. To create more than one | |
361 | image, put this property in the binman node. For example, this will | |
362 | create image1.bin containing u-boot.bin, and image2.bin containing | |
363 | both spl/u-boot-spl.bin and u-boot.bin: | |
364 | ||
365 | binman { | |
366 | multiple-images; | |
367 | image1 { | |
368 | u-boot { | |
369 | }; | |
370 | }; | |
371 | ||
372 | image2 { | |
373 | spl { | |
374 | }; | |
375 | u-boot { | |
376 | }; | |
377 | }; | |
378 | }; | |
379 | ||
380 | end-at-4gb: | |
381 | For x86 machines the ROM positions start just before 4GB and extend | |
382 | up so that the image finished at the 4GB boundary. This boolean | |
383 | option can be enabled to support this. The image size must be | |
384 | provided so that binman knows when the image should start. For an | |
385 | 8MB ROM, the position of the first entry would be 0xfff80000 with | |
386 | this option, instead of 0 without this option. | |
387 | ||
388 | ||
389 | Examples of the above options can be found in the tests. See the | |
390 | tools/binman/test directory. | |
391 | ||
392 | ||
e0ff8551 SG |
393 | Special properties |
394 | ------------------ | |
395 | ||
396 | Some entries support special properties, documented here: | |
397 | ||
398 | u-boot-with-ucode-ptr: | |
399 | optional-ucode: boolean property to make microcode optional. If the | |
400 | u-boot.bin image does not include microcode, no error will | |
401 | be generated. | |
402 | ||
403 | ||
bf7fd50b SG |
404 | Order of image creation |
405 | ----------------------- | |
406 | ||
407 | Image creation proceeds in the following order, for each entry in the image. | |
408 | ||
409 | 1. GetEntryContents() - the contents of each entry are obtained, normally by | |
410 | reading from a file. This calls the Entry.ObtainContents() to read the | |
411 | contents. The default version of Entry.ObtainContents() calls | |
412 | Entry.GetDefaultFilename() and then reads that file. So a common mechanism | |
413 | to select a file to read is to override that function in the subclass. The | |
414 | functions must return True when they have read the contents. Binman will | |
415 | retry calling the functions a few times if False is returned, allowing | |
416 | dependencies between the contents of different entries. | |
417 | ||
418 | 2. GetEntryPositions() - calls Entry.GetPositions() for each entry. This can | |
419 | return a dict containing entries that need updating. The key should be the | |
420 | entry name and the value is a tuple (pos, size). This allows an entry to | |
421 | provide the position and size for other entries. The default implementation | |
422 | of GetEntryPositions() returns {}. | |
423 | ||
424 | 3. PackEntries() - calls Entry.Pack() which figures out the position and | |
425 | size of an entry. The 'current' image position is passed in, and the function | |
426 | returns the position immediately after the entry being packed. The default | |
427 | implementation of Pack() is usually sufficient. | |
428 | ||
429 | 4. CheckSize() - checks that the contents of all the entries fits within | |
430 | the image size. If the image does not have a defined size, the size is set | |
431 | large enough to hold all the entries. | |
432 | ||
433 | 5. CheckEntries() - checks that the entries do not overlap, nor extend | |
434 | outside the image. | |
435 | ||
436 | 6. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry. | |
437 | The default implementatoin does nothing. This can be overriden to adjust the | |
438 | contents of an entry in some way. For example, it would be possible to create | |
439 | an entry containing a hash of the contents of some other entries. At this | |
440 | stage the position and size of entries should not be adjusted. | |
441 | ||
442 | 7. BuildImage() - builds the image and writes it to a file. This is the final | |
443 | step. | |
444 | ||
445 | ||
6d427c6b SG |
446 | Automatic .dtsi inclusion |
447 | ------------------------- | |
448 | ||
449 | It is sometimes inconvenient to add a 'binman' node to the .dts file for each | |
450 | board. This can be done by using #include to bring in a common file. Another | |
451 | approach supported by the U-Boot build system is to automatically include | |
452 | a common header. You can then put the binman node (and anything else that is | |
453 | specific to U-Boot, such as u-boot,dm-pre-reloc properies) in that header | |
454 | file. | |
455 | ||
456 | Binman will search for the following files in arch/<arch>/dts: | |
457 | ||
458 | <dts>-u-boot.dtsi where <dts> is the base name of the .dts file | |
459 | <CONFIG_SYS_SOC>-u-boot.dtsi | |
460 | <CONFIG_SYS_CPU>-u-boot.dtsi | |
461 | <CONFIG_SYS_VENDOR>-u-boot.dtsi | |
462 | u-boot.dtsi | |
463 | ||
464 | U-Boot will only use the first one that it finds. If you need to include a | |
465 | more general file you can do that from the more specific file using #include. | |
466 | If you are having trouble figuring out what is going on, you can uncomment | |
467 | the 'warning' line in scripts/Makefile.lib to see what it has found: | |
468 | ||
469 | # Uncomment for debugging | |
511fd0b2 SG |
470 | # This shows all the files that were considered and the one that we chose. |
471 | # u_boot_dtsi_options_debug = $(u_boot_dtsi_options_raw) | |
6d427c6b SG |
472 | |
473 | ||
474 | Code coverage | |
475 | ------------- | |
476 | ||
477 | Binman is a critical tool and is designed to be very testable. Entry | |
478 | implementations target 100% test coverage. Run 'binman -T' to check this. | |
479 | ||
480 | To enable Python test coverage on Debian-type distributions (e.g. Ubuntu): | |
481 | ||
482 | $ sudo apt-get install python-pip python-pytest | |
483 | $ sudo pip install coverage | |
484 | ||
485 | ||
bf7fd50b SG |
486 | Advanced Features / Technical docs |
487 | ---------------------------------- | |
488 | ||
489 | The behaviour of entries is defined by the Entry class. All other entries are | |
490 | a subclass of this. An important subclass is Entry_blob which takes binary | |
491 | data from a file and places it in the entry. In fact most entry types are | |
492 | subclasses of Entry_blob. | |
493 | ||
494 | Each entry type is a separate file in the tools/binman/etype directory. Each | |
495 | file contains a class called Entry_<type> where <type> is the entry type. | |
496 | New entry types can be supported by adding new files in that directory. | |
497 | These will automatically be detected by binman when needed. | |
498 | ||
499 | Entry properties are documented in entry.py. The entry subclasses are free | |
500 | to change the values of properties to support special behaviour. For example, | |
501 | when Entry_blob loads a file, it sets content_size to the size of the file. | |
502 | Entry classes can adjust other entries. For example, an entry that knows | |
503 | where other entries should be positioned can set up those entries' positions | |
504 | so they don't need to be set in the binman decription. It can also adjust | |
505 | entry contents. | |
506 | ||
507 | Most of the time such essoteric behaviour is not needed, but it can be | |
508 | essential for complex images. | |
509 | ||
510 | ||
511 | History / Credits | |
512 | ----------------- | |
513 | ||
514 | Binman takes a lot of inspiration from a Chrome OS tool called | |
515 | 'cros_bundle_firmware', which I wrote some years ago. That tool was based on | |
516 | a reasonably simple and sound design but has expanded greatly over the | |
517 | years. In particular its handling of x86 images is convoluted. | |
518 | ||
519 | Quite a few lessons have been learned which are hopefully be applied here. | |
520 | ||
521 | ||
522 | Design notes | |
523 | ------------ | |
524 | ||
525 | On the face of it, a tool to create firmware images should be fairly simple: | |
526 | just find all the input binaries and place them at the right place in the | |
527 | image. The difficulty comes from the wide variety of input types (simple | |
528 | flat binaries containing code, packaged data with various headers), packing | |
529 | requirments (alignment, spacing, device boundaries) and other required | |
530 | features such as hierarchical images. | |
531 | ||
532 | The design challenge is to make it easy to create simple images, while | |
533 | allowing the more complex cases to be supported. For example, for most | |
534 | images we don't much care exactly where each binary ends up, so we should | |
535 | not have to specify that unnecessarily. | |
536 | ||
537 | New entry types should aim to provide simple usage where possible. If new | |
538 | core features are needed, they can be added in the Entry base class. | |
539 | ||
540 | ||
541 | To do | |
542 | ----- | |
543 | ||
544 | Some ideas: | |
545 | - Fill out the device tree to include the final position and size of each | |
546 | entry (since the input file may not always specify these) | |
547 | - Use of-platdata to make the information available to code that is unable | |
548 | to use device tree (such as a very small SPL image) | |
549 | - Write an image map to a text file | |
550 | - Allow easy building of images by specifying just the board name | |
551 | - Produce a full Python binding for libfdt (for upstream) | |
552 | - Add an option to decode an image into the constituent binaries | |
553 | - Suppoort hierarchical images (packing of binaries into another binary | |
554 | which is then placed in the image) | |
555 | - Support building an image for a board (-b) more completely, with a | |
556 | configurable build directory | |
557 | - Consider making binman work with buildman, although if it is used in the | |
558 | Makefile, this will be automatic | |
559 | - Implement align-end | |
560 | ||
561 | -- | |
562 | Simon Glass <[email protected]> | |
563 | 7/7/2016 |