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