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