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