]> git.ipfire.org Git - people/ms/u-boot.git/blob - tools/binman/ftest.py
dtoc: Allow DTC environment variable to provide path to dtc
[people/ms/u-boot.git] / tools / binman / ftest.py
1 #
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
4 #
5 # SPDX-License-Identifier: GPL-2.0+
6 #
7 # To run a single test, change to this directory, and:
8 #
9 # python -m unittest func_test.TestFunctional.testHelp
10
11 from optparse import OptionParser
12 import os
13 import shutil
14 import struct
15 import sys
16 import tempfile
17 import unittest
18
19 import binman
20 import cmdline
21 import command
22 import control
23 import elf
24 import fdt
25 import fdt_util
26 import tools
27 import tout
28
29 # Contents of test files, corresponding to different entry types
30 U_BOOT_DATA = '1234'
31 U_BOOT_IMG_DATA = 'img'
32 U_BOOT_SPL_DATA = '56780123456789abcde'
33 BLOB_DATA = '89'
34 ME_DATA = '0abcd'
35 VGA_DATA = 'vga'
36 U_BOOT_DTB_DATA = 'udtb'
37 U_BOOT_SPL_DTB_DATA = 'spldtb'
38 X86_START16_DATA = 'start16'
39 X86_START16_SPL_DATA = 'start16spl'
40 U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here'
41 U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here'
42 FSP_DATA = 'fsp'
43 CMC_DATA = 'cmc'
44 VBT_DATA = 'vbt'
45 MRC_DATA = 'mrc'
46
47 class TestFunctional(unittest.TestCase):
48 """Functional tests for binman
49
50 Most of these use a sample .dts file to build an image and then check
51 that it looks correct. The sample files are in the test/ subdirectory
52 and are numbered.
53
54 For each entry type a very small test file is created using fixed
55 string contents. This makes it easy to test that things look right, and
56 debug problems.
57
58 In some cases a 'real' file must be used - these are also supplied in
59 the test/ diurectory.
60 """
61 @classmethod
62 def setUpClass(self):
63 global entry
64 import entry
65
66 # Handle the case where argv[0] is 'python'
67 self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
68 self._binman_pathname = os.path.join(self._binman_dir, 'binman')
69
70 # Create a temporary directory for input files
71 self._indir = tempfile.mkdtemp(prefix='binmant.')
72
73 # Create some test files
74 TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
75 TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
76 TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
77 TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
78 TestFunctional._MakeInputFile('me.bin', ME_DATA)
79 TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
80 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
81 TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
82 TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
83 TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
84 X86_START16_SPL_DATA)
85 TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
86 TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
87 U_BOOT_SPL_NODTB_DATA)
88 TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
89 TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
90 TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
91 TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
92 self._output_setup = False
93
94 # ELF file with a '_dt_ucode_base_size' symbol
95 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
96 TestFunctional._MakeInputFile('u-boot', fd.read())
97
98 # Intel flash descriptor file
99 with open(self.TestFile('descriptor.bin')) as fd:
100 TestFunctional._MakeInputFile('descriptor.bin', fd.read())
101
102 @classmethod
103 def tearDownClass(self):
104 """Remove the temporary input directory and its contents"""
105 if self._indir:
106 shutil.rmtree(self._indir)
107 self._indir = None
108
109 def setUp(self):
110 # Enable this to turn on debugging output
111 # tout.Init(tout.DEBUG)
112 command.test_result = None
113
114 def tearDown(self):
115 """Remove the temporary output directory"""
116 tools._FinaliseForTest()
117
118 def _RunBinman(self, *args, **kwargs):
119 """Run binman using the command line
120
121 Args:
122 Arguments to pass, as a list of strings
123 kwargs: Arguments to pass to Command.RunPipe()
124 """
125 result = command.RunPipe([[self._binman_pathname] + list(args)],
126 capture=True, capture_stderr=True, raise_on_error=False)
127 if result.return_code and kwargs.get('raise_on_error', True):
128 raise Exception("Error running '%s': %s" % (' '.join(args),
129 result.stdout + result.stderr))
130 return result
131
132 def _DoBinman(self, *args):
133 """Run binman using directly (in the same process)
134
135 Args:
136 Arguments to pass, as a list of strings
137 Returns:
138 Return value (0 for success)
139 """
140 args = list(args)
141 if '-D' in sys.argv:
142 args = args + ['-D']
143 (options, args) = cmdline.ParseArgs(args)
144 options.pager = 'binman-invalid-pager'
145 options.build_dir = self._indir
146
147 # For testing, you can force an increase in verbosity here
148 # options.verbosity = tout.DEBUG
149 return control.Binman(options, args)
150
151 def _DoTestFile(self, fname, debug=False):
152 """Run binman with a given test file
153
154 Args:
155 fname: Device tree source filename to use (e.g. 05_simple.dts)
156 """
157 args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
158 if debug:
159 args.append('-D')
160 return self._DoBinman(*args)
161
162 def _SetupDtb(self, fname, outfile='u-boot.dtb'):
163 """Set up a new test device-tree file
164
165 The given file is compiled and set up as the device tree to be used
166 for ths test.
167
168 Args:
169 fname: Filename of .dts file to read
170 outfile: Output filename for compiled device tree binary
171
172 Returns:
173 Contents of device tree binary
174 """
175 if not self._output_setup:
176 tools.PrepareOutputDir(self._indir, True)
177 self._output_setup = True
178 dtb = fdt_util.EnsureCompiled(self.TestFile(fname))
179 with open(dtb) as fd:
180 data = fd.read()
181 TestFunctional._MakeInputFile(outfile, data)
182 return data
183
184 def _DoReadFileDtb(self, fname, use_real_dtb=False):
185 """Run binman and return the resulting image
186
187 This runs binman with a given test file and then reads the resulting
188 output file. It is a shortcut function since most tests need to do
189 these steps.
190
191 Raises an assertion failure if binman returns a non-zero exit code.
192
193 Args:
194 fname: Device tree source filename to use (e.g. 05_simple.dts)
195 use_real_dtb: True to use the test file as the contents of
196 the u-boot-dtb entry. Normally this is not needed and the
197 test contents (the U_BOOT_DTB_DATA string) can be used.
198 But in some test we need the real contents.
199
200 Returns:
201 Tuple:
202 Resulting image contents
203 Device tree contents
204 """
205 dtb_data = None
206 # Use the compiled test file as the u-boot-dtb input
207 if use_real_dtb:
208 dtb_data = self._SetupDtb(fname)
209
210 try:
211 retcode = self._DoTestFile(fname)
212 self.assertEqual(0, retcode)
213
214 # Find the (only) image, read it and return its contents
215 image = control.images['image']
216 fname = tools.GetOutputFilename('image.bin')
217 self.assertTrue(os.path.exists(fname))
218 with open(fname) as fd:
219 return fd.read(), dtb_data
220 finally:
221 # Put the test file back
222 if use_real_dtb:
223 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
224
225 def _DoReadFile(self, fname, use_real_dtb=False):
226 """Helper function which discards the device-tree binary"""
227 return self._DoReadFileDtb(fname, use_real_dtb)[0]
228
229 @classmethod
230 def _MakeInputFile(self, fname, contents):
231 """Create a new test input file, creating directories as needed
232
233 Args:
234 fname: Filenaem to create
235 contents: File contents to write in to the file
236 Returns:
237 Full pathname of file created
238 """
239 pathname = os.path.join(self._indir, fname)
240 dirname = os.path.dirname(pathname)
241 if dirname and not os.path.exists(dirname):
242 os.makedirs(dirname)
243 with open(pathname, 'wb') as fd:
244 fd.write(contents)
245 return pathname
246
247 @classmethod
248 def TestFile(self, fname):
249 return os.path.join(self._binman_dir, 'test', fname)
250
251 def AssertInList(self, grep_list, target):
252 """Assert that at least one of a list of things is in a target
253
254 Args:
255 grep_list: List of strings to check
256 target: Target string
257 """
258 for grep in grep_list:
259 if grep in target:
260 return
261 self.fail("Error: '%' not found in '%s'" % (grep_list, target))
262
263 def CheckNoGaps(self, entries):
264 """Check that all entries fit together without gaps
265
266 Args:
267 entries: List of entries to check
268 """
269 pos = 0
270 for entry in entries.values():
271 self.assertEqual(pos, entry.pos)
272 pos += entry.size
273
274 def GetFdtLen(self, dtb):
275 """Get the totalsize field from a device tree binary
276
277 Args:
278 dtb: Device tree binary contents
279
280 Returns:
281 Total size of device tree binary, from the header
282 """
283 return struct.unpack('>L', dtb[4:8])[0]
284
285 def testRun(self):
286 """Test a basic run with valid args"""
287 result = self._RunBinman('-h')
288
289 def testFullHelp(self):
290 """Test that the full help is displayed with -H"""
291 result = self._RunBinman('-H')
292 help_file = os.path.join(self._binman_dir, 'README')
293 self.assertEqual(len(result.stdout), os.path.getsize(help_file))
294 self.assertEqual(0, len(result.stderr))
295 self.assertEqual(0, result.return_code)
296
297 def testFullHelpInternal(self):
298 """Test that the full help is displayed with -H"""
299 try:
300 command.test_result = command.CommandResult()
301 result = self._DoBinman('-H')
302 help_file = os.path.join(self._binman_dir, 'README')
303 finally:
304 command.test_result = None
305
306 def testHelp(self):
307 """Test that the basic help is displayed with -h"""
308 result = self._RunBinman('-h')
309 self.assertTrue(len(result.stdout) > 200)
310 self.assertEqual(0, len(result.stderr))
311 self.assertEqual(0, result.return_code)
312
313 def testBoard(self):
314 """Test that we can run it with a specific board"""
315 self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb')
316 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
317 result = self._DoBinman('-b', 'sandbox')
318 self.assertEqual(0, result)
319
320 def testNeedBoard(self):
321 """Test that we get an error when no board ius supplied"""
322 with self.assertRaises(ValueError) as e:
323 result = self._DoBinman()
324 self.assertIn("Must provide a board to process (use -b <board>)",
325 str(e.exception))
326
327 def testMissingDt(self):
328 """Test that an invalid device tree file generates an error"""
329 with self.assertRaises(Exception) as e:
330 self._RunBinman('-d', 'missing_file')
331 # We get one error from libfdt, and a different one from fdtget.
332 self.AssertInList(["Couldn't open blob from 'missing_file'",
333 'No such file or directory'], str(e.exception))
334
335 def testBrokenDt(self):
336 """Test that an invalid device tree source file generates an error
337
338 Since this is a source file it should be compiled and the error
339 will come from the device-tree compiler (dtc).
340 """
341 with self.assertRaises(Exception) as e:
342 self._RunBinman('-d', self.TestFile('01_invalid.dts'))
343 self.assertIn("FATAL ERROR: Unable to parse input tree",
344 str(e.exception))
345
346 def testMissingNode(self):
347 """Test that a device tree without a 'binman' node generates an error"""
348 with self.assertRaises(Exception) as e:
349 self._DoBinman('-d', self.TestFile('02_missing_node.dts'))
350 self.assertIn("does not have a 'binman' node", str(e.exception))
351
352 def testEmpty(self):
353 """Test that an empty binman node works OK (i.e. does nothing)"""
354 result = self._RunBinman('-d', self.TestFile('03_empty.dts'))
355 self.assertEqual(0, len(result.stderr))
356 self.assertEqual(0, result.return_code)
357
358 def testInvalidEntry(self):
359 """Test that an invalid entry is flagged"""
360 with self.assertRaises(Exception) as e:
361 result = self._RunBinman('-d',
362 self.TestFile('04_invalid_entry.dts'))
363 #print e.exception
364 self.assertIn("Unknown entry type 'not-a-valid-type' in node "
365 "'/binman/not-a-valid-type'", str(e.exception))
366
367 def testSimple(self):
368 """Test a simple binman with a single file"""
369 data = self._DoReadFile('05_simple.dts')
370 self.assertEqual(U_BOOT_DATA, data)
371
372 def testSimpleDebug(self):
373 """Test a simple binman run with debugging enabled"""
374 data = self._DoTestFile('05_simple.dts', debug=True)
375
376 def testDual(self):
377 """Test that we can handle creating two images
378
379 This also tests image padding.
380 """
381 retcode = self._DoTestFile('06_dual_image.dts')
382 self.assertEqual(0, retcode)
383
384 image = control.images['image1']
385 self.assertEqual(len(U_BOOT_DATA), image._size)
386 fname = tools.GetOutputFilename('image1.bin')
387 self.assertTrue(os.path.exists(fname))
388 with open(fname) as fd:
389 data = fd.read()
390 self.assertEqual(U_BOOT_DATA, data)
391
392 image = control.images['image2']
393 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size)
394 fname = tools.GetOutputFilename('image2.bin')
395 self.assertTrue(os.path.exists(fname))
396 with open(fname) as fd:
397 data = fd.read()
398 self.assertEqual(U_BOOT_DATA, data[3:7])
399 self.assertEqual(chr(0) * 3, data[:3])
400 self.assertEqual(chr(0) * 5, data[7:])
401
402 def testBadAlign(self):
403 """Test that an invalid alignment value is detected"""
404 with self.assertRaises(ValueError) as e:
405 self._DoTestFile('07_bad_align.dts')
406 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
407 "of two", str(e.exception))
408
409 def testPackSimple(self):
410 """Test that packing works as expected"""
411 retcode = self._DoTestFile('08_pack.dts')
412 self.assertEqual(0, retcode)
413 self.assertIn('image', control.images)
414 image = control.images['image']
415 entries = image._entries
416 self.assertEqual(5, len(entries))
417
418 # First u-boot
419 self.assertIn('u-boot', entries)
420 entry = entries['u-boot']
421 self.assertEqual(0, entry.pos)
422 self.assertEqual(len(U_BOOT_DATA), entry.size)
423
424 # Second u-boot, aligned to 16-byte boundary
425 self.assertIn('u-boot-align', entries)
426 entry = entries['u-boot-align']
427 self.assertEqual(16, entry.pos)
428 self.assertEqual(len(U_BOOT_DATA), entry.size)
429
430 # Third u-boot, size 23 bytes
431 self.assertIn('u-boot-size', entries)
432 entry = entries['u-boot-size']
433 self.assertEqual(20, entry.pos)
434 self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
435 self.assertEqual(23, entry.size)
436
437 # Fourth u-boot, placed immediate after the above
438 self.assertIn('u-boot-next', entries)
439 entry = entries['u-boot-next']
440 self.assertEqual(43, entry.pos)
441 self.assertEqual(len(U_BOOT_DATA), entry.size)
442
443 # Fifth u-boot, placed at a fixed position
444 self.assertIn('u-boot-fixed', entries)
445 entry = entries['u-boot-fixed']
446 self.assertEqual(61, entry.pos)
447 self.assertEqual(len(U_BOOT_DATA), entry.size)
448
449 self.assertEqual(65, image._size)
450
451 def testPackExtra(self):
452 """Test that extra packing feature works as expected"""
453 retcode = self._DoTestFile('09_pack_extra.dts')
454
455 self.assertEqual(0, retcode)
456 self.assertIn('image', control.images)
457 image = control.images['image']
458 entries = image._entries
459 self.assertEqual(5, len(entries))
460
461 # First u-boot with padding before and after
462 self.assertIn('u-boot', entries)
463 entry = entries['u-boot']
464 self.assertEqual(0, entry.pos)
465 self.assertEqual(3, entry.pad_before)
466 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
467
468 # Second u-boot has an aligned size, but it has no effect
469 self.assertIn('u-boot-align-size-nop', entries)
470 entry = entries['u-boot-align-size-nop']
471 self.assertEqual(12, entry.pos)
472 self.assertEqual(4, entry.size)
473
474 # Third u-boot has an aligned size too
475 self.assertIn('u-boot-align-size', entries)
476 entry = entries['u-boot-align-size']
477 self.assertEqual(16, entry.pos)
478 self.assertEqual(32, entry.size)
479
480 # Fourth u-boot has an aligned end
481 self.assertIn('u-boot-align-end', entries)
482 entry = entries['u-boot-align-end']
483 self.assertEqual(48, entry.pos)
484 self.assertEqual(16, entry.size)
485
486 # Fifth u-boot immediately afterwards
487 self.assertIn('u-boot-align-both', entries)
488 entry = entries['u-boot-align-both']
489 self.assertEqual(64, entry.pos)
490 self.assertEqual(64, entry.size)
491
492 self.CheckNoGaps(entries)
493 self.assertEqual(128, image._size)
494
495 def testPackAlignPowerOf2(self):
496 """Test that invalid entry alignment is detected"""
497 with self.assertRaises(ValueError) as e:
498 self._DoTestFile('10_pack_align_power2.dts')
499 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
500 "of two", str(e.exception))
501
502 def testPackAlignSizePowerOf2(self):
503 """Test that invalid entry size alignment is detected"""
504 with self.assertRaises(ValueError) as e:
505 self._DoTestFile('11_pack_align_size_power2.dts')
506 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
507 "power of two", str(e.exception))
508
509 def testPackInvalidAlign(self):
510 """Test detection of an position that does not match its alignment"""
511 with self.assertRaises(ValueError) as e:
512 self._DoTestFile('12_pack_inv_align.dts')
513 self.assertIn("Node '/binman/u-boot': Position 0x5 (5) does not match "
514 "align 0x4 (4)", str(e.exception))
515
516 def testPackInvalidSizeAlign(self):
517 """Test that invalid entry size alignment is detected"""
518 with self.assertRaises(ValueError) as e:
519 self._DoTestFile('13_pack_inv_size_align.dts')
520 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
521 "align-size 0x4 (4)", str(e.exception))
522
523 def testPackOverlap(self):
524 """Test that overlapping regions are detected"""
525 with self.assertRaises(ValueError) as e:
526 self._DoTestFile('14_pack_overlap.dts')
527 self.assertIn("Node '/binman/u-boot-align': Position 0x3 (3) overlaps "
528 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
529 str(e.exception))
530
531 def testPackEntryOverflow(self):
532 """Test that entries that overflow their size are detected"""
533 with self.assertRaises(ValueError) as e:
534 self._DoTestFile('15_pack_overflow.dts')
535 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
536 "but entry size is 0x3 (3)", str(e.exception))
537
538 def testPackImageOverflow(self):
539 """Test that entries which overflow the image size are detected"""
540 with self.assertRaises(ValueError) as e:
541 self._DoTestFile('16_pack_image_overflow.dts')
542 self.assertIn("Image '/binman': contents size 0x4 (4) exceeds image "
543 "size 0x3 (3)", str(e.exception))
544
545 def testPackImageSize(self):
546 """Test that the image size can be set"""
547 retcode = self._DoTestFile('17_pack_image_size.dts')
548 self.assertEqual(0, retcode)
549 self.assertIn('image', control.images)
550 image = control.images['image']
551 self.assertEqual(7, image._size)
552
553 def testPackImageSizeAlign(self):
554 """Test that image size alignemnt works as expected"""
555 retcode = self._DoTestFile('18_pack_image_align.dts')
556 self.assertEqual(0, retcode)
557 self.assertIn('image', control.images)
558 image = control.images['image']
559 self.assertEqual(16, image._size)
560
561 def testPackInvalidImageAlign(self):
562 """Test that invalid image alignment is detected"""
563 with self.assertRaises(ValueError) as e:
564 self._DoTestFile('19_pack_inv_image_align.dts')
565 self.assertIn("Image '/binman': Size 0x7 (7) does not match "
566 "align-size 0x8 (8)", str(e.exception))
567
568 def testPackAlignPowerOf2(self):
569 """Test that invalid image alignment is detected"""
570 with self.assertRaises(ValueError) as e:
571 self._DoTestFile('20_pack_inv_image_align_power2.dts')
572 self.assertIn("Image '/binman': Alignment size 131 must be a power of "
573 "two", str(e.exception))
574
575 def testImagePadByte(self):
576 """Test that the image pad byte can be specified"""
577 with open(self.TestFile('bss_data')) as fd:
578 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
579 data = self._DoReadFile('21_image_pad.dts')
580 self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data)
581
582 def testImageName(self):
583 """Test that image files can be named"""
584 retcode = self._DoTestFile('22_image_name.dts')
585 self.assertEqual(0, retcode)
586 image = control.images['image1']
587 fname = tools.GetOutputFilename('test-name')
588 self.assertTrue(os.path.exists(fname))
589
590 image = control.images['image2']
591 fname = tools.GetOutputFilename('test-name.xx')
592 self.assertTrue(os.path.exists(fname))
593
594 def testBlobFilename(self):
595 """Test that generic blobs can be provided by filename"""
596 data = self._DoReadFile('23_blob.dts')
597 self.assertEqual(BLOB_DATA, data)
598
599 def testPackSorted(self):
600 """Test that entries can be sorted"""
601 data = self._DoReadFile('24_sorted.dts')
602 self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 +
603 U_BOOT_DATA, data)
604
605 def testPackZeroPosition(self):
606 """Test that an entry at position 0 is not given a new position"""
607 with self.assertRaises(ValueError) as e:
608 self._DoTestFile('25_pack_zero_size.dts')
609 self.assertIn("Node '/binman/u-boot-spl': Position 0x0 (0) overlaps "
610 "with previous entry '/binman/u-boot' ending at 0x4 (4)",
611 str(e.exception))
612
613 def testPackUbootDtb(self):
614 """Test that a device tree can be added to U-Boot"""
615 data = self._DoReadFile('26_pack_u_boot_dtb.dts')
616 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
617
618 def testPackX86RomNoSize(self):
619 """Test that the end-at-4gb property requires a size property"""
620 with self.assertRaises(ValueError) as e:
621 self._DoTestFile('27_pack_4gb_no_size.dts')
622 self.assertIn("Image '/binman': Image size must be provided when "
623 "using end-at-4gb", str(e.exception))
624
625 def testPackX86RomOutside(self):
626 """Test that the end-at-4gb property checks for position boundaries"""
627 with self.assertRaises(ValueError) as e:
628 self._DoTestFile('28_pack_4gb_outside.dts')
629 self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside "
630 "the image starting at 0xffffffe0 (4294967264)",
631 str(e.exception))
632
633 def testPackX86Rom(self):
634 """Test that a basic x86 ROM can be created"""
635 data = self._DoReadFile('29_x86-rom.dts')
636 self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA +
637 chr(0) * 2, data)
638
639 def testPackX86RomMeNoDesc(self):
640 """Test that an invalid Intel descriptor entry is detected"""
641 TestFunctional._MakeInputFile('descriptor.bin', '')
642 with self.assertRaises(ValueError) as e:
643 self._DoTestFile('31_x86-rom-me.dts')
644 self.assertIn("Node '/binman/intel-descriptor': Cannot find FD "
645 "signature", str(e.exception))
646
647 def testPackX86RomBadDesc(self):
648 """Test that the Intel requires a descriptor entry"""
649 with self.assertRaises(ValueError) as e:
650 self._DoTestFile('30_x86-rom-me-no-desc.dts')
651 self.assertIn("Node '/binman/intel-me': No position set with "
652 "pos-unset: should another entry provide this correct "
653 "position?", str(e.exception))
654
655 def testPackX86RomMe(self):
656 """Test that an x86 ROM with an ME region can be created"""
657 data = self._DoReadFile('31_x86-rom-me.dts')
658 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
659
660 def testPackVga(self):
661 """Test that an image with a VGA binary can be created"""
662 data = self._DoReadFile('32_intel-vga.dts')
663 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
664
665 def testPackStart16(self):
666 """Test that an image with an x86 start16 region can be created"""
667 data = self._DoReadFile('33_x86-start16.dts')
668 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
669
670 def _RunMicrocodeTest(self, dts_fname, nodtb_data):
671 data = self._DoReadFile(dts_fname, True)
672
673 # Now check the device tree has no microcode
674 second = data[len(nodtb_data):]
675 fname = tools.GetOutputFilename('test.dtb')
676 with open(fname, 'wb') as fd:
677 fd.write(second)
678 dtb = fdt.FdtScan(fname)
679 ucode = dtb.GetNode('/microcode')
680 self.assertTrue(ucode)
681 for node in ucode.subnodes:
682 self.assertFalse(node.props.get('data'))
683
684 fdt_len = self.GetFdtLen(second)
685 third = second[fdt_len:]
686
687 # Check that the microcode appears immediately after the Fdt
688 # This matches the concatenation of the data properties in
689 # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
690 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
691 0x78235609)
692 self.assertEqual(ucode_data, third[:len(ucode_data)])
693 ucode_pos = len(nodtb_data) + fdt_len
694
695 # Check that the microcode pointer was inserted. It should match the
696 # expected position and size
697 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
698 len(ucode_data))
699 first = data[:len(nodtb_data)]
700 return first, pos_and_size
701
702 def testPackUbootMicrocode(self):
703 """Test that x86 microcode can be handled correctly
704
705 We expect to see the following in the image, in order:
706 u-boot-nodtb.bin with a microcode pointer inserted at the correct
707 place
708 u-boot.dtb with the microcode removed
709 the microcode
710 """
711 first, pos_and_size = self._RunMicrocodeTest('34_x86_ucode.dts',
712 U_BOOT_NODTB_DATA)
713 self.assertEqual('nodtb with microcode' + pos_and_size +
714 ' somewhere in here', first)
715
716 def _RunPackUbootSingleMicrocode(self):
717 """Test that x86 microcode can be handled correctly
718
719 We expect to see the following in the image, in order:
720 u-boot-nodtb.bin with a microcode pointer inserted at the correct
721 place
722 u-boot.dtb with the microcode
723 an empty microcode region
724 """
725 # We need the libfdt library to run this test since only that allows
726 # finding the offset of a property. This is required by
727 # Entry_u_boot_dtb_with_ucode.ObtainContents().
728 data = self._DoReadFile('35_x86_single_ucode.dts', True)
729
730 second = data[len(U_BOOT_NODTB_DATA):]
731
732 fdt_len = self.GetFdtLen(second)
733 third = second[fdt_len:]
734 second = second[:fdt_len]
735
736 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
737 self.assertIn(ucode_data, second)
738 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
739
740 # Check that the microcode pointer was inserted. It should match the
741 # expected position and size
742 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
743 len(ucode_data))
744 first = data[:len(U_BOOT_NODTB_DATA)]
745 self.assertEqual('nodtb with microcode' + pos_and_size +
746 ' somewhere in here', first)
747
748 def testPackUbootSingleMicrocode(self):
749 """Test that x86 microcode can be handled correctly with fdt_normal.
750 """
751 self._RunPackUbootSingleMicrocode()
752
753 def testUBootImg(self):
754 """Test that u-boot.img can be put in a file"""
755 data = self._DoReadFile('36_u_boot_img.dts')
756 self.assertEqual(U_BOOT_IMG_DATA, data)
757
758 def testNoMicrocode(self):
759 """Test that a missing microcode region is detected"""
760 with self.assertRaises(ValueError) as e:
761 self._DoReadFile('37_x86_no_ucode.dts', True)
762 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
763 "node found in ", str(e.exception))
764
765 def testMicrocodeWithoutNode(self):
766 """Test that a missing u-boot-dtb-with-ucode node is detected"""
767 with self.assertRaises(ValueError) as e:
768 self._DoReadFile('38_x86_ucode_missing_node.dts', True)
769 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
770 "microcode region u-boot-dtb-with-ucode", str(e.exception))
771
772 def testMicrocodeWithoutNode2(self):
773 """Test that a missing u-boot-ucode node is detected"""
774 with self.assertRaises(ValueError) as e:
775 self._DoReadFile('39_x86_ucode_missing_node2.dts', True)
776 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
777 "microcode region u-boot-ucode", str(e.exception))
778
779 def testMicrocodeWithoutPtrInElf(self):
780 """Test that a U-Boot binary without the microcode symbol is detected"""
781 # ELF file without a '_dt_ucode_base_size' symbol
782 try:
783 with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
784 TestFunctional._MakeInputFile('u-boot', fd.read())
785
786 with self.assertRaises(ValueError) as e:
787 self._RunPackUbootSingleMicrocode()
788 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
789 "_dt_ucode_base_size symbol in u-boot", str(e.exception))
790
791 finally:
792 # Put the original file back
793 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
794 TestFunctional._MakeInputFile('u-boot', fd.read())
795
796 def testMicrocodeNotInImage(self):
797 """Test that microcode must be placed within the image"""
798 with self.assertRaises(ValueError) as e:
799 self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
800 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
801 "pointer _dt_ucode_base_size at fffffe14 is outside the "
802 "image ranging from 00000000 to 0000002e", str(e.exception))
803
804 def testWithoutMicrocode(self):
805 """Test that we can cope with an image without microcode (e.g. qemu)"""
806 with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
807 TestFunctional._MakeInputFile('u-boot', fd.read())
808 data, dtb = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
809
810 # Now check the device tree has no microcode
811 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
812 second = data[len(U_BOOT_NODTB_DATA):]
813
814 fdt_len = self.GetFdtLen(second)
815 self.assertEqual(dtb, second[:fdt_len])
816
817 used_len = len(U_BOOT_NODTB_DATA) + fdt_len
818 third = data[used_len:]
819 self.assertEqual(chr(0) * (0x200 - used_len), third)
820
821 def testUnknownPosSize(self):
822 """Test that microcode must be placed within the image"""
823 with self.assertRaises(ValueError) as e:
824 self._DoReadFile('41_unknown_pos_size.dts', True)
825 self.assertIn("Image '/binman': Unable to set pos/size for unknown "
826 "entry 'invalid-entry'", str(e.exception))
827
828 def testPackFsp(self):
829 """Test that an image with a FSP binary can be created"""
830 data = self._DoReadFile('42_intel-fsp.dts')
831 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
832
833 def testPackCmc(self):
834 """Test that an image with a CMC binary can be created"""
835 data = self._DoReadFile('43_intel-cmc.dts')
836 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
837
838 def testPackVbt(self):
839 """Test that an image with a VBT binary can be created"""
840 data = self._DoReadFile('46_intel-vbt.dts')
841 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
842
843 def testSplBssPad(self):
844 """Test that we can pad SPL's BSS with zeros"""
845 # ELF file with a '__bss_size' symbol
846 with open(self.TestFile('bss_data')) as fd:
847 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
848 data = self._DoReadFile('47_spl_bss_pad.dts')
849 self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data)
850
851 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
852 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
853 with self.assertRaises(ValueError) as e:
854 data = self._DoReadFile('47_spl_bss_pad.dts')
855 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
856 str(e.exception))
857
858 def testPackStart16Spl(self):
859 """Test that an image with an x86 start16 region can be created"""
860 data = self._DoReadFile('48_x86-start16-spl.dts')
861 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
862
863 def testPackUbootSplMicrocode(self):
864 """Test that x86 microcode can be handled correctly in SPL
865
866 We expect to see the following in the image, in order:
867 u-boot-spl-nodtb.bin with a microcode pointer inserted at the
868 correct place
869 u-boot.dtb with the microcode removed
870 the microcode
871 """
872 # ELF file with a '_dt_ucode_base_size' symbol
873 with open(self.TestFile('u_boot_ucode_ptr')) as fd:
874 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
875 first, pos_and_size = self._RunMicrocodeTest('49_x86_ucode_spl.dts',
876 U_BOOT_SPL_NODTB_DATA)
877 self.assertEqual('splnodtb with microc' + pos_and_size +
878 'ter somewhere in here', first)
879
880 def testPackMrc(self):
881 """Test that an image with an MRC binary can be created"""
882 data = self._DoReadFile('50_intel_mrc.dts')
883 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
884
885 def testSplDtb(self):
886 """Test that an image with spl/u-boot-spl.dtb can be created"""
887 data = self._DoReadFile('51_u_boot_spl_dtb.dts')
888 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
889
890 def testSplNoDtb(self):
891 """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
892 data = self._DoReadFile('52_u_boot_spl_nodtb.dts')
893 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
894
895 def testSymbols(self):
896 """Test binman can assign symbols embedded in U-Boot"""
897 elf_fname = self.TestFile('u_boot_binman_syms')
898 syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
899 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
900 self.assertEqual(syms['_binman_u_boot_spl_prop_pos'].address, addr)
901
902 with open(self.TestFile('u_boot_binman_syms')) as fd:
903 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
904 data = self._DoReadFile('53_symbols.dts')
905 sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20)
906 expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) +
907 U_BOOT_DATA +
908 sym_values + U_BOOT_SPL_DATA[16:])
909 self.assertEqual(expected, data)
910
911
912 if __name__ == "__main__":
913 unittest.main()