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