Prepare v2022.01
[platform/kernel/u-boot.git] / tools / binman / ftest.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
4 #
5 # To run a single test, change to this directory, and:
6 #
7 #    python -m unittest func_test.TestFunctional.testHelp
8
9 import collections
10 import gzip
11 import hashlib
12 from optparse import OptionParser
13 import os
14 import re
15 import shutil
16 import struct
17 import sys
18 import tempfile
19 import unittest
20
21 from binman import cbfs_util
22 from binman import cmdline
23 from binman import control
24 from binman import elf
25 from binman import elf_test
26 from binman import fmap_util
27 from binman import state
28 from dtoc import fdt
29 from dtoc import fdt_util
30 from binman.etype import fdtmap
31 from binman.etype import image_header
32 from binman.image import Image
33 from patman import command
34 from patman import test_util
35 from patman import tools
36 from patman import tout
37
38 # Contents of test files, corresponding to different entry types
39 U_BOOT_DATA           = b'1234'
40 U_BOOT_IMG_DATA       = b'img'
41 U_BOOT_SPL_DATA       = b'56780123456789abcdefghi'
42 U_BOOT_TPL_DATA       = b'tpl9876543210fedcbazyw'
43 BLOB_DATA             = b'89'
44 ME_DATA               = b'0abcd'
45 VGA_DATA              = b'vga'
46 U_BOOT_DTB_DATA       = b'udtb'
47 U_BOOT_SPL_DTB_DATA   = b'spldtb'
48 U_BOOT_TPL_DTB_DATA   = b'tpldtb'
49 X86_START16_DATA      = b'start16'
50 X86_START16_SPL_DATA  = b'start16spl'
51 X86_START16_TPL_DATA  = b'start16tpl'
52 X86_RESET16_DATA      = b'reset16'
53 X86_RESET16_SPL_DATA  = b'reset16spl'
54 X86_RESET16_TPL_DATA  = b'reset16tpl'
55 PPC_MPC85XX_BR_DATA   = b'ppcmpc85xxbr'
56 U_BOOT_NODTB_DATA     = b'nodtb with microcode pointer somewhere in here'
57 U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
58 U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
59 FSP_DATA              = b'fsp'
60 CMC_DATA              = b'cmc'
61 VBT_DATA              = b'vbt'
62 MRC_DATA              = b'mrc'
63 TEXT_DATA             = 'text'
64 TEXT_DATA2            = 'text2'
65 TEXT_DATA3            = 'text3'
66 CROS_EC_RW_DATA       = b'ecrw'
67 GBB_DATA              = b'gbbd'
68 BMPBLK_DATA           = b'bmp'
69 VBLOCK_DATA           = b'vblk'
70 FILES_DATA            = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
71                          b"sorry you're alive\n")
72 COMPRESS_DATA         = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
73 COMPRESS_DATA_BIG     = COMPRESS_DATA * 2
74 REFCODE_DATA          = b'refcode'
75 FSP_M_DATA            = b'fsp_m'
76 FSP_S_DATA            = b'fsp_s'
77 FSP_T_DATA            = b'fsp_t'
78 ATF_BL31_DATA         = b'bl31'
79 OPENSBI_DATA          = b'opensbi'
80 SCP_DATA              = b'scp'
81 TEST_FDT1_DATA        = b'fdt1'
82 TEST_FDT2_DATA        = b'test-fdt2'
83 ENV_DATA              = b'var1=1\nvar2="2"'
84
85 # Subdirectory of the input dir to use to put test FDTs
86 TEST_FDT_SUBDIR       = 'fdts'
87
88 # The expected size for the device tree in some tests
89 EXTRACT_DTB_SIZE = 0x3c9
90
91 # Properties expected to be in the device tree when update_dtb is used
92 BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
93
94 # Extra properties expected to be in the device tree when allow-repack is used
95 REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
96
97
98 class TestFunctional(unittest.TestCase):
99     """Functional tests for binman
100
101     Most of these use a sample .dts file to build an image and then check
102     that it looks correct. The sample files are in the test/ subdirectory
103     and are numbered.
104
105     For each entry type a very small test file is created using fixed
106     string contents. This makes it easy to test that things look right, and
107     debug problems.
108
109     In some cases a 'real' file must be used - these are also supplied in
110     the test/ diurectory.
111     """
112     @classmethod
113     def setUpClass(cls):
114         global entry
115         from binman import entry
116
117         # Handle the case where argv[0] is 'python'
118         cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
119         cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
120
121         # Create a temporary directory for input files
122         cls._indir = tempfile.mkdtemp(prefix='binmant.')
123
124         # Create some test files
125         TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
126         TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
127         TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
128         TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
129         TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
130         TestFunctional._MakeInputFile('me.bin', ME_DATA)
131         TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
132         cls._ResetDtbs()
133
134         TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
135
136         TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
137         TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
138                                       X86_START16_SPL_DATA)
139         TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
140                                       X86_START16_TPL_DATA)
141
142         TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
143                                       X86_RESET16_DATA)
144         TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
145                                       X86_RESET16_SPL_DATA)
146         TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
147                                       X86_RESET16_TPL_DATA)
148
149         TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
150         TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
151                                       U_BOOT_SPL_NODTB_DATA)
152         TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
153                                       U_BOOT_TPL_NODTB_DATA)
154         TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
155         TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
156         TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
157         TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
158         TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
159         TestFunctional._MakeInputDir('devkeys')
160         TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
161         TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
162         TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
163         TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
164         TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
165
166         cls._elf_testdir = os.path.join(cls._indir, 'elftest')
167         elf_test.BuildElfTestFiles(cls._elf_testdir)
168
169         # ELF file with a '_dt_ucode_base_size' symbol
170         TestFunctional._MakeInputFile('u-boot',
171             tools.ReadFile(cls.ElfTestFile('u_boot_ucode_ptr')))
172
173         # Intel flash descriptor file
174         cls._SetupDescriptor()
175
176         shutil.copytree(cls.TestFile('files'),
177                         os.path.join(cls._indir, 'files'))
178
179         TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
180         TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
181         TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
182         TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
183         TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
184
185         # Add a few .dtb files for testing
186         TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
187                                       TEST_FDT1_DATA)
188         TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR,
189                                       TEST_FDT2_DATA)
190
191         TestFunctional._MakeInputFile('env.txt', ENV_DATA)
192
193         # Travis-CI may have an old lz4
194         cls.have_lz4 = True
195         try:
196             tools.Run('lz4', '--no-frame-crc', '-c',
197                       os.path.join(cls._indir, 'u-boot.bin'), binary=True)
198         except:
199             cls.have_lz4 = False
200
201     @classmethod
202     def tearDownClass(cls):
203         """Remove the temporary input directory and its contents"""
204         if cls.preserve_indir:
205             print('Preserving input dir: %s' % cls._indir)
206         else:
207             if cls._indir:
208                 shutil.rmtree(cls._indir)
209         cls._indir = None
210
211     @classmethod
212     def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
213                         toolpath=None, verbosity=None):
214         """Accept arguments controlling test execution
215
216         Args:
217             preserve_indir: Preserve the shared input directory used by all
218                 tests in this class.
219             preserve_outdir: Preserve the output directories used by tests. Each
220                 test has its own, so this is normally only useful when running a
221                 single test.
222             toolpath: ist of paths to use for tools
223         """
224         cls.preserve_indir = preserve_indir
225         cls.preserve_outdirs = preserve_outdirs
226         cls.toolpath = toolpath
227         cls.verbosity = verbosity
228
229     def _CheckLz4(self):
230         if not self.have_lz4:
231             self.skipTest('lz4 --no-frame-crc not available')
232
233     def _CleanupOutputDir(self):
234         """Remove the temporary output directory"""
235         if self.preserve_outdirs:
236             print('Preserving output dir: %s' % tools.outdir)
237         else:
238             tools._FinaliseForTest()
239
240     def setUp(self):
241         # Enable this to turn on debugging output
242         # tout.Init(tout.DEBUG)
243         command.test_result = None
244
245     def tearDown(self):
246         """Remove the temporary output directory"""
247         self._CleanupOutputDir()
248
249     def _SetupImageInTmpdir(self):
250         """Set up the output image in a new temporary directory
251
252         This is used when an image has been generated in the output directory,
253         but we want to run binman again. This will create a new output
254         directory and fail to delete the original one.
255
256         This creates a new temporary directory, copies the image to it (with a
257         new name) and removes the old output directory.
258
259         Returns:
260             Tuple:
261                 Temporary directory to use
262                 New image filename
263         """
264         image_fname = tools.GetOutputFilename('image.bin')
265         tmpdir = tempfile.mkdtemp(prefix='binman.')
266         updated_fname = os.path.join(tmpdir, 'image-updated.bin')
267         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
268         self._CleanupOutputDir()
269         return tmpdir, updated_fname
270
271     @classmethod
272     def _ResetDtbs(cls):
273         TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
274         TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
275         TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
276
277     def _RunBinman(self, *args, **kwargs):
278         """Run binman using the command line
279
280         Args:
281             Arguments to pass, as a list of strings
282             kwargs: Arguments to pass to Command.RunPipe()
283         """
284         result = command.RunPipe([[self._binman_pathname] + list(args)],
285                 capture=True, capture_stderr=True, raise_on_error=False)
286         if result.return_code and kwargs.get('raise_on_error', True):
287             raise Exception("Error running '%s': %s" % (' '.join(args),
288                             result.stdout + result.stderr))
289         return result
290
291     def _DoBinman(self, *argv):
292         """Run binman using directly (in the same process)
293
294         Args:
295             Arguments to pass, as a list of strings
296         Returns:
297             Return value (0 for success)
298         """
299         argv = list(argv)
300         args = cmdline.ParseArgs(argv)
301         args.pager = 'binman-invalid-pager'
302         args.build_dir = self._indir
303
304         # For testing, you can force an increase in verbosity here
305         # args.verbosity = tout.DEBUG
306         return control.Binman(args)
307
308     def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
309                     entry_args=None, images=None, use_real_dtb=False,
310                     use_expanded=False, verbosity=None, allow_missing=False,
311                     allow_fake_blobs=False, extra_indirs=None, threads=None,
312                     test_section_timeout=False, update_fdt_in_elf=None):
313         """Run binman with a given test file
314
315         Args:
316             fname: Device-tree source filename to use (e.g. 005_simple.dts)
317             debug: True to enable debugging output
318             map: True to output map files for the images
319             update_dtb: Update the offset and size of each entry in the device
320                 tree before packing it into the image
321             entry_args: Dict of entry args to supply to binman
322                 key: arg name
323                 value: value of that arg
324             images: List of image names to build
325             use_real_dtb: True to use the test file as the contents of
326                 the u-boot-dtb entry. Normally this is not needed and the
327                 test contents (the U_BOOT_DTB_DATA string) can be used.
328                 But in some test we need the real contents.
329             use_expanded: True to use expanded entries where available, e.g.
330                 'u-boot-expanded' instead of 'u-boot'
331             verbosity: Verbosity level to use (0-3, None=don't set it)
332             allow_missing: Set the '--allow-missing' flag so that missing
333                 external binaries just produce a warning instead of an error
334             allow_fake_blobs: Set the '--fake-ext-blobs' flag
335             extra_indirs: Extra input directories to add using -I
336             threads: Number of threads to use (None for default, 0 for
337                 single-threaded)
338             test_section_timeout: True to force the first time to timeout, as
339                 used in testThreadTimeout()
340             update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx
341
342         Returns:
343             int return code, 0 on success
344         """
345         args = []
346         if debug:
347             args.append('-D')
348         if verbosity is not None:
349             args.append('-v%d' % verbosity)
350         elif self.verbosity:
351             args.append('-v%d' % self.verbosity)
352         if self.toolpath:
353             for path in self.toolpath:
354                 args += ['--toolpath', path]
355         if threads is not None:
356             args.append('-T%d' % threads)
357         if test_section_timeout:
358             args.append('--test-section-timeout')
359         args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
360         if map:
361             args.append('-m')
362         if update_dtb:
363             args.append('-u')
364         if not use_real_dtb:
365             args.append('--fake-dtb')
366         if not use_expanded:
367             args.append('--no-expanded')
368         if entry_args:
369             for arg, value in entry_args.items():
370                 args.append('-a%s=%s' % (arg, value))
371         if allow_missing:
372             args.append('-M')
373         if allow_fake_blobs:
374             args.append('--fake-ext-blobs')
375         if update_fdt_in_elf:
376             args += ['--update-fdt-in-elf', update_fdt_in_elf]
377         if images:
378             for image in images:
379                 args += ['-i', image]
380         if extra_indirs:
381             for indir in extra_indirs:
382                 args += ['-I', indir]
383         return self._DoBinman(*args)
384
385     def _SetupDtb(self, fname, outfile='u-boot.dtb'):
386         """Set up a new test device-tree file
387
388         The given file is compiled and set up as the device tree to be used
389         for ths test.
390
391         Args:
392             fname: Filename of .dts file to read
393             outfile: Output filename for compiled device-tree binary
394
395         Returns:
396             Contents of device-tree binary
397         """
398         tmpdir = tempfile.mkdtemp(prefix='binmant.')
399         dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
400         with open(dtb, 'rb') as fd:
401             data = fd.read()
402             TestFunctional._MakeInputFile(outfile, data)
403         shutil.rmtree(tmpdir)
404         return data
405
406     def _GetDtbContentsForSplTpl(self, dtb_data, name):
407         """Create a version of the main DTB for SPL or SPL
408
409         For testing we don't actually have different versions of the DTB. With
410         U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
411         we don't normally have any unwanted nodes.
412
413         We still want the DTBs for SPL and TPL to be different though, since
414         otherwise it is confusing to know which one we are looking at. So add
415         an 'spl' or 'tpl' property to the top-level node.
416
417         Args:
418             dtb_data: dtb data to modify (this should be a value devicetree)
419             name: Name of a new property to add
420
421         Returns:
422             New dtb data with the property added
423         """
424         dtb = fdt.Fdt.FromData(dtb_data)
425         dtb.Scan()
426         dtb.GetNode('/binman').AddZeroProp(name)
427         dtb.Sync(auto_resize=True)
428         dtb.Pack()
429         return dtb.GetContents()
430
431     def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
432                        map=False, update_dtb=False, entry_args=None,
433                        reset_dtbs=True, extra_indirs=None, threads=None):
434         """Run binman and return the resulting image
435
436         This runs binman with a given test file and then reads the resulting
437         output file. It is a shortcut function since most tests need to do
438         these steps.
439
440         Raises an assertion failure if binman returns a non-zero exit code.
441
442         Args:
443             fname: Device-tree source filename to use (e.g. 005_simple.dts)
444             use_real_dtb: True to use the test file as the contents of
445                 the u-boot-dtb entry. Normally this is not needed and the
446                 test contents (the U_BOOT_DTB_DATA string) can be used.
447                 But in some test we need the real contents.
448             use_expanded: True to use expanded entries where available, e.g.
449                 'u-boot-expanded' instead of 'u-boot'
450             map: True to output map files for the images
451             update_dtb: Update the offset and size of each entry in the device
452                 tree before packing it into the image
453             entry_args: Dict of entry args to supply to binman
454                 key: arg name
455                 value: value of that arg
456             reset_dtbs: With use_real_dtb the test dtb is overwritten by this
457                 function. If reset_dtbs is True, then the original test dtb
458                 is written back before this function finishes
459             extra_indirs: Extra input directories to add using -I
460             threads: Number of threads to use (None for default, 0 for
461                 single-threaded)
462
463         Returns:
464             Tuple:
465                 Resulting image contents
466                 Device tree contents
467                 Map data showing contents of image (or None if none)
468                 Output device tree binary filename ('u-boot.dtb' path)
469         """
470         dtb_data = None
471         # Use the compiled test file as the u-boot-dtb input
472         if use_real_dtb:
473             dtb_data = self._SetupDtb(fname)
474
475             # For testing purposes, make a copy of the DT for SPL and TPL. Add
476             # a node indicating which it is, so aid verification.
477             for name in ['spl', 'tpl']:
478                 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
479                 outfile = os.path.join(self._indir, dtb_fname)
480                 TestFunctional._MakeInputFile(dtb_fname,
481                         self._GetDtbContentsForSplTpl(dtb_data, name))
482
483         try:
484             retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
485                     entry_args=entry_args, use_real_dtb=use_real_dtb,
486                     use_expanded=use_expanded, extra_indirs=extra_indirs,
487                     threads=threads)
488             self.assertEqual(0, retcode)
489             out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out')
490
491             # Find the (only) image, read it and return its contents
492             image = control.images['image']
493             image_fname = tools.GetOutputFilename('image.bin')
494             self.assertTrue(os.path.exists(image_fname))
495             if map:
496                 map_fname = tools.GetOutputFilename('image.map')
497                 with open(map_fname) as fd:
498                     map_data = fd.read()
499             else:
500                 map_data = None
501             with open(image_fname, 'rb') as fd:
502                 return fd.read(), dtb_data, map_data, out_dtb_fname
503         finally:
504             # Put the test file back
505             if reset_dtbs and use_real_dtb:
506                 self._ResetDtbs()
507
508     def _DoReadFileRealDtb(self, fname):
509         """Run binman with a real .dtb file and return the resulting data
510
511         Args:
512             fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
513
514         Returns:
515             Resulting image contents
516         """
517         return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
518
519     def _DoReadFile(self, fname, use_real_dtb=False):
520         """Helper function which discards the device-tree binary
521
522         Args:
523             fname: Device-tree source filename to use (e.g. 005_simple.dts)
524             use_real_dtb: True to use the test file as the contents of
525                 the u-boot-dtb entry. Normally this is not needed and the
526                 test contents (the U_BOOT_DTB_DATA string) can be used.
527                 But in some test we need the real contents.
528
529         Returns:
530             Resulting image contents
531         """
532         return self._DoReadFileDtb(fname, use_real_dtb)[0]
533
534     @classmethod
535     def _MakeInputFile(cls, fname, contents):
536         """Create a new test input file, creating directories as needed
537
538         Args:
539             fname: Filename to create
540             contents: File contents to write in to the file
541         Returns:
542             Full pathname of file created
543         """
544         pathname = os.path.join(cls._indir, fname)
545         dirname = os.path.dirname(pathname)
546         if dirname and not os.path.exists(dirname):
547             os.makedirs(dirname)
548         with open(pathname, 'wb') as fd:
549             fd.write(contents)
550         return pathname
551
552     @classmethod
553     def _MakeInputDir(cls, dirname):
554         """Create a new test input directory, creating directories as needed
555
556         Args:
557             dirname: Directory name to create
558
559         Returns:
560             Full pathname of directory created
561         """
562         pathname = os.path.join(cls._indir, dirname)
563         if not os.path.exists(pathname):
564             os.makedirs(pathname)
565         return pathname
566
567     @classmethod
568     def _SetupSplElf(cls, src_fname='bss_data'):
569         """Set up an ELF file with a '_dt_ucode_base_size' symbol
570
571         Args:
572             Filename of ELF file to use as SPL
573         """
574         TestFunctional._MakeInputFile('spl/u-boot-spl',
575             tools.ReadFile(cls.ElfTestFile(src_fname)))
576
577     @classmethod
578     def _SetupTplElf(cls, src_fname='bss_data'):
579         """Set up an ELF file with a '_dt_ucode_base_size' symbol
580
581         Args:
582             Filename of ELF file to use as TPL
583         """
584         TestFunctional._MakeInputFile('tpl/u-boot-tpl',
585             tools.ReadFile(cls.ElfTestFile(src_fname)))
586
587     @classmethod
588     def _SetupDescriptor(cls):
589         with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
590             TestFunctional._MakeInputFile('descriptor.bin', fd.read())
591
592     @classmethod
593     def TestFile(cls, fname):
594         return os.path.join(cls._binman_dir, 'test', fname)
595
596     @classmethod
597     def ElfTestFile(cls, fname):
598         return os.path.join(cls._elf_testdir, fname)
599
600     def AssertInList(self, grep_list, target):
601         """Assert that at least one of a list of things is in a target
602
603         Args:
604             grep_list: List of strings to check
605             target: Target string
606         """
607         for grep in grep_list:
608             if grep in target:
609                 return
610         self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
611
612     def CheckNoGaps(self, entries):
613         """Check that all entries fit together without gaps
614
615         Args:
616             entries: List of entries to check
617         """
618         offset = 0
619         for entry in entries.values():
620             self.assertEqual(offset, entry.offset)
621             offset += entry.size
622
623     def GetFdtLen(self, dtb):
624         """Get the totalsize field from a device-tree binary
625
626         Args:
627             dtb: Device-tree binary contents
628
629         Returns:
630             Total size of device-tree binary, from the header
631         """
632         return struct.unpack('>L', dtb[4:8])[0]
633
634     def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
635         def AddNode(node, path):
636             if node.name != '/':
637                 path += '/' + node.name
638             for prop in node.props.values():
639                 if prop.name in prop_names:
640                     prop_path = path + ':' + prop.name
641                     tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
642                         prop.value)
643             for subnode in node.subnodes:
644                 AddNode(subnode, path)
645
646         tree = {}
647         AddNode(dtb.GetRoot(), '')
648         return tree
649
650     def testRun(self):
651         """Test a basic run with valid args"""
652         result = self._RunBinman('-h')
653
654     def testFullHelp(self):
655         """Test that the full help is displayed with -H"""
656         result = self._RunBinman('-H')
657         help_file = os.path.join(self._binman_dir, 'README.rst')
658         # Remove possible extraneous strings
659         extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
660         gothelp = result.stdout.replace(extra, '')
661         self.assertEqual(len(gothelp), os.path.getsize(help_file))
662         self.assertEqual(0, len(result.stderr))
663         self.assertEqual(0, result.return_code)
664
665     def testFullHelpInternal(self):
666         """Test that the full help is displayed with -H"""
667         try:
668             command.test_result = command.CommandResult()
669             result = self._DoBinman('-H')
670             help_file = os.path.join(self._binman_dir, 'README.rst')
671         finally:
672             command.test_result = None
673
674     def testHelp(self):
675         """Test that the basic help is displayed with -h"""
676         result = self._RunBinman('-h')
677         self.assertTrue(len(result.stdout) > 200)
678         self.assertEqual(0, len(result.stderr))
679         self.assertEqual(0, result.return_code)
680
681     def testBoard(self):
682         """Test that we can run it with a specific board"""
683         self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
684         TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
685         result = self._DoBinman('build', '-n', '-b', 'sandbox')
686         self.assertEqual(0, result)
687
688     def testNeedBoard(self):
689         """Test that we get an error when no board ius supplied"""
690         with self.assertRaises(ValueError) as e:
691             result = self._DoBinman('build')
692         self.assertIn("Must provide a board to process (use -b <board>)",
693                 str(e.exception))
694
695     def testMissingDt(self):
696         """Test that an invalid device-tree file generates an error"""
697         with self.assertRaises(Exception) as e:
698             self._RunBinman('build', '-d', 'missing_file')
699         # We get one error from libfdt, and a different one from fdtget.
700         self.AssertInList(["Couldn't open blob from 'missing_file'",
701                            'No such file or directory'], str(e.exception))
702
703     def testBrokenDt(self):
704         """Test that an invalid device-tree source file generates an error
705
706         Since this is a source file it should be compiled and the error
707         will come from the device-tree compiler (dtc).
708         """
709         with self.assertRaises(Exception) as e:
710             self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
711         self.assertIn("FATAL ERROR: Unable to parse input tree",
712                 str(e.exception))
713
714     def testMissingNode(self):
715         """Test that a device tree without a 'binman' node generates an error"""
716         with self.assertRaises(Exception) as e:
717             self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
718         self.assertIn("does not have a 'binman' node", str(e.exception))
719
720     def testEmpty(self):
721         """Test that an empty binman node works OK (i.e. does nothing)"""
722         result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
723         self.assertEqual(0, len(result.stderr))
724         self.assertEqual(0, result.return_code)
725
726     def testInvalidEntry(self):
727         """Test that an invalid entry is flagged"""
728         with self.assertRaises(Exception) as e:
729             result = self._RunBinman('build', '-d',
730                                      self.TestFile('004_invalid_entry.dts'))
731         self.assertIn("Unknown entry type 'not-a-valid-type' in node "
732                 "'/binman/not-a-valid-type'", str(e.exception))
733
734     def testSimple(self):
735         """Test a simple binman with a single file"""
736         data = self._DoReadFile('005_simple.dts')
737         self.assertEqual(U_BOOT_DATA, data)
738
739     def testSimpleDebug(self):
740         """Test a simple binman run with debugging enabled"""
741         self._DoTestFile('005_simple.dts', debug=True)
742
743     def testDual(self):
744         """Test that we can handle creating two images
745
746         This also tests image padding.
747         """
748         retcode = self._DoTestFile('006_dual_image.dts')
749         self.assertEqual(0, retcode)
750
751         image = control.images['image1']
752         self.assertEqual(len(U_BOOT_DATA), image.size)
753         fname = tools.GetOutputFilename('image1.bin')
754         self.assertTrue(os.path.exists(fname))
755         with open(fname, 'rb') as fd:
756             data = fd.read()
757             self.assertEqual(U_BOOT_DATA, data)
758
759         image = control.images['image2']
760         self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
761         fname = tools.GetOutputFilename('image2.bin')
762         self.assertTrue(os.path.exists(fname))
763         with open(fname, 'rb') as fd:
764             data = fd.read()
765             self.assertEqual(U_BOOT_DATA, data[3:7])
766             self.assertEqual(tools.GetBytes(0, 3), data[:3])
767             self.assertEqual(tools.GetBytes(0, 5), data[7:])
768
769     def testBadAlign(self):
770         """Test that an invalid alignment value is detected"""
771         with self.assertRaises(ValueError) as e:
772             self._DoTestFile('007_bad_align.dts')
773         self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
774                       "of two", str(e.exception))
775
776     def testPackSimple(self):
777         """Test that packing works as expected"""
778         retcode = self._DoTestFile('008_pack.dts')
779         self.assertEqual(0, retcode)
780         self.assertIn('image', control.images)
781         image = control.images['image']
782         entries = image.GetEntries()
783         self.assertEqual(5, len(entries))
784
785         # First u-boot
786         self.assertIn('u-boot', entries)
787         entry = entries['u-boot']
788         self.assertEqual(0, entry.offset)
789         self.assertEqual(len(U_BOOT_DATA), entry.size)
790
791         # Second u-boot, aligned to 16-byte boundary
792         self.assertIn('u-boot-align', entries)
793         entry = entries['u-boot-align']
794         self.assertEqual(16, entry.offset)
795         self.assertEqual(len(U_BOOT_DATA), entry.size)
796
797         # Third u-boot, size 23 bytes
798         self.assertIn('u-boot-size', entries)
799         entry = entries['u-boot-size']
800         self.assertEqual(20, entry.offset)
801         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
802         self.assertEqual(23, entry.size)
803
804         # Fourth u-boot, placed immediate after the above
805         self.assertIn('u-boot-next', entries)
806         entry = entries['u-boot-next']
807         self.assertEqual(43, entry.offset)
808         self.assertEqual(len(U_BOOT_DATA), entry.size)
809
810         # Fifth u-boot, placed at a fixed offset
811         self.assertIn('u-boot-fixed', entries)
812         entry = entries['u-boot-fixed']
813         self.assertEqual(61, entry.offset)
814         self.assertEqual(len(U_BOOT_DATA), entry.size)
815
816         self.assertEqual(65, image.size)
817
818     def testPackExtra(self):
819         """Test that extra packing feature works as expected"""
820         data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
821                                                         update_dtb=True)
822
823         self.assertIn('image', control.images)
824         image = control.images['image']
825         entries = image.GetEntries()
826         self.assertEqual(5, len(entries))
827
828         # First u-boot with padding before and after
829         self.assertIn('u-boot', entries)
830         entry = entries['u-boot']
831         self.assertEqual(0, entry.offset)
832         self.assertEqual(3, entry.pad_before)
833         self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
834         self.assertEqual(U_BOOT_DATA, entry.data)
835         self.assertEqual(tools.GetBytes(0, 3) + U_BOOT_DATA +
836                          tools.GetBytes(0, 5), data[:entry.size])
837         pos = entry.size
838
839         # Second u-boot has an aligned size, but it has no effect
840         self.assertIn('u-boot-align-size-nop', entries)
841         entry = entries['u-boot-align-size-nop']
842         self.assertEqual(pos, entry.offset)
843         self.assertEqual(len(U_BOOT_DATA), entry.size)
844         self.assertEqual(U_BOOT_DATA, entry.data)
845         self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
846         pos += entry.size
847
848         # Third u-boot has an aligned size too
849         self.assertIn('u-boot-align-size', entries)
850         entry = entries['u-boot-align-size']
851         self.assertEqual(pos, entry.offset)
852         self.assertEqual(32, entry.size)
853         self.assertEqual(U_BOOT_DATA, entry.data)
854         self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 32 - len(U_BOOT_DATA)),
855                          data[pos:pos + entry.size])
856         pos += entry.size
857
858         # Fourth u-boot has an aligned end
859         self.assertIn('u-boot-align-end', entries)
860         entry = entries['u-boot-align-end']
861         self.assertEqual(48, entry.offset)
862         self.assertEqual(16, entry.size)
863         self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
864         self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 16 - len(U_BOOT_DATA)),
865                          data[pos:pos + entry.size])
866         pos += entry.size
867
868         # Fifth u-boot immediately afterwards
869         self.assertIn('u-boot-align-both', entries)
870         entry = entries['u-boot-align-both']
871         self.assertEqual(64, entry.offset)
872         self.assertEqual(64, entry.size)
873         self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
874         self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 64 - len(U_BOOT_DATA)),
875                          data[pos:pos + entry.size])
876
877         self.CheckNoGaps(entries)
878         self.assertEqual(128, image.size)
879
880         dtb = fdt.Fdt(out_dtb_fname)
881         dtb.Scan()
882         props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
883         expected = {
884             'image-pos': 0,
885             'offset': 0,
886             'size': 128,
887
888             'u-boot:image-pos': 0,
889             'u-boot:offset': 0,
890             'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
891
892             'u-boot-align-size-nop:image-pos': 12,
893             'u-boot-align-size-nop:offset': 12,
894             'u-boot-align-size-nop:size': 4,
895
896             'u-boot-align-size:image-pos': 16,
897             'u-boot-align-size:offset': 16,
898             'u-boot-align-size:size': 32,
899
900             'u-boot-align-end:image-pos': 48,
901             'u-boot-align-end:offset': 48,
902             'u-boot-align-end:size': 16,
903
904             'u-boot-align-both:image-pos': 64,
905             'u-boot-align-both:offset': 64,
906             'u-boot-align-both:size': 64,
907             }
908         self.assertEqual(expected, props)
909
910     def testPackAlignPowerOf2(self):
911         """Test that invalid entry alignment is detected"""
912         with self.assertRaises(ValueError) as e:
913             self._DoTestFile('010_pack_align_power2.dts')
914         self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
915                       "of two", str(e.exception))
916
917     def testPackAlignSizePowerOf2(self):
918         """Test that invalid entry size alignment is detected"""
919         with self.assertRaises(ValueError) as e:
920             self._DoTestFile('011_pack_align_size_power2.dts')
921         self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
922                       "power of two", str(e.exception))
923
924     def testPackInvalidAlign(self):
925         """Test detection of an offset that does not match its alignment"""
926         with self.assertRaises(ValueError) as e:
927             self._DoTestFile('012_pack_inv_align.dts')
928         self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
929                       "align 0x4 (4)", str(e.exception))
930
931     def testPackInvalidSizeAlign(self):
932         """Test that invalid entry size alignment is detected"""
933         with self.assertRaises(ValueError) as e:
934             self._DoTestFile('013_pack_inv_size_align.dts')
935         self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
936                       "align-size 0x4 (4)", str(e.exception))
937
938     def testPackOverlap(self):
939         """Test that overlapping regions are detected"""
940         with self.assertRaises(ValueError) as e:
941             self._DoTestFile('014_pack_overlap.dts')
942         self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
943                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
944                       str(e.exception))
945
946     def testPackEntryOverflow(self):
947         """Test that entries that overflow their size are detected"""
948         with self.assertRaises(ValueError) as e:
949             self._DoTestFile('015_pack_overflow.dts')
950         self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
951                       "but entry size is 0x3 (3)", str(e.exception))
952
953     def testPackImageOverflow(self):
954         """Test that entries which overflow the image size are detected"""
955         with self.assertRaises(ValueError) as e:
956             self._DoTestFile('016_pack_image_overflow.dts')
957         self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
958                       "size 0x3 (3)", str(e.exception))
959
960     def testPackImageSize(self):
961         """Test that the image size can be set"""
962         retcode = self._DoTestFile('017_pack_image_size.dts')
963         self.assertEqual(0, retcode)
964         self.assertIn('image', control.images)
965         image = control.images['image']
966         self.assertEqual(7, image.size)
967
968     def testPackImageSizeAlign(self):
969         """Test that image size alignemnt works as expected"""
970         retcode = self._DoTestFile('018_pack_image_align.dts')
971         self.assertEqual(0, retcode)
972         self.assertIn('image', control.images)
973         image = control.images['image']
974         self.assertEqual(16, image.size)
975
976     def testPackInvalidImageAlign(self):
977         """Test that invalid image alignment is detected"""
978         with self.assertRaises(ValueError) as e:
979             self._DoTestFile('019_pack_inv_image_align.dts')
980         self.assertIn("Section '/binman': Size 0x7 (7) does not match "
981                       "align-size 0x8 (8)", str(e.exception))
982
983     def testPackAlignPowerOf2(self):
984         """Test that invalid image alignment is detected"""
985         with self.assertRaises(ValueError) as e:
986             self._DoTestFile('020_pack_inv_image_align_power2.dts')
987         self.assertIn("Image '/binman': Alignment size 131 must be a power of "
988                       "two", str(e.exception))
989
990     def testImagePadByte(self):
991         """Test that the image pad byte can be specified"""
992         self._SetupSplElf()
993         data = self._DoReadFile('021_image_pad.dts')
994         self.assertEqual(U_BOOT_SPL_DATA + tools.GetBytes(0xff, 1) +
995                          U_BOOT_DATA, data)
996
997     def testImageName(self):
998         """Test that image files can be named"""
999         retcode = self._DoTestFile('022_image_name.dts')
1000         self.assertEqual(0, retcode)
1001         image = control.images['image1']
1002         fname = tools.GetOutputFilename('test-name')
1003         self.assertTrue(os.path.exists(fname))
1004
1005         image = control.images['image2']
1006         fname = tools.GetOutputFilename('test-name.xx')
1007         self.assertTrue(os.path.exists(fname))
1008
1009     def testBlobFilename(self):
1010         """Test that generic blobs can be provided by filename"""
1011         data = self._DoReadFile('023_blob.dts')
1012         self.assertEqual(BLOB_DATA, data)
1013
1014     def testPackSorted(self):
1015         """Test that entries can be sorted"""
1016         self._SetupSplElf()
1017         data = self._DoReadFile('024_sorted.dts')
1018         self.assertEqual(tools.GetBytes(0, 1) + U_BOOT_SPL_DATA +
1019                          tools.GetBytes(0, 2) + U_BOOT_DATA, data)
1020
1021     def testPackZeroOffset(self):
1022         """Test that an entry at offset 0 is not given a new offset"""
1023         with self.assertRaises(ValueError) as e:
1024             self._DoTestFile('025_pack_zero_size.dts')
1025         self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
1026                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1027                       str(e.exception))
1028
1029     def testPackUbootDtb(self):
1030         """Test that a device tree can be added to U-Boot"""
1031         data = self._DoReadFile('026_pack_u_boot_dtb.dts')
1032         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
1033
1034     def testPackX86RomNoSize(self):
1035         """Test that the end-at-4gb property requires a size property"""
1036         with self.assertRaises(ValueError) as e:
1037             self._DoTestFile('027_pack_4gb_no_size.dts')
1038         self.assertIn("Image '/binman': Section size must be provided when "
1039                       "using end-at-4gb", str(e.exception))
1040
1041     def test4gbAndSkipAtStartTogether(self):
1042         """Test that the end-at-4gb and skip-at-size property can't be used
1043         together"""
1044         with self.assertRaises(ValueError) as e:
1045             self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
1046         self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
1047                       "'skip-at-start'", str(e.exception))
1048
1049     def testPackX86RomOutside(self):
1050         """Test that the end-at-4gb property checks for offset boundaries"""
1051         with self.assertRaises(ValueError) as e:
1052             self._DoTestFile('028_pack_4gb_outside.dts')
1053         self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1054                       "is outside the section '/binman' starting at "
1055                       '0xffffffe0 (4294967264) of size 0x20 (32)',
1056                       str(e.exception))
1057
1058     def testPackX86Rom(self):
1059         """Test that a basic x86 ROM can be created"""
1060         self._SetupSplElf()
1061         data = self._DoReadFile('029_x86_rom.dts')
1062         self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 3) + U_BOOT_SPL_DATA +
1063                          tools.GetBytes(0, 2), data)
1064
1065     def testPackX86RomMeNoDesc(self):
1066         """Test that an invalid Intel descriptor entry is detected"""
1067         try:
1068             TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
1069             with self.assertRaises(ValueError) as e:
1070                 self._DoTestFile('163_x86_rom_me_empty.dts')
1071             self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1072                           str(e.exception))
1073         finally:
1074             self._SetupDescriptor()
1075
1076     def testPackX86RomBadDesc(self):
1077         """Test that the Intel requires a descriptor entry"""
1078         with self.assertRaises(ValueError) as e:
1079             self._DoTestFile('030_x86_rom_me_no_desc.dts')
1080         self.assertIn("Node '/binman/intel-me': No offset set with "
1081                       "offset-unset: should another entry provide this correct "
1082                       "offset?", str(e.exception))
1083
1084     def testPackX86RomMe(self):
1085         """Test that an x86 ROM with an ME region can be created"""
1086         data = self._DoReadFile('031_x86_rom_me.dts')
1087         expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
1088         if data[:0x1000] != expected_desc:
1089             self.fail('Expected descriptor binary at start of image')
1090         self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1091
1092     def testPackVga(self):
1093         """Test that an image with a VGA binary can be created"""
1094         data = self._DoReadFile('032_intel_vga.dts')
1095         self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1096
1097     def testPackStart16(self):
1098         """Test that an image with an x86 start16 region can be created"""
1099         data = self._DoReadFile('033_x86_start16.dts')
1100         self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1101
1102     def testPackPowerpcMpc85xxBootpgResetvec(self):
1103         """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1104         created"""
1105         data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
1106         self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1107
1108     def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
1109         """Handle running a test for insertion of microcode
1110
1111         Args:
1112             dts_fname: Name of test .dts file
1113             nodtb_data: Data that we expect in the first section
1114             ucode_second: True if the microsecond entry is second instead of
1115                 third
1116
1117         Returns:
1118             Tuple:
1119                 Contents of first region (U-Boot or SPL)
1120                 Offset and size components of microcode pointer, as inserted
1121                     in the above (two 4-byte words)
1122         """
1123         data = self._DoReadFile(dts_fname, True)
1124
1125         # Now check the device tree has no microcode
1126         if ucode_second:
1127             ucode_content = data[len(nodtb_data):]
1128             ucode_pos = len(nodtb_data)
1129             dtb_with_ucode = ucode_content[16:]
1130             fdt_len = self.GetFdtLen(dtb_with_ucode)
1131         else:
1132             dtb_with_ucode = data[len(nodtb_data):]
1133             fdt_len = self.GetFdtLen(dtb_with_ucode)
1134             ucode_content = dtb_with_ucode[fdt_len:]
1135             ucode_pos = len(nodtb_data) + fdt_len
1136         fname = tools.GetOutputFilename('test.dtb')
1137         with open(fname, 'wb') as fd:
1138             fd.write(dtb_with_ucode)
1139         dtb = fdt.FdtScan(fname)
1140         ucode = dtb.GetNode('/microcode')
1141         self.assertTrue(ucode)
1142         for node in ucode.subnodes:
1143             self.assertFalse(node.props.get('data'))
1144
1145         # Check that the microcode appears immediately after the Fdt
1146         # This matches the concatenation of the data properties in
1147         # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
1148         ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1149                                  0x78235609)
1150         self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
1151
1152         # Check that the microcode pointer was inserted. It should match the
1153         # expected offset and size
1154         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1155                                    len(ucode_data))
1156         u_boot = data[:len(nodtb_data)]
1157         return u_boot, pos_and_size
1158
1159     def testPackUbootMicrocode(self):
1160         """Test that x86 microcode can be handled correctly
1161
1162         We expect to see the following in the image, in order:
1163             u-boot-nodtb.bin with a microcode pointer inserted at the correct
1164                 place
1165             u-boot.dtb with the microcode removed
1166             the microcode
1167         """
1168         first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
1169                                                      U_BOOT_NODTB_DATA)
1170         self.assertEqual(b'nodtb with microcode' + pos_and_size +
1171                          b' somewhere in here', first)
1172
1173     def _RunPackUbootSingleMicrocode(self):
1174         """Test that x86 microcode can be handled correctly
1175
1176         We expect to see the following in the image, in order:
1177             u-boot-nodtb.bin with a microcode pointer inserted at the correct
1178                 place
1179             u-boot.dtb with the microcode
1180             an empty microcode region
1181         """
1182         # We need the libfdt library to run this test since only that allows
1183         # finding the offset of a property. This is required by
1184         # Entry_u_boot_dtb_with_ucode.ObtainContents().
1185         data = self._DoReadFile('035_x86_single_ucode.dts', True)
1186
1187         second = data[len(U_BOOT_NODTB_DATA):]
1188
1189         fdt_len = self.GetFdtLen(second)
1190         third = second[fdt_len:]
1191         second = second[:fdt_len]
1192
1193         ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1194         self.assertIn(ucode_data, second)
1195         ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
1196
1197         # Check that the microcode pointer was inserted. It should match the
1198         # expected offset and size
1199         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1200                                    len(ucode_data))
1201         first = data[:len(U_BOOT_NODTB_DATA)]
1202         self.assertEqual(b'nodtb with microcode' + pos_and_size +
1203                          b' somewhere in here', first)
1204
1205     def testPackUbootSingleMicrocode(self):
1206         """Test that x86 microcode can be handled correctly with fdt_normal.
1207         """
1208         self._RunPackUbootSingleMicrocode()
1209
1210     def testUBootImg(self):
1211         """Test that u-boot.img can be put in a file"""
1212         data = self._DoReadFile('036_u_boot_img.dts')
1213         self.assertEqual(U_BOOT_IMG_DATA, data)
1214
1215     def testNoMicrocode(self):
1216         """Test that a missing microcode region is detected"""
1217         with self.assertRaises(ValueError) as e:
1218             self._DoReadFile('037_x86_no_ucode.dts', True)
1219         self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1220                       "node found in ", str(e.exception))
1221
1222     def testMicrocodeWithoutNode(self):
1223         """Test that a missing u-boot-dtb-with-ucode node is detected"""
1224         with self.assertRaises(ValueError) as e:
1225             self._DoReadFile('038_x86_ucode_missing_node.dts', True)
1226         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1227                 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1228
1229     def testMicrocodeWithoutNode2(self):
1230         """Test that a missing u-boot-ucode node is detected"""
1231         with self.assertRaises(ValueError) as e:
1232             self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
1233         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1234             "microcode region u-boot-ucode", str(e.exception))
1235
1236     def testMicrocodeWithoutPtrInElf(self):
1237         """Test that a U-Boot binary without the microcode symbol is detected"""
1238         # ELF file without a '_dt_ucode_base_size' symbol
1239         try:
1240             TestFunctional._MakeInputFile('u-boot',
1241                 tools.ReadFile(self.ElfTestFile('u_boot_no_ucode_ptr')))
1242
1243             with self.assertRaises(ValueError) as e:
1244                 self._RunPackUbootSingleMicrocode()
1245             self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1246                     "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1247
1248         finally:
1249             # Put the original file back
1250             TestFunctional._MakeInputFile('u-boot',
1251                 tools.ReadFile(self.ElfTestFile('u_boot_ucode_ptr')))
1252
1253     def testMicrocodeNotInImage(self):
1254         """Test that microcode must be placed within the image"""
1255         with self.assertRaises(ValueError) as e:
1256             self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
1257         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1258                 "pointer _dt_ucode_base_size at fffffe14 is outside the "
1259                 "section ranging from 00000000 to 0000002e", str(e.exception))
1260
1261     def testWithoutMicrocode(self):
1262         """Test that we can cope with an image without microcode (e.g. qemu)"""
1263         TestFunctional._MakeInputFile('u-boot',
1264             tools.ReadFile(self.ElfTestFile('u_boot_no_ucode_ptr')))
1265         data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
1266
1267         # Now check the device tree has no microcode
1268         self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1269         second = data[len(U_BOOT_NODTB_DATA):]
1270
1271         fdt_len = self.GetFdtLen(second)
1272         self.assertEqual(dtb, second[:fdt_len])
1273
1274         used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1275         third = data[used_len:]
1276         self.assertEqual(tools.GetBytes(0, 0x200 - used_len), third)
1277
1278     def testUnknownPosSize(self):
1279         """Test that microcode must be placed within the image"""
1280         with self.assertRaises(ValueError) as e:
1281             self._DoReadFile('041_unknown_pos_size.dts', True)
1282         self.assertIn("Section '/binman': Unable to set offset/size for unknown "
1283                 "entry 'invalid-entry'", str(e.exception))
1284
1285     def testPackFsp(self):
1286         """Test that an image with a FSP binary can be created"""
1287         data = self._DoReadFile('042_intel_fsp.dts')
1288         self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1289
1290     def testPackCmc(self):
1291         """Test that an image with a CMC binary can be created"""
1292         data = self._DoReadFile('043_intel_cmc.dts')
1293         self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
1294
1295     def testPackVbt(self):
1296         """Test that an image with a VBT binary can be created"""
1297         data = self._DoReadFile('046_intel_vbt.dts')
1298         self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
1299
1300     def testSplBssPad(self):
1301         """Test that we can pad SPL's BSS with zeros"""
1302         # ELF file with a '__bss_size' symbol
1303         self._SetupSplElf()
1304         data = self._DoReadFile('047_spl_bss_pad.dts')
1305         self.assertEqual(U_BOOT_SPL_DATA + tools.GetBytes(0, 10) + U_BOOT_DATA,
1306                          data)
1307
1308     def testSplBssPadMissing(self):
1309         """Test that a missing symbol is detected"""
1310         self._SetupSplElf('u_boot_ucode_ptr')
1311         with self.assertRaises(ValueError) as e:
1312             self._DoReadFile('047_spl_bss_pad.dts')
1313         self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1314                       str(e.exception))
1315
1316     def testPackStart16Spl(self):
1317         """Test that an image with an x86 start16 SPL region can be created"""
1318         data = self._DoReadFile('048_x86_start16_spl.dts')
1319         self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1320
1321     def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1322         """Helper function for microcode tests
1323
1324         We expect to see the following in the image, in order:
1325             u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1326                 correct place
1327             u-boot.dtb with the microcode removed
1328             the microcode
1329
1330         Args:
1331             dts: Device tree file to use for test
1332             ucode_second: True if the microsecond entry is second instead of
1333                 third
1334         """
1335         self._SetupSplElf('u_boot_ucode_ptr')
1336         first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1337                                                      ucode_second=ucode_second)
1338         self.assertEqual(b'splnodtb with microc' + pos_and_size +
1339                          b'ter somewhere in here', first)
1340
1341     def testPackUbootSplMicrocode(self):
1342         """Test that x86 microcode can be handled correctly in SPL"""
1343         self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
1344
1345     def testPackUbootSplMicrocodeReorder(self):
1346         """Test that order doesn't matter for microcode entries
1347
1348         This is the same as testPackUbootSplMicrocode but when we process the
1349         u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1350         entry, so we reply on binman to try later.
1351         """
1352         self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
1353                                     ucode_second=True)
1354
1355     def testPackMrc(self):
1356         """Test that an image with an MRC binary can be created"""
1357         data = self._DoReadFile('050_intel_mrc.dts')
1358         self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1359
1360     def testSplDtb(self):
1361         """Test that an image with spl/u-boot-spl.dtb can be created"""
1362         data = self._DoReadFile('051_u_boot_spl_dtb.dts')
1363         self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1364
1365     def testSplNoDtb(self):
1366         """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
1367         self._SetupSplElf()
1368         data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
1369         self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1370
1371     def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
1372                      use_expanded=False):
1373         """Check the image contains the expected symbol values
1374
1375         Args:
1376             dts: Device tree file to use for test
1377             base_data: Data before and after 'u-boot' section
1378             u_boot_offset: Offset of 'u-boot' section in image
1379             entry_args: Dict of entry args to supply to binman
1380                 key: arg name
1381                 value: value of that arg
1382             use_expanded: True to use expanded entries where available, e.g.
1383                 'u-boot-expanded' instead of 'u-boot'
1384         """
1385         elf_fname = self.ElfTestFile('u_boot_binman_syms')
1386         syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1387         addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
1388         self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
1389                          addr)
1390
1391         self._SetupSplElf('u_boot_binman_syms')
1392         data = self._DoReadFileDtb(dts, entry_args=entry_args,
1393                                    use_expanded=use_expanded)[0]
1394         # The image should contain the symbols from u_boot_binman_syms.c
1395         # Note that image_pos is adjusted by the base address of the image,
1396         # which is 0x10 in our test image
1397         sym_values = struct.pack('<LQLL', 0x00,
1398                                  u_boot_offset + len(U_BOOT_DATA),
1399                                  0x10 + u_boot_offset, 0x04)
1400         expected = (sym_values + base_data[20:] +
1401                     tools.GetBytes(0xff, 1) + U_BOOT_DATA + sym_values +
1402                     base_data[20:])
1403         self.assertEqual(expected, data)
1404
1405     def testSymbols(self):
1406         """Test binman can assign symbols embedded in U-Boot"""
1407         self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x18)
1408
1409     def testSymbolsNoDtb(self):
1410         """Test binman can assign symbols embedded in U-Boot SPL"""
1411         self.checkSymbols('196_symbols_nodtb.dts',
1412                           U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1413                           0x38)
1414
1415     def testPackUnitAddress(self):
1416         """Test that we support multiple binaries with the same name"""
1417         data = self._DoReadFile('054_unit_address.dts')
1418         self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1419
1420     def testSections(self):
1421         """Basic test of sections"""
1422         data = self._DoReadFile('055_sections.dts')
1423         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
1424                     U_BOOT_DATA + tools.GetBytes(ord('a'), 12) +
1425                     U_BOOT_DATA + tools.GetBytes(ord('&'), 4))
1426         self.assertEqual(expected, data)
1427
1428     def testMap(self):
1429         """Tests outputting a map of the images"""
1430         _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
1431         self.assertEqual('''ImagePos    Offset      Size  Name
1432 00000000  00000000  00000028  main-section
1433 00000000   00000000  00000010  section@0
1434 00000000    00000000  00000004  u-boot
1435 00000010   00000010  00000010  section@1
1436 00000010    00000000  00000004  u-boot
1437 00000020   00000020  00000004  section@2
1438 00000020    00000000  00000004  u-boot
1439 ''', map_data)
1440
1441     def testNamePrefix(self):
1442         """Tests that name prefixes are used"""
1443         _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
1444         self.assertEqual('''ImagePos    Offset      Size  Name
1445 00000000  00000000  00000028  main-section
1446 00000000   00000000  00000010  section@0
1447 00000000    00000000  00000004  ro-u-boot
1448 00000010   00000010  00000010  section@1
1449 00000010    00000000  00000004  rw-u-boot
1450 ''', map_data)
1451
1452     def testUnknownContents(self):
1453         """Test that obtaining the contents works as expected"""
1454         with self.assertRaises(ValueError) as e:
1455             self._DoReadFile('057_unknown_contents.dts', True)
1456         self.assertIn("Image '/binman': Internal error: Could not complete "
1457                 "processing of contents: remaining ["
1458                 "<binman.etype._testing.Entry__testing ", str(e.exception))
1459
1460     def testBadChangeSize(self):
1461         """Test that trying to change the size of an entry fails"""
1462         try:
1463             state.SetAllowEntryExpansion(False)
1464             with self.assertRaises(ValueError) as e:
1465                 self._DoReadFile('059_change_size.dts', True)
1466             self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
1467                           str(e.exception))
1468         finally:
1469             state.SetAllowEntryExpansion(True)
1470
1471     def testUpdateFdt(self):
1472         """Test that we can update the device tree with offset/size info"""
1473         _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
1474                                                      update_dtb=True)
1475         dtb = fdt.Fdt(out_dtb_fname)
1476         dtb.Scan()
1477         props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
1478         self.assertEqual({
1479             'image-pos': 0,
1480             'offset': 0,
1481             '_testing:offset': 32,
1482             '_testing:size': 2,
1483             '_testing:image-pos': 32,
1484             'section@0/u-boot:offset': 0,
1485             'section@0/u-boot:size': len(U_BOOT_DATA),
1486             'section@0/u-boot:image-pos': 0,
1487             'section@0:offset': 0,
1488             'section@0:size': 16,
1489             'section@0:image-pos': 0,
1490
1491             'section@1/u-boot:offset': 0,
1492             'section@1/u-boot:size': len(U_BOOT_DATA),
1493             'section@1/u-boot:image-pos': 16,
1494             'section@1:offset': 16,
1495             'section@1:size': 16,
1496             'section@1:image-pos': 16,
1497             'size': 40
1498         }, props)
1499
1500     def testUpdateFdtBad(self):
1501         """Test that we detect when ProcessFdt never completes"""
1502         with self.assertRaises(ValueError) as e:
1503             self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
1504         self.assertIn('Could not complete processing of Fdt: remaining '
1505                       '[<binman.etype._testing.Entry__testing',
1506                         str(e.exception))
1507
1508     def testEntryArgs(self):
1509         """Test passing arguments to entries from the command line"""
1510         entry_args = {
1511             'test-str-arg': 'test1',
1512             'test-int-arg': '456',
1513         }
1514         self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1515         self.assertIn('image', control.images)
1516         entry = control.images['image'].GetEntries()['_testing']
1517         self.assertEqual('test0', entry.test_str_fdt)
1518         self.assertEqual('test1', entry.test_str_arg)
1519         self.assertEqual(123, entry.test_int_fdt)
1520         self.assertEqual(456, entry.test_int_arg)
1521
1522     def testEntryArgsMissing(self):
1523         """Test missing arguments and properties"""
1524         entry_args = {
1525             'test-int-arg': '456',
1526         }
1527         self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
1528         entry = control.images['image'].GetEntries()['_testing']
1529         self.assertEqual('test0', entry.test_str_fdt)
1530         self.assertEqual(None, entry.test_str_arg)
1531         self.assertEqual(None, entry.test_int_fdt)
1532         self.assertEqual(456, entry.test_int_arg)
1533
1534     def testEntryArgsRequired(self):
1535         """Test missing arguments and properties"""
1536         entry_args = {
1537             'test-int-arg': '456',
1538         }
1539         with self.assertRaises(ValueError) as e:
1540             self._DoReadFileDtb('064_entry_args_required.dts')
1541         self.assertIn("Node '/binman/_testing': "
1542             'Missing required properties/entry args: test-str-arg, '
1543             'test-int-fdt, test-int-arg',
1544             str(e.exception))
1545
1546     def testEntryArgsInvalidFormat(self):
1547         """Test that an invalid entry-argument format is detected"""
1548         args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1549                 '-ano-value']
1550         with self.assertRaises(ValueError) as e:
1551             self._DoBinman(*args)
1552         self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1553
1554     def testEntryArgsInvalidInteger(self):
1555         """Test that an invalid entry-argument integer is detected"""
1556         entry_args = {
1557             'test-int-arg': 'abc',
1558         }
1559         with self.assertRaises(ValueError) as e:
1560             self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1561         self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1562                       "'test-int-arg' (value 'abc') to integer",
1563             str(e.exception))
1564
1565     def testEntryArgsInvalidDatatype(self):
1566         """Test that an invalid entry-argument datatype is detected
1567
1568         This test could be written in entry_test.py except that it needs
1569         access to control.entry_args, which seems more than that module should
1570         be able to see.
1571         """
1572         entry_args = {
1573             'test-bad-datatype-arg': '12',
1574         }
1575         with self.assertRaises(ValueError) as e:
1576             self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
1577                                 entry_args=entry_args)
1578         self.assertIn('GetArg() internal error: Unknown data type ',
1579                       str(e.exception))
1580
1581     def testText(self):
1582         """Test for a text entry type"""
1583         entry_args = {
1584             'test-id': TEXT_DATA,
1585             'test-id2': TEXT_DATA2,
1586             'test-id3': TEXT_DATA3,
1587         }
1588         data, _, _, _ = self._DoReadFileDtb('066_text.dts',
1589                                             entry_args=entry_args)
1590         expected = (tools.ToBytes(TEXT_DATA) +
1591                     tools.GetBytes(0, 8 - len(TEXT_DATA)) +
1592                     tools.ToBytes(TEXT_DATA2) + tools.ToBytes(TEXT_DATA3) +
1593                     b'some text' + b'more text')
1594         self.assertEqual(expected, data)
1595
1596     def testEntryDocs(self):
1597         """Test for creation of entry documentation"""
1598         with test_util.capture_sys_output() as (stdout, stderr):
1599             control.WriteEntryDocs(control.GetEntryModules())
1600         self.assertTrue(len(stdout.getvalue()) > 0)
1601
1602     def testEntryDocsMissing(self):
1603         """Test handling of missing entry documentation"""
1604         with self.assertRaises(ValueError) as e:
1605             with test_util.capture_sys_output() as (stdout, stderr):
1606                 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
1607         self.assertIn('Documentation is missing for modules: u_boot',
1608                       str(e.exception))
1609
1610     def testFmap(self):
1611         """Basic test of generation of a flashrom fmap"""
1612         data = self._DoReadFile('067_fmap.dts')
1613         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1614         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
1615                     U_BOOT_DATA + tools.GetBytes(ord('a'), 12))
1616         self.assertEqual(expected, data[:32])
1617         self.assertEqual(b'__FMAP__', fhdr.signature)
1618         self.assertEqual(1, fhdr.ver_major)
1619         self.assertEqual(0, fhdr.ver_minor)
1620         self.assertEqual(0, fhdr.base)
1621         expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
1622         self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
1623         self.assertEqual(b'FMAP', fhdr.name)
1624         self.assertEqual(5, fhdr.nareas)
1625         fiter = iter(fentries)
1626
1627         fentry = next(fiter)
1628         self.assertEqual(b'SECTION0', fentry.name)
1629         self.assertEqual(0, fentry.offset)
1630         self.assertEqual(16, fentry.size)
1631         self.assertEqual(0, fentry.flags)
1632
1633         fentry = next(fiter)
1634         self.assertEqual(b'RO_U_BOOT', fentry.name)
1635         self.assertEqual(0, fentry.offset)
1636         self.assertEqual(4, fentry.size)
1637         self.assertEqual(0, fentry.flags)
1638
1639         fentry = next(fiter)
1640         self.assertEqual(b'SECTION1', fentry.name)
1641         self.assertEqual(16, fentry.offset)
1642         self.assertEqual(16, fentry.size)
1643         self.assertEqual(0, fentry.flags)
1644
1645         fentry = next(fiter)
1646         self.assertEqual(b'RW_U_BOOT', fentry.name)
1647         self.assertEqual(16, fentry.offset)
1648         self.assertEqual(4, fentry.size)
1649         self.assertEqual(0, fentry.flags)
1650
1651         fentry = next(fiter)
1652         self.assertEqual(b'FMAP', fentry.name)
1653         self.assertEqual(32, fentry.offset)
1654         self.assertEqual(expect_size, fentry.size)
1655         self.assertEqual(0, fentry.flags)
1656
1657     def testBlobNamedByArg(self):
1658         """Test we can add a blob with the filename coming from an entry arg"""
1659         entry_args = {
1660             'cros-ec-rw-path': 'ecrw.bin',
1661         }
1662         self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
1663
1664     def testFill(self):
1665         """Test for an fill entry type"""
1666         data = self._DoReadFile('069_fill.dts')
1667         expected = tools.GetBytes(0xff, 8) + tools.GetBytes(0, 8)
1668         self.assertEqual(expected, data)
1669
1670     def testFillNoSize(self):
1671         """Test for an fill entry type with no size"""
1672         with self.assertRaises(ValueError) as e:
1673             self._DoReadFile('070_fill_no_size.dts')
1674         self.assertIn("'fill' entry must have a size property",
1675                       str(e.exception))
1676
1677     def _HandleGbbCommand(self, pipe_list):
1678         """Fake calls to the futility utility"""
1679         if pipe_list[0][0] == 'futility':
1680             fname = pipe_list[0][-1]
1681             # Append our GBB data to the file, which will happen every time the
1682             # futility command is called.
1683             with open(fname, 'ab') as fd:
1684                 fd.write(GBB_DATA)
1685             return command.CommandResult()
1686
1687     def testGbb(self):
1688         """Test for the Chromium OS Google Binary Block"""
1689         command.test_result = self._HandleGbbCommand
1690         entry_args = {
1691             'keydir': 'devkeys',
1692             'bmpblk': 'bmpblk.bin',
1693         }
1694         data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
1695
1696         # Since futility
1697         expected = (GBB_DATA + GBB_DATA + tools.GetBytes(0, 8) +
1698                     tools.GetBytes(0, 0x2180 - 16))
1699         self.assertEqual(expected, data)
1700
1701     def testGbbTooSmall(self):
1702         """Test for the Chromium OS Google Binary Block being large enough"""
1703         with self.assertRaises(ValueError) as e:
1704             self._DoReadFileDtb('072_gbb_too_small.dts')
1705         self.assertIn("Node '/binman/gbb': GBB is too small",
1706                       str(e.exception))
1707
1708     def testGbbNoSize(self):
1709         """Test for the Chromium OS Google Binary Block having a size"""
1710         with self.assertRaises(ValueError) as e:
1711             self._DoReadFileDtb('073_gbb_no_size.dts')
1712         self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1713                       str(e.exception))
1714
1715     def _HandleVblockCommand(self, pipe_list):
1716         """Fake calls to the futility utility
1717
1718         The expected pipe is:
1719
1720            [('futility', 'vbutil_firmware', '--vblock',
1721              'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1722              '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1723              '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1724              'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1725
1726         This writes to the output file (here, 'vblock.vblock'). If
1727         self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1728         of the input data (here, 'input.vblock').
1729         """
1730         if pipe_list[0][0] == 'futility':
1731             fname = pipe_list[0][3]
1732             with open(fname, 'wb') as fd:
1733                 if self._hash_data:
1734                     infile = pipe_list[0][11]
1735                     m = hashlib.sha256()
1736                     data = tools.ReadFile(infile)
1737                     m.update(data)
1738                     fd.write(m.digest())
1739                 else:
1740                     fd.write(VBLOCK_DATA)
1741
1742             return command.CommandResult()
1743
1744     def testVblock(self):
1745         """Test for the Chromium OS Verified Boot Block"""
1746         self._hash_data = False
1747         command.test_result = self._HandleVblockCommand
1748         entry_args = {
1749             'keydir': 'devkeys',
1750         }
1751         data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
1752                                             entry_args=entry_args)
1753         expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1754         self.assertEqual(expected, data)
1755
1756     def testVblockNoContent(self):
1757         """Test we detect a vblock which has no content to sign"""
1758         with self.assertRaises(ValueError) as e:
1759             self._DoReadFile('075_vblock_no_content.dts')
1760         self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
1761                       'property', str(e.exception))
1762
1763     def testVblockBadPhandle(self):
1764         """Test that we detect a vblock with an invalid phandle in contents"""
1765         with self.assertRaises(ValueError) as e:
1766             self._DoReadFile('076_vblock_bad_phandle.dts')
1767         self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1768                       '1000', str(e.exception))
1769
1770     def testVblockBadEntry(self):
1771         """Test that we detect an entry that points to a non-entry"""
1772         with self.assertRaises(ValueError) as e:
1773             self._DoReadFile('077_vblock_bad_entry.dts')
1774         self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1775                       "'other'", str(e.exception))
1776
1777     def testVblockContent(self):
1778         """Test that the vblock signs the right data"""
1779         self._hash_data = True
1780         command.test_result = self._HandleVblockCommand
1781         entry_args = {
1782             'keydir': 'devkeys',
1783         }
1784         data = self._DoReadFileDtb(
1785             '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
1786             entry_args=entry_args)[0]
1787         hashlen = 32  # SHA256 hash is 32 bytes
1788         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
1789         hashval = data[-hashlen:]
1790         dtb = data[len(U_BOOT_DATA):-hashlen]
1791
1792         expected_data = U_BOOT_DATA + dtb
1793
1794         # The hashval should be a hash of the dtb
1795         m = hashlib.sha256()
1796         m.update(expected_data)
1797         expected_hashval = m.digest()
1798         self.assertEqual(expected_hashval, hashval)
1799
1800     def testTpl(self):
1801         """Test that an image with TPL and its device tree can be created"""
1802         # ELF file with a '__bss_size' symbol
1803         self._SetupTplElf()
1804         data = self._DoReadFile('078_u_boot_tpl.dts')
1805         self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1806
1807     def testUsesPos(self):
1808         """Test that the 'pos' property cannot be used anymore"""
1809         with self.assertRaises(ValueError) as e:
1810            data = self._DoReadFile('079_uses_pos.dts')
1811         self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1812                       "'pos'", str(e.exception))
1813
1814     def testFillZero(self):
1815         """Test for an fill entry type with a size of 0"""
1816         data = self._DoReadFile('080_fill_empty.dts')
1817         self.assertEqual(tools.GetBytes(0, 16), data)
1818
1819     def testTextMissing(self):
1820         """Test for a text entry type where there is no text"""
1821         with self.assertRaises(ValueError) as e:
1822             self._DoReadFileDtb('066_text.dts',)
1823         self.assertIn("Node '/binman/text': No value provided for text label "
1824                       "'test-id'", str(e.exception))
1825
1826     def testPackStart16Tpl(self):
1827         """Test that an image with an x86 start16 TPL region can be created"""
1828         data = self._DoReadFile('081_x86_start16_tpl.dts')
1829         self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1830
1831     def testSelectImage(self):
1832         """Test that we can select which images to build"""
1833         expected = 'Skipping images: image1'
1834
1835         # We should only get the expected message in verbose mode
1836         for verbosity in (0, 2):
1837             with test_util.capture_sys_output() as (stdout, stderr):
1838                 retcode = self._DoTestFile('006_dual_image.dts',
1839                                            verbosity=verbosity,
1840                                            images=['image2'])
1841             self.assertEqual(0, retcode)
1842             if verbosity:
1843                 self.assertIn(expected, stdout.getvalue())
1844             else:
1845                 self.assertNotIn(expected, stdout.getvalue())
1846
1847             self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin')))
1848             self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin')))
1849             self._CleanupOutputDir()
1850
1851     def testUpdateFdtAll(self):
1852         """Test that all device trees are updated with offset/size info"""
1853         data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
1854
1855         base_expected = {
1856             'section:image-pos': 0,
1857             'u-boot-tpl-dtb:size': 513,
1858             'u-boot-spl-dtb:size': 513,
1859             'u-boot-spl-dtb:offset': 493,
1860             'image-pos': 0,
1861             'section/u-boot-dtb:image-pos': 0,
1862             'u-boot-spl-dtb:image-pos': 493,
1863             'section/u-boot-dtb:size': 493,
1864             'u-boot-tpl-dtb:image-pos': 1006,
1865             'section/u-boot-dtb:offset': 0,
1866             'section:size': 493,
1867             'offset': 0,
1868             'section:offset': 0,
1869             'u-boot-tpl-dtb:offset': 1006,
1870             'size': 1519
1871         }
1872
1873         # We expect three device-tree files in the output, one after the other.
1874         # Read them in sequence. We look for an 'spl' property in the SPL tree,
1875         # and 'tpl' in the TPL tree, to make sure they are distinct from the
1876         # main U-Boot tree. All three should have the same postions and offset.
1877         start = 0
1878         for item in ['', 'spl', 'tpl']:
1879             dtb = fdt.Fdt.FromData(data[start:])
1880             dtb.Scan()
1881             props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
1882                                       ['spl', 'tpl'])
1883             expected = dict(base_expected)
1884             if item:
1885                 expected[item] = 0
1886             self.assertEqual(expected, props)
1887             start += dtb._fdt_obj.totalsize()
1888
1889     def testUpdateFdtOutput(self):
1890         """Test that output DTB files are updated"""
1891         try:
1892             data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
1893                     use_real_dtb=True, update_dtb=True, reset_dtbs=False)
1894
1895             # Unfortunately, compiling a source file always results in a file
1896             # called source.dtb (see fdt_util.EnsureCompiled()). The test
1897             # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
1898             # binman as a file called u-boot.dtb. To fix this, copy the file
1899             # over to the expected place.
1900             start = 0
1901             for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
1902                           'tpl/u-boot-tpl.dtb.out']:
1903                 dtb = fdt.Fdt.FromData(data[start:])
1904                 size = dtb._fdt_obj.totalsize()
1905                 pathname = tools.GetOutputFilename(os.path.split(fname)[1])
1906                 outdata = tools.ReadFile(pathname)
1907                 name = os.path.split(fname)[0]
1908
1909                 if name:
1910                     orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
1911                 else:
1912                     orig_indata = dtb_data
1913                 self.assertNotEqual(outdata, orig_indata,
1914                         "Expected output file '%s' be updated" % pathname)
1915                 self.assertEqual(outdata, data[start:start + size],
1916                         "Expected output file '%s' to match output image" %
1917                         pathname)
1918                 start += size
1919         finally:
1920             self._ResetDtbs()
1921
1922     def _decompress(self, data):
1923         return tools.Decompress(data, 'lz4')
1924
1925     def testCompress(self):
1926         """Test compression of blobs"""
1927         self._CheckLz4()
1928         data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
1929                                             use_real_dtb=True, update_dtb=True)
1930         dtb = fdt.Fdt(out_dtb_fname)
1931         dtb.Scan()
1932         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
1933         orig = self._decompress(data)
1934         self.assertEquals(COMPRESS_DATA, orig)
1935
1936         # Do a sanity check on various fields
1937         image = control.images['image']
1938         entries = image.GetEntries()
1939         self.assertEqual(1, len(entries))
1940
1941         entry = entries['blob']
1942         self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
1943         self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
1944         orig = self._decompress(entry.data)
1945         self.assertEqual(orig, entry.uncomp_data)
1946
1947         self.assertEqual(image.data, entry.data)
1948
1949         expected = {
1950             'blob:uncomp-size': len(COMPRESS_DATA),
1951             'blob:size': len(data),
1952             'size': len(data),
1953             }
1954         self.assertEqual(expected, props)
1955
1956     def testFiles(self):
1957         """Test bringing in multiple files"""
1958         data = self._DoReadFile('084_files.dts')
1959         self.assertEqual(FILES_DATA, data)
1960
1961     def testFilesCompress(self):
1962         """Test bringing in multiple files and compressing them"""
1963         self._CheckLz4()
1964         data = self._DoReadFile('085_files_compress.dts')
1965
1966         image = control.images['image']
1967         entries = image.GetEntries()
1968         files = entries['files']
1969         entries = files._entries
1970
1971         orig = b''
1972         for i in range(1, 3):
1973             key = '%d.dat' % i
1974             start = entries[key].image_pos
1975             len = entries[key].size
1976             chunk = data[start:start + len]
1977             orig += self._decompress(chunk)
1978
1979         self.assertEqual(FILES_DATA, orig)
1980
1981     def testFilesMissing(self):
1982         """Test missing files"""
1983         with self.assertRaises(ValueError) as e:
1984             data = self._DoReadFile('086_files_none.dts')
1985         self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
1986                       'no files', str(e.exception))
1987
1988     def testFilesNoPattern(self):
1989         """Test missing files"""
1990         with self.assertRaises(ValueError) as e:
1991             data = self._DoReadFile('087_files_no_pattern.dts')
1992         self.assertIn("Node '/binman/files': Missing 'pattern' property",
1993                       str(e.exception))
1994
1995     def testExpandSize(self):
1996         """Test an expanding entry"""
1997         data, _, map_data, _ = self._DoReadFileDtb('088_expand_size.dts',
1998                                                    map=True)
1999         expect = (tools.GetBytes(ord('a'), 8) + U_BOOT_DATA +
2000                   MRC_DATA + tools.GetBytes(ord('b'), 1) + U_BOOT_DATA +
2001                   tools.GetBytes(ord('c'), 8) + U_BOOT_DATA +
2002                   tools.GetBytes(ord('d'), 8))
2003         self.assertEqual(expect, data)
2004         self.assertEqual('''ImagePos    Offset      Size  Name
2005 00000000  00000000  00000028  main-section
2006 00000000   00000000  00000008  fill
2007 00000008   00000008  00000004  u-boot
2008 0000000c   0000000c  00000004  section
2009 0000000c    00000000  00000003  intel-mrc
2010 00000010   00000010  00000004  u-boot2
2011 00000014   00000014  0000000c  section2
2012 00000014    00000000  00000008  fill
2013 0000001c    00000008  00000004  u-boot
2014 00000020   00000020  00000008  fill2
2015 ''', map_data)
2016
2017     def testExpandSizeBad(self):
2018         """Test an expanding entry which fails to provide contents"""
2019         with test_util.capture_sys_output() as (stdout, stderr):
2020             with self.assertRaises(ValueError) as e:
2021                 self._DoReadFileDtb('089_expand_size_bad.dts', map=True)
2022         self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2023                       'expanding entry', str(e.exception))
2024
2025     def testHash(self):
2026         """Test hashing of the contents of an entry"""
2027         _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
2028                 use_real_dtb=True, update_dtb=True)
2029         dtb = fdt.Fdt(out_dtb_fname)
2030         dtb.Scan()
2031         hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2032         m = hashlib.sha256()
2033         m.update(U_BOOT_DATA)
2034         self.assertEqual(m.digest(), b''.join(hash_node.value))
2035
2036     def testHashNoAlgo(self):
2037         with self.assertRaises(ValueError) as e:
2038             self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
2039         self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2040                       'hash node', str(e.exception))
2041
2042     def testHashBadAlgo(self):
2043         with self.assertRaises(ValueError) as e:
2044             self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
2045         self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
2046                       str(e.exception))
2047
2048     def testHashSection(self):
2049         """Test hashing of the contents of an entry"""
2050         _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
2051                 use_real_dtb=True, update_dtb=True)
2052         dtb = fdt.Fdt(out_dtb_fname)
2053         dtb.Scan()
2054         hash_node = dtb.GetNode('/binman/section/hash').props['value']
2055         m = hashlib.sha256()
2056         m.update(U_BOOT_DATA)
2057         m.update(tools.GetBytes(ord('a'), 16))
2058         self.assertEqual(m.digest(), b''.join(hash_node.value))
2059
2060     def testPackUBootTplMicrocode(self):
2061         """Test that x86 microcode can be handled correctly in TPL
2062
2063         We expect to see the following in the image, in order:
2064             u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2065                 place
2066             u-boot-tpl.dtb with the microcode removed
2067             the microcode
2068         """
2069         self._SetupTplElf('u_boot_ucode_ptr')
2070         first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
2071                                                      U_BOOT_TPL_NODTB_DATA)
2072         self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2073                          b'ter somewhere in here', first)
2074
2075     def testFmapX86(self):
2076         """Basic test of generation of a flashrom fmap"""
2077         data = self._DoReadFile('094_fmap_x86.dts')
2078         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2079         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('a'), 32 - 7)
2080         self.assertEqual(expected, data[:32])
2081         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2082
2083         self.assertEqual(0x100, fhdr.image_size)
2084
2085         self.assertEqual(0, fentries[0].offset)
2086         self.assertEqual(4, fentries[0].size)
2087         self.assertEqual(b'U_BOOT', fentries[0].name)
2088
2089         self.assertEqual(4, fentries[1].offset)
2090         self.assertEqual(3, fentries[1].size)
2091         self.assertEqual(b'INTEL_MRC', fentries[1].name)
2092
2093         self.assertEqual(32, fentries[2].offset)
2094         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2095                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
2096         self.assertEqual(b'FMAP', fentries[2].name)
2097
2098     def testFmapX86Section(self):
2099         """Basic test of generation of a flashrom fmap"""
2100         data = self._DoReadFile('095_fmap_x86_section.dts')
2101         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('b'), 32 - 7)
2102         self.assertEqual(expected, data[:32])
2103         fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2104
2105         self.assertEqual(0x180, fhdr.image_size)
2106         expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
2107         fiter = iter(fentries)
2108
2109         fentry = next(fiter)
2110         self.assertEqual(b'U_BOOT', fentry.name)
2111         self.assertEqual(0, fentry.offset)
2112         self.assertEqual(4, fentry.size)
2113
2114         fentry = next(fiter)
2115         self.assertEqual(b'SECTION', fentry.name)
2116         self.assertEqual(4, fentry.offset)
2117         self.assertEqual(0x20 + expect_size, fentry.size)
2118
2119         fentry = next(fiter)
2120         self.assertEqual(b'INTEL_MRC', fentry.name)
2121         self.assertEqual(4, fentry.offset)
2122         self.assertEqual(3, fentry.size)
2123
2124         fentry = next(fiter)
2125         self.assertEqual(b'FMAP', fentry.name)
2126         self.assertEqual(36, fentry.offset)
2127         self.assertEqual(expect_size, fentry.size)
2128
2129     def testElf(self):
2130         """Basic test of ELF entries"""
2131         self._SetupSplElf()
2132         self._SetupTplElf()
2133         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
2134             TestFunctional._MakeInputFile('-boot', fd.read())
2135         data = self._DoReadFile('096_elf.dts')
2136
2137     def testElfStrip(self):
2138         """Basic test of ELF entries"""
2139         self._SetupSplElf()
2140         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
2141             TestFunctional._MakeInputFile('-boot', fd.read())
2142         data = self._DoReadFile('097_elf_strip.dts')
2143
2144     def testPackOverlapMap(self):
2145         """Test that overlapping regions are detected"""
2146         with test_util.capture_sys_output() as (stdout, stderr):
2147             with self.assertRaises(ValueError) as e:
2148                 self._DoTestFile('014_pack_overlap.dts', map=True)
2149         map_fname = tools.GetOutputFilename('image.map')
2150         self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2151                          stdout.getvalue())
2152
2153         # We should not get an inmage, but there should be a map file
2154         self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin')))
2155         self.assertTrue(os.path.exists(map_fname))
2156         map_data = tools.ReadFile(map_fname, binary=False)
2157         self.assertEqual('''ImagePos    Offset      Size  Name
2158 <none>    00000000  00000008  main-section
2159 <none>     00000000  00000004  u-boot
2160 <none>     00000003  00000004  u-boot-align
2161 ''', map_data)
2162
2163     def testPackRefCode(self):
2164         """Test that an image with an Intel Reference code binary works"""
2165         data = self._DoReadFile('100_intel_refcode.dts')
2166         self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2167
2168     def testSectionOffset(self):
2169         """Tests use of a section with an offset"""
2170         data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2171                                                    map=True)
2172         self.assertEqual('''ImagePos    Offset      Size  Name
2173 00000000  00000000  00000038  main-section
2174 00000004   00000004  00000010  section@0
2175 00000004    00000000  00000004  u-boot
2176 00000018   00000018  00000010  section@1
2177 00000018    00000000  00000004  u-boot
2178 0000002c   0000002c  00000004  section@2
2179 0000002c    00000000  00000004  u-boot
2180 ''', map_data)
2181         self.assertEqual(data,
2182                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2183                              tools.GetBytes(0x21, 12) +
2184                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2185                              tools.GetBytes(0x61, 12) +
2186                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2187                              tools.GetBytes(0x26, 8))
2188
2189     def testCbfsRaw(self):
2190         """Test base handling of a Coreboot Filesystem (CBFS)
2191
2192         The exact contents of the CBFS is verified by similar tests in
2193         cbfs_util_test.py. The tests here merely check that the files added to
2194         the CBFS can be found in the final image.
2195         """
2196         data = self._DoReadFile('102_cbfs_raw.dts')
2197         size = 0xb0
2198
2199         cbfs = cbfs_util.CbfsReader(data)
2200         self.assertEqual(size, cbfs.rom_size)
2201
2202         self.assertIn('u-boot-dtb', cbfs.files)
2203         cfile = cbfs.files['u-boot-dtb']
2204         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2205
2206     def testCbfsArch(self):
2207         """Test on non-x86 architecture"""
2208         data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2209         size = 0x100
2210
2211         cbfs = cbfs_util.CbfsReader(data)
2212         self.assertEqual(size, cbfs.rom_size)
2213
2214         self.assertIn('u-boot-dtb', cbfs.files)
2215         cfile = cbfs.files['u-boot-dtb']
2216         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2217
2218     def testCbfsStage(self):
2219         """Tests handling of a Coreboot Filesystem (CBFS)"""
2220         if not elf.ELF_TOOLS:
2221             self.skipTest('Python elftools not available')
2222         elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2223         elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2224         size = 0xb0
2225
2226         data = self._DoReadFile('104_cbfs_stage.dts')
2227         cbfs = cbfs_util.CbfsReader(data)
2228         self.assertEqual(size, cbfs.rom_size)
2229
2230         self.assertIn('u-boot', cbfs.files)
2231         cfile = cbfs.files['u-boot']
2232         self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2233
2234     def testCbfsRawCompress(self):
2235         """Test handling of compressing raw files"""
2236         self._CheckLz4()
2237         data = self._DoReadFile('105_cbfs_raw_compress.dts')
2238         size = 0x140
2239
2240         cbfs = cbfs_util.CbfsReader(data)
2241         self.assertIn('u-boot', cbfs.files)
2242         cfile = cbfs.files['u-boot']
2243         self.assertEqual(COMPRESS_DATA, cfile.data)
2244
2245     def testCbfsBadArch(self):
2246         """Test handling of a bad architecture"""
2247         with self.assertRaises(ValueError) as e:
2248             self._DoReadFile('106_cbfs_bad_arch.dts')
2249         self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2250
2251     def testCbfsNoSize(self):
2252         """Test handling of a missing size property"""
2253         with self.assertRaises(ValueError) as e:
2254             self._DoReadFile('107_cbfs_no_size.dts')
2255         self.assertIn('entry must have a size property', str(e.exception))
2256
2257     def testCbfsNoCOntents(self):
2258         """Test handling of a CBFS entry which does not provide contentsy"""
2259         with self.assertRaises(ValueError) as e:
2260             self._DoReadFile('108_cbfs_no_contents.dts')
2261         self.assertIn('Could not complete processing of contents',
2262                       str(e.exception))
2263
2264     def testCbfsBadCompress(self):
2265         """Test handling of a bad architecture"""
2266         with self.assertRaises(ValueError) as e:
2267             self._DoReadFile('109_cbfs_bad_compress.dts')
2268         self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2269                       str(e.exception))
2270
2271     def testCbfsNamedEntries(self):
2272         """Test handling of named entries"""
2273         data = self._DoReadFile('110_cbfs_name.dts')
2274
2275         cbfs = cbfs_util.CbfsReader(data)
2276         self.assertIn('FRED', cbfs.files)
2277         cfile1 = cbfs.files['FRED']
2278         self.assertEqual(U_BOOT_DATA, cfile1.data)
2279
2280         self.assertIn('hello', cbfs.files)
2281         cfile2 = cbfs.files['hello']
2282         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2283
2284     def _SetupIfwi(self, fname):
2285         """Set up to run an IFWI test
2286
2287         Args:
2288             fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2289         """
2290         self._SetupSplElf()
2291         self._SetupTplElf()
2292
2293         # Intel Integrated Firmware Image (IFWI) file
2294         with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2295             data = fd.read()
2296         TestFunctional._MakeInputFile(fname,data)
2297
2298     def _CheckIfwi(self, data):
2299         """Check that an image with an IFWI contains the correct output
2300
2301         Args:
2302             data: Conents of output file
2303         """
2304         expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
2305         if data[:0x1000] != expected_desc:
2306             self.fail('Expected descriptor binary at start of image')
2307
2308         # We expect to find the TPL wil in subpart IBBP entry IBBL
2309         image_fname = tools.GetOutputFilename('image.bin')
2310         tpl_fname = tools.GetOutputFilename('tpl.out')
2311         tools.RunIfwiTool(image_fname, tools.CMD_EXTRACT, fname=tpl_fname,
2312                           subpart='IBBP', entry_name='IBBL')
2313
2314         tpl_data = tools.ReadFile(tpl_fname)
2315         self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
2316
2317     def testPackX86RomIfwi(self):
2318         """Test that an x86 ROM with Integrated Firmware Image can be created"""
2319         self._SetupIfwi('fitimage.bin')
2320         data = self._DoReadFile('111_x86_rom_ifwi.dts')
2321         self._CheckIfwi(data)
2322
2323     def testPackX86RomIfwiNoDesc(self):
2324         """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2325         self._SetupIfwi('ifwi.bin')
2326         data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
2327         self._CheckIfwi(data)
2328
2329     def testPackX86RomIfwiNoData(self):
2330         """Test that an x86 ROM with IFWI handles missing data"""
2331         self._SetupIfwi('ifwi.bin')
2332         with self.assertRaises(ValueError) as e:
2333             data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
2334         self.assertIn('Could not complete processing of contents',
2335                       str(e.exception))
2336
2337     def testCbfsOffset(self):
2338         """Test a CBFS with files at particular offsets
2339
2340         Like all CFBS tests, this is just checking the logic that calls
2341         cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2342         """
2343         data = self._DoReadFile('114_cbfs_offset.dts')
2344         size = 0x200
2345
2346         cbfs = cbfs_util.CbfsReader(data)
2347         self.assertEqual(size, cbfs.rom_size)
2348
2349         self.assertIn('u-boot', cbfs.files)
2350         cfile = cbfs.files['u-boot']
2351         self.assertEqual(U_BOOT_DATA, cfile.data)
2352         self.assertEqual(0x40, cfile.cbfs_offset)
2353
2354         self.assertIn('u-boot-dtb', cbfs.files)
2355         cfile2 = cbfs.files['u-boot-dtb']
2356         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2357         self.assertEqual(0x140, cfile2.cbfs_offset)
2358
2359     def testFdtmap(self):
2360         """Test an FDT map can be inserted in the image"""
2361         data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2362         fdtmap_data = data[len(U_BOOT_DATA):]
2363         magic = fdtmap_data[:8]
2364         self.assertEqual(b'_FDTMAP_', magic)
2365         self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16])
2366
2367         fdt_data = fdtmap_data[16:]
2368         dtb = fdt.Fdt.FromData(fdt_data)
2369         dtb.Scan()
2370         props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
2371         self.assertEqual({
2372             'image-pos': 0,
2373             'offset': 0,
2374             'u-boot:offset': 0,
2375             'u-boot:size': len(U_BOOT_DATA),
2376             'u-boot:image-pos': 0,
2377             'fdtmap:image-pos': 4,
2378             'fdtmap:offset': 4,
2379             'fdtmap:size': len(fdtmap_data),
2380             'size': len(data),
2381         }, props)
2382
2383     def testFdtmapNoMatch(self):
2384         """Check handling of an FDT map when the section cannot be found"""
2385         self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2386
2387         # Mangle the section name, which should cause a mismatch between the
2388         # correct FDT path and the one expected by the section
2389         image = control.images['image']
2390         image._node.path += '-suffix'
2391         entries = image.GetEntries()
2392         fdtmap = entries['fdtmap']
2393         with self.assertRaises(ValueError) as e:
2394             fdtmap._GetFdtmap()
2395         self.assertIn("Cannot locate node for path '/binman-suffix'",
2396                       str(e.exception))
2397
2398     def testFdtmapHeader(self):
2399         """Test an FDT map and image header can be inserted in the image"""
2400         data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2401         fdtmap_pos = len(U_BOOT_DATA)
2402         fdtmap_data = data[fdtmap_pos:]
2403         fdt_data = fdtmap_data[16:]
2404         dtb = fdt.Fdt.FromData(fdt_data)
2405         fdt_size = dtb.GetFdtObj().totalsize()
2406         hdr_data = data[-8:]
2407         self.assertEqual(b'BinM', hdr_data[:4])
2408         offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2409         self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2410
2411     def testFdtmapHeaderStart(self):
2412         """Test an image header can be inserted at the image start"""
2413         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2414         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2415         hdr_data = data[:8]
2416         self.assertEqual(b'BinM', hdr_data[:4])
2417         offset = struct.unpack('<I', hdr_data[4:])[0]
2418         self.assertEqual(fdtmap_pos, offset)
2419
2420     def testFdtmapHeaderPos(self):
2421         """Test an image header can be inserted at a chosen position"""
2422         data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2423         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2424         hdr_data = data[0x80:0x88]
2425         self.assertEqual(b'BinM', hdr_data[:4])
2426         offset = struct.unpack('<I', hdr_data[4:])[0]
2427         self.assertEqual(fdtmap_pos, offset)
2428
2429     def testHeaderMissingFdtmap(self):
2430         """Test an image header requires an fdtmap"""
2431         with self.assertRaises(ValueError) as e:
2432             self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2433         self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2434                       str(e.exception))
2435
2436     def testHeaderNoLocation(self):
2437         """Test an image header with a no specified location is detected"""
2438         with self.assertRaises(ValueError) as e:
2439             self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2440         self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2441                       str(e.exception))
2442
2443     def testEntryExpand(self):
2444         """Test expanding an entry after it is packed"""
2445         data = self._DoReadFile('121_entry_expand.dts')
2446         self.assertEqual(b'aaa', data[:3])
2447         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2448         self.assertEqual(b'aaa', data[-3:])
2449
2450     def testEntryExpandBad(self):
2451         """Test expanding an entry after it is packed, twice"""
2452         with self.assertRaises(ValueError) as e:
2453             self._DoReadFile('122_entry_expand_twice.dts')
2454         self.assertIn("Image '/binman': Entries changed size after packing",
2455                       str(e.exception))
2456
2457     def testEntryExpandSection(self):
2458         """Test expanding an entry within a section after it is packed"""
2459         data = self._DoReadFile('123_entry_expand_section.dts')
2460         self.assertEqual(b'aaa', data[:3])
2461         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2462         self.assertEqual(b'aaa', data[-3:])
2463
2464     def testCompressDtb(self):
2465         """Test that compress of device-tree files is supported"""
2466         self._CheckLz4()
2467         data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2468         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2469         comp_data = data[len(U_BOOT_DATA):]
2470         orig = self._decompress(comp_data)
2471         dtb = fdt.Fdt.FromData(orig)
2472         dtb.Scan()
2473         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2474         expected = {
2475             'u-boot:size': len(U_BOOT_DATA),
2476             'u-boot-dtb:uncomp-size': len(orig),
2477             'u-boot-dtb:size': len(comp_data),
2478             'size': len(data),
2479             }
2480         self.assertEqual(expected, props)
2481
2482     def testCbfsUpdateFdt(self):
2483         """Test that we can update the device tree with CBFS offset/size info"""
2484         self._CheckLz4()
2485         data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2486                                                         update_dtb=True)
2487         dtb = fdt.Fdt(out_dtb_fname)
2488         dtb.Scan()
2489         props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
2490         del props['cbfs/u-boot:size']
2491         self.assertEqual({
2492             'offset': 0,
2493             'size': len(data),
2494             'image-pos': 0,
2495             'cbfs:offset': 0,
2496             'cbfs:size': len(data),
2497             'cbfs:image-pos': 0,
2498             'cbfs/u-boot:offset': 0x38,
2499             'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
2500             'cbfs/u-boot:image-pos': 0x38,
2501             'cbfs/u-boot-dtb:offset': 0xb8,
2502             'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
2503             'cbfs/u-boot-dtb:image-pos': 0xb8,
2504             }, props)
2505
2506     def testCbfsBadType(self):
2507         """Test an image header with a no specified location is detected"""
2508         with self.assertRaises(ValueError) as e:
2509             self._DoReadFile('126_cbfs_bad_type.dts')
2510         self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2511
2512     def testList(self):
2513         """Test listing the files in an image"""
2514         self._CheckLz4()
2515         data = self._DoReadFile('127_list.dts')
2516         image = control.images['image']
2517         entries = image.BuildEntryList()
2518         self.assertEqual(7, len(entries))
2519
2520         ent = entries[0]
2521         self.assertEqual(0, ent.indent)
2522         self.assertEqual('main-section', ent.name)
2523         self.assertEqual('section', ent.etype)
2524         self.assertEqual(len(data), ent.size)
2525         self.assertEqual(0, ent.image_pos)
2526         self.assertEqual(None, ent.uncomp_size)
2527         self.assertEqual(0, ent.offset)
2528
2529         ent = entries[1]
2530         self.assertEqual(1, ent.indent)
2531         self.assertEqual('u-boot', ent.name)
2532         self.assertEqual('u-boot', ent.etype)
2533         self.assertEqual(len(U_BOOT_DATA), ent.size)
2534         self.assertEqual(0, ent.image_pos)
2535         self.assertEqual(None, ent.uncomp_size)
2536         self.assertEqual(0, ent.offset)
2537
2538         ent = entries[2]
2539         self.assertEqual(1, ent.indent)
2540         self.assertEqual('section', ent.name)
2541         self.assertEqual('section', ent.etype)
2542         section_size = ent.size
2543         self.assertEqual(0x100, ent.image_pos)
2544         self.assertEqual(None, ent.uncomp_size)
2545         self.assertEqual(0x100, ent.offset)
2546
2547         ent = entries[3]
2548         self.assertEqual(2, ent.indent)
2549         self.assertEqual('cbfs', ent.name)
2550         self.assertEqual('cbfs', ent.etype)
2551         self.assertEqual(0x400, ent.size)
2552         self.assertEqual(0x100, ent.image_pos)
2553         self.assertEqual(None, ent.uncomp_size)
2554         self.assertEqual(0, ent.offset)
2555
2556         ent = entries[4]
2557         self.assertEqual(3, ent.indent)
2558         self.assertEqual('u-boot', ent.name)
2559         self.assertEqual('u-boot', ent.etype)
2560         self.assertEqual(len(U_BOOT_DATA), ent.size)
2561         self.assertEqual(0x138, ent.image_pos)
2562         self.assertEqual(None, ent.uncomp_size)
2563         self.assertEqual(0x38, ent.offset)
2564
2565         ent = entries[5]
2566         self.assertEqual(3, ent.indent)
2567         self.assertEqual('u-boot-dtb', ent.name)
2568         self.assertEqual('text', ent.etype)
2569         self.assertGreater(len(COMPRESS_DATA), ent.size)
2570         self.assertEqual(0x178, ent.image_pos)
2571         self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2572         self.assertEqual(0x78, ent.offset)
2573
2574         ent = entries[6]
2575         self.assertEqual(2, ent.indent)
2576         self.assertEqual('u-boot-dtb', ent.name)
2577         self.assertEqual('u-boot-dtb', ent.etype)
2578         self.assertEqual(0x500, ent.image_pos)
2579         self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2580         dtb_size = ent.size
2581         # Compressing this data expands it since headers are added
2582         self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2583         self.assertEqual(0x400, ent.offset)
2584
2585         self.assertEqual(len(data), 0x100 + section_size)
2586         self.assertEqual(section_size, 0x400 + dtb_size)
2587
2588     def testFindFdtmap(self):
2589         """Test locating an FDT map in an image"""
2590         self._CheckLz4()
2591         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2592         image = control.images['image']
2593         entries = image.GetEntries()
2594         entry = entries['fdtmap']
2595         self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2596
2597     def testFindFdtmapMissing(self):
2598         """Test failing to locate an FDP map"""
2599         data = self._DoReadFile('005_simple.dts')
2600         self.assertEqual(None, fdtmap.LocateFdtmap(data))
2601
2602     def testFindImageHeader(self):
2603         """Test locating a image header"""
2604         self._CheckLz4()
2605         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2606         image = control.images['image']
2607         entries = image.GetEntries()
2608         entry = entries['fdtmap']
2609         # The header should point to the FDT map
2610         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2611
2612     def testFindImageHeaderStart(self):
2613         """Test locating a image header located at the start of an image"""
2614         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2615         image = control.images['image']
2616         entries = image.GetEntries()
2617         entry = entries['fdtmap']
2618         # The header should point to the FDT map
2619         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2620
2621     def testFindImageHeaderMissing(self):
2622         """Test failing to locate an image header"""
2623         data = self._DoReadFile('005_simple.dts')
2624         self.assertEqual(None, image_header.LocateHeaderOffset(data))
2625
2626     def testReadImage(self):
2627         """Test reading an image and accessing its FDT map"""
2628         self._CheckLz4()
2629         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2630         image_fname = tools.GetOutputFilename('image.bin')
2631         orig_image = control.images['image']
2632         image = Image.FromFile(image_fname)
2633         self.assertEqual(orig_image.GetEntries().keys(),
2634                          image.GetEntries().keys())
2635
2636         orig_entry = orig_image.GetEntries()['fdtmap']
2637         entry = image.GetEntries()['fdtmap']
2638         self.assertEquals(orig_entry.offset, entry.offset)
2639         self.assertEquals(orig_entry.size, entry.size)
2640         self.assertEquals(orig_entry.image_pos, entry.image_pos)
2641
2642     def testReadImageNoHeader(self):
2643         """Test accessing an image's FDT map without an image header"""
2644         self._CheckLz4()
2645         data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
2646         image_fname = tools.GetOutputFilename('image.bin')
2647         image = Image.FromFile(image_fname)
2648         self.assertTrue(isinstance(image, Image))
2649         self.assertEqual('image', image.image_name[-5:])
2650
2651     def testReadImageFail(self):
2652         """Test failing to read an image image's FDT map"""
2653         self._DoReadFile('005_simple.dts')
2654         image_fname = tools.GetOutputFilename('image.bin')
2655         with self.assertRaises(ValueError) as e:
2656             image = Image.FromFile(image_fname)
2657         self.assertIn("Cannot find FDT map in image", str(e.exception))
2658
2659     def testListCmd(self):
2660         """Test listing the files in an image using an Fdtmap"""
2661         self._CheckLz4()
2662         data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2663
2664         # lz4 compression size differs depending on the version
2665         image = control.images['image']
2666         entries = image.GetEntries()
2667         section_size = entries['section'].size
2668         fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2669         fdtmap_offset = entries['fdtmap'].offset
2670
2671         try:
2672             tmpdir, updated_fname = self._SetupImageInTmpdir()
2673             with test_util.capture_sys_output() as (stdout, stderr):
2674                 self._DoBinman('ls', '-i', updated_fname)
2675         finally:
2676             shutil.rmtree(tmpdir)
2677         lines = stdout.getvalue().splitlines()
2678         expected = [
2679 'Name              Image-pos  Size  Entry-type    Offset  Uncomp-size',
2680 '----------------------------------------------------------------------',
2681 'main-section              0   c00  section            0',
2682 '  u-boot                  0     4  u-boot             0',
2683 '  section               100   %x  section          100' % section_size,
2684 '    cbfs                100   400  cbfs               0',
2685 '      u-boot            138     4  u-boot            38',
2686 '      u-boot-dtb        180   105  u-boot-dtb        80          3c9',
2687 '    u-boot-dtb          500   %x  u-boot-dtb       400          3c9' % fdt_size,
2688 '  fdtmap                %x   3bd  fdtmap           %x' %
2689         (fdtmap_offset, fdtmap_offset),
2690 '  image-header          bf8     8  image-header     bf8',
2691             ]
2692         self.assertEqual(expected, lines)
2693
2694     def testListCmdFail(self):
2695         """Test failing to list an image"""
2696         self._DoReadFile('005_simple.dts')
2697         try:
2698             tmpdir, updated_fname = self._SetupImageInTmpdir()
2699             with self.assertRaises(ValueError) as e:
2700                 self._DoBinman('ls', '-i', updated_fname)
2701         finally:
2702             shutil.rmtree(tmpdir)
2703         self.assertIn("Cannot find FDT map in image", str(e.exception))
2704
2705     def _RunListCmd(self, paths, expected):
2706         """List out entries and check the result
2707
2708         Args:
2709             paths: List of paths to pass to the list command
2710             expected: Expected list of filenames to be returned, in order
2711         """
2712         self._CheckLz4()
2713         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2714         image_fname = tools.GetOutputFilename('image.bin')
2715         image = Image.FromFile(image_fname)
2716         lines = image.GetListEntries(paths)[1]
2717         files = [line[0].strip() for line in lines[1:]]
2718         self.assertEqual(expected, files)
2719
2720     def testListCmdSection(self):
2721         """Test listing the files in a section"""
2722         self._RunListCmd(['section'],
2723             ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2724
2725     def testListCmdFile(self):
2726         """Test listing a particular file"""
2727         self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2728
2729     def testListCmdWildcard(self):
2730         """Test listing a wildcarded file"""
2731         self._RunListCmd(['*boot*'],
2732             ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2733
2734     def testListCmdWildcardMulti(self):
2735         """Test listing a wildcarded file"""
2736         self._RunListCmd(['*cb*', '*head*'],
2737             ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2738
2739     def testListCmdEmpty(self):
2740         """Test listing a wildcarded file"""
2741         self._RunListCmd(['nothing'], [])
2742
2743     def testListCmdPath(self):
2744         """Test listing the files in a sub-entry of a section"""
2745         self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2746
2747     def _RunExtractCmd(self, entry_name, decomp=True):
2748         """Extract an entry from an image
2749
2750         Args:
2751             entry_name: Entry name to extract
2752             decomp: True to decompress the data if compressed, False to leave
2753                 it in its raw uncompressed format
2754
2755         Returns:
2756             data from entry
2757         """
2758         self._CheckLz4()
2759         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2760         image_fname = tools.GetOutputFilename('image.bin')
2761         return control.ReadEntry(image_fname, entry_name, decomp)
2762
2763     def testExtractSimple(self):
2764         """Test extracting a single file"""
2765         data = self._RunExtractCmd('u-boot')
2766         self.assertEqual(U_BOOT_DATA, data)
2767
2768     def testExtractSection(self):
2769         """Test extracting the files in a section"""
2770         data = self._RunExtractCmd('section')
2771         cbfs_data = data[:0x400]
2772         cbfs = cbfs_util.CbfsReader(cbfs_data)
2773         self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
2774         dtb_data = data[0x400:]
2775         dtb = self._decompress(dtb_data)
2776         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2777
2778     def testExtractCompressed(self):
2779         """Test extracting compressed data"""
2780         data = self._RunExtractCmd('section/u-boot-dtb')
2781         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2782
2783     def testExtractRaw(self):
2784         """Test extracting compressed data without decompressing it"""
2785         data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
2786         dtb = self._decompress(data)
2787         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2788
2789     def testExtractCbfs(self):
2790         """Test extracting CBFS data"""
2791         data = self._RunExtractCmd('section/cbfs/u-boot')
2792         self.assertEqual(U_BOOT_DATA, data)
2793
2794     def testExtractCbfsCompressed(self):
2795         """Test extracting CBFS compressed data"""
2796         data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
2797         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2798
2799     def testExtractCbfsRaw(self):
2800         """Test extracting CBFS compressed data without decompressing it"""
2801         data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
2802         dtb = tools.Decompress(data, 'lzma', with_header=False)
2803         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2804
2805     def testExtractBadEntry(self):
2806         """Test extracting a bad section path"""
2807         with self.assertRaises(ValueError) as e:
2808             self._RunExtractCmd('section/does-not-exist')
2809         self.assertIn("Entry 'does-not-exist' not found in '/section'",
2810                       str(e.exception))
2811
2812     def testExtractMissingFile(self):
2813         """Test extracting file that does not exist"""
2814         with self.assertRaises(IOError) as e:
2815             control.ReadEntry('missing-file', 'name')
2816
2817     def testExtractBadFile(self):
2818         """Test extracting an invalid file"""
2819         fname = os.path.join(self._indir, 'badfile')
2820         tools.WriteFile(fname, b'')
2821         with self.assertRaises(ValueError) as e:
2822             control.ReadEntry(fname, 'name')
2823
2824     def testExtractCmd(self):
2825         """Test extracting a file fron an image on the command line"""
2826         self._CheckLz4()
2827         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2828         fname = os.path.join(self._indir, 'output.extact')
2829         try:
2830             tmpdir, updated_fname = self._SetupImageInTmpdir()
2831             with test_util.capture_sys_output() as (stdout, stderr):
2832                 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
2833                                '-f', fname)
2834         finally:
2835             shutil.rmtree(tmpdir)
2836         data = tools.ReadFile(fname)
2837         self.assertEqual(U_BOOT_DATA, data)
2838
2839     def testExtractOneEntry(self):
2840         """Test extracting a single entry fron an image """
2841         self._CheckLz4()
2842         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2843         image_fname = tools.GetOutputFilename('image.bin')
2844         fname = os.path.join(self._indir, 'output.extact')
2845         control.ExtractEntries(image_fname, fname, None, ['u-boot'])
2846         data = tools.ReadFile(fname)
2847         self.assertEqual(U_BOOT_DATA, data)
2848
2849     def _CheckExtractOutput(self, decomp):
2850         """Helper to test file output with and without decompression
2851
2852         Args:
2853             decomp: True to decompress entry data, False to output it raw
2854         """
2855         def _CheckPresent(entry_path, expect_data, expect_size=None):
2856             """Check and remove expected file
2857
2858             This checks the data/size of a file and removes the file both from
2859             the outfiles set and from the output directory. Once all files are
2860             processed, both the set and directory should be empty.
2861
2862             Args:
2863                 entry_path: Entry path
2864                 expect_data: Data to expect in file, or None to skip check
2865                 expect_size: Size of data to expect in file, or None to skip
2866             """
2867             path = os.path.join(outdir, entry_path)
2868             data = tools.ReadFile(path)
2869             os.remove(path)
2870             if expect_data:
2871                 self.assertEqual(expect_data, data)
2872             elif expect_size:
2873                 self.assertEqual(expect_size, len(data))
2874             outfiles.remove(path)
2875
2876         def _CheckDirPresent(name):
2877             """Remove expected directory
2878
2879             This gives an error if the directory does not exist as expected
2880
2881             Args:
2882                 name: Name of directory to remove
2883             """
2884             path = os.path.join(outdir, name)
2885             os.rmdir(path)
2886
2887         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2888         image_fname = tools.GetOutputFilename('image.bin')
2889         outdir = os.path.join(self._indir, 'extract')
2890         einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
2891
2892         # Create a set of all file that were output (should be 9)
2893         outfiles = set()
2894         for root, dirs, files in os.walk(outdir):
2895             outfiles |= set([os.path.join(root, fname) for fname in files])
2896         self.assertEqual(9, len(outfiles))
2897         self.assertEqual(9, len(einfos))
2898
2899         image = control.images['image']
2900         entries = image.GetEntries()
2901
2902         # Check the 9 files in various ways
2903         section = entries['section']
2904         section_entries = section.GetEntries()
2905         cbfs_entries = section_entries['cbfs'].GetEntries()
2906         _CheckPresent('u-boot', U_BOOT_DATA)
2907         _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
2908         dtb_len = EXTRACT_DTB_SIZE
2909         if not decomp:
2910             dtb_len = cbfs_entries['u-boot-dtb'].size
2911         _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
2912         if not decomp:
2913             dtb_len = section_entries['u-boot-dtb'].size
2914         _CheckPresent('section/u-boot-dtb', None, dtb_len)
2915
2916         fdtmap = entries['fdtmap']
2917         _CheckPresent('fdtmap', fdtmap.data)
2918         hdr = entries['image-header']
2919         _CheckPresent('image-header', hdr.data)
2920
2921         _CheckPresent('section/root', section.data)
2922         cbfs = section_entries['cbfs']
2923         _CheckPresent('section/cbfs/root', cbfs.data)
2924         data = tools.ReadFile(image_fname)
2925         _CheckPresent('root', data)
2926
2927         # There should be no files left. Remove all the directories to check.
2928         # If there are any files/dirs remaining, one of these checks will fail.
2929         self.assertEqual(0, len(outfiles))
2930         _CheckDirPresent('section/cbfs')
2931         _CheckDirPresent('section')
2932         _CheckDirPresent('')
2933         self.assertFalse(os.path.exists(outdir))
2934
2935     def testExtractAllEntries(self):
2936         """Test extracting all entries"""
2937         self._CheckLz4()
2938         self._CheckExtractOutput(decomp=True)
2939
2940     def testExtractAllEntriesRaw(self):
2941         """Test extracting all entries without decompressing them"""
2942         self._CheckLz4()
2943         self._CheckExtractOutput(decomp=False)
2944
2945     def testExtractSelectedEntries(self):
2946         """Test extracting some entries"""
2947         self._CheckLz4()
2948         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2949         image_fname = tools.GetOutputFilename('image.bin')
2950         outdir = os.path.join(self._indir, 'extract')
2951         einfos = control.ExtractEntries(image_fname, None, outdir,
2952                                         ['*cb*', '*head*'])
2953
2954         # File output is tested by testExtractAllEntries(), so just check that
2955         # the expected entries are selected
2956         names = [einfo.name for einfo in einfos]
2957         self.assertEqual(names,
2958                          ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2959
2960     def testExtractNoEntryPaths(self):
2961         """Test extracting some entries"""
2962         self._CheckLz4()
2963         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2964         image_fname = tools.GetOutputFilename('image.bin')
2965         with self.assertRaises(ValueError) as e:
2966             control.ExtractEntries(image_fname, 'fname', None, [])
2967         self.assertIn('Must specify an entry path to write with -f',
2968                       str(e.exception))
2969
2970     def testExtractTooManyEntryPaths(self):
2971         """Test extracting some entries"""
2972         self._CheckLz4()
2973         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2974         image_fname = tools.GetOutputFilename('image.bin')
2975         with self.assertRaises(ValueError) as e:
2976             control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
2977         self.assertIn('Must specify exactly one entry path to write with -f',
2978                       str(e.exception))
2979
2980     def testPackAlignSection(self):
2981         """Test that sections can have alignment"""
2982         self._DoReadFile('131_pack_align_section.dts')
2983
2984         self.assertIn('image', control.images)
2985         image = control.images['image']
2986         entries = image.GetEntries()
2987         self.assertEqual(3, len(entries))
2988
2989         # First u-boot
2990         self.assertIn('u-boot', entries)
2991         entry = entries['u-boot']
2992         self.assertEqual(0, entry.offset)
2993         self.assertEqual(0, entry.image_pos)
2994         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2995         self.assertEqual(len(U_BOOT_DATA), entry.size)
2996
2997         # Section0
2998         self.assertIn('section0', entries)
2999         section0 = entries['section0']
3000         self.assertEqual(0x10, section0.offset)
3001         self.assertEqual(0x10, section0.image_pos)
3002         self.assertEqual(len(U_BOOT_DATA), section0.size)
3003
3004         # Second u-boot
3005         section_entries = section0.GetEntries()
3006         self.assertIn('u-boot', section_entries)
3007         entry = section_entries['u-boot']
3008         self.assertEqual(0, entry.offset)
3009         self.assertEqual(0x10, entry.image_pos)
3010         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3011         self.assertEqual(len(U_BOOT_DATA), entry.size)
3012
3013         # Section1
3014         self.assertIn('section1', entries)
3015         section1 = entries['section1']
3016         self.assertEqual(0x14, section1.offset)
3017         self.assertEqual(0x14, section1.image_pos)
3018         self.assertEqual(0x20, section1.size)
3019
3020         # Second u-boot
3021         section_entries = section1.GetEntries()
3022         self.assertIn('u-boot', section_entries)
3023         entry = section_entries['u-boot']
3024         self.assertEqual(0, entry.offset)
3025         self.assertEqual(0x14, entry.image_pos)
3026         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3027         self.assertEqual(len(U_BOOT_DATA), entry.size)
3028
3029         # Section2
3030         self.assertIn('section2', section_entries)
3031         section2 = section_entries['section2']
3032         self.assertEqual(0x4, section2.offset)
3033         self.assertEqual(0x18, section2.image_pos)
3034         self.assertEqual(4, section2.size)
3035
3036         # Third u-boot
3037         section_entries = section2.GetEntries()
3038         self.assertIn('u-boot', section_entries)
3039         entry = section_entries['u-boot']
3040         self.assertEqual(0, entry.offset)
3041         self.assertEqual(0x18, entry.image_pos)
3042         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3043         self.assertEqual(len(U_BOOT_DATA), entry.size)
3044
3045     def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3046                        dts='132_replace.dts'):
3047         """Replace an entry in an image
3048
3049         This writes the entry data to update it, then opens the updated file and
3050         returns the value that it now finds there.
3051
3052         Args:
3053             entry_name: Entry name to replace
3054             data: Data to replace it with
3055             decomp: True to compress the data if needed, False if data is
3056                 already compressed so should be used as is
3057             allow_resize: True to allow entries to change size, False to raise
3058                 an exception
3059
3060         Returns:
3061             Tuple:
3062                 data from entry
3063                 data from fdtmap (excluding header)
3064                 Image object that was modified
3065         """
3066         dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
3067                                        update_dtb=True)[1]
3068
3069         self.assertIn('image', control.images)
3070         image = control.images['image']
3071         entries = image.GetEntries()
3072         orig_dtb_data = entries['u-boot-dtb'].data
3073         orig_fdtmap_data = entries['fdtmap'].data
3074
3075         image_fname = tools.GetOutputFilename('image.bin')
3076         updated_fname = tools.GetOutputFilename('image-updated.bin')
3077         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
3078         image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3079                                    allow_resize)
3080         data = control.ReadEntry(updated_fname, entry_name, decomp)
3081
3082         # The DT data should not change unless resized:
3083         if not allow_resize:
3084             new_dtb_data = entries['u-boot-dtb'].data
3085             self.assertEqual(new_dtb_data, orig_dtb_data)
3086             new_fdtmap_data = entries['fdtmap'].data
3087             self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
3088
3089         return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
3090
3091     def testReplaceSimple(self):
3092         """Test replacing a single file"""
3093         expected = b'x' * len(U_BOOT_DATA)
3094         data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3095                                                     allow_resize=False)
3096         self.assertEqual(expected, data)
3097
3098         # Test that the state looks right. There should be an FDT for the fdtmap
3099         # that we jsut read back in, and it should match what we find in the
3100         # 'control' tables. Checking for an FDT that does not exist should
3101         # return None.
3102         path, fdtmap = state.GetFdtContents('fdtmap')
3103         self.assertIsNotNone(path)
3104         self.assertEqual(expected_fdtmap, fdtmap)
3105
3106         dtb = state.GetFdtForEtype('fdtmap')
3107         self.assertEqual(dtb.GetContents(), fdtmap)
3108
3109         missing_path, missing_fdtmap = state.GetFdtContents('missing')
3110         self.assertIsNone(missing_path)
3111         self.assertIsNone(missing_fdtmap)
3112
3113         missing_dtb = state.GetFdtForEtype('missing')
3114         self.assertIsNone(missing_dtb)
3115
3116         self.assertEqual('/binman', state.fdt_path_prefix)
3117
3118     def testReplaceResizeFail(self):
3119         """Test replacing a file by something larger"""
3120         expected = U_BOOT_DATA + b'x'
3121         with self.assertRaises(ValueError) as e:
3122             self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3123                                 dts='139_replace_repack.dts')
3124         self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3125                       str(e.exception))
3126
3127     def testReplaceMulti(self):
3128         """Test replacing entry data where multiple images are generated"""
3129         data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3130                                    update_dtb=True)[0]
3131         expected = b'x' * len(U_BOOT_DATA)
3132         updated_fname = tools.GetOutputFilename('image-updated.bin')
3133         tools.WriteFile(updated_fname, data)
3134         entry_name = 'u-boot'
3135         control.WriteEntry(updated_fname, entry_name, expected,
3136                            allow_resize=False)
3137         data = control.ReadEntry(updated_fname, entry_name)
3138         self.assertEqual(expected, data)
3139
3140         # Check the state looks right.
3141         self.assertEqual('/binman/image', state.fdt_path_prefix)
3142
3143         # Now check we can write the first image
3144         image_fname = tools.GetOutputFilename('first-image.bin')
3145         updated_fname = tools.GetOutputFilename('first-updated.bin')
3146         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
3147         entry_name = 'u-boot'
3148         control.WriteEntry(updated_fname, entry_name, expected,
3149                            allow_resize=False)
3150         data = control.ReadEntry(updated_fname, entry_name)
3151         self.assertEqual(expected, data)
3152
3153         # Check the state looks right.
3154         self.assertEqual('/binman/first-image', state.fdt_path_prefix)
3155
3156     def testUpdateFdtAllRepack(self):
3157         """Test that all device trees are updated with offset/size info"""
3158         data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3159         SECTION_SIZE = 0x300
3160         DTB_SIZE = 602
3161         FDTMAP_SIZE = 608
3162         base_expected = {
3163             'offset': 0,
3164             'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3165             'image-pos': 0,
3166             'section:offset': 0,
3167             'section:size': SECTION_SIZE,
3168             'section:image-pos': 0,
3169             'section/u-boot-dtb:offset': 4,
3170             'section/u-boot-dtb:size': 636,
3171             'section/u-boot-dtb:image-pos': 4,
3172             'u-boot-spl-dtb:offset': SECTION_SIZE,
3173             'u-boot-spl-dtb:size': DTB_SIZE,
3174             'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3175             'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3176             'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3177             'u-boot-tpl-dtb:size': DTB_SIZE,
3178             'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3179             'fdtmap:size': FDTMAP_SIZE,
3180             'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3181         }
3182         main_expected = {
3183             'section:orig-size': SECTION_SIZE,
3184             'section/u-boot-dtb:orig-offset': 4,
3185         }
3186
3187         # We expect three device-tree files in the output, with the first one
3188         # within a fixed-size section.
3189         # Read them in sequence. We look for an 'spl' property in the SPL tree,
3190         # and 'tpl' in the TPL tree, to make sure they are distinct from the
3191         # main U-Boot tree. All three should have the same positions and offset
3192         # except that the main tree should include the main_expected properties
3193         start = 4
3194         for item in ['', 'spl', 'tpl', None]:
3195             if item is None:
3196                 start += 16  # Move past fdtmap header
3197             dtb = fdt.Fdt.FromData(data[start:])
3198             dtb.Scan()
3199             props = self._GetPropTree(dtb,
3200                 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3201                 prefix='/' if item is None else '/binman/')
3202             expected = dict(base_expected)
3203             if item:
3204                 expected[item] = 0
3205             else:
3206                 # Main DTB and fdtdec should include the 'orig-' properties
3207                 expected.update(main_expected)
3208             # Helpful for debugging:
3209             #for prop in sorted(props):
3210                 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3211             self.assertEqual(expected, props)
3212             if item == '':
3213                 start = SECTION_SIZE
3214             else:
3215                 start += dtb._fdt_obj.totalsize()
3216
3217     def testFdtmapHeaderMiddle(self):
3218         """Test an FDT map in the middle of an image when it should be at end"""
3219         with self.assertRaises(ValueError) as e:
3220             self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3221         self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3222                       str(e.exception))
3223
3224     def testFdtmapHeaderStartBad(self):
3225         """Test an FDT map in middle of an image when it should be at start"""
3226         with self.assertRaises(ValueError) as e:
3227             self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3228         self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3229                       str(e.exception))
3230
3231     def testFdtmapHeaderEndBad(self):
3232         """Test an FDT map at the start of an image when it should be at end"""
3233         with self.assertRaises(ValueError) as e:
3234             self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3235         self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3236                       str(e.exception))
3237
3238     def testFdtmapHeaderNoSize(self):
3239         """Test an image header at the end of an image with undefined size"""
3240         self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3241
3242     def testReplaceResize(self):
3243         """Test replacing a single file in an entry with a larger file"""
3244         expected = U_BOOT_DATA + b'x'
3245         data, _, image = self._RunReplaceCmd('u-boot', expected,
3246                                              dts='139_replace_repack.dts')
3247         self.assertEqual(expected, data)
3248
3249         entries = image.GetEntries()
3250         dtb_data = entries['u-boot-dtb'].data
3251         dtb = fdt.Fdt.FromData(dtb_data)
3252         dtb.Scan()
3253
3254         # The u-boot section should now be larger in the dtb
3255         node = dtb.GetNode('/binman/u-boot')
3256         self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3257
3258         # Same for the fdtmap
3259         fdata = entries['fdtmap'].data
3260         fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3261         fdtb.Scan()
3262         fnode = fdtb.GetNode('/u-boot')
3263         self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3264
3265     def testReplaceResizeNoRepack(self):
3266         """Test replacing an entry with a larger file when not allowed"""
3267         expected = U_BOOT_DATA + b'x'
3268         with self.assertRaises(ValueError) as e:
3269             self._RunReplaceCmd('u-boot', expected)
3270         self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3271                       str(e.exception))
3272
3273     def testEntryShrink(self):
3274         """Test contracting an entry after it is packed"""
3275         try:
3276             state.SetAllowEntryContraction(True)
3277             data = self._DoReadFileDtb('140_entry_shrink.dts',
3278                                        update_dtb=True)[0]
3279         finally:
3280             state.SetAllowEntryContraction(False)
3281         self.assertEqual(b'a', data[:1])
3282         self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3283         self.assertEqual(b'a', data[-1:])
3284
3285     def testEntryShrinkFail(self):
3286         """Test not being allowed to contract an entry after it is packed"""
3287         data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3288
3289         # In this case there is a spare byte at the end of the data. The size of
3290         # the contents is only 1 byte but we still have the size before it
3291         # shrunk.
3292         self.assertEqual(b'a\0', data[:2])
3293         self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3294         self.assertEqual(b'a\0', data[-2:])
3295
3296     def testDescriptorOffset(self):
3297         """Test that the Intel descriptor is always placed at at the start"""
3298         data = self._DoReadFileDtb('141_descriptor_offset.dts')
3299         image = control.images['image']
3300         entries = image.GetEntries()
3301         desc = entries['intel-descriptor']
3302         self.assertEqual(0xff800000, desc.offset);
3303         self.assertEqual(0xff800000, desc.image_pos);
3304
3305     def testReplaceCbfs(self):
3306         """Test replacing a single file in CBFS without changing the size"""
3307         self._CheckLz4()
3308         expected = b'x' * len(U_BOOT_DATA)
3309         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3310         updated_fname = tools.GetOutputFilename('image-updated.bin')
3311         tools.WriteFile(updated_fname, data)
3312         entry_name = 'section/cbfs/u-boot'
3313         control.WriteEntry(updated_fname, entry_name, expected,
3314                            allow_resize=True)
3315         data = control.ReadEntry(updated_fname, entry_name)
3316         self.assertEqual(expected, data)
3317
3318     def testReplaceResizeCbfs(self):
3319         """Test replacing a single file in CBFS with one of a different size"""
3320         self._CheckLz4()
3321         expected = U_BOOT_DATA + b'x'
3322         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3323         updated_fname = tools.GetOutputFilename('image-updated.bin')
3324         tools.WriteFile(updated_fname, data)
3325         entry_name = 'section/cbfs/u-boot'
3326         control.WriteEntry(updated_fname, entry_name, expected,
3327                            allow_resize=True)
3328         data = control.ReadEntry(updated_fname, entry_name)
3329         self.assertEqual(expected, data)
3330
3331     def _SetupForReplace(self):
3332         """Set up some files to use to replace entries
3333
3334         This generates an image, copies it to a new file, extracts all the files
3335         in it and updates some of them
3336
3337         Returns:
3338             List
3339                 Image filename
3340                 Output directory
3341                 Expected values for updated entries, each a string
3342         """
3343         data = self._DoReadFileRealDtb('143_replace_all.dts')
3344
3345         updated_fname = tools.GetOutputFilename('image-updated.bin')
3346         tools.WriteFile(updated_fname, data)
3347
3348         outdir = os.path.join(self._indir, 'extract')
3349         einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3350
3351         expected1 = b'x' + U_BOOT_DATA + b'y'
3352         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3353         tools.WriteFile(u_boot_fname1, expected1)
3354
3355         expected2 = b'a' + U_BOOT_DATA + b'b'
3356         u_boot_fname2 = os.path.join(outdir, 'u-boot2')
3357         tools.WriteFile(u_boot_fname2, expected2)
3358
3359         expected_text = b'not the same text'
3360         text_fname = os.path.join(outdir, 'text')
3361         tools.WriteFile(text_fname, expected_text)
3362
3363         dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3364         dtb = fdt.FdtScan(dtb_fname)
3365         node = dtb.GetNode('/binman/text')
3366         node.AddString('my-property', 'the value')
3367         dtb.Sync(auto_resize=True)
3368         dtb.Flush()
3369
3370         return updated_fname, outdir, expected1, expected2, expected_text
3371
3372     def _CheckReplaceMultiple(self, entry_paths):
3373         """Handle replacing the contents of multiple entries
3374
3375         Args:
3376             entry_paths: List of entry paths to replace
3377
3378         Returns:
3379             List
3380                 Dict of entries in the image:
3381                     key: Entry name
3382                     Value: Entry object
3383             Expected values for updated entries, each a string
3384         """
3385         updated_fname, outdir, expected1, expected2, expected_text = (
3386             self._SetupForReplace())
3387         control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3388
3389         image = Image.FromFile(updated_fname)
3390         image.LoadData()
3391         return image.GetEntries(), expected1, expected2, expected_text
3392
3393     def testReplaceAll(self):
3394         """Test replacing the contents of all entries"""
3395         entries, expected1, expected2, expected_text = (
3396             self._CheckReplaceMultiple([]))
3397         data = entries['u-boot'].data
3398         self.assertEqual(expected1, data)
3399
3400         data = entries['u-boot2'].data
3401         self.assertEqual(expected2, data)
3402
3403         data = entries['text'].data
3404         self.assertEqual(expected_text, data)
3405
3406         # Check that the device tree is updated
3407         data = entries['u-boot-dtb'].data
3408         dtb = fdt.Fdt.FromData(data)
3409         dtb.Scan()
3410         node = dtb.GetNode('/binman/text')
3411         self.assertEqual('the value', node.props['my-property'].value)
3412
3413     def testReplaceSome(self):
3414         """Test replacing the contents of a few entries"""
3415         entries, expected1, expected2, expected_text = (
3416             self._CheckReplaceMultiple(['u-boot2', 'text']))
3417
3418         # This one should not change
3419         data = entries['u-boot'].data
3420         self.assertEqual(U_BOOT_DATA, data)
3421
3422         data = entries['u-boot2'].data
3423         self.assertEqual(expected2, data)
3424
3425         data = entries['text'].data
3426         self.assertEqual(expected_text, data)
3427
3428     def testReplaceCmd(self):
3429         """Test replacing a file fron an image on the command line"""
3430         self._DoReadFileRealDtb('143_replace_all.dts')
3431
3432         try:
3433             tmpdir, updated_fname = self._SetupImageInTmpdir()
3434
3435             fname = os.path.join(tmpdir, 'update-u-boot.bin')
3436             expected = b'x' * len(U_BOOT_DATA)
3437             tools.WriteFile(fname, expected)
3438
3439             self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
3440             data = tools.ReadFile(updated_fname)
3441             self.assertEqual(expected, data[:len(expected)])
3442             map_fname = os.path.join(tmpdir, 'image-updated.map')
3443             self.assertFalse(os.path.exists(map_fname))
3444         finally:
3445             shutil.rmtree(tmpdir)
3446
3447     def testReplaceCmdSome(self):
3448         """Test replacing some files fron an image on the command line"""
3449         updated_fname, outdir, expected1, expected2, expected_text = (
3450             self._SetupForReplace())
3451
3452         self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3453                        'u-boot2', 'text')
3454
3455         tools.PrepareOutputDir(None)
3456         image = Image.FromFile(updated_fname)
3457         image.LoadData()
3458         entries = image.GetEntries()
3459
3460         # This one should not change
3461         data = entries['u-boot'].data
3462         self.assertEqual(U_BOOT_DATA, data)
3463
3464         data = entries['u-boot2'].data
3465         self.assertEqual(expected2, data)
3466
3467         data = entries['text'].data
3468         self.assertEqual(expected_text, data)
3469
3470     def testReplaceMissing(self):
3471         """Test replacing entries where the file is missing"""
3472         updated_fname, outdir, expected1, expected2, expected_text = (
3473             self._SetupForReplace())
3474
3475         # Remove one of the files, to generate a warning
3476         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3477         os.remove(u_boot_fname1)
3478
3479         with test_util.capture_sys_output() as (stdout, stderr):
3480             control.ReplaceEntries(updated_fname, None, outdir, [])
3481         self.assertIn("Skipping entry '/u-boot' from missing file",
3482                       stderr.getvalue())
3483
3484     def testReplaceCmdMap(self):
3485         """Test replacing a file fron an image on the command line"""
3486         self._DoReadFileRealDtb('143_replace_all.dts')
3487
3488         try:
3489             tmpdir, updated_fname = self._SetupImageInTmpdir()
3490
3491             fname = os.path.join(self._indir, 'update-u-boot.bin')
3492             expected = b'x' * len(U_BOOT_DATA)
3493             tools.WriteFile(fname, expected)
3494
3495             self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3496                            '-f', fname, '-m')
3497             map_fname = os.path.join(tmpdir, 'image-updated.map')
3498             self.assertTrue(os.path.exists(map_fname))
3499         finally:
3500             shutil.rmtree(tmpdir)
3501
3502     def testReplaceNoEntryPaths(self):
3503         """Test replacing an entry without an entry path"""
3504         self._DoReadFileRealDtb('143_replace_all.dts')
3505         image_fname = tools.GetOutputFilename('image.bin')
3506         with self.assertRaises(ValueError) as e:
3507             control.ReplaceEntries(image_fname, 'fname', None, [])
3508         self.assertIn('Must specify an entry path to read with -f',
3509                       str(e.exception))
3510
3511     def testReplaceTooManyEntryPaths(self):
3512         """Test extracting some entries"""
3513         self._DoReadFileRealDtb('143_replace_all.dts')
3514         image_fname = tools.GetOutputFilename('image.bin')
3515         with self.assertRaises(ValueError) as e:
3516             control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3517         self.assertIn('Must specify exactly one entry path to write with -f',
3518                       str(e.exception))
3519
3520     def testPackReset16(self):
3521         """Test that an image with an x86 reset16 region can be created"""
3522         data = self._DoReadFile('144_x86_reset16.dts')
3523         self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3524
3525     def testPackReset16Spl(self):
3526         """Test that an image with an x86 reset16-spl region can be created"""
3527         data = self._DoReadFile('145_x86_reset16_spl.dts')
3528         self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3529
3530     def testPackReset16Tpl(self):
3531         """Test that an image with an x86 reset16-tpl region can be created"""
3532         data = self._DoReadFile('146_x86_reset16_tpl.dts')
3533         self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3534
3535     def testPackIntelFit(self):
3536         """Test that an image with an Intel FIT and pointer can be created"""
3537         data = self._DoReadFile('147_intel_fit.dts')
3538         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3539         fit = data[16:32];
3540         self.assertEqual(b'_FIT_   \x01\x00\x00\x00\x00\x01\x80}' , fit)
3541         ptr = struct.unpack('<i', data[0x40:0x44])[0]
3542
3543         image = control.images['image']
3544         entries = image.GetEntries()
3545         expected_ptr = entries['intel-fit'].image_pos - (1 << 32)
3546         self.assertEqual(expected_ptr, ptr)
3547
3548     def testPackIntelFitMissing(self):
3549         """Test detection of a FIT pointer with not FIT region"""
3550         with self.assertRaises(ValueError) as e:
3551             self._DoReadFile('148_intel_fit_missing.dts')
3552         self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3553                       str(e.exception))
3554
3555     def _CheckSymbolsTplSection(self, dts, expected_vals):
3556         data = self._DoReadFile(dts)
3557         sym_values = struct.pack('<LQLL', *expected_vals)
3558         upto1 = 4 + len(U_BOOT_SPL_DATA)
3559         expected1 = tools.GetBytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[20:]
3560         self.assertEqual(expected1, data[:upto1])
3561
3562         upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
3563         expected2 = tools.GetBytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[20:]
3564         self.assertEqual(expected2, data[upto1:upto2])
3565
3566         upto3 = 0x34 + len(U_BOOT_DATA)
3567         expected3 = tools.GetBytes(0xff, 1) + U_BOOT_DATA
3568         self.assertEqual(expected3, data[upto2:upto3])
3569
3570         expected4 = sym_values + U_BOOT_TPL_DATA[20:]
3571         self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3572
3573     def testSymbolsTplSection(self):
3574         """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3575         self._SetupSplElf('u_boot_binman_syms')
3576         self._SetupTplElf('u_boot_binman_syms')
3577         self._CheckSymbolsTplSection('149_symbols_tpl.dts',
3578                                      [0x04, 0x1c, 0x10 + 0x34, 0x04])
3579
3580     def testSymbolsTplSectionX86(self):
3581         """Test binman can assign symbols in a section with end-at-4gb"""
3582         self._SetupSplElf('u_boot_binman_syms_x86')
3583         self._SetupTplElf('u_boot_binman_syms_x86')
3584         self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
3585                                      [0xffffff04, 0xffffff1c, 0xffffff34,
3586                                       0x04])
3587
3588     def testPackX86RomIfwiSectiom(self):
3589         """Test that a section can be placed in an IFWI region"""
3590         self._SetupIfwi('fitimage.bin')
3591         data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3592         self._CheckIfwi(data)
3593
3594     def testPackFspM(self):
3595         """Test that an image with a FSP memory-init binary can be created"""
3596         data = self._DoReadFile('152_intel_fsp_m.dts')
3597         self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3598
3599     def testPackFspS(self):
3600         """Test that an image with a FSP silicon-init binary can be created"""
3601         data = self._DoReadFile('153_intel_fsp_s.dts')
3602         self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
3603
3604     def testPackFspT(self):
3605         """Test that an image with a FSP temp-ram-init binary can be created"""
3606         data = self._DoReadFile('154_intel_fsp_t.dts')
3607         self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3608
3609     def testMkimage(self):
3610         """Test using mkimage to build an image"""
3611         data = self._DoReadFile('156_mkimage.dts')
3612
3613         # Just check that the data appears in the file somewhere
3614         self.assertIn(U_BOOT_SPL_DATA, data)
3615
3616     def testExtblob(self):
3617         """Test an image with an external blob"""
3618         data = self._DoReadFile('157_blob_ext.dts')
3619         self.assertEqual(REFCODE_DATA, data)
3620
3621     def testExtblobMissing(self):
3622         """Test an image with a missing external blob"""
3623         with self.assertRaises(ValueError) as e:
3624             self._DoReadFile('158_blob_ext_missing.dts')
3625         self.assertIn("Filename 'missing-file' not found in input path",
3626                       str(e.exception))
3627
3628     def testExtblobMissingOk(self):
3629         """Test an image with an missing external blob that is allowed"""
3630         with test_util.capture_sys_output() as (stdout, stderr):
3631             self._DoTestFile('158_blob_ext_missing.dts', allow_missing=True)
3632         err = stderr.getvalue()
3633         self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext")
3634
3635     def testExtblobMissingOkSect(self):
3636         """Test an image with an missing external blob that is allowed"""
3637         with test_util.capture_sys_output() as (stdout, stderr):
3638             self._DoTestFile('159_blob_ext_missing_sect.dts',
3639                              allow_missing=True)
3640         err = stderr.getvalue()
3641         self.assertRegex(err, "Image 'main-section'.*missing.*: "
3642                          "blob-ext blob-ext2")
3643
3644     def testPackX86RomMeMissingDesc(self):
3645         """Test that an missing Intel descriptor entry is allowed"""
3646         with test_util.capture_sys_output() as (stdout, stderr):
3647             self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
3648         err = stderr.getvalue()
3649         self.assertRegex(err,
3650                          "Image 'main-section'.*missing.*: intel-descriptor")
3651
3652     def testPackX86RomMissingIfwi(self):
3653         """Test that an x86 ROM with Integrated Firmware Image can be created"""
3654         self._SetupIfwi('fitimage.bin')
3655         pathname = os.path.join(self._indir, 'fitimage.bin')
3656         os.remove(pathname)
3657         with test_util.capture_sys_output() as (stdout, stderr):
3658             self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3659         err = stderr.getvalue()
3660         self.assertRegex(err, "Image 'main-section'.*missing.*: intel-ifwi")
3661
3662     def testPackOverlap(self):
3663         """Test that zero-size overlapping regions are ignored"""
3664         self._DoTestFile('160_pack_overlap_zero.dts')
3665
3666     def testSimpleFit(self):
3667         """Test an image with a FIT inside"""
3668         data = self._DoReadFile('161_fit.dts')
3669         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3670         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3671         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3672
3673         # The data should be inside the FIT
3674         dtb = fdt.Fdt.FromData(fit_data)
3675         dtb.Scan()
3676         fnode = dtb.GetNode('/images/kernel')
3677         self.assertIn('data', fnode.props)
3678
3679         fname = os.path.join(self._indir, 'fit_data.fit')
3680         tools.WriteFile(fname, fit_data)
3681         out = tools.Run('dumpimage', '-l', fname)
3682
3683         # Check a few features to make sure the plumbing works. We don't need
3684         # to test the operation of mkimage or dumpimage here. First convert the
3685         # output into a dict where the keys are the fields printed by dumpimage
3686         # and the values are a list of values for each field
3687         lines = out.splitlines()
3688
3689         # Converts "Compression:  gzip compressed" into two groups:
3690         # 'Compression' and 'gzip compressed'
3691         re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3692         vals = collections.defaultdict(list)
3693         for line in lines:
3694             mat = re_line.match(line)
3695             vals[mat.group(1)].append(mat.group(2))
3696
3697         self.assertEquals('FIT description: test-desc', lines[0])
3698         self.assertIn('Created:', lines[1])
3699         self.assertIn('Image 0 (kernel)', vals)
3700         self.assertIn('Hash value', vals)
3701         data_sizes = vals.get('Data Size')
3702         self.assertIsNotNone(data_sizes)
3703         self.assertEqual(2, len(data_sizes))
3704         # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
3705         self.assertEqual(len(U_BOOT_DATA), int(data_sizes[0].split()[0]))
3706         self.assertEqual(len(U_BOOT_SPL_DTB_DATA), int(data_sizes[1].split()[0]))
3707
3708     def testFitExternal(self):
3709         """Test an image with an FIT with external images"""
3710         data = self._DoReadFile('162_fit_external.dts')
3711         fit_data = data[len(U_BOOT_DATA):-2]  # _testing is 2 bytes
3712
3713         # The data should be outside the FIT
3714         dtb = fdt.Fdt.FromData(fit_data)
3715         dtb.Scan()
3716         fnode = dtb.GetNode('/images/kernel')
3717         self.assertNotIn('data', fnode.props)
3718
3719     def testSectionIgnoreHashSignature(self):
3720         """Test that sections ignore hash, signature nodes for its data"""
3721         data = self._DoReadFile('165_section_ignore_hash_signature.dts')
3722         expected = (U_BOOT_DATA + U_BOOT_DATA)
3723         self.assertEqual(expected, data)
3724
3725     def testPadInSections(self):
3726         """Test pad-before, pad-after for entries in sections"""
3727         data, _, _, out_dtb_fname = self._DoReadFileDtb(
3728             '166_pad_in_sections.dts', update_dtb=True)
3729         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
3730                     U_BOOT_DATA + tools.GetBytes(ord('!'), 6) +
3731                     U_BOOT_DATA)
3732         self.assertEqual(expected, data)
3733
3734         dtb = fdt.Fdt(out_dtb_fname)
3735         dtb.Scan()
3736         props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
3737         expected = {
3738             'image-pos': 0,
3739             'offset': 0,
3740             'size': 12 + 6 + 3 * len(U_BOOT_DATA),
3741
3742             'section:image-pos': 0,
3743             'section:offset': 0,
3744             'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
3745
3746             'section/before:image-pos': 0,
3747             'section/before:offset': 0,
3748             'section/before:size': len(U_BOOT_DATA),
3749
3750             'section/u-boot:image-pos': 4,
3751             'section/u-boot:offset': 4,
3752             'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
3753
3754             'section/after:image-pos': 26,
3755             'section/after:offset': 26,
3756             'section/after:size': len(U_BOOT_DATA),
3757             }
3758         self.assertEqual(expected, props)
3759
3760     def testFitImageSubentryAlignment(self):
3761         """Test relative alignability of FIT image subentries"""
3762         entry_args = {
3763             'test-id': TEXT_DATA,
3764         }
3765         data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
3766                                             entry_args=entry_args)
3767         dtb = fdt.Fdt.FromData(data)
3768         dtb.Scan()
3769
3770         node = dtb.GetNode('/images/kernel')
3771         data = dtb.GetProps(node)["data"].bytes
3772         align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
3773         expected = (tools.GetBytes(0, 0x20) + U_BOOT_SPL_DATA +
3774                     tools.GetBytes(0, align_pad) + U_BOOT_DATA)
3775         self.assertEqual(expected, data)
3776
3777         node = dtb.GetNode('/images/fdt-1')
3778         data = dtb.GetProps(node)["data"].bytes
3779         expected = (U_BOOT_SPL_DTB_DATA + tools.GetBytes(0, 20) +
3780                     tools.ToBytes(TEXT_DATA) + tools.GetBytes(0, 30) +
3781                     U_BOOT_DTB_DATA)
3782         self.assertEqual(expected, data)
3783
3784     def testFitExtblobMissingOk(self):
3785         """Test a FIT with a missing external blob that is allowed"""
3786         with test_util.capture_sys_output() as (stdout, stderr):
3787             self._DoTestFile('168_fit_missing_blob.dts',
3788                              allow_missing=True)
3789         err = stderr.getvalue()
3790         self.assertRegex(err, "Image 'main-section'.*missing.*: atf-bl31")
3791
3792     def testBlobNamedByArgMissing(self):
3793         """Test handling of a missing entry arg"""
3794         with self.assertRaises(ValueError) as e:
3795             self._DoReadFile('068_blob_named_by_arg.dts')
3796         self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
3797                       str(e.exception))
3798
3799     def testPackBl31(self):
3800         """Test that an image with an ATF BL31 binary can be created"""
3801         data = self._DoReadFile('169_atf_bl31.dts')
3802         self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
3803
3804     def testPackScp(self):
3805         """Test that an image with an SCP binary can be created"""
3806         data = self._DoReadFile('172_scp.dts')
3807         self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
3808
3809     def testFitFdt(self):
3810         """Test an image with an FIT with multiple FDT images"""
3811         def _CheckFdt(seq, expected_data):
3812             """Check the FDT nodes
3813
3814             Args:
3815                 seq: Sequence number to check (0 or 1)
3816                 expected_data: Expected contents of 'data' property
3817             """
3818             name = 'fdt-%d' % seq
3819             fnode = dtb.GetNode('/images/%s' % name)
3820             self.assertIsNotNone(fnode)
3821             self.assertEqual({'description','type', 'compression', 'data'},
3822                              set(fnode.props.keys()))
3823             self.assertEqual(expected_data, fnode.props['data'].bytes)
3824             self.assertEqual('fdt-test-fdt%d.dtb' % seq,
3825                              fnode.props['description'].value)
3826
3827         def _CheckConfig(seq, expected_data):
3828             """Check the configuration nodes
3829
3830             Args:
3831                 seq: Sequence number to check (0 or 1)
3832                 expected_data: Expected contents of 'data' property
3833             """
3834             cnode = dtb.GetNode('/configurations')
3835             self.assertIn('default', cnode.props)
3836             self.assertEqual('config-2', cnode.props['default'].value)
3837
3838             name = 'config-%d' % seq
3839             fnode = dtb.GetNode('/configurations/%s' % name)
3840             self.assertIsNotNone(fnode)
3841             self.assertEqual({'description','firmware', 'loadables', 'fdt'},
3842                              set(fnode.props.keys()))
3843             self.assertEqual('conf-test-fdt%d.dtb' % seq,
3844                              fnode.props['description'].value)
3845             self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value)
3846
3847         entry_args = {
3848             'of-list': 'test-fdt1 test-fdt2',
3849             'default-dt': 'test-fdt2',
3850         }
3851         data = self._DoReadFileDtb(
3852             '170_fit_fdt.dts',
3853             entry_args=entry_args,
3854             extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3855         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3856         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3857
3858         dtb = fdt.Fdt.FromData(fit_data)
3859         dtb.Scan()
3860         fnode = dtb.GetNode('/images/kernel')
3861         self.assertIn('data', fnode.props)
3862
3863         # Check all the properties in fdt-1 and fdt-2
3864         _CheckFdt(1, TEST_FDT1_DATA)
3865         _CheckFdt(2, TEST_FDT2_DATA)
3866
3867         # Check configurations
3868         _CheckConfig(1, TEST_FDT1_DATA)
3869         _CheckConfig(2, TEST_FDT2_DATA)
3870
3871     def testFitFdtMissingList(self):
3872         """Test handling of a missing 'of-list' entry arg"""
3873         with self.assertRaises(ValueError) as e:
3874             self._DoReadFile('170_fit_fdt.dts')
3875         self.assertIn("Generator node requires 'of-list' entry argument",
3876                       str(e.exception))
3877
3878     def testFitFdtEmptyList(self):
3879         """Test handling of an empty 'of-list' entry arg"""
3880         entry_args = {
3881             'of-list': '',
3882         }
3883         data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
3884
3885     def testFitFdtMissingProp(self):
3886         """Test handling of a missing 'fit,fdt-list' property"""
3887         with self.assertRaises(ValueError) as e:
3888             self._DoReadFile('171_fit_fdt_missing_prop.dts')
3889         self.assertIn("Generator node requires 'fit,fdt-list' property",
3890                       str(e.exception))
3891
3892     def testFitFdtEmptyList(self):
3893         """Test handling of an empty 'of-list' entry arg"""
3894         entry_args = {
3895             'of-list': '',
3896         }
3897         data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
3898
3899     def testFitFdtMissing(self):
3900         """Test handling of a missing 'default-dt' entry arg"""
3901         entry_args = {
3902             'of-list': 'test-fdt1 test-fdt2',
3903         }
3904         with self.assertRaises(ValueError) as e:
3905             self._DoReadFileDtb(
3906                 '170_fit_fdt.dts',
3907                 entry_args=entry_args,
3908                 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3909         self.assertIn("Generated 'default' node requires default-dt entry argument",
3910                       str(e.exception))
3911
3912     def testFitFdtNotInList(self):
3913         """Test handling of a default-dt that is not in the of-list"""
3914         entry_args = {
3915             'of-list': 'test-fdt1 test-fdt2',
3916             'default-dt': 'test-fdt3',
3917         }
3918         with self.assertRaises(ValueError) as e:
3919             self._DoReadFileDtb(
3920                 '170_fit_fdt.dts',
3921                 entry_args=entry_args,
3922                 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3923         self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
3924                       str(e.exception))
3925
3926     def testFitExtblobMissingHelp(self):
3927         """Test display of help messages when an external blob is missing"""
3928         control.missing_blob_help = control._ReadMissingBlobHelp()
3929         control.missing_blob_help['wibble'] = 'Wibble test'
3930         control.missing_blob_help['another'] = 'Another test'
3931         with test_util.capture_sys_output() as (stdout, stderr):
3932             self._DoTestFile('168_fit_missing_blob.dts',
3933                              allow_missing=True)
3934         err = stderr.getvalue()
3935
3936         # We can get the tag from the name, the type or the missing-msg
3937         # property. Check all three.
3938         self.assertIn('You may need to build ARM Trusted', err)
3939         self.assertIn('Wibble test', err)
3940         self.assertIn('Another test', err)
3941
3942     def testMissingBlob(self):
3943         """Test handling of a blob containing a missing file"""
3944         with self.assertRaises(ValueError) as e:
3945             self._DoTestFile('173_missing_blob.dts', allow_missing=True)
3946         self.assertIn("Filename 'missing' not found in input path",
3947                       str(e.exception))
3948
3949     def testEnvironment(self):
3950         """Test adding a U-Boot environment"""
3951         data = self._DoReadFile('174_env.dts')
3952         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3953         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3954         env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3955         self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
3956                          env)
3957
3958     def testEnvironmentNoSize(self):
3959         """Test that a missing 'size' property is detected"""
3960         with self.assertRaises(ValueError) as e:
3961             self._DoTestFile('175_env_no_size.dts')
3962         self.assertIn("'u-boot-env' entry must have a size property",
3963                       str(e.exception))
3964
3965     def testEnvironmentTooSmall(self):
3966         """Test handling of an environment that does not fit"""
3967         with self.assertRaises(ValueError) as e:
3968             self._DoTestFile('176_env_too_small.dts')
3969
3970         # checksum, start byte, environment with \0 terminator, final \0
3971         need = 4 + 1 + len(ENV_DATA) + 1 + 1
3972         short = need - 0x8
3973         self.assertIn("too small to hold data (need %#x more bytes)" % short,
3974                       str(e.exception))
3975
3976     def testSkipAtStart(self):
3977         """Test handling of skip-at-start section"""
3978         data = self._DoReadFile('177_skip_at_start.dts')
3979         self.assertEqual(U_BOOT_DATA, data)
3980
3981         image = control.images['image']
3982         entries = image.GetEntries()
3983         section = entries['section']
3984         self.assertEqual(0, section.offset)
3985         self.assertEqual(len(U_BOOT_DATA), section.size)
3986         self.assertEqual(U_BOOT_DATA, section.GetData())
3987
3988         entry = section.GetEntries()['u-boot']
3989         self.assertEqual(16, entry.offset)
3990         self.assertEqual(len(U_BOOT_DATA), entry.size)
3991         self.assertEqual(U_BOOT_DATA, entry.data)
3992
3993     def testSkipAtStartPad(self):
3994         """Test handling of skip-at-start section with padded entry"""
3995         data = self._DoReadFile('178_skip_at_start_pad.dts')
3996         before = tools.GetBytes(0, 8)
3997         after = tools.GetBytes(0, 4)
3998         all = before + U_BOOT_DATA + after
3999         self.assertEqual(all, data)
4000
4001         image = control.images['image']
4002         entries = image.GetEntries()
4003         section = entries['section']
4004         self.assertEqual(0, section.offset)
4005         self.assertEqual(len(all), section.size)
4006         self.assertEqual(all, section.GetData())
4007
4008         entry = section.GetEntries()['u-boot']
4009         self.assertEqual(16, entry.offset)
4010         self.assertEqual(len(all), entry.size)
4011         self.assertEqual(U_BOOT_DATA, entry.data)
4012
4013     def testSkipAtStartSectionPad(self):
4014         """Test handling of skip-at-start section with padding"""
4015         data = self._DoReadFile('179_skip_at_start_section_pad.dts')
4016         before = tools.GetBytes(0, 8)
4017         after = tools.GetBytes(0, 4)
4018         all = before + U_BOOT_DATA + after
4019         self.assertEqual(all, data)
4020
4021         image = control.images['image']
4022         entries = image.GetEntries()
4023         section = entries['section']
4024         self.assertEqual(0, section.offset)
4025         self.assertEqual(len(all), section.size)
4026         self.assertEqual(U_BOOT_DATA, section.data)
4027         self.assertEqual(all, section.GetPaddedData())
4028
4029         entry = section.GetEntries()['u-boot']
4030         self.assertEqual(16, entry.offset)
4031         self.assertEqual(len(U_BOOT_DATA), entry.size)
4032         self.assertEqual(U_BOOT_DATA, entry.data)
4033
4034     def testSectionPad(self):
4035         """Testing padding with sections"""
4036         data = self._DoReadFile('180_section_pad.dts')
4037         expected = (tools.GetBytes(ord('&'), 3) +
4038                     tools.GetBytes(ord('!'), 5) +
4039                     U_BOOT_DATA +
4040                     tools.GetBytes(ord('!'), 1) +
4041                     tools.GetBytes(ord('&'), 2))
4042         self.assertEqual(expected, data)
4043
4044     def testSectionAlign(self):
4045         """Testing alignment with sections"""
4046         data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4047         expected = (b'\0' +                         # fill section
4048                     tools.GetBytes(ord('&'), 1) +   # padding to section align
4049                     b'\0' +                         # fill section
4050                     tools.GetBytes(ord('!'), 3) +   # padding to u-boot align
4051                     U_BOOT_DATA +
4052                     tools.GetBytes(ord('!'), 4) +   # padding to u-boot size
4053                     tools.GetBytes(ord('!'), 4))    # padding to section size
4054         self.assertEqual(expected, data)
4055
4056     def testCompressImage(self):
4057         """Test compression of the entire image"""
4058         self._CheckLz4()
4059         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4060             '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4061         dtb = fdt.Fdt(out_dtb_fname)
4062         dtb.Scan()
4063         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4064                                         'uncomp-size'])
4065         orig = self._decompress(data)
4066         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4067
4068         # Do a sanity check on various fields
4069         image = control.images['image']
4070         entries = image.GetEntries()
4071         self.assertEqual(2, len(entries))
4072
4073         entry = entries['blob']
4074         self.assertEqual(COMPRESS_DATA, entry.data)
4075         self.assertEqual(len(COMPRESS_DATA), entry.size)
4076
4077         entry = entries['u-boot']
4078         self.assertEqual(U_BOOT_DATA, entry.data)
4079         self.assertEqual(len(U_BOOT_DATA), entry.size)
4080
4081         self.assertEqual(len(data), image.size)
4082         self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4083         self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4084         orig = self._decompress(image.data)
4085         self.assertEqual(orig, image.uncomp_data)
4086
4087         expected = {
4088             'blob:offset': 0,
4089             'blob:size': len(COMPRESS_DATA),
4090             'u-boot:offset': len(COMPRESS_DATA),
4091             'u-boot:size': len(U_BOOT_DATA),
4092             'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4093             'offset': 0,
4094             'image-pos': 0,
4095             'size': len(data),
4096             }
4097         self.assertEqual(expected, props)
4098
4099     def testCompressImageLess(self):
4100         """Test compression where compression reduces the image size"""
4101         self._CheckLz4()
4102         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4103             '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4104         dtb = fdt.Fdt(out_dtb_fname)
4105         dtb.Scan()
4106         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4107                                         'uncomp-size'])
4108         orig = self._decompress(data)
4109
4110         self.assertEquals(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
4111
4112         # Do a sanity check on various fields
4113         image = control.images['image']
4114         entries = image.GetEntries()
4115         self.assertEqual(2, len(entries))
4116
4117         entry = entries['blob']
4118         self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4119         self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4120
4121         entry = entries['u-boot']
4122         self.assertEqual(U_BOOT_DATA, entry.data)
4123         self.assertEqual(len(U_BOOT_DATA), entry.size)
4124
4125         self.assertEqual(len(data), image.size)
4126         self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4127         self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4128                          image.uncomp_size)
4129         orig = self._decompress(image.data)
4130         self.assertEqual(orig, image.uncomp_data)
4131
4132         expected = {
4133             'blob:offset': 0,
4134             'blob:size': len(COMPRESS_DATA_BIG),
4135             'u-boot:offset': len(COMPRESS_DATA_BIG),
4136             'u-boot:size': len(U_BOOT_DATA),
4137             'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4138             'offset': 0,
4139             'image-pos': 0,
4140             'size': len(data),
4141             }
4142         self.assertEqual(expected, props)
4143
4144     def testCompressSectionSize(self):
4145         """Test compression of a section with a fixed size"""
4146         self._CheckLz4()
4147         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4148             '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4149         dtb = fdt.Fdt(out_dtb_fname)
4150         dtb.Scan()
4151         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4152                                         'uncomp-size'])
4153         orig = self._decompress(data)
4154         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4155         expected = {
4156             'section/blob:offset': 0,
4157             'section/blob:size': len(COMPRESS_DATA),
4158             'section/u-boot:offset': len(COMPRESS_DATA),
4159             'section/u-boot:size': len(U_BOOT_DATA),
4160             'section:offset': 0,
4161             'section:image-pos': 0,
4162             'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4163             'section:size': 0x30,
4164             'offset': 0,
4165             'image-pos': 0,
4166             'size': 0x30,
4167             }
4168         self.assertEqual(expected, props)
4169
4170     def testCompressSection(self):
4171         """Test compression of a section with no fixed size"""
4172         self._CheckLz4()
4173         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4174             '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4175         dtb = fdt.Fdt(out_dtb_fname)
4176         dtb.Scan()
4177         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4178                                         'uncomp-size'])
4179         orig = self._decompress(data)
4180         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4181         expected = {
4182             'section/blob:offset': 0,
4183             'section/blob:size': len(COMPRESS_DATA),
4184             'section/u-boot:offset': len(COMPRESS_DATA),
4185             'section/u-boot:size': len(U_BOOT_DATA),
4186             'section:offset': 0,
4187             'section:image-pos': 0,
4188             'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4189             'section:size': len(data),
4190             'offset': 0,
4191             'image-pos': 0,
4192             'size': len(data),
4193             }
4194         self.assertEqual(expected, props)
4195
4196     def testCompressExtra(self):
4197         """Test compression of a section with no fixed size"""
4198         self._CheckLz4()
4199         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4200             '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4201         dtb = fdt.Fdt(out_dtb_fname)
4202         dtb.Scan()
4203         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4204                                         'uncomp-size'])
4205
4206         base = data[len(U_BOOT_DATA):]
4207         self.assertEquals(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
4208         rest = base[len(U_BOOT_DATA):]
4209
4210         # Check compressed data
4211         section1 = self._decompress(rest)
4212         expect1 = tools.Compress(COMPRESS_DATA + U_BOOT_DATA, 'lz4')
4213         self.assertEquals(expect1, rest[:len(expect1)])
4214         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1)
4215         rest1 = rest[len(expect1):]
4216
4217         section2 = self._decompress(rest1)
4218         expect2 = tools.Compress(COMPRESS_DATA + COMPRESS_DATA, 'lz4')
4219         self.assertEquals(expect2, rest1[:len(expect2)])
4220         self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2)
4221         rest2 = rest1[len(expect2):]
4222
4223         expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4224                        len(expect2) + len(U_BOOT_DATA))
4225         #self.assertEquals(expect_size, len(data))
4226
4227         #self.assertEquals(U_BOOT_DATA, rest2)
4228
4229         self.maxDiff = None
4230         expected = {
4231             'u-boot:offset': 0,
4232             'u-boot:image-pos': 0,
4233             'u-boot:size': len(U_BOOT_DATA),
4234
4235             'base:offset': len(U_BOOT_DATA),
4236             'base:image-pos': len(U_BOOT_DATA),
4237             'base:size': len(data) - len(U_BOOT_DATA),
4238             'base/u-boot:offset': 0,
4239             'base/u-boot:image-pos': len(U_BOOT_DATA),
4240             'base/u-boot:size': len(U_BOOT_DATA),
4241             'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4242                 len(expect2),
4243             'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4244                 len(expect2),
4245             'base/u-boot2:size': len(U_BOOT_DATA),
4246
4247             'base/section:offset': len(U_BOOT_DATA),
4248             'base/section:image-pos': len(U_BOOT_DATA) * 2,
4249             'base/section:size': len(expect1),
4250             'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4251             'base/section/blob:offset': 0,
4252             'base/section/blob:size': len(COMPRESS_DATA),
4253             'base/section/u-boot:offset': len(COMPRESS_DATA),
4254             'base/section/u-boot:size': len(U_BOOT_DATA),
4255
4256             'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4257             'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4258             'base/section2:size': len(expect2),
4259             'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4260             'base/section2/blob:offset': 0,
4261             'base/section2/blob:size': len(COMPRESS_DATA),
4262             'base/section2/blob2:offset': len(COMPRESS_DATA),
4263             'base/section2/blob2:size': len(COMPRESS_DATA),
4264
4265             'offset': 0,
4266             'image-pos': 0,
4267             'size': len(data),
4268             }
4269         self.assertEqual(expected, props)
4270
4271     def testSymbolsSubsection(self):
4272         """Test binman can assign symbols from a subsection"""
4273         self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x18)
4274
4275     def testReadImageEntryArg(self):
4276         """Test reading an image that would need an entry arg to generate"""
4277         entry_args = {
4278             'cros-ec-rw-path': 'ecrw.bin',
4279         }
4280         data = self.data = self._DoReadFileDtb(
4281             '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4282             entry_args=entry_args)
4283
4284         image_fname = tools.GetOutputFilename('image.bin')
4285         orig_image = control.images['image']
4286
4287         # This should not generate an error about the missing 'cros-ec-rw-path'
4288         # since we are reading the image from a file. Compare with
4289         # testEntryArgsRequired()
4290         image = Image.FromFile(image_fname)
4291         self.assertEqual(orig_image.GetEntries().keys(),
4292                          image.GetEntries().keys())
4293
4294     def testFilesAlign(self):
4295         """Test alignment with files"""
4296         data = self._DoReadFile('190_files_align.dts')
4297
4298         # The first string is 15 bytes so will align to 16
4299         expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4300         self.assertEqual(expect, data)
4301
4302     def testReadImageSkip(self):
4303         """Test reading an image and accessing its FDT map"""
4304         data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
4305         image_fname = tools.GetOutputFilename('image.bin')
4306         orig_image = control.images['image']
4307         image = Image.FromFile(image_fname)
4308         self.assertEqual(orig_image.GetEntries().keys(),
4309                          image.GetEntries().keys())
4310
4311         orig_entry = orig_image.GetEntries()['fdtmap']
4312         entry = image.GetEntries()['fdtmap']
4313         self.assertEqual(orig_entry.offset, entry.offset)
4314         self.assertEqual(orig_entry.size, entry.size)
4315         self.assertEqual(16, entry.image_pos)
4316
4317         u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4318
4319         self.assertEquals(U_BOOT_DATA, u_boot.ReadData())
4320
4321     def testTplNoDtb(self):
4322         """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
4323         self._SetupTplElf()
4324         data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4325         self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4326                          data[:len(U_BOOT_TPL_NODTB_DATA)])
4327
4328     def testTplBssPad(self):
4329         """Test that we can pad TPL's BSS with zeros"""
4330         # ELF file with a '__bss_size' symbol
4331         self._SetupTplElf()
4332         data = self._DoReadFile('193_tpl_bss_pad.dts')
4333         self.assertEqual(U_BOOT_TPL_DATA + tools.GetBytes(0, 10) + U_BOOT_DATA,
4334                          data)
4335
4336     def testTplBssPadMissing(self):
4337         """Test that a missing symbol is detected"""
4338         self._SetupTplElf('u_boot_ucode_ptr')
4339         with self.assertRaises(ValueError) as e:
4340             self._DoReadFile('193_tpl_bss_pad.dts')
4341         self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4342                       str(e.exception))
4343
4344     def checkDtbSizes(self, data, pad_len, start):
4345         """Check the size arguments in a dtb embedded in an image
4346
4347         Args:
4348             data: The image data
4349             pad_len: Length of the pad section in the image, in bytes
4350             start: Start offset of the devicetree to examine, within the image
4351
4352         Returns:
4353             Size of the devicetree in bytes
4354         """
4355         dtb_data = data[start:]
4356         dtb = fdt.Fdt.FromData(dtb_data)
4357         fdt_size = dtb.GetFdtObj().totalsize()
4358         dtb.Scan()
4359         props = self._GetPropTree(dtb, 'size')
4360         self.assertEqual({
4361             'size': len(data),
4362             'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4363             'u-boot-spl/u-boot-spl-dtb:size': 801,
4364             'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4365             'u-boot-spl:size': 860,
4366             'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4367             'u-boot/u-boot-dtb:size': 781,
4368             'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4369             'u-boot:size': 827,
4370             }, props)
4371         return fdt_size
4372
4373     def testExpanded(self):
4374         """Test that an expanded entry type is selected when needed"""
4375         self._SetupSplElf()
4376         self._SetupTplElf()
4377
4378         # SPL has a devicetree, TPL does not
4379         entry_args = {
4380             'spl-dtb': '1',
4381             'spl-bss-pad': 'y',
4382             'tpl-dtb': '',
4383         }
4384         self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4385                             entry_args=entry_args)
4386         image = control.images['image']
4387         entries = image.GetEntries()
4388         self.assertEqual(3, len(entries))
4389
4390         # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4391         self.assertIn('u-boot', entries)
4392         entry = entries['u-boot']
4393         self.assertEqual('u-boot-expanded', entry.etype)
4394         subent = entry.GetEntries()
4395         self.assertEqual(2, len(subent))
4396         self.assertIn('u-boot-nodtb', subent)
4397         self.assertIn('u-boot-dtb', subent)
4398
4399         # Second, u-boot-spl, which should be expanded into three parts
4400         self.assertIn('u-boot-spl', entries)
4401         entry = entries['u-boot-spl']
4402         self.assertEqual('u-boot-spl-expanded', entry.etype)
4403         subent = entry.GetEntries()
4404         self.assertEqual(3, len(subent))
4405         self.assertIn('u-boot-spl-nodtb', subent)
4406         self.assertIn('u-boot-spl-bss-pad', subent)
4407         self.assertIn('u-boot-spl-dtb', subent)
4408
4409         # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4410         # devicetree
4411         self.assertIn('u-boot-tpl', entries)
4412         entry = entries['u-boot-tpl']
4413         self.assertEqual('u-boot-tpl', entry.etype)
4414         self.assertEqual(None, entry.GetEntries())
4415
4416     def testExpandedTpl(self):
4417         """Test that an expanded entry type is selected for TPL when needed"""
4418         self._SetupTplElf()
4419
4420         entry_args = {
4421             'tpl-bss-pad': 'y',
4422             'tpl-dtb': 'y',
4423         }
4424         self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4425                             entry_args=entry_args)
4426         image = control.images['image']
4427         entries = image.GetEntries()
4428         self.assertEqual(1, len(entries))
4429
4430         # We only have u-boot-tpl, which be expanded
4431         self.assertIn('u-boot-tpl', entries)
4432         entry = entries['u-boot-tpl']
4433         self.assertEqual('u-boot-tpl-expanded', entry.etype)
4434         subent = entry.GetEntries()
4435         self.assertEqual(3, len(subent))
4436         self.assertIn('u-boot-tpl-nodtb', subent)
4437         self.assertIn('u-boot-tpl-bss-pad', subent)
4438         self.assertIn('u-boot-tpl-dtb', subent)
4439
4440     def testExpandedNoPad(self):
4441         """Test an expanded entry without BSS pad enabled"""
4442         self._SetupSplElf()
4443         self._SetupTplElf()
4444
4445         # SPL has a devicetree, TPL does not
4446         entry_args = {
4447             'spl-dtb': 'something',
4448             'spl-bss-pad': 'n',
4449             'tpl-dtb': '',
4450         }
4451         self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4452                             entry_args=entry_args)
4453         image = control.images['image']
4454         entries = image.GetEntries()
4455
4456         # Just check u-boot-spl, which should be expanded into two parts
4457         self.assertIn('u-boot-spl', entries)
4458         entry = entries['u-boot-spl']
4459         self.assertEqual('u-boot-spl-expanded', entry.etype)
4460         subent = entry.GetEntries()
4461         self.assertEqual(2, len(subent))
4462         self.assertIn('u-boot-spl-nodtb', subent)
4463         self.assertIn('u-boot-spl-dtb', subent)
4464
4465     def testExpandedTplNoPad(self):
4466         """Test that an expanded entry type with padding disabled in TPL"""
4467         self._SetupTplElf()
4468
4469         entry_args = {
4470             'tpl-bss-pad': '',
4471             'tpl-dtb': 'y',
4472         }
4473         self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4474                             entry_args=entry_args)
4475         image = control.images['image']
4476         entries = image.GetEntries()
4477         self.assertEqual(1, len(entries))
4478
4479         # We only have u-boot-tpl, which be expanded
4480         self.assertIn('u-boot-tpl', entries)
4481         entry = entries['u-boot-tpl']
4482         self.assertEqual('u-boot-tpl-expanded', entry.etype)
4483         subent = entry.GetEntries()
4484         self.assertEqual(2, len(subent))
4485         self.assertIn('u-boot-tpl-nodtb', subent)
4486         self.assertIn('u-boot-tpl-dtb', subent)
4487
4488     def testFdtInclude(self):
4489         """Test that an Fdt is update within all binaries"""
4490         self._SetupSplElf()
4491         self._SetupTplElf()
4492
4493         # SPL has a devicetree, TPL does not
4494         self.maxDiff = None
4495         entry_args = {
4496             'spl-dtb': '1',
4497             'spl-bss-pad': 'y',
4498             'tpl-dtb': '',
4499         }
4500         # Build the image. It includes two separate devicetree binaries, each
4501         # with their own contents, but all contain the binman definition.
4502         data = self._DoReadFileDtb(
4503             '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4504             update_dtb=True, entry_args=entry_args)[0]
4505         pad_len = 10
4506
4507         # Check the U-Boot dtb
4508         start = len(U_BOOT_NODTB_DATA)
4509         fdt_size = self.checkDtbSizes(data, pad_len, start)
4510
4511         # Now check SPL
4512         start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4513         fdt_size = self.checkDtbSizes(data, pad_len, start)
4514
4515         # TPL has no devicetree
4516         start += fdt_size + len(U_BOOT_TPL_DATA)
4517         self.assertEqual(len(data), start)
4518
4519     def testSymbolsExpanded(self):
4520         """Test binman can assign symbols in expanded entries"""
4521         entry_args = {
4522             'spl-dtb': '1',
4523         }
4524         self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
4525                           U_BOOT_SPL_DTB_DATA, 0x38,
4526                           entry_args=entry_args, use_expanded=True)
4527
4528     def testCollection(self):
4529         """Test a collection"""
4530         data = self._DoReadFile('198_collection.dts')
4531         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
4532                          tools.GetBytes(0xff, 2) + U_BOOT_NODTB_DATA +
4533                          tools.GetBytes(0xfe, 3) + U_BOOT_DTB_DATA,
4534                          data)
4535
4536     def testCollectionSection(self):
4537         """Test a collection where a section must be built first"""
4538         # Sections never have their contents when GetData() is called, but when
4539         # _BuildSectionData() is called with required=True, a section will force
4540         # building the contents, producing an error is anything is still
4541         # missing.
4542         data = self._DoReadFile('199_collection_section.dts')
4543         section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
4544         self.assertEqual(section + U_BOOT_DATA + tools.GetBytes(0xff, 2) +
4545                          section + tools.GetBytes(0xfe, 3) + U_BOOT_DATA,
4546                          data)
4547
4548     def testAlignDefault(self):
4549         """Test that default alignment works on sections"""
4550         data = self._DoReadFile('200_align_default.dts')
4551         expected = (U_BOOT_DATA + tools.GetBytes(0, 8 - len(U_BOOT_DATA)) +
4552                     U_BOOT_DATA)
4553         # Special alignment for section
4554         expected += tools.GetBytes(0, 32 - len(expected))
4555         # No alignment within the nested section
4556         expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
4557         # Now the final piece, which should be default-aligned
4558         expected += tools.GetBytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
4559         self.assertEqual(expected, data)
4560
4561     def testPackOpenSBI(self):
4562         """Test that an image with an OpenSBI binary can be created"""
4563         data = self._DoReadFile('201_opensbi.dts')
4564         self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
4565
4566     def testSectionsSingleThread(self):
4567         """Test sections without multithreading"""
4568         data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
4569         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
4570                     U_BOOT_DATA + tools.GetBytes(ord('a'), 12) +
4571                     U_BOOT_DATA + tools.GetBytes(ord('&'), 4))
4572         self.assertEqual(expected, data)
4573
4574     def testThreadTimeout(self):
4575         """Test handling a thread that takes too long"""
4576         with self.assertRaises(ValueError) as e:
4577             self._DoTestFile('202_section_timeout.dts',
4578                              test_section_timeout=True)
4579         self.assertIn("Timed out obtaining contents", str(e.exception))
4580
4581     def testTiming(self):
4582         """Test output of timing information"""
4583         data = self._DoReadFile('055_sections.dts')
4584         with test_util.capture_sys_output() as (stdout, stderr):
4585             state.TimingShow()
4586         self.assertIn('read:', stdout.getvalue())
4587         self.assertIn('compress:', stdout.getvalue())
4588
4589     def testUpdateFdtInElf(self):
4590         """Test that we can update the devicetree in an ELF file"""
4591         infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
4592         outfile = os.path.join(self._indir, 'u-boot.out')
4593         begin_sym = 'dtb_embed_begin'
4594         end_sym = 'dtb_embed_end'
4595         retcode = self._DoTestFile(
4596             '060_fdt_update.dts', update_dtb=True,
4597             update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4598         self.assertEqual(0, retcode)
4599
4600         # Check that the output file does in fact contact a dtb with the binman
4601         # definition in the correct place
4602         syms = elf.GetSymbolFileOffset(infile,
4603                                        ['dtb_embed_begin', 'dtb_embed_end'])
4604         data = tools.ReadFile(outfile)
4605         dtb_data = data[syms['dtb_embed_begin'].offset:
4606                         syms['dtb_embed_end'].offset]
4607
4608         dtb = fdt.Fdt.FromData(dtb_data)
4609         dtb.Scan()
4610         props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4611         self.assertEqual({
4612             'image-pos': 0,
4613             'offset': 0,
4614             '_testing:offset': 32,
4615             '_testing:size': 2,
4616             '_testing:image-pos': 32,
4617             'section@0/u-boot:offset': 0,
4618             'section@0/u-boot:size': len(U_BOOT_DATA),
4619             'section@0/u-boot:image-pos': 0,
4620             'section@0:offset': 0,
4621             'section@0:size': 16,
4622             'section@0:image-pos': 0,
4623
4624             'section@1/u-boot:offset': 0,
4625             'section@1/u-boot:size': len(U_BOOT_DATA),
4626             'section@1/u-boot:image-pos': 16,
4627             'section@1:offset': 16,
4628             'section@1:size': 16,
4629             'section@1:image-pos': 16,
4630             'size': 40
4631         }, props)
4632
4633     def testUpdateFdtInElfInvalid(self):
4634         """Test that invalid args are detected with --update-fdt-in-elf"""
4635         with self.assertRaises(ValueError) as e:
4636             self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
4637         self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
4638                       str(e.exception))
4639
4640     def testUpdateFdtInElfNoSyms(self):
4641         """Test that missing symbols are detected with --update-fdt-in-elf"""
4642         infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
4643         outfile = ''
4644         begin_sym = 'wrong_begin'
4645         end_sym = 'wrong_end'
4646         with self.assertRaises(ValueError) as e:
4647             self._DoTestFile(
4648                 '060_fdt_update.dts',
4649                 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4650         self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
4651                       str(e.exception))
4652
4653     def testUpdateFdtInElfTooSmall(self):
4654         """Test that an over-large dtb is detected with --update-fdt-in-elf"""
4655         infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
4656         outfile = os.path.join(self._indir, 'u-boot.out')
4657         begin_sym = 'dtb_embed_begin'
4658         end_sym = 'dtb_embed_end'
4659         with self.assertRaises(ValueError) as e:
4660             self._DoTestFile(
4661                 '060_fdt_update.dts', update_dtb=True,
4662                 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4663         self.assertRegex(
4664             str(e.exception),
4665             "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
4666
4667     def testFakeBlob(self):
4668         """Test handling of faking an external blob"""
4669         with test_util.capture_sys_output() as (stdout, stderr):
4670             self._DoTestFile('203_fake_blob.dts', allow_missing=True,
4671                              allow_fake_blobs=True)
4672         err = stderr.getvalue()
4673         self.assertRegex(err,
4674                          "Image '.*' has faked external blobs and is non-functional: .*")
4675         os.remove('binman_faking_test_blob')
4676
4677
4678 if __name__ == "__main__":
4679     unittest.main()