binman: Set section contents in GetData()
[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         self.assertEqual(image.data, entry.data)
1826
1827         expected = {
1828             'blob:uncomp-size': len(COMPRESS_DATA),
1829             'blob:size': len(data),
1830             'size': len(data),
1831             }
1832         self.assertEqual(expected, props)
1833
1834     def testFiles(self):
1835         """Test bringing in multiple files"""
1836         data = self._DoReadFile('084_files.dts')
1837         self.assertEqual(FILES_DATA, data)
1838
1839     def testFilesCompress(self):
1840         """Test bringing in multiple files and compressing them"""
1841         self._CheckLz4()
1842         data = self._DoReadFile('085_files_compress.dts')
1843
1844         image = control.images['image']
1845         entries = image.GetEntries()
1846         files = entries['files']
1847         entries = files._entries
1848
1849         orig = b''
1850         for i in range(1, 3):
1851             key = '%d.dat' % i
1852             start = entries[key].image_pos
1853             len = entries[key].size
1854             chunk = data[start:start + len]
1855             orig += self._decompress(chunk)
1856
1857         self.assertEqual(FILES_DATA, orig)
1858
1859     def testFilesMissing(self):
1860         """Test missing files"""
1861         with self.assertRaises(ValueError) as e:
1862             data = self._DoReadFile('086_files_none.dts')
1863         self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
1864                       'no files', str(e.exception))
1865
1866     def testFilesNoPattern(self):
1867         """Test missing files"""
1868         with self.assertRaises(ValueError) as e:
1869             data = self._DoReadFile('087_files_no_pattern.dts')
1870         self.assertIn("Node '/binman/files': Missing 'pattern' property",
1871                       str(e.exception))
1872
1873     def testExpandSize(self):
1874         """Test an expanding entry"""
1875         data, _, map_data, _ = self._DoReadFileDtb('088_expand_size.dts',
1876                                                    map=True)
1877         expect = (tools.GetBytes(ord('a'), 8) + U_BOOT_DATA +
1878                   MRC_DATA + tools.GetBytes(ord('b'), 1) + U_BOOT_DATA +
1879                   tools.GetBytes(ord('c'), 8) + U_BOOT_DATA +
1880                   tools.GetBytes(ord('d'), 8))
1881         self.assertEqual(expect, data)
1882         self.assertEqual('''ImagePos    Offset      Size  Name
1883 00000000  00000000  00000028  main-section
1884 00000000   00000000  00000008  fill
1885 00000008   00000008  00000004  u-boot
1886 0000000c   0000000c  00000004  section
1887 0000000c    00000000  00000003  intel-mrc
1888 00000010   00000010  00000004  u-boot2
1889 00000014   00000014  0000000c  section2
1890 00000014    00000000  00000008  fill
1891 0000001c    00000008  00000004  u-boot
1892 00000020   00000020  00000008  fill2
1893 ''', map_data)
1894
1895     def testExpandSizeBad(self):
1896         """Test an expanding entry which fails to provide contents"""
1897         with test_util.capture_sys_output() as (stdout, stderr):
1898             with self.assertRaises(ValueError) as e:
1899                 self._DoReadFileDtb('089_expand_size_bad.dts', map=True)
1900         self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
1901                       'expanding entry', str(e.exception))
1902
1903     def testHash(self):
1904         """Test hashing of the contents of an entry"""
1905         _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
1906                 use_real_dtb=True, update_dtb=True)
1907         dtb = fdt.Fdt(out_dtb_fname)
1908         dtb.Scan()
1909         hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
1910         m = hashlib.sha256()
1911         m.update(U_BOOT_DATA)
1912         self.assertEqual(m.digest(), b''.join(hash_node.value))
1913
1914     def testHashNoAlgo(self):
1915         with self.assertRaises(ValueError) as e:
1916             self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
1917         self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
1918                       'hash node', str(e.exception))
1919
1920     def testHashBadAlgo(self):
1921         with self.assertRaises(ValueError) as e:
1922             self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
1923         self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
1924                       str(e.exception))
1925
1926     def testHashSection(self):
1927         """Test hashing of the contents of an entry"""
1928         _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
1929                 use_real_dtb=True, update_dtb=True)
1930         dtb = fdt.Fdt(out_dtb_fname)
1931         dtb.Scan()
1932         hash_node = dtb.GetNode('/binman/section/hash').props['value']
1933         m = hashlib.sha256()
1934         m.update(U_BOOT_DATA)
1935         m.update(tools.GetBytes(ord('a'), 16))
1936         self.assertEqual(m.digest(), b''.join(hash_node.value))
1937
1938     def testPackUBootTplMicrocode(self):
1939         """Test that x86 microcode can be handled correctly in TPL
1940
1941         We expect to see the following in the image, in order:
1942             u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
1943                 place
1944             u-boot-tpl.dtb with the microcode removed
1945             the microcode
1946         """
1947         self._SetupTplElf('u_boot_ucode_ptr')
1948         first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
1949                                                      U_BOOT_TPL_NODTB_DATA)
1950         self.assertEqual(b'tplnodtb with microc' + pos_and_size +
1951                          b'ter somewhere in here', first)
1952
1953     def testFmapX86(self):
1954         """Basic test of generation of a flashrom fmap"""
1955         data = self._DoReadFile('094_fmap_x86.dts')
1956         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1957         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('a'), 32 - 7)
1958         self.assertEqual(expected, data[:32])
1959         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1960
1961         self.assertEqual(0x100, fhdr.image_size)
1962
1963         self.assertEqual(0, fentries[0].offset)
1964         self.assertEqual(4, fentries[0].size)
1965         self.assertEqual(b'U_BOOT', fentries[0].name)
1966
1967         self.assertEqual(4, fentries[1].offset)
1968         self.assertEqual(3, fentries[1].size)
1969         self.assertEqual(b'INTEL_MRC', fentries[1].name)
1970
1971         self.assertEqual(32, fentries[2].offset)
1972         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1973                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1974         self.assertEqual(b'FMAP', fentries[2].name)
1975
1976     def testFmapX86Section(self):
1977         """Basic test of generation of a flashrom fmap"""
1978         data = self._DoReadFile('095_fmap_x86_section.dts')
1979         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('b'), 32 - 7)
1980         self.assertEqual(expected, data[:32])
1981         fhdr, fentries = fmap_util.DecodeFmap(data[36:])
1982
1983         self.assertEqual(0x100, fhdr.image_size)
1984
1985         self.assertEqual(0, fentries[0].offset)
1986         self.assertEqual(4, fentries[0].size)
1987         self.assertEqual(b'U_BOOT', fentries[0].name)
1988
1989         self.assertEqual(4, fentries[1].offset)
1990         self.assertEqual(3, fentries[1].size)
1991         self.assertEqual(b'INTEL_MRC', fentries[1].name)
1992
1993         self.assertEqual(36, fentries[2].offset)
1994         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1995                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1996         self.assertEqual(b'FMAP', fentries[2].name)
1997
1998     def testElf(self):
1999         """Basic test of ELF entries"""
2000         self._SetupSplElf()
2001         self._SetupTplElf()
2002         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
2003             TestFunctional._MakeInputFile('-boot', fd.read())
2004         data = self._DoReadFile('096_elf.dts')
2005
2006     def testElfStrip(self):
2007         """Basic test of ELF entries"""
2008         self._SetupSplElf()
2009         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
2010             TestFunctional._MakeInputFile('-boot', fd.read())
2011         data = self._DoReadFile('097_elf_strip.dts')
2012
2013     def testPackOverlapMap(self):
2014         """Test that overlapping regions are detected"""
2015         with test_util.capture_sys_output() as (stdout, stderr):
2016             with self.assertRaises(ValueError) as e:
2017                 self._DoTestFile('014_pack_overlap.dts', map=True)
2018         map_fname = tools.GetOutputFilename('image.map')
2019         self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2020                          stdout.getvalue())
2021
2022         # We should not get an inmage, but there should be a map file
2023         self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin')))
2024         self.assertTrue(os.path.exists(map_fname))
2025         map_data = tools.ReadFile(map_fname, binary=False)
2026         self.assertEqual('''ImagePos    Offset      Size  Name
2027 <none>    00000000  00000007  main-section
2028 <none>     00000000  00000004  u-boot
2029 <none>     00000003  00000004  u-boot-align
2030 ''', map_data)
2031
2032     def testPackRefCode(self):
2033         """Test that an image with an Intel Reference code binary works"""
2034         data = self._DoReadFile('100_intel_refcode.dts')
2035         self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2036
2037     def testSectionOffset(self):
2038         """Tests use of a section with an offset"""
2039         data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2040                                                    map=True)
2041         self.assertEqual('''ImagePos    Offset      Size  Name
2042 00000000  00000000  00000038  main-section
2043 00000004   00000004  00000010  section@0
2044 00000004    00000000  00000004  u-boot
2045 00000018   00000018  00000010  section@1
2046 00000018    00000000  00000004  u-boot
2047 0000002c   0000002c  00000004  section@2
2048 0000002c    00000000  00000004  u-boot
2049 ''', map_data)
2050         self.assertEqual(data,
2051                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2052                              tools.GetBytes(0x21, 12) +
2053                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2054                              tools.GetBytes(0x61, 12) +
2055                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2056                              tools.GetBytes(0x26, 8))
2057
2058     def testCbfsRaw(self):
2059         """Test base handling of a Coreboot Filesystem (CBFS)
2060
2061         The exact contents of the CBFS is verified by similar tests in
2062         cbfs_util_test.py. The tests here merely check that the files added to
2063         the CBFS can be found in the final image.
2064         """
2065         data = self._DoReadFile('102_cbfs_raw.dts')
2066         size = 0xb0
2067
2068         cbfs = cbfs_util.CbfsReader(data)
2069         self.assertEqual(size, cbfs.rom_size)
2070
2071         self.assertIn('u-boot-dtb', cbfs.files)
2072         cfile = cbfs.files['u-boot-dtb']
2073         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2074
2075     def testCbfsArch(self):
2076         """Test on non-x86 architecture"""
2077         data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2078         size = 0x100
2079
2080         cbfs = cbfs_util.CbfsReader(data)
2081         self.assertEqual(size, cbfs.rom_size)
2082
2083         self.assertIn('u-boot-dtb', cbfs.files)
2084         cfile = cbfs.files['u-boot-dtb']
2085         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2086
2087     def testCbfsStage(self):
2088         """Tests handling of a Coreboot Filesystem (CBFS)"""
2089         if not elf.ELF_TOOLS:
2090             self.skipTest('Python elftools not available')
2091         elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2092         elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2093         size = 0xb0
2094
2095         data = self._DoReadFile('104_cbfs_stage.dts')
2096         cbfs = cbfs_util.CbfsReader(data)
2097         self.assertEqual(size, cbfs.rom_size)
2098
2099         self.assertIn('u-boot', cbfs.files)
2100         cfile = cbfs.files['u-boot']
2101         self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2102
2103     def testCbfsRawCompress(self):
2104         """Test handling of compressing raw files"""
2105         self._CheckLz4()
2106         data = self._DoReadFile('105_cbfs_raw_compress.dts')
2107         size = 0x140
2108
2109         cbfs = cbfs_util.CbfsReader(data)
2110         self.assertIn('u-boot', cbfs.files)
2111         cfile = cbfs.files['u-boot']
2112         self.assertEqual(COMPRESS_DATA, cfile.data)
2113
2114     def testCbfsBadArch(self):
2115         """Test handling of a bad architecture"""
2116         with self.assertRaises(ValueError) as e:
2117             self._DoReadFile('106_cbfs_bad_arch.dts')
2118         self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2119
2120     def testCbfsNoSize(self):
2121         """Test handling of a missing size property"""
2122         with self.assertRaises(ValueError) as e:
2123             self._DoReadFile('107_cbfs_no_size.dts')
2124         self.assertIn('entry must have a size property', str(e.exception))
2125
2126     def testCbfsNoCOntents(self):
2127         """Test handling of a CBFS entry which does not provide contentsy"""
2128         with self.assertRaises(ValueError) as e:
2129             self._DoReadFile('108_cbfs_no_contents.dts')
2130         self.assertIn('Could not complete processing of contents',
2131                       str(e.exception))
2132
2133     def testCbfsBadCompress(self):
2134         """Test handling of a bad architecture"""
2135         with self.assertRaises(ValueError) as e:
2136             self._DoReadFile('109_cbfs_bad_compress.dts')
2137         self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2138                       str(e.exception))
2139
2140     def testCbfsNamedEntries(self):
2141         """Test handling of named entries"""
2142         data = self._DoReadFile('110_cbfs_name.dts')
2143
2144         cbfs = cbfs_util.CbfsReader(data)
2145         self.assertIn('FRED', cbfs.files)
2146         cfile1 = cbfs.files['FRED']
2147         self.assertEqual(U_BOOT_DATA, cfile1.data)
2148
2149         self.assertIn('hello', cbfs.files)
2150         cfile2 = cbfs.files['hello']
2151         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2152
2153     def _SetupIfwi(self, fname):
2154         """Set up to run an IFWI test
2155
2156         Args:
2157             fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2158         """
2159         self._SetupSplElf()
2160         self._SetupTplElf()
2161
2162         # Intel Integrated Firmware Image (IFWI) file
2163         with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2164             data = fd.read()
2165         TestFunctional._MakeInputFile(fname,data)
2166
2167     def _CheckIfwi(self, data):
2168         """Check that an image with an IFWI contains the correct output
2169
2170         Args:
2171             data: Conents of output file
2172         """
2173         expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
2174         if data[:0x1000] != expected_desc:
2175             self.fail('Expected descriptor binary at start of image')
2176
2177         # We expect to find the TPL wil in subpart IBBP entry IBBL
2178         image_fname = tools.GetOutputFilename('image.bin')
2179         tpl_fname = tools.GetOutputFilename('tpl.out')
2180         tools.RunIfwiTool(image_fname, tools.CMD_EXTRACT, fname=tpl_fname,
2181                           subpart='IBBP', entry_name='IBBL')
2182
2183         tpl_data = tools.ReadFile(tpl_fname)
2184         self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
2185
2186     def testPackX86RomIfwi(self):
2187         """Test that an x86 ROM with Integrated Firmware Image can be created"""
2188         self._SetupIfwi('fitimage.bin')
2189         data = self._DoReadFile('111_x86_rom_ifwi.dts')
2190         self._CheckIfwi(data)
2191
2192     def testPackX86RomIfwiNoDesc(self):
2193         """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2194         self._SetupIfwi('ifwi.bin')
2195         data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
2196         self._CheckIfwi(data)
2197
2198     def testPackX86RomIfwiNoData(self):
2199         """Test that an x86 ROM with IFWI handles missing data"""
2200         self._SetupIfwi('ifwi.bin')
2201         with self.assertRaises(ValueError) as e:
2202             data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
2203         self.assertIn('Could not complete processing of contents',
2204                       str(e.exception))
2205
2206     def testCbfsOffset(self):
2207         """Test a CBFS with files at particular offsets
2208
2209         Like all CFBS tests, this is just checking the logic that calls
2210         cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2211         """
2212         data = self._DoReadFile('114_cbfs_offset.dts')
2213         size = 0x200
2214
2215         cbfs = cbfs_util.CbfsReader(data)
2216         self.assertEqual(size, cbfs.rom_size)
2217
2218         self.assertIn('u-boot', cbfs.files)
2219         cfile = cbfs.files['u-boot']
2220         self.assertEqual(U_BOOT_DATA, cfile.data)
2221         self.assertEqual(0x40, cfile.cbfs_offset)
2222
2223         self.assertIn('u-boot-dtb', cbfs.files)
2224         cfile2 = cbfs.files['u-boot-dtb']
2225         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2226         self.assertEqual(0x140, cfile2.cbfs_offset)
2227
2228     def testFdtmap(self):
2229         """Test an FDT map can be inserted in the image"""
2230         data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2231         fdtmap_data = data[len(U_BOOT_DATA):]
2232         magic = fdtmap_data[:8]
2233         self.assertEqual(b'_FDTMAP_', magic)
2234         self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16])
2235
2236         fdt_data = fdtmap_data[16:]
2237         dtb = fdt.Fdt.FromData(fdt_data)
2238         dtb.Scan()
2239         props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
2240         self.assertEqual({
2241             'image-pos': 0,
2242             'offset': 0,
2243             'u-boot:offset': 0,
2244             'u-boot:size': len(U_BOOT_DATA),
2245             'u-boot:image-pos': 0,
2246             'fdtmap:image-pos': 4,
2247             'fdtmap:offset': 4,
2248             'fdtmap:size': len(fdtmap_data),
2249             'size': len(data),
2250         }, props)
2251
2252     def testFdtmapNoMatch(self):
2253         """Check handling of an FDT map when the section cannot be found"""
2254         self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2255
2256         # Mangle the section name, which should cause a mismatch between the
2257         # correct FDT path and the one expected by the section
2258         image = control.images['image']
2259         image._node.path += '-suffix'
2260         entries = image.GetEntries()
2261         fdtmap = entries['fdtmap']
2262         with self.assertRaises(ValueError) as e:
2263             fdtmap._GetFdtmap()
2264         self.assertIn("Cannot locate node for path '/binman-suffix'",
2265                       str(e.exception))
2266
2267     def testFdtmapHeader(self):
2268         """Test an FDT map and image header can be inserted in the image"""
2269         data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2270         fdtmap_pos = len(U_BOOT_DATA)
2271         fdtmap_data = data[fdtmap_pos:]
2272         fdt_data = fdtmap_data[16:]
2273         dtb = fdt.Fdt.FromData(fdt_data)
2274         fdt_size = dtb.GetFdtObj().totalsize()
2275         hdr_data = data[-8:]
2276         self.assertEqual(b'BinM', hdr_data[:4])
2277         offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2278         self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2279
2280     def testFdtmapHeaderStart(self):
2281         """Test an image header can be inserted at the image start"""
2282         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2283         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2284         hdr_data = data[:8]
2285         self.assertEqual(b'BinM', hdr_data[:4])
2286         offset = struct.unpack('<I', hdr_data[4:])[0]
2287         self.assertEqual(fdtmap_pos, offset)
2288
2289     def testFdtmapHeaderPos(self):
2290         """Test an image header can be inserted at a chosen position"""
2291         data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2292         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2293         hdr_data = data[0x80:0x88]
2294         self.assertEqual(b'BinM', hdr_data[:4])
2295         offset = struct.unpack('<I', hdr_data[4:])[0]
2296         self.assertEqual(fdtmap_pos, offset)
2297
2298     def testHeaderMissingFdtmap(self):
2299         """Test an image header requires an fdtmap"""
2300         with self.assertRaises(ValueError) as e:
2301             self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2302         self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2303                       str(e.exception))
2304
2305     def testHeaderNoLocation(self):
2306         """Test an image header with a no specified location is detected"""
2307         with self.assertRaises(ValueError) as e:
2308             self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2309         self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2310                       str(e.exception))
2311
2312     def testEntryExpand(self):
2313         """Test expanding an entry after it is packed"""
2314         data = self._DoReadFile('121_entry_expand.dts')
2315         self.assertEqual(b'aaa', data[:3])
2316         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2317         self.assertEqual(b'aaa', data[-3:])
2318
2319     def testEntryExpandBad(self):
2320         """Test expanding an entry after it is packed, twice"""
2321         with self.assertRaises(ValueError) as e:
2322             self._DoReadFile('122_entry_expand_twice.dts')
2323         self.assertIn("Image '/binman': Entries changed size after packing",
2324                       str(e.exception))
2325
2326     def testEntryExpandSection(self):
2327         """Test expanding an entry within a section after it is packed"""
2328         data = self._DoReadFile('123_entry_expand_section.dts')
2329         self.assertEqual(b'aaa', data[:3])
2330         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2331         self.assertEqual(b'aaa', data[-3:])
2332
2333     def testCompressDtb(self):
2334         """Test that compress of device-tree files is supported"""
2335         self._CheckLz4()
2336         data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2337         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2338         comp_data = data[len(U_BOOT_DATA):]
2339         orig = self._decompress(comp_data)
2340         dtb = fdt.Fdt.FromData(orig)
2341         dtb.Scan()
2342         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2343         expected = {
2344             'u-boot:size': len(U_BOOT_DATA),
2345             'u-boot-dtb:uncomp-size': len(orig),
2346             'u-boot-dtb:size': len(comp_data),
2347             'size': len(data),
2348             }
2349         self.assertEqual(expected, props)
2350
2351     def testCbfsUpdateFdt(self):
2352         """Test that we can update the device tree with CBFS offset/size info"""
2353         self._CheckLz4()
2354         data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2355                                                         update_dtb=True)
2356         dtb = fdt.Fdt(out_dtb_fname)
2357         dtb.Scan()
2358         props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
2359         del props['cbfs/u-boot:size']
2360         self.assertEqual({
2361             'offset': 0,
2362             'size': len(data),
2363             'image-pos': 0,
2364             'cbfs:offset': 0,
2365             'cbfs:size': len(data),
2366             'cbfs:image-pos': 0,
2367             'cbfs/u-boot:offset': 0x38,
2368             'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
2369             'cbfs/u-boot:image-pos': 0x38,
2370             'cbfs/u-boot-dtb:offset': 0xb8,
2371             'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
2372             'cbfs/u-boot-dtb:image-pos': 0xb8,
2373             }, props)
2374
2375     def testCbfsBadType(self):
2376         """Test an image header with a no specified location is detected"""
2377         with self.assertRaises(ValueError) as e:
2378             self._DoReadFile('126_cbfs_bad_type.dts')
2379         self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2380
2381     def testList(self):
2382         """Test listing the files in an image"""
2383         self._CheckLz4()
2384         data = self._DoReadFile('127_list.dts')
2385         image = control.images['image']
2386         entries = image.BuildEntryList()
2387         self.assertEqual(7, len(entries))
2388
2389         ent = entries[0]
2390         self.assertEqual(0, ent.indent)
2391         self.assertEqual('main-section', ent.name)
2392         self.assertEqual('section', ent.etype)
2393         self.assertEqual(len(data), ent.size)
2394         self.assertEqual(0, ent.image_pos)
2395         self.assertEqual(None, ent.uncomp_size)
2396         self.assertEqual(0, ent.offset)
2397
2398         ent = entries[1]
2399         self.assertEqual(1, ent.indent)
2400         self.assertEqual('u-boot', ent.name)
2401         self.assertEqual('u-boot', ent.etype)
2402         self.assertEqual(len(U_BOOT_DATA), ent.size)
2403         self.assertEqual(0, ent.image_pos)
2404         self.assertEqual(None, ent.uncomp_size)
2405         self.assertEqual(0, ent.offset)
2406
2407         ent = entries[2]
2408         self.assertEqual(1, ent.indent)
2409         self.assertEqual('section', ent.name)
2410         self.assertEqual('section', ent.etype)
2411         section_size = ent.size
2412         self.assertEqual(0x100, ent.image_pos)
2413         self.assertEqual(None, ent.uncomp_size)
2414         self.assertEqual(0x100, ent.offset)
2415
2416         ent = entries[3]
2417         self.assertEqual(2, ent.indent)
2418         self.assertEqual('cbfs', ent.name)
2419         self.assertEqual('cbfs', ent.etype)
2420         self.assertEqual(0x400, ent.size)
2421         self.assertEqual(0x100, ent.image_pos)
2422         self.assertEqual(None, ent.uncomp_size)
2423         self.assertEqual(0, ent.offset)
2424
2425         ent = entries[4]
2426         self.assertEqual(3, ent.indent)
2427         self.assertEqual('u-boot', ent.name)
2428         self.assertEqual('u-boot', ent.etype)
2429         self.assertEqual(len(U_BOOT_DATA), ent.size)
2430         self.assertEqual(0x138, ent.image_pos)
2431         self.assertEqual(None, ent.uncomp_size)
2432         self.assertEqual(0x38, ent.offset)
2433
2434         ent = entries[5]
2435         self.assertEqual(3, ent.indent)
2436         self.assertEqual('u-boot-dtb', ent.name)
2437         self.assertEqual('text', ent.etype)
2438         self.assertGreater(len(COMPRESS_DATA), ent.size)
2439         self.assertEqual(0x178, ent.image_pos)
2440         self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2441         self.assertEqual(0x78, ent.offset)
2442
2443         ent = entries[6]
2444         self.assertEqual(2, ent.indent)
2445         self.assertEqual('u-boot-dtb', ent.name)
2446         self.assertEqual('u-boot-dtb', ent.etype)
2447         self.assertEqual(0x500, ent.image_pos)
2448         self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2449         dtb_size = ent.size
2450         # Compressing this data expands it since headers are added
2451         self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2452         self.assertEqual(0x400, ent.offset)
2453
2454         self.assertEqual(len(data), 0x100 + section_size)
2455         self.assertEqual(section_size, 0x400 + dtb_size)
2456
2457     def testFindFdtmap(self):
2458         """Test locating an FDT map in an image"""
2459         self._CheckLz4()
2460         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2461         image = control.images['image']
2462         entries = image.GetEntries()
2463         entry = entries['fdtmap']
2464         self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2465
2466     def testFindFdtmapMissing(self):
2467         """Test failing to locate an FDP map"""
2468         data = self._DoReadFile('005_simple.dts')
2469         self.assertEqual(None, fdtmap.LocateFdtmap(data))
2470
2471     def testFindImageHeader(self):
2472         """Test locating a image header"""
2473         self._CheckLz4()
2474         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2475         image = control.images['image']
2476         entries = image.GetEntries()
2477         entry = entries['fdtmap']
2478         # The header should point to the FDT map
2479         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2480
2481     def testFindImageHeaderStart(self):
2482         """Test locating a image header located at the start of an image"""
2483         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2484         image = control.images['image']
2485         entries = image.GetEntries()
2486         entry = entries['fdtmap']
2487         # The header should point to the FDT map
2488         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2489
2490     def testFindImageHeaderMissing(self):
2491         """Test failing to locate an image header"""
2492         data = self._DoReadFile('005_simple.dts')
2493         self.assertEqual(None, image_header.LocateHeaderOffset(data))
2494
2495     def testReadImage(self):
2496         """Test reading an image and accessing its FDT map"""
2497         self._CheckLz4()
2498         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2499         image_fname = tools.GetOutputFilename('image.bin')
2500         orig_image = control.images['image']
2501         image = Image.FromFile(image_fname)
2502         self.assertEqual(orig_image.GetEntries().keys(),
2503                          image.GetEntries().keys())
2504
2505         orig_entry = orig_image.GetEntries()['fdtmap']
2506         entry = image.GetEntries()['fdtmap']
2507         self.assertEquals(orig_entry.offset, entry.offset)
2508         self.assertEquals(orig_entry.size, entry.size)
2509         self.assertEquals(orig_entry.image_pos, entry.image_pos)
2510
2511     def testReadImageNoHeader(self):
2512         """Test accessing an image's FDT map without an image header"""
2513         self._CheckLz4()
2514         data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
2515         image_fname = tools.GetOutputFilename('image.bin')
2516         image = Image.FromFile(image_fname)
2517         self.assertTrue(isinstance(image, Image))
2518         self.assertEqual('image', image.image_name[-5:])
2519
2520     def testReadImageFail(self):
2521         """Test failing to read an image image's FDT map"""
2522         self._DoReadFile('005_simple.dts')
2523         image_fname = tools.GetOutputFilename('image.bin')
2524         with self.assertRaises(ValueError) as e:
2525             image = Image.FromFile(image_fname)
2526         self.assertIn("Cannot find FDT map in image", str(e.exception))
2527
2528     def testListCmd(self):
2529         """Test listing the files in an image using an Fdtmap"""
2530         self._CheckLz4()
2531         data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2532
2533         # lz4 compression size differs depending on the version
2534         image = control.images['image']
2535         entries = image.GetEntries()
2536         section_size = entries['section'].size
2537         fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2538         fdtmap_offset = entries['fdtmap'].offset
2539
2540         try:
2541             tmpdir, updated_fname = self._SetupImageInTmpdir()
2542             with test_util.capture_sys_output() as (stdout, stderr):
2543                 self._DoBinman('ls', '-i', updated_fname)
2544         finally:
2545             shutil.rmtree(tmpdir)
2546         lines = stdout.getvalue().splitlines()
2547         expected = [
2548 'Name              Image-pos  Size  Entry-type    Offset  Uncomp-size',
2549 '----------------------------------------------------------------------',
2550 'main-section              0   c00  section            0',
2551 '  u-boot                  0     4  u-boot             0',
2552 '  section               100   %x  section          100' % section_size,
2553 '    cbfs                100   400  cbfs               0',
2554 '      u-boot            138     4  u-boot            38',
2555 '      u-boot-dtb        180   105  u-boot-dtb        80          3c9',
2556 '    u-boot-dtb          500   %x  u-boot-dtb       400          3c9' % fdt_size,
2557 '  fdtmap                %x   3bd  fdtmap           %x' %
2558         (fdtmap_offset, fdtmap_offset),
2559 '  image-header          bf8     8  image-header     bf8',
2560             ]
2561         self.assertEqual(expected, lines)
2562
2563     def testListCmdFail(self):
2564         """Test failing to list an image"""
2565         self._DoReadFile('005_simple.dts')
2566         try:
2567             tmpdir, updated_fname = self._SetupImageInTmpdir()
2568             with self.assertRaises(ValueError) as e:
2569                 self._DoBinman('ls', '-i', updated_fname)
2570         finally:
2571             shutil.rmtree(tmpdir)
2572         self.assertIn("Cannot find FDT map in image", str(e.exception))
2573
2574     def _RunListCmd(self, paths, expected):
2575         """List out entries and check the result
2576
2577         Args:
2578             paths: List of paths to pass to the list command
2579             expected: Expected list of filenames to be returned, in order
2580         """
2581         self._CheckLz4()
2582         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2583         image_fname = tools.GetOutputFilename('image.bin')
2584         image = Image.FromFile(image_fname)
2585         lines = image.GetListEntries(paths)[1]
2586         files = [line[0].strip() for line in lines[1:]]
2587         self.assertEqual(expected, files)
2588
2589     def testListCmdSection(self):
2590         """Test listing the files in a section"""
2591         self._RunListCmd(['section'],
2592             ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2593
2594     def testListCmdFile(self):
2595         """Test listing a particular file"""
2596         self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2597
2598     def testListCmdWildcard(self):
2599         """Test listing a wildcarded file"""
2600         self._RunListCmd(['*boot*'],
2601             ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2602
2603     def testListCmdWildcardMulti(self):
2604         """Test listing a wildcarded file"""
2605         self._RunListCmd(['*cb*', '*head*'],
2606             ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2607
2608     def testListCmdEmpty(self):
2609         """Test listing a wildcarded file"""
2610         self._RunListCmd(['nothing'], [])
2611
2612     def testListCmdPath(self):
2613         """Test listing the files in a sub-entry of a section"""
2614         self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2615
2616     def _RunExtractCmd(self, entry_name, decomp=True):
2617         """Extract an entry from an image
2618
2619         Args:
2620             entry_name: Entry name to extract
2621             decomp: True to decompress the data if compressed, False to leave
2622                 it in its raw uncompressed format
2623
2624         Returns:
2625             data from entry
2626         """
2627         self._CheckLz4()
2628         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2629         image_fname = tools.GetOutputFilename('image.bin')
2630         return control.ReadEntry(image_fname, entry_name, decomp)
2631
2632     def testExtractSimple(self):
2633         """Test extracting a single file"""
2634         data = self._RunExtractCmd('u-boot')
2635         self.assertEqual(U_BOOT_DATA, data)
2636
2637     def testExtractSection(self):
2638         """Test extracting the files in a section"""
2639         data = self._RunExtractCmd('section')
2640         cbfs_data = data[:0x400]
2641         cbfs = cbfs_util.CbfsReader(cbfs_data)
2642         self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
2643         dtb_data = data[0x400:]
2644         dtb = self._decompress(dtb_data)
2645         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2646
2647     def testExtractCompressed(self):
2648         """Test extracting compressed data"""
2649         data = self._RunExtractCmd('section/u-boot-dtb')
2650         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2651
2652     def testExtractRaw(self):
2653         """Test extracting compressed data without decompressing it"""
2654         data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
2655         dtb = self._decompress(data)
2656         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2657
2658     def testExtractCbfs(self):
2659         """Test extracting CBFS data"""
2660         data = self._RunExtractCmd('section/cbfs/u-boot')
2661         self.assertEqual(U_BOOT_DATA, data)
2662
2663     def testExtractCbfsCompressed(self):
2664         """Test extracting CBFS compressed data"""
2665         data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
2666         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2667
2668     def testExtractCbfsRaw(self):
2669         """Test extracting CBFS compressed data without decompressing it"""
2670         data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
2671         dtb = tools.Decompress(data, 'lzma', with_header=False)
2672         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2673
2674     def testExtractBadEntry(self):
2675         """Test extracting a bad section path"""
2676         with self.assertRaises(ValueError) as e:
2677             self._RunExtractCmd('section/does-not-exist')
2678         self.assertIn("Entry 'does-not-exist' not found in '/section'",
2679                       str(e.exception))
2680
2681     def testExtractMissingFile(self):
2682         """Test extracting file that does not exist"""
2683         with self.assertRaises(IOError) as e:
2684             control.ReadEntry('missing-file', 'name')
2685
2686     def testExtractBadFile(self):
2687         """Test extracting an invalid file"""
2688         fname = os.path.join(self._indir, 'badfile')
2689         tools.WriteFile(fname, b'')
2690         with self.assertRaises(ValueError) as e:
2691             control.ReadEntry(fname, 'name')
2692
2693     def testExtractCmd(self):
2694         """Test extracting a file fron an image on the command line"""
2695         self._CheckLz4()
2696         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2697         fname = os.path.join(self._indir, 'output.extact')
2698         try:
2699             tmpdir, updated_fname = self._SetupImageInTmpdir()
2700             with test_util.capture_sys_output() as (stdout, stderr):
2701                 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
2702                                '-f', fname)
2703         finally:
2704             shutil.rmtree(tmpdir)
2705         data = tools.ReadFile(fname)
2706         self.assertEqual(U_BOOT_DATA, data)
2707
2708     def testExtractOneEntry(self):
2709         """Test extracting a single entry fron an image """
2710         self._CheckLz4()
2711         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2712         image_fname = tools.GetOutputFilename('image.bin')
2713         fname = os.path.join(self._indir, 'output.extact')
2714         control.ExtractEntries(image_fname, fname, None, ['u-boot'])
2715         data = tools.ReadFile(fname)
2716         self.assertEqual(U_BOOT_DATA, data)
2717
2718     def _CheckExtractOutput(self, decomp):
2719         """Helper to test file output with and without decompression
2720
2721         Args:
2722             decomp: True to decompress entry data, False to output it raw
2723         """
2724         def _CheckPresent(entry_path, expect_data, expect_size=None):
2725             """Check and remove expected file
2726
2727             This checks the data/size of a file and removes the file both from
2728             the outfiles set and from the output directory. Once all files are
2729             processed, both the set and directory should be empty.
2730
2731             Args:
2732                 entry_path: Entry path
2733                 expect_data: Data to expect in file, or None to skip check
2734                 expect_size: Size of data to expect in file, or None to skip
2735             """
2736             path = os.path.join(outdir, entry_path)
2737             data = tools.ReadFile(path)
2738             os.remove(path)
2739             if expect_data:
2740                 self.assertEqual(expect_data, data)
2741             elif expect_size:
2742                 self.assertEqual(expect_size, len(data))
2743             outfiles.remove(path)
2744
2745         def _CheckDirPresent(name):
2746             """Remove expected directory
2747
2748             This gives an error if the directory does not exist as expected
2749
2750             Args:
2751                 name: Name of directory to remove
2752             """
2753             path = os.path.join(outdir, name)
2754             os.rmdir(path)
2755
2756         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2757         image_fname = tools.GetOutputFilename('image.bin')
2758         outdir = os.path.join(self._indir, 'extract')
2759         einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
2760
2761         # Create a set of all file that were output (should be 9)
2762         outfiles = set()
2763         for root, dirs, files in os.walk(outdir):
2764             outfiles |= set([os.path.join(root, fname) for fname in files])
2765         self.assertEqual(9, len(outfiles))
2766         self.assertEqual(9, len(einfos))
2767
2768         image = control.images['image']
2769         entries = image.GetEntries()
2770
2771         # Check the 9 files in various ways
2772         section = entries['section']
2773         section_entries = section.GetEntries()
2774         cbfs_entries = section_entries['cbfs'].GetEntries()
2775         _CheckPresent('u-boot', U_BOOT_DATA)
2776         _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
2777         dtb_len = EXTRACT_DTB_SIZE
2778         if not decomp:
2779             dtb_len = cbfs_entries['u-boot-dtb'].size
2780         _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
2781         if not decomp:
2782             dtb_len = section_entries['u-boot-dtb'].size
2783         _CheckPresent('section/u-boot-dtb', None, dtb_len)
2784
2785         fdtmap = entries['fdtmap']
2786         _CheckPresent('fdtmap', fdtmap.data)
2787         hdr = entries['image-header']
2788         _CheckPresent('image-header', hdr.data)
2789
2790         _CheckPresent('section/root', section.data)
2791         cbfs = section_entries['cbfs']
2792         _CheckPresent('section/cbfs/root', cbfs.data)
2793         data = tools.ReadFile(image_fname)
2794         _CheckPresent('root', data)
2795
2796         # There should be no files left. Remove all the directories to check.
2797         # If there are any files/dirs remaining, one of these checks will fail.
2798         self.assertEqual(0, len(outfiles))
2799         _CheckDirPresent('section/cbfs')
2800         _CheckDirPresent('section')
2801         _CheckDirPresent('')
2802         self.assertFalse(os.path.exists(outdir))
2803
2804     def testExtractAllEntries(self):
2805         """Test extracting all entries"""
2806         self._CheckLz4()
2807         self._CheckExtractOutput(decomp=True)
2808
2809     def testExtractAllEntriesRaw(self):
2810         """Test extracting all entries without decompressing them"""
2811         self._CheckLz4()
2812         self._CheckExtractOutput(decomp=False)
2813
2814     def testExtractSelectedEntries(self):
2815         """Test extracting some entries"""
2816         self._CheckLz4()
2817         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2818         image_fname = tools.GetOutputFilename('image.bin')
2819         outdir = os.path.join(self._indir, 'extract')
2820         einfos = control.ExtractEntries(image_fname, None, outdir,
2821                                         ['*cb*', '*head*'])
2822
2823         # File output is tested by testExtractAllEntries(), so just check that
2824         # the expected entries are selected
2825         names = [einfo.name for einfo in einfos]
2826         self.assertEqual(names,
2827                          ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2828
2829     def testExtractNoEntryPaths(self):
2830         """Test extracting some entries"""
2831         self._CheckLz4()
2832         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2833         image_fname = tools.GetOutputFilename('image.bin')
2834         with self.assertRaises(ValueError) as e:
2835             control.ExtractEntries(image_fname, 'fname', None, [])
2836         self.assertIn('Must specify an entry path to write with -f',
2837                       str(e.exception))
2838
2839     def testExtractTooManyEntryPaths(self):
2840         """Test extracting some entries"""
2841         self._CheckLz4()
2842         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2843         image_fname = tools.GetOutputFilename('image.bin')
2844         with self.assertRaises(ValueError) as e:
2845             control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
2846         self.assertIn('Must specify exactly one entry path to write with -f',
2847                       str(e.exception))
2848
2849     def testPackAlignSection(self):
2850         """Test that sections can have alignment"""
2851         self._DoReadFile('131_pack_align_section.dts')
2852
2853         self.assertIn('image', control.images)
2854         image = control.images['image']
2855         entries = image.GetEntries()
2856         self.assertEqual(3, len(entries))
2857
2858         # First u-boot
2859         self.assertIn('u-boot', entries)
2860         entry = entries['u-boot']
2861         self.assertEqual(0, entry.offset)
2862         self.assertEqual(0, entry.image_pos)
2863         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2864         self.assertEqual(len(U_BOOT_DATA), entry.size)
2865
2866         # Section0
2867         self.assertIn('section0', entries)
2868         section0 = entries['section0']
2869         self.assertEqual(0x10, section0.offset)
2870         self.assertEqual(0x10, section0.image_pos)
2871         self.assertEqual(len(U_BOOT_DATA), section0.size)
2872
2873         # Second u-boot
2874         section_entries = section0.GetEntries()
2875         self.assertIn('u-boot', section_entries)
2876         entry = section_entries['u-boot']
2877         self.assertEqual(0, entry.offset)
2878         self.assertEqual(0x10, entry.image_pos)
2879         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2880         self.assertEqual(len(U_BOOT_DATA), entry.size)
2881
2882         # Section1
2883         self.assertIn('section1', entries)
2884         section1 = entries['section1']
2885         self.assertEqual(0x14, section1.offset)
2886         self.assertEqual(0x14, section1.image_pos)
2887         self.assertEqual(0x20, section1.size)
2888
2889         # Second u-boot
2890         section_entries = section1.GetEntries()
2891         self.assertIn('u-boot', section_entries)
2892         entry = section_entries['u-boot']
2893         self.assertEqual(0, entry.offset)
2894         self.assertEqual(0x14, entry.image_pos)
2895         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2896         self.assertEqual(len(U_BOOT_DATA), entry.size)
2897
2898         # Section2
2899         self.assertIn('section2', section_entries)
2900         section2 = section_entries['section2']
2901         self.assertEqual(0x4, section2.offset)
2902         self.assertEqual(0x18, section2.image_pos)
2903         self.assertEqual(4, section2.size)
2904
2905         # Third u-boot
2906         section_entries = section2.GetEntries()
2907         self.assertIn('u-boot', section_entries)
2908         entry = section_entries['u-boot']
2909         self.assertEqual(0, entry.offset)
2910         self.assertEqual(0x18, entry.image_pos)
2911         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2912         self.assertEqual(len(U_BOOT_DATA), entry.size)
2913
2914     def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
2915                        dts='132_replace.dts'):
2916         """Replace an entry in an image
2917
2918         This writes the entry data to update it, then opens the updated file and
2919         returns the value that it now finds there.
2920
2921         Args:
2922             entry_name: Entry name to replace
2923             data: Data to replace it with
2924             decomp: True to compress the data if needed, False if data is
2925                 already compressed so should be used as is
2926             allow_resize: True to allow entries to change size, False to raise
2927                 an exception
2928
2929         Returns:
2930             Tuple:
2931                 data from entry
2932                 data from fdtmap (excluding header)
2933                 Image object that was modified
2934         """
2935         dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
2936                                        update_dtb=True)[1]
2937
2938         self.assertIn('image', control.images)
2939         image = control.images['image']
2940         entries = image.GetEntries()
2941         orig_dtb_data = entries['u-boot-dtb'].data
2942         orig_fdtmap_data = entries['fdtmap'].data
2943
2944         image_fname = tools.GetOutputFilename('image.bin')
2945         updated_fname = tools.GetOutputFilename('image-updated.bin')
2946         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
2947         image = control.WriteEntry(updated_fname, entry_name, data, decomp,
2948                                    allow_resize)
2949         data = control.ReadEntry(updated_fname, entry_name, decomp)
2950
2951         # The DT data should not change unless resized:
2952         if not allow_resize:
2953             new_dtb_data = entries['u-boot-dtb'].data
2954             self.assertEqual(new_dtb_data, orig_dtb_data)
2955             new_fdtmap_data = entries['fdtmap'].data
2956             self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
2957
2958         return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
2959
2960     def testReplaceSimple(self):
2961         """Test replacing a single file"""
2962         expected = b'x' * len(U_BOOT_DATA)
2963         data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
2964                                                     allow_resize=False)
2965         self.assertEqual(expected, data)
2966
2967         # Test that the state looks right. There should be an FDT for the fdtmap
2968         # that we jsut read back in, and it should match what we find in the
2969         # 'control' tables. Checking for an FDT that does not exist should
2970         # return None.
2971         path, fdtmap = state.GetFdtContents('fdtmap')
2972         self.assertIsNotNone(path)
2973         self.assertEqual(expected_fdtmap, fdtmap)
2974
2975         dtb = state.GetFdtForEtype('fdtmap')
2976         self.assertEqual(dtb.GetContents(), fdtmap)
2977
2978         missing_path, missing_fdtmap = state.GetFdtContents('missing')
2979         self.assertIsNone(missing_path)
2980         self.assertIsNone(missing_fdtmap)
2981
2982         missing_dtb = state.GetFdtForEtype('missing')
2983         self.assertIsNone(missing_dtb)
2984
2985         self.assertEqual('/binman', state.fdt_path_prefix)
2986
2987     def testReplaceResizeFail(self):
2988         """Test replacing a file by something larger"""
2989         expected = U_BOOT_DATA + b'x'
2990         with self.assertRaises(ValueError) as e:
2991             self._RunReplaceCmd('u-boot', expected, allow_resize=False,
2992                                 dts='139_replace_repack.dts')
2993         self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
2994                       str(e.exception))
2995
2996     def testReplaceMulti(self):
2997         """Test replacing entry data where multiple images are generated"""
2998         data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
2999                                    update_dtb=True)[0]
3000         expected = b'x' * len(U_BOOT_DATA)
3001         updated_fname = tools.GetOutputFilename('image-updated.bin')
3002         tools.WriteFile(updated_fname, data)
3003         entry_name = 'u-boot'
3004         control.WriteEntry(updated_fname, entry_name, expected,
3005                            allow_resize=False)
3006         data = control.ReadEntry(updated_fname, entry_name)
3007         self.assertEqual(expected, data)
3008
3009         # Check the state looks right.
3010         self.assertEqual('/binman/image', state.fdt_path_prefix)
3011
3012         # Now check we can write the first image
3013         image_fname = tools.GetOutputFilename('first-image.bin')
3014         updated_fname = tools.GetOutputFilename('first-updated.bin')
3015         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
3016         entry_name = 'u-boot'
3017         control.WriteEntry(updated_fname, entry_name, expected,
3018                            allow_resize=False)
3019         data = control.ReadEntry(updated_fname, entry_name)
3020         self.assertEqual(expected, data)
3021
3022         # Check the state looks right.
3023         self.assertEqual('/binman/first-image', state.fdt_path_prefix)
3024
3025     def testUpdateFdtAllRepack(self):
3026         """Test that all device trees are updated with offset/size info"""
3027         data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3028         SECTION_SIZE = 0x300
3029         DTB_SIZE = 602
3030         FDTMAP_SIZE = 608
3031         base_expected = {
3032             'offset': 0,
3033             'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3034             'image-pos': 0,
3035             'section:offset': 0,
3036             'section:size': SECTION_SIZE,
3037             'section:image-pos': 0,
3038             'section/u-boot-dtb:offset': 4,
3039             'section/u-boot-dtb:size': 636,
3040             'section/u-boot-dtb:image-pos': 4,
3041             'u-boot-spl-dtb:offset': SECTION_SIZE,
3042             'u-boot-spl-dtb:size': DTB_SIZE,
3043             'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3044             'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3045             'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3046             'u-boot-tpl-dtb:size': DTB_SIZE,
3047             'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3048             'fdtmap:size': FDTMAP_SIZE,
3049             'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3050         }
3051         main_expected = {
3052             'section:orig-size': SECTION_SIZE,
3053             'section/u-boot-dtb:orig-offset': 4,
3054         }
3055
3056         # We expect three device-tree files in the output, with the first one
3057         # within a fixed-size section.
3058         # Read them in sequence. We look for an 'spl' property in the SPL tree,
3059         # and 'tpl' in the TPL tree, to make sure they are distinct from the
3060         # main U-Boot tree. All three should have the same positions and offset
3061         # except that the main tree should include the main_expected properties
3062         start = 4
3063         for item in ['', 'spl', 'tpl', None]:
3064             if item is None:
3065                 start += 16  # Move past fdtmap header
3066             dtb = fdt.Fdt.FromData(data[start:])
3067             dtb.Scan()
3068             props = self._GetPropTree(dtb,
3069                 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3070                 prefix='/' if item is None else '/binman/')
3071             expected = dict(base_expected)
3072             if item:
3073                 expected[item] = 0
3074             else:
3075                 # Main DTB and fdtdec should include the 'orig-' properties
3076                 expected.update(main_expected)
3077             # Helpful for debugging:
3078             #for prop in sorted(props):
3079                 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3080             self.assertEqual(expected, props)
3081             if item == '':
3082                 start = SECTION_SIZE
3083             else:
3084                 start += dtb._fdt_obj.totalsize()
3085
3086     def testFdtmapHeaderMiddle(self):
3087         """Test an FDT map in the middle of an image when it should be at end"""
3088         with self.assertRaises(ValueError) as e:
3089             self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3090         self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3091                       str(e.exception))
3092
3093     def testFdtmapHeaderStartBad(self):
3094         """Test an FDT map in middle of an image when it should be at start"""
3095         with self.assertRaises(ValueError) as e:
3096             self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3097         self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3098                       str(e.exception))
3099
3100     def testFdtmapHeaderEndBad(self):
3101         """Test an FDT map at the start of an image when it should be at end"""
3102         with self.assertRaises(ValueError) as e:
3103             self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3104         self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3105                       str(e.exception))
3106
3107     def testFdtmapHeaderNoSize(self):
3108         """Test an image header at the end of an image with undefined size"""
3109         self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3110
3111     def testReplaceResize(self):
3112         """Test replacing a single file in an entry with a larger file"""
3113         expected = U_BOOT_DATA + b'x'
3114         data, _, image = self._RunReplaceCmd('u-boot', expected,
3115                                              dts='139_replace_repack.dts')
3116         self.assertEqual(expected, data)
3117
3118         entries = image.GetEntries()
3119         dtb_data = entries['u-boot-dtb'].data
3120         dtb = fdt.Fdt.FromData(dtb_data)
3121         dtb.Scan()
3122
3123         # The u-boot section should now be larger in the dtb
3124         node = dtb.GetNode('/binman/u-boot')
3125         self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3126
3127         # Same for the fdtmap
3128         fdata = entries['fdtmap'].data
3129         fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3130         fdtb.Scan()
3131         fnode = fdtb.GetNode('/u-boot')
3132         self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3133
3134     def testReplaceResizeNoRepack(self):
3135         """Test replacing an entry with a larger file when not allowed"""
3136         expected = U_BOOT_DATA + b'x'
3137         with self.assertRaises(ValueError) as e:
3138             self._RunReplaceCmd('u-boot', expected)
3139         self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3140                       str(e.exception))
3141
3142     def testEntryShrink(self):
3143         """Test contracting an entry after it is packed"""
3144         try:
3145             state.SetAllowEntryContraction(True)
3146             data = self._DoReadFileDtb('140_entry_shrink.dts',
3147                                        update_dtb=True)[0]
3148         finally:
3149             state.SetAllowEntryContraction(False)
3150         self.assertEqual(b'a', data[:1])
3151         self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3152         self.assertEqual(b'a', data[-1:])
3153
3154     def testEntryShrinkFail(self):
3155         """Test not being allowed to contract an entry after it is packed"""
3156         data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3157
3158         # In this case there is a spare byte at the end of the data. The size of
3159         # the contents is only 1 byte but we still have the size before it
3160         # shrunk.
3161         self.assertEqual(b'a\0', data[:2])
3162         self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3163         self.assertEqual(b'a\0', data[-2:])
3164
3165     def testDescriptorOffset(self):
3166         """Test that the Intel descriptor is always placed at at the start"""
3167         data = self._DoReadFileDtb('141_descriptor_offset.dts')
3168         image = control.images['image']
3169         entries = image.GetEntries()
3170         desc = entries['intel-descriptor']
3171         self.assertEqual(0xff800000, desc.offset);
3172         self.assertEqual(0xff800000, desc.image_pos);
3173
3174     def testReplaceCbfs(self):
3175         """Test replacing a single file in CBFS without changing the size"""
3176         self._CheckLz4()
3177         expected = b'x' * len(U_BOOT_DATA)
3178         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3179         updated_fname = tools.GetOutputFilename('image-updated.bin')
3180         tools.WriteFile(updated_fname, data)
3181         entry_name = 'section/cbfs/u-boot'
3182         control.WriteEntry(updated_fname, entry_name, expected,
3183                            allow_resize=True)
3184         data = control.ReadEntry(updated_fname, entry_name)
3185         self.assertEqual(expected, data)
3186
3187     def testReplaceResizeCbfs(self):
3188         """Test replacing a single file in CBFS with one of a different size"""
3189         self._CheckLz4()
3190         expected = U_BOOT_DATA + b'x'
3191         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3192         updated_fname = tools.GetOutputFilename('image-updated.bin')
3193         tools.WriteFile(updated_fname, data)
3194         entry_name = 'section/cbfs/u-boot'
3195         control.WriteEntry(updated_fname, entry_name, expected,
3196                            allow_resize=True)
3197         data = control.ReadEntry(updated_fname, entry_name)
3198         self.assertEqual(expected, data)
3199
3200     def _SetupForReplace(self):
3201         """Set up some files to use to replace entries
3202
3203         This generates an image, copies it to a new file, extracts all the files
3204         in it and updates some of them
3205
3206         Returns:
3207             List
3208                 Image filename
3209                 Output directory
3210                 Expected values for updated entries, each a string
3211         """
3212         data = self._DoReadFileRealDtb('143_replace_all.dts')
3213
3214         updated_fname = tools.GetOutputFilename('image-updated.bin')
3215         tools.WriteFile(updated_fname, data)
3216
3217         outdir = os.path.join(self._indir, 'extract')
3218         einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3219
3220         expected1 = b'x' + U_BOOT_DATA + b'y'
3221         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3222         tools.WriteFile(u_boot_fname1, expected1)
3223
3224         expected2 = b'a' + U_BOOT_DATA + b'b'
3225         u_boot_fname2 = os.path.join(outdir, 'u-boot2')
3226         tools.WriteFile(u_boot_fname2, expected2)
3227
3228         expected_text = b'not the same text'
3229         text_fname = os.path.join(outdir, 'text')
3230         tools.WriteFile(text_fname, expected_text)
3231
3232         dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3233         dtb = fdt.FdtScan(dtb_fname)
3234         node = dtb.GetNode('/binman/text')
3235         node.AddString('my-property', 'the value')
3236         dtb.Sync(auto_resize=True)
3237         dtb.Flush()
3238
3239         return updated_fname, outdir, expected1, expected2, expected_text
3240
3241     def _CheckReplaceMultiple(self, entry_paths):
3242         """Handle replacing the contents of multiple entries
3243
3244         Args:
3245             entry_paths: List of entry paths to replace
3246
3247         Returns:
3248             List
3249                 Dict of entries in the image:
3250                     key: Entry name
3251                     Value: Entry object
3252             Expected values for updated entries, each a string
3253         """
3254         updated_fname, outdir, expected1, expected2, expected_text = (
3255             self._SetupForReplace())
3256         control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3257
3258         image = Image.FromFile(updated_fname)
3259         image.LoadData()
3260         return image.GetEntries(), expected1, expected2, expected_text
3261
3262     def testReplaceAll(self):
3263         """Test replacing the contents of all entries"""
3264         entries, expected1, expected2, expected_text = (
3265             self._CheckReplaceMultiple([]))
3266         data = entries['u-boot'].data
3267         self.assertEqual(expected1, data)
3268
3269         data = entries['u-boot2'].data
3270         self.assertEqual(expected2, data)
3271
3272         data = entries['text'].data
3273         self.assertEqual(expected_text, data)
3274
3275         # Check that the device tree is updated
3276         data = entries['u-boot-dtb'].data
3277         dtb = fdt.Fdt.FromData(data)
3278         dtb.Scan()
3279         node = dtb.GetNode('/binman/text')
3280         self.assertEqual('the value', node.props['my-property'].value)
3281
3282     def testReplaceSome(self):
3283         """Test replacing the contents of a few entries"""
3284         entries, expected1, expected2, expected_text = (
3285             self._CheckReplaceMultiple(['u-boot2', 'text']))
3286
3287         # This one should not change
3288         data = entries['u-boot'].data
3289         self.assertEqual(U_BOOT_DATA, data)
3290
3291         data = entries['u-boot2'].data
3292         self.assertEqual(expected2, data)
3293
3294         data = entries['text'].data
3295         self.assertEqual(expected_text, data)
3296
3297     def testReplaceCmd(self):
3298         """Test replacing a file fron an image on the command line"""
3299         self._DoReadFileRealDtb('143_replace_all.dts')
3300
3301         try:
3302             tmpdir, updated_fname = self._SetupImageInTmpdir()
3303
3304             fname = os.path.join(tmpdir, 'update-u-boot.bin')
3305             expected = b'x' * len(U_BOOT_DATA)
3306             tools.WriteFile(fname, expected)
3307
3308             self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
3309             data = tools.ReadFile(updated_fname)
3310             self.assertEqual(expected, data[:len(expected)])
3311             map_fname = os.path.join(tmpdir, 'image-updated.map')
3312             self.assertFalse(os.path.exists(map_fname))
3313         finally:
3314             shutil.rmtree(tmpdir)
3315
3316     def testReplaceCmdSome(self):
3317         """Test replacing some files fron an image on the command line"""
3318         updated_fname, outdir, expected1, expected2, expected_text = (
3319             self._SetupForReplace())
3320
3321         self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3322                        'u-boot2', 'text')
3323
3324         tools.PrepareOutputDir(None)
3325         image = Image.FromFile(updated_fname)
3326         image.LoadData()
3327         entries = image.GetEntries()
3328
3329         # This one should not change
3330         data = entries['u-boot'].data
3331         self.assertEqual(U_BOOT_DATA, data)
3332
3333         data = entries['u-boot2'].data
3334         self.assertEqual(expected2, data)
3335
3336         data = entries['text'].data
3337         self.assertEqual(expected_text, data)
3338
3339     def testReplaceMissing(self):
3340         """Test replacing entries where the file is missing"""
3341         updated_fname, outdir, expected1, expected2, expected_text = (
3342             self._SetupForReplace())
3343
3344         # Remove one of the files, to generate a warning
3345         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3346         os.remove(u_boot_fname1)
3347
3348         with test_util.capture_sys_output() as (stdout, stderr):
3349             control.ReplaceEntries(updated_fname, None, outdir, [])
3350         self.assertIn("Skipping entry '/u-boot' from missing file",
3351                       stderr.getvalue())
3352
3353     def testReplaceCmdMap(self):
3354         """Test replacing a file fron an image on the command line"""
3355         self._DoReadFileRealDtb('143_replace_all.dts')
3356
3357         try:
3358             tmpdir, updated_fname = self._SetupImageInTmpdir()
3359
3360             fname = os.path.join(self._indir, 'update-u-boot.bin')
3361             expected = b'x' * len(U_BOOT_DATA)
3362             tools.WriteFile(fname, expected)
3363
3364             self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3365                            '-f', fname, '-m')
3366             map_fname = os.path.join(tmpdir, 'image-updated.map')
3367             self.assertTrue(os.path.exists(map_fname))
3368         finally:
3369             shutil.rmtree(tmpdir)
3370
3371     def testReplaceNoEntryPaths(self):
3372         """Test replacing an entry without an entry path"""
3373         self._DoReadFileRealDtb('143_replace_all.dts')
3374         image_fname = tools.GetOutputFilename('image.bin')
3375         with self.assertRaises(ValueError) as e:
3376             control.ReplaceEntries(image_fname, 'fname', None, [])
3377         self.assertIn('Must specify an entry path to read with -f',
3378                       str(e.exception))
3379
3380     def testReplaceTooManyEntryPaths(self):
3381         """Test extracting some entries"""
3382         self._DoReadFileRealDtb('143_replace_all.dts')
3383         image_fname = tools.GetOutputFilename('image.bin')
3384         with self.assertRaises(ValueError) as e:
3385             control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3386         self.assertIn('Must specify exactly one entry path to write with -f',
3387                       str(e.exception))
3388
3389     def testPackReset16(self):
3390         """Test that an image with an x86 reset16 region can be created"""
3391         data = self._DoReadFile('144_x86_reset16.dts')
3392         self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3393
3394     def testPackReset16Spl(self):
3395         """Test that an image with an x86 reset16-spl region can be created"""
3396         data = self._DoReadFile('145_x86_reset16_spl.dts')
3397         self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3398
3399     def testPackReset16Tpl(self):
3400         """Test that an image with an x86 reset16-tpl region can be created"""
3401         data = self._DoReadFile('146_x86_reset16_tpl.dts')
3402         self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3403
3404     def testPackIntelFit(self):
3405         """Test that an image with an Intel FIT and pointer can be created"""
3406         data = self._DoReadFile('147_intel_fit.dts')
3407         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3408         fit = data[16:32];
3409         self.assertEqual(b'_FIT_   \x01\x00\x00\x00\x00\x01\x80}' , fit)
3410         ptr = struct.unpack('<i', data[0x40:0x44])[0]
3411
3412         image = control.images['image']
3413         entries = image.GetEntries()
3414         expected_ptr = entries['intel-fit'].image_pos - (1 << 32)
3415         self.assertEqual(expected_ptr, ptr)
3416
3417     def testPackIntelFitMissing(self):
3418         """Test detection of a FIT pointer with not FIT region"""
3419         with self.assertRaises(ValueError) as e:
3420             self._DoReadFile('148_intel_fit_missing.dts')
3421         self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3422                       str(e.exception))
3423
3424     def _CheckSymbolsTplSection(self, dts, expected_vals):
3425         data = self._DoReadFile(dts)
3426         sym_values = struct.pack('<LQLL', *expected_vals)
3427         upto1 = 4 + len(U_BOOT_SPL_DATA)
3428         expected1 = tools.GetBytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[20:]
3429         self.assertEqual(expected1, data[:upto1])
3430
3431         upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
3432         expected2 = tools.GetBytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[20:]
3433         self.assertEqual(expected2, data[upto1:upto2])
3434
3435         upto3 = 0x34 + len(U_BOOT_DATA)
3436         expected3 = tools.GetBytes(0xff, 1) + U_BOOT_DATA
3437         self.assertEqual(expected3, data[upto2:upto3])
3438
3439         expected4 = sym_values + U_BOOT_TPL_DATA[20:]
3440         self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3441
3442     def testSymbolsTplSection(self):
3443         """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3444         self._SetupSplElf('u_boot_binman_syms')
3445         self._SetupTplElf('u_boot_binman_syms')
3446         self._CheckSymbolsTplSection('149_symbols_tpl.dts',
3447                                      [0x04, 0x1c, 0x10 + 0x34, 0x04])
3448
3449     def testSymbolsTplSectionX86(self):
3450         """Test binman can assign symbols in a section with end-at-4gb"""
3451         self._SetupSplElf('u_boot_binman_syms_x86')
3452         self._SetupTplElf('u_boot_binman_syms_x86')
3453         self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
3454                                      [0xffffff04, 0xffffff1c, 0xffffff34,
3455                                       0x04])
3456
3457     def testPackX86RomIfwiSectiom(self):
3458         """Test that a section can be placed in an IFWI region"""
3459         self._SetupIfwi('fitimage.bin')
3460         data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3461         self._CheckIfwi(data)
3462
3463     def testPackFspM(self):
3464         """Test that an image with a FSP memory-init binary can be created"""
3465         data = self._DoReadFile('152_intel_fsp_m.dts')
3466         self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3467
3468     def testPackFspS(self):
3469         """Test that an image with a FSP silicon-init binary can be created"""
3470         data = self._DoReadFile('153_intel_fsp_s.dts')
3471         self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
3472
3473     def testPackFspT(self):
3474         """Test that an image with a FSP temp-ram-init binary can be created"""
3475         data = self._DoReadFile('154_intel_fsp_t.dts')
3476         self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3477
3478     def testMkimage(self):
3479         """Test using mkimage to build an image"""
3480         data = self._DoReadFile('156_mkimage.dts')
3481
3482         # Just check that the data appears in the file somewhere
3483         self.assertIn(U_BOOT_SPL_DATA, data)
3484
3485     def testExtblob(self):
3486         """Test an image with an external blob"""
3487         data = self._DoReadFile('157_blob_ext.dts')
3488         self.assertEqual(REFCODE_DATA, data)
3489
3490     def testExtblobMissing(self):
3491         """Test an image with a missing external blob"""
3492         with self.assertRaises(ValueError) as e:
3493             self._DoReadFile('158_blob_ext_missing.dts')
3494         self.assertIn("Filename 'missing-file' not found in input path",
3495                       str(e.exception))
3496
3497     def testExtblobMissingOk(self):
3498         """Test an image with an missing external blob that is allowed"""
3499         with test_util.capture_sys_output() as (stdout, stderr):
3500             self._DoTestFile('158_blob_ext_missing.dts', allow_missing=True)
3501         err = stderr.getvalue()
3502         self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext")
3503
3504     def testExtblobMissingOkSect(self):
3505         """Test an image with an missing external blob that is allowed"""
3506         with test_util.capture_sys_output() as (stdout, stderr):
3507             self._DoTestFile('159_blob_ext_missing_sect.dts',
3508                              allow_missing=True)
3509         err = stderr.getvalue()
3510         self.assertRegex(err, "Image 'main-section'.*missing.*: "
3511                          "blob-ext blob-ext2")
3512
3513     def testPackX86RomMeMissingDesc(self):
3514         """Test that an missing Intel descriptor entry is allowed"""
3515         with test_util.capture_sys_output() as (stdout, stderr):
3516             self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
3517         err = stderr.getvalue()
3518         self.assertRegex(err,
3519                          "Image 'main-section'.*missing.*: intel-descriptor")
3520
3521     def testPackX86RomMissingIfwi(self):
3522         """Test that an x86 ROM with Integrated Firmware Image can be created"""
3523         self._SetupIfwi('fitimage.bin')
3524         pathname = os.path.join(self._indir, 'fitimage.bin')
3525         os.remove(pathname)
3526         with test_util.capture_sys_output() as (stdout, stderr):
3527             self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3528         err = stderr.getvalue()
3529         self.assertRegex(err, "Image 'main-section'.*missing.*: intel-ifwi")
3530
3531     def testPackOverlap(self):
3532         """Test that zero-size overlapping regions are ignored"""
3533         self._DoTestFile('160_pack_overlap_zero.dts')
3534
3535     def testSimpleFit(self):
3536         """Test an image with a FIT inside"""
3537         data = self._DoReadFile('161_fit.dts')
3538         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3539         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3540         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3541
3542         # The data should be inside the FIT
3543         dtb = fdt.Fdt.FromData(fit_data)
3544         dtb.Scan()
3545         fnode = dtb.GetNode('/images/kernel')
3546         self.assertIn('data', fnode.props)
3547
3548         fname = os.path.join(self._indir, 'fit_data.fit')
3549         tools.WriteFile(fname, fit_data)
3550         out = tools.Run('dumpimage', '-l', fname)
3551
3552         # Check a few features to make sure the plumbing works. We don't need
3553         # to test the operation of mkimage or dumpimage here. First convert the
3554         # output into a dict where the keys are the fields printed by dumpimage
3555         # and the values are a list of values for each field
3556         lines = out.splitlines()
3557
3558         # Converts "Compression:  gzip compressed" into two groups:
3559         # 'Compression' and 'gzip compressed'
3560         re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3561         vals = collections.defaultdict(list)
3562         for line in lines:
3563             mat = re_line.match(line)
3564             vals[mat.group(1)].append(mat.group(2))
3565
3566         self.assertEquals('FIT description: test-desc', lines[0])
3567         self.assertIn('Created:', lines[1])
3568         self.assertIn('Image 0 (kernel)', vals)
3569         self.assertIn('Hash value', vals)
3570         data_sizes = vals.get('Data Size')
3571         self.assertIsNotNone(data_sizes)
3572         self.assertEqual(2, len(data_sizes))
3573         # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
3574         self.assertEqual(len(U_BOOT_DATA), int(data_sizes[0].split()[0]))
3575         self.assertEqual(len(U_BOOT_SPL_DTB_DATA), int(data_sizes[1].split()[0]))
3576
3577     def testFitExternal(self):
3578         """Test an image with an FIT with external images"""
3579         data = self._DoReadFile('162_fit_external.dts')
3580         fit_data = data[len(U_BOOT_DATA):-2]  # _testing is 2 bytes
3581
3582         # The data should be outside the FIT
3583         dtb = fdt.Fdt.FromData(fit_data)
3584         dtb.Scan()
3585         fnode = dtb.GetNode('/images/kernel')
3586         self.assertNotIn('data', fnode.props)
3587
3588     def testSectionIgnoreHashSignature(self):
3589         """Test that sections ignore hash, signature nodes for its data"""
3590         data = self._DoReadFile('165_section_ignore_hash_signature.dts')
3591         expected = (U_BOOT_DATA + U_BOOT_DATA)
3592         self.assertEqual(expected, data)
3593
3594     def testPadInSections(self):
3595         """Test pad-before, pad-after for entries in sections"""
3596         data, _, _, out_dtb_fname = self._DoReadFileDtb(
3597             '166_pad_in_sections.dts', update_dtb=True)
3598         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
3599                     U_BOOT_DATA + tools.GetBytes(ord('!'), 6) +
3600                     U_BOOT_DATA)
3601         self.assertEqual(expected, data)
3602
3603         dtb = fdt.Fdt(out_dtb_fname)
3604         dtb.Scan()
3605         props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
3606         expected = {
3607             'image-pos': 0,
3608             'offset': 0,
3609             'size': 12 + 6 + 3 * len(U_BOOT_DATA),
3610
3611             'section:image-pos': 0,
3612             'section:offset': 0,
3613             'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
3614
3615             'section/before:image-pos': 0,
3616             'section/before:offset': 0,
3617             'section/before:size': len(U_BOOT_DATA),
3618
3619             'section/u-boot:image-pos': 4,
3620             'section/u-boot:offset': 4,
3621             'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
3622
3623             'section/after:image-pos': 26,
3624             'section/after:offset': 26,
3625             'section/after:size': len(U_BOOT_DATA),
3626             }
3627         self.assertEqual(expected, props)
3628
3629     def testFitImageSubentryAlignment(self):
3630         """Test relative alignability of FIT image subentries"""
3631         entry_args = {
3632             'test-id': TEXT_DATA,
3633         }
3634         data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
3635                                             entry_args=entry_args)
3636         dtb = fdt.Fdt.FromData(data)
3637         dtb.Scan()
3638
3639         node = dtb.GetNode('/images/kernel')
3640         data = dtb.GetProps(node)["data"].bytes
3641         align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
3642         expected = (tools.GetBytes(0, 0x20) + U_BOOT_SPL_DATA +
3643                     tools.GetBytes(0, align_pad) + U_BOOT_DATA)
3644         self.assertEqual(expected, data)
3645
3646         node = dtb.GetNode('/images/fdt-1')
3647         data = dtb.GetProps(node)["data"].bytes
3648         expected = (U_BOOT_SPL_DTB_DATA + tools.GetBytes(0, 20) +
3649                     tools.ToBytes(TEXT_DATA) + tools.GetBytes(0, 30) +
3650                     U_BOOT_DTB_DATA)
3651         self.assertEqual(expected, data)
3652
3653     def testFitExtblobMissingOk(self):
3654         """Test a FIT with a missing external blob that is allowed"""
3655         with test_util.capture_sys_output() as (stdout, stderr):
3656             self._DoTestFile('168_fit_missing_blob.dts',
3657                              allow_missing=True)
3658         err = stderr.getvalue()
3659         self.assertRegex(err, "Image 'main-section'.*missing.*: atf-bl31")
3660
3661     def testBlobNamedByArgMissing(self):
3662         """Test handling of a missing entry arg"""
3663         with self.assertRaises(ValueError) as e:
3664             self._DoReadFile('068_blob_named_by_arg.dts')
3665         self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
3666                       str(e.exception))
3667
3668     def testPackBl31(self):
3669         """Test that an image with an ATF BL31 binary can be created"""
3670         data = self._DoReadFile('169_atf_bl31.dts')
3671         self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
3672
3673     def testPackScp(self):
3674         """Test that an image with an SCP binary can be created"""
3675         data = self._DoReadFile('172_scp.dts')
3676         self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
3677
3678     def testFitFdt(self):
3679         """Test an image with an FIT with multiple FDT images"""
3680         def _CheckFdt(seq, expected_data):
3681             """Check the FDT nodes
3682
3683             Args:
3684                 seq: Sequence number to check (0 or 1)
3685                 expected_data: Expected contents of 'data' property
3686             """
3687             name = 'fdt-%d' % seq
3688             fnode = dtb.GetNode('/images/%s' % name)
3689             self.assertIsNotNone(fnode)
3690             self.assertEqual({'description','type', 'compression', 'data'},
3691                              set(fnode.props.keys()))
3692             self.assertEqual(expected_data, fnode.props['data'].bytes)
3693             self.assertEqual('fdt-test-fdt%d.dtb' % seq,
3694                              fnode.props['description'].value)
3695
3696         def _CheckConfig(seq, expected_data):
3697             """Check the configuration nodes
3698
3699             Args:
3700                 seq: Sequence number to check (0 or 1)
3701                 expected_data: Expected contents of 'data' property
3702             """
3703             cnode = dtb.GetNode('/configurations')
3704             self.assertIn('default', cnode.props)
3705             self.assertEqual('config-2', cnode.props['default'].value)
3706
3707             name = 'config-%d' % seq
3708             fnode = dtb.GetNode('/configurations/%s' % name)
3709             self.assertIsNotNone(fnode)
3710             self.assertEqual({'description','firmware', 'loadables', 'fdt'},
3711                              set(fnode.props.keys()))
3712             self.assertEqual('conf-test-fdt%d.dtb' % seq,
3713                              fnode.props['description'].value)
3714             self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value)
3715
3716         entry_args = {
3717             'of-list': 'test-fdt1 test-fdt2',
3718             'default-dt': 'test-fdt2',
3719         }
3720         data = self._DoReadFileDtb(
3721             '172_fit_fdt.dts',
3722             entry_args=entry_args,
3723             extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3724         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3725         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3726
3727         dtb = fdt.Fdt.FromData(fit_data)
3728         dtb.Scan()
3729         fnode = dtb.GetNode('/images/kernel')
3730         self.assertIn('data', fnode.props)
3731
3732         # Check all the properties in fdt-1 and fdt-2
3733         _CheckFdt(1, TEST_FDT1_DATA)
3734         _CheckFdt(2, TEST_FDT2_DATA)
3735
3736         # Check configurations
3737         _CheckConfig(1, TEST_FDT1_DATA)
3738         _CheckConfig(2, TEST_FDT2_DATA)
3739
3740     def testFitFdtMissingList(self):
3741         """Test handling of a missing 'of-list' entry arg"""
3742         with self.assertRaises(ValueError) as e:
3743             self._DoReadFile('172_fit_fdt.dts')
3744         self.assertIn("Generator node requires 'of-list' entry argument",
3745                       str(e.exception))
3746
3747     def testFitFdtEmptyList(self):
3748         """Test handling of an empty 'of-list' entry arg"""
3749         entry_args = {
3750             'of-list': '',
3751         }
3752         data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
3753
3754     def testFitFdtMissingProp(self):
3755         """Test handling of a missing 'fit,fdt-list' property"""
3756         with self.assertRaises(ValueError) as e:
3757             self._DoReadFile('171_fit_fdt_missing_prop.dts')
3758         self.assertIn("Generator node requires 'fit,fdt-list' property",
3759                       str(e.exception))
3760
3761     def testFitFdtEmptyList(self):
3762         """Test handling of an empty 'of-list' entry arg"""
3763         entry_args = {
3764             'of-list': '',
3765         }
3766         data = self._DoReadFileDtb('172_fit_fdt.dts', entry_args=entry_args)[0]
3767
3768     def testFitFdtMissing(self):
3769         """Test handling of a missing 'default-dt' entry arg"""
3770         entry_args = {
3771             'of-list': 'test-fdt1 test-fdt2',
3772         }
3773         with self.assertRaises(ValueError) as e:
3774             self._DoReadFileDtb(
3775                 '172_fit_fdt.dts',
3776                 entry_args=entry_args,
3777                 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3778         self.assertIn("Generated 'default' node requires default-dt entry argument",
3779                       str(e.exception))
3780
3781     def testFitFdtNotInList(self):
3782         """Test handling of a default-dt that is not in the of-list"""
3783         entry_args = {
3784             'of-list': 'test-fdt1 test-fdt2',
3785             'default-dt': 'test-fdt3',
3786         }
3787         with self.assertRaises(ValueError) as e:
3788             self._DoReadFileDtb(
3789                 '172_fit_fdt.dts',
3790                 entry_args=entry_args,
3791                 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3792         self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
3793                       str(e.exception))
3794
3795     def testFitExtblobMissingHelp(self):
3796         """Test display of help messages when an external blob is missing"""
3797         control.missing_blob_help = control._ReadMissingBlobHelp()
3798         control.missing_blob_help['wibble'] = 'Wibble test'
3799         control.missing_blob_help['another'] = 'Another test'
3800         with test_util.capture_sys_output() as (stdout, stderr):
3801             self._DoTestFile('168_fit_missing_blob.dts',
3802                              allow_missing=True)
3803         err = stderr.getvalue()
3804
3805         # We can get the tag from the name, the type or the missing-msg
3806         # property. Check all three.
3807         self.assertIn('You may need to build ARM Trusted', err)
3808         self.assertIn('Wibble test', err)
3809         self.assertIn('Another test', err)
3810
3811     def testMissingBlob(self):
3812         """Test handling of a blob containing a missing file"""
3813         with self.assertRaises(ValueError) as e:
3814             self._DoTestFile('173_missing_blob.dts', allow_missing=True)
3815         self.assertIn("Filename 'missing' not found in input path",
3816                       str(e.exception))
3817
3818     def testEnvironment(self):
3819         """Test adding a U-Boot environment"""
3820         data = self._DoReadFile('174_env.dts')
3821         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3822         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3823         env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3824         self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
3825                          env)
3826
3827     def testEnvironmentNoSize(self):
3828         """Test that a missing 'size' property is detected"""
3829         with self.assertRaises(ValueError) as e:
3830             self._DoTestFile('175_env_no_size.dts')
3831         self.assertIn("'u-boot-env' entry must have a size property",
3832                       str(e.exception))
3833
3834     def testEnvironmentTooSmall(self):
3835         """Test handling of an environment that does not fit"""
3836         with self.assertRaises(ValueError) as e:
3837             self._DoTestFile('176_env_too_small.dts')
3838
3839         # checksum, start byte, environment with \0 terminator, final \0
3840         need = 4 + 1 + len(ENV_DATA) + 1 + 1
3841         short = need - 0x8
3842         self.assertIn("too small to hold data (need %#x more bytes)" % short,
3843                       str(e.exception))
3844
3845     def testSkipAtStart(self):
3846         """Test handling of skip-at-start section"""
3847         data = self._DoReadFile('177_skip_at_start.dts')
3848         self.assertEqual(U_BOOT_DATA, data)
3849
3850         image = control.images['image']
3851         entries = image.GetEntries()
3852         section = entries['section']
3853         self.assertEqual(0, section.offset)
3854         self.assertEqual(len(U_BOOT_DATA), section.size)
3855         self.assertEqual(U_BOOT_DATA, section.GetData())
3856
3857         entry = section.GetEntries()['u-boot']
3858         self.assertEqual(16, entry.offset)
3859         self.assertEqual(len(U_BOOT_DATA), entry.size)
3860         self.assertEqual(U_BOOT_DATA, entry.data)
3861
3862     def testSkipAtStartPad(self):
3863         """Test handling of skip-at-start section with padded entry"""
3864         data = self._DoReadFile('178_skip_at_start_pad.dts')
3865         before = tools.GetBytes(0, 8)
3866         after = tools.GetBytes(0, 4)
3867         all = before + U_BOOT_DATA + after
3868         self.assertEqual(all, data)
3869
3870         image = control.images['image']
3871         entries = image.GetEntries()
3872         section = entries['section']
3873         self.assertEqual(0, section.offset)
3874         self.assertEqual(len(all), section.size)
3875         self.assertEqual(all, section.GetData())
3876
3877         entry = section.GetEntries()['u-boot']
3878         self.assertEqual(16, entry.offset)
3879         self.assertEqual(len(all), entry.size)
3880         self.assertEqual(U_BOOT_DATA, entry.data)
3881
3882     def testSkipAtStartSectionPad(self):
3883         """Test handling of skip-at-start section with padding"""
3884         data = self._DoReadFile('179_skip_at_start_section_pad.dts')
3885         before = tools.GetBytes(0, 8)
3886         after = tools.GetBytes(0, 4)
3887         all = before + U_BOOT_DATA + after
3888         self.assertEqual(all, data)
3889
3890         image = control.images['image']
3891         entries = image.GetEntries()
3892         section = entries['section']
3893         self.assertEqual(0, section.offset)
3894         self.assertEqual(len(all), section.size)
3895         self.assertEqual(U_BOOT_DATA, section.data)
3896         self.assertEqual(all, section.GetPaddedData())
3897
3898         entry = section.GetEntries()['u-boot']
3899         self.assertEqual(16, entry.offset)
3900         self.assertEqual(len(U_BOOT_DATA), entry.size)
3901         self.assertEqual(U_BOOT_DATA, entry.data)
3902
3903     def testSectionPad(self):
3904         """Testing padding with sections"""
3905         data = self._DoReadFile('180_section_pad.dts')
3906         expected = (tools.GetBytes(ord('&'), 3) +
3907                     tools.GetBytes(ord('!'), 5) +
3908                     U_BOOT_DATA +
3909                     tools.GetBytes(ord('!'), 1) +
3910                     tools.GetBytes(ord('&'), 2))
3911         self.assertEqual(expected, data)
3912
3913     def testSectionAlign(self):
3914         """Testing alignment with sections"""
3915         data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
3916         expected = (b'\0' +                         # fill section
3917                     tools.GetBytes(ord('&'), 1) +   # padding to section align
3918                     b'\0' +                         # fill section
3919                     tools.GetBytes(ord('!'), 3) +   # padding to u-boot align
3920                     U_BOOT_DATA +
3921                     tools.GetBytes(ord('!'), 4) +   # padding to u-boot size
3922                     tools.GetBytes(ord('!'), 4))    # padding to section size
3923         self.assertEqual(expected, data)
3924
3925
3926 if __name__ == "__main__":
3927     unittest.main()