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