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