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 self.assertEqual(len(result.stdout), os.path.getsize(help_file)) 294 self.assertEqual(0, len(result.stderr)) 295 self.assertEqual(0, result.return_code) 296 297 def testFullHelpInternal(self): 298 """Test that the full help is displayed with -H""" 299 try: 300 command.test_result = command.CommandResult() 301 result = self._DoBinman('-H') 302 help_file = os.path.join(self._binman_dir, 'README') 303 finally: 304 command.test_result = None 305 306 def testHelp(self): 307 """Test that the basic help is displayed with -h""" 308 result = self._RunBinman('-h') 309 self.assertTrue(len(result.stdout) > 200) 310 self.assertEqual(0, len(result.stderr)) 311 self.assertEqual(0, result.return_code) 312 313 def testBoard(self): 314 """Test that we can run it with a specific board""" 315 self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb') 316 TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA) 317 result = self._DoBinman('-b', 'sandbox') 318 self.assertEqual(0, result) 319 320 def testNeedBoard(self): 321 """Test that we get an error when no board ius supplied""" 322 with self.assertRaises(ValueError) as e: 323 result = self._DoBinman() 324 self.assertIn("Must provide a board to process (use -b <board>)", 325 str(e.exception)) 326 327 def testMissingDt(self): 328 """Test that an invalid device tree file generates an error""" 329 with self.assertRaises(Exception) as e: 330 self._RunBinman('-d', 'missing_file') 331 # We get one error from libfdt, and a different one from fdtget. 332 self.AssertInList(["Couldn't open blob from 'missing_file'", 333 'No such file or directory'], str(e.exception)) 334 335 def testBrokenDt(self): 336 """Test that an invalid device tree source file generates an error 337 338 Since this is a source file it should be compiled and the error 339 will come from the device-tree compiler (dtc). 340 """ 341 with self.assertRaises(Exception) as e: 342 self._RunBinman('-d', self.TestFile('01_invalid.dts')) 343 self.assertIn("FATAL ERROR: Unable to parse input tree", 344 str(e.exception)) 345 346 def testMissingNode(self): 347 """Test that a device tree without a 'binman' node generates an error""" 348 with self.assertRaises(Exception) as e: 349 self._DoBinman('-d', self.TestFile('02_missing_node.dts')) 350 self.assertIn("does not have a 'binman' node", str(e.exception)) 351 352 def testEmpty(self): 353 """Test that an empty binman node works OK (i.e. does nothing)""" 354 result = self._RunBinman('-d', self.TestFile('03_empty.dts')) 355 self.assertEqual(0, len(result.stderr)) 356 self.assertEqual(0, result.return_code) 357 358 def testInvalidEntry(self): 359 """Test that an invalid entry is flagged""" 360 with self.assertRaises(Exception) as e: 361 result = self._RunBinman('-d', 362 self.TestFile('04_invalid_entry.dts')) 363 #print e.exception 364 self.assertIn("Unknown entry type 'not-a-valid-type' in node " 365 "'/binman/not-a-valid-type'", str(e.exception)) 366 367 def testSimple(self): 368 """Test a simple binman with a single file""" 369 data = self._DoReadFile('05_simple.dts') 370 self.assertEqual(U_BOOT_DATA, data) 371 372 def testSimpleDebug(self): 373 """Test a simple binman run with debugging enabled""" 374 data = self._DoTestFile('05_simple.dts', debug=True) 375 376 def testDual(self): 377 """Test that we can handle creating two images 378 379 This also tests image padding. 380 """ 381 retcode = self._DoTestFile('06_dual_image.dts') 382 self.assertEqual(0, retcode) 383 384 image = control.images['image1'] 385 self.assertEqual(len(U_BOOT_DATA), image._size) 386 fname = tools.GetOutputFilename('image1.bin') 387 self.assertTrue(os.path.exists(fname)) 388 with open(fname) as fd: 389 data = fd.read() 390 self.assertEqual(U_BOOT_DATA, data) 391 392 image = control.images['image2'] 393 self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size) 394 fname = tools.GetOutputFilename('image2.bin') 395 self.assertTrue(os.path.exists(fname)) 396 with open(fname) as fd: 397 data = fd.read() 398 self.assertEqual(U_BOOT_DATA, data[3:7]) 399 self.assertEqual(chr(0) * 3, data[:3]) 400 self.assertEqual(chr(0) * 5, data[7:]) 401 402 def testBadAlign(self): 403 """Test that an invalid alignment value is detected""" 404 with self.assertRaises(ValueError) as e: 405 self._DoTestFile('07_bad_align.dts') 406 self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power " 407 "of two", str(e.exception)) 408 409 def testPackSimple(self): 410 """Test that packing works as expected""" 411 retcode = self._DoTestFile('08_pack.dts') 412 self.assertEqual(0, retcode) 413 self.assertIn('image', control.images) 414 image = control.images['image'] 415 entries = image._entries 416 self.assertEqual(5, len(entries)) 417 418 # First u-boot 419 self.assertIn('u-boot', entries) 420 entry = entries['u-boot'] 421 self.assertEqual(0, entry.pos) 422 self.assertEqual(len(U_BOOT_DATA), entry.size) 423 424 # Second u-boot, aligned to 16-byte boundary 425 self.assertIn('u-boot-align', entries) 426 entry = entries['u-boot-align'] 427 self.assertEqual(16, entry.pos) 428 self.assertEqual(len(U_BOOT_DATA), entry.size) 429 430 # Third u-boot, size 23 bytes 431 self.assertIn('u-boot-size', entries) 432 entry = entries['u-boot-size'] 433 self.assertEqual(20, entry.pos) 434 self.assertEqual(len(U_BOOT_DATA), entry.contents_size) 435 self.assertEqual(23, entry.size) 436 437 # Fourth u-boot, placed immediate after the above 438 self.assertIn('u-boot-next', entries) 439 entry = entries['u-boot-next'] 440 self.assertEqual(43, entry.pos) 441 self.assertEqual(len(U_BOOT_DATA), entry.size) 442 443 # Fifth u-boot, placed at a fixed position 444 self.assertIn('u-boot-fixed', entries) 445 entry = entries['u-boot-fixed'] 446 self.assertEqual(61, entry.pos) 447 self.assertEqual(len(U_BOOT_DATA), entry.size) 448 449 self.assertEqual(65, image._size) 450 451 def testPackExtra(self): 452 """Test that extra packing feature works as expected""" 453 retcode = self._DoTestFile('09_pack_extra.dts') 454 455 self.assertEqual(0, retcode) 456 self.assertIn('image', control.images) 457 image = control.images['image'] 458 entries = image._entries 459 self.assertEqual(5, len(entries)) 460 461 # First u-boot with padding before and after 462 self.assertIn('u-boot', entries) 463 entry = entries['u-boot'] 464 self.assertEqual(0, entry.pos) 465 self.assertEqual(3, entry.pad_before) 466 self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size) 467 468 # Second u-boot has an aligned size, but it has no effect 469 self.assertIn('u-boot-align-size-nop', entries) 470 entry = entries['u-boot-align-size-nop'] 471 self.assertEqual(12, entry.pos) 472 self.assertEqual(4, entry.size) 473 474 # Third u-boot has an aligned size too 475 self.assertIn('u-boot-align-size', entries) 476 entry = entries['u-boot-align-size'] 477 self.assertEqual(16, entry.pos) 478 self.assertEqual(32, entry.size) 479 480 # Fourth u-boot has an aligned end 481 self.assertIn('u-boot-align-end', entries) 482 entry = entries['u-boot-align-end'] 483 self.assertEqual(48, entry.pos) 484 self.assertEqual(16, entry.size) 485 486 # Fifth u-boot immediately afterwards 487 self.assertIn('u-boot-align-both', entries) 488 entry = entries['u-boot-align-both'] 489 self.assertEqual(64, entry.pos) 490 self.assertEqual(64, entry.size) 491 492 self.CheckNoGaps(entries) 493 self.assertEqual(128, image._size) 494 495 def testPackAlignPowerOf2(self): 496 """Test that invalid entry alignment is detected""" 497 with self.assertRaises(ValueError) as e: 498 self._DoTestFile('10_pack_align_power2.dts') 499 self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power " 500 "of two", str(e.exception)) 501 502 def testPackAlignSizePowerOf2(self): 503 """Test that invalid entry size alignment is detected""" 504 with self.assertRaises(ValueError) as e: 505 self._DoTestFile('11_pack_align_size_power2.dts') 506 self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a " 507 "power of two", str(e.exception)) 508 509 def testPackInvalidAlign(self): 510 """Test detection of an position that does not match its alignment""" 511 with self.assertRaises(ValueError) as e: 512 self._DoTestFile('12_pack_inv_align.dts') 513 self.assertIn("Node '/binman/u-boot': Position 0x5 (5) does not match " 514 "align 0x4 (4)", str(e.exception)) 515 516 def testPackInvalidSizeAlign(self): 517 """Test that invalid entry size alignment is detected""" 518 with self.assertRaises(ValueError) as e: 519 self._DoTestFile('13_pack_inv_size_align.dts') 520 self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match " 521 "align-size 0x4 (4)", str(e.exception)) 522 523 def testPackOverlap(self): 524 """Test that overlapping regions are detected""" 525 with self.assertRaises(ValueError) as e: 526 self._DoTestFile('14_pack_overlap.dts') 527 self.assertIn("Node '/binman/u-boot-align': Position 0x3 (3) overlaps " 528 "with previous entry '/binman/u-boot' ending at 0x4 (4)", 529 str(e.exception)) 530 531 def testPackEntryOverflow(self): 532 """Test that entries that overflow their size are detected""" 533 with self.assertRaises(ValueError) as e: 534 self._DoTestFile('15_pack_overflow.dts') 535 self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) " 536 "but entry size is 0x3 (3)", str(e.exception)) 537 538 def testPackImageOverflow(self): 539 """Test that entries which overflow the image size are detected""" 540 with self.assertRaises(ValueError) as e: 541 self._DoTestFile('16_pack_image_overflow.dts') 542 self.assertIn("Image '/binman': contents size 0x4 (4) exceeds image " 543 "size 0x3 (3)", str(e.exception)) 544 545 def testPackImageSize(self): 546 """Test that the image size can be set""" 547 retcode = self._DoTestFile('17_pack_image_size.dts') 548 self.assertEqual(0, retcode) 549 self.assertIn('image', control.images) 550 image = control.images['image'] 551 self.assertEqual(7, image._size) 552 553 def testPackImageSizeAlign(self): 554 """Test that image size alignemnt works as expected""" 555 retcode = self._DoTestFile('18_pack_image_align.dts') 556 self.assertEqual(0, retcode) 557 self.assertIn('image', control.images) 558 image = control.images['image'] 559 self.assertEqual(16, image._size) 560 561 def testPackInvalidImageAlign(self): 562 """Test that invalid image alignment is detected""" 563 with self.assertRaises(ValueError) as e: 564 self._DoTestFile('19_pack_inv_image_align.dts') 565 self.assertIn("Image '/binman': Size 0x7 (7) does not match " 566 "align-size 0x8 (8)", str(e.exception)) 567 568 def testPackAlignPowerOf2(self): 569 """Test that invalid image alignment is detected""" 570 with self.assertRaises(ValueError) as e: 571 self._DoTestFile('20_pack_inv_image_align_power2.dts') 572 self.assertIn("Image '/binman': Alignment size 131 must be a power of " 573 "two", str(e.exception)) 574 575 def testImagePadByte(self): 576 """Test that the image pad byte can be specified""" 577 with open(self.TestFile('bss_data')) as fd: 578 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) 579 data = self._DoReadFile('21_image_pad.dts') 580 self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data) 581 582 def testImageName(self): 583 """Test that image files can be named""" 584 retcode = self._DoTestFile('22_image_name.dts') 585 self.assertEqual(0, retcode) 586 image = control.images['image1'] 587 fname = tools.GetOutputFilename('test-name') 588 self.assertTrue(os.path.exists(fname)) 589 590 image = control.images['image2'] 591 fname = tools.GetOutputFilename('test-name.xx') 592 self.assertTrue(os.path.exists(fname)) 593 594 def testBlobFilename(self): 595 """Test that generic blobs can be provided by filename""" 596 data = self._DoReadFile('23_blob.dts') 597 self.assertEqual(BLOB_DATA, data) 598 599 def testPackSorted(self): 600 """Test that entries can be sorted""" 601 data = self._DoReadFile('24_sorted.dts') 602 self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 + 603 U_BOOT_DATA, data) 604 605 def testPackZeroPosition(self): 606 """Test that an entry at position 0 is not given a new position""" 607 with self.assertRaises(ValueError) as e: 608 self._DoTestFile('25_pack_zero_size.dts') 609 self.assertIn("Node '/binman/u-boot-spl': Position 0x0 (0) overlaps " 610 "with previous entry '/binman/u-boot' ending at 0x4 (4)", 611 str(e.exception)) 612 613 def testPackUbootDtb(self): 614 """Test that a device tree can be added to U-Boot""" 615 data = self._DoReadFile('26_pack_u_boot_dtb.dts') 616 self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data) 617 618 def testPackX86RomNoSize(self): 619 """Test that the end-at-4gb property requires a size property""" 620 with self.assertRaises(ValueError) as e: 621 self._DoTestFile('27_pack_4gb_no_size.dts') 622 self.assertIn("Image '/binman': Image size must be provided when " 623 "using end-at-4gb", str(e.exception)) 624 625 def testPackX86RomOutside(self): 626 """Test that the end-at-4gb property checks for position boundaries""" 627 with self.assertRaises(ValueError) as e: 628 self._DoTestFile('28_pack_4gb_outside.dts') 629 self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside " 630 "the image starting at 0xffffffe0 (4294967264)", 631 str(e.exception)) 632 633 def testPackX86Rom(self): 634 """Test that a basic x86 ROM can be created""" 635 data = self._DoReadFile('29_x86-rom.dts') 636 self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA + 637 chr(0) * 2, data) 638 639 def testPackX86RomMeNoDesc(self): 640 """Test that an invalid Intel descriptor entry is detected""" 641 TestFunctional._MakeInputFile('descriptor.bin', '') 642 with self.assertRaises(ValueError) as e: 643 self._DoTestFile('31_x86-rom-me.dts') 644 self.assertIn("Node '/binman/intel-descriptor': Cannot find FD " 645 "signature", str(e.exception)) 646 647 def testPackX86RomBadDesc(self): 648 """Test that the Intel requires a descriptor entry""" 649 with self.assertRaises(ValueError) as e: 650 self._DoTestFile('30_x86-rom-me-no-desc.dts') 651 self.assertIn("Node '/binman/intel-me': No position set with " 652 "pos-unset: should another entry provide this correct " 653 "position?", str(e.exception)) 654 655 def testPackX86RomMe(self): 656 """Test that an x86 ROM with an ME region can be created""" 657 data = self._DoReadFile('31_x86-rom-me.dts') 658 self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)]) 659 660 def testPackVga(self): 661 """Test that an image with a VGA binary can be created""" 662 data = self._DoReadFile('32_intel-vga.dts') 663 self.assertEqual(VGA_DATA, data[:len(VGA_DATA)]) 664 665 def testPackStart16(self): 666 """Test that an image with an x86 start16 region can be created""" 667 data = self._DoReadFile('33_x86-start16.dts') 668 self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)]) 669 670 def _RunMicrocodeTest(self, dts_fname, nodtb_data): 671 data = self._DoReadFile(dts_fname, True) 672 673 # Now check the device tree has no microcode 674 second = data[len(nodtb_data):] 675 fname = tools.GetOutputFilename('test.dtb') 676 with open(fname, 'wb') as fd: 677 fd.write(second) 678 dtb = fdt.FdtScan(fname) 679 ucode = dtb.GetNode('/microcode') 680 self.assertTrue(ucode) 681 for node in ucode.subnodes: 682 self.assertFalse(node.props.get('data')) 683 684 fdt_len = self.GetFdtLen(second) 685 third = second[fdt_len:] 686 687 # Check that the microcode appears immediately after the Fdt 688 # This matches the concatenation of the data properties in 689 # the /microcode/update@xxx nodes in 34_x86_ucode.dts. 690 ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000, 691 0x78235609) 692 self.assertEqual(ucode_data, third[:len(ucode_data)]) 693 ucode_pos = len(nodtb_data) + fdt_len 694 695 # Check that the microcode pointer was inserted. It should match the 696 # expected position and size 697 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, 698 len(ucode_data)) 699 first = data[:len(nodtb_data)] 700 return first, pos_and_size 701 702 def testPackUbootMicrocode(self): 703 """Test that x86 microcode can be handled correctly 704 705 We expect to see the following in the image, in order: 706 u-boot-nodtb.bin with a microcode pointer inserted at the correct 707 place 708 u-boot.dtb with the microcode removed 709 the microcode 710 """ 711 first, pos_and_size = self._RunMicrocodeTest('34_x86_ucode.dts', 712 U_BOOT_NODTB_DATA) 713 self.assertEqual('nodtb with microcode' + pos_and_size + 714 ' somewhere in here', first) 715 716 def _RunPackUbootSingleMicrocode(self): 717 """Test that x86 microcode can be handled correctly 718 719 We expect to see the following in the image, in order: 720 u-boot-nodtb.bin with a microcode pointer inserted at the correct 721 place 722 u-boot.dtb with the microcode 723 an empty microcode region 724 """ 725 # We need the libfdt library to run this test since only that allows 726 # finding the offset of a property. This is required by 727 # Entry_u_boot_dtb_with_ucode.ObtainContents(). 728 data = self._DoReadFile('35_x86_single_ucode.dts', True) 729 730 second = data[len(U_BOOT_NODTB_DATA):] 731 732 fdt_len = self.GetFdtLen(second) 733 third = second[fdt_len:] 734 second = second[:fdt_len] 735 736 ucode_data = struct.pack('>2L', 0x12345678, 0x12345679) 737 self.assertIn(ucode_data, second) 738 ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA) 739 740 # Check that the microcode pointer was inserted. It should match the 741 # expected position and size 742 pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, 743 len(ucode_data)) 744 first = data[:len(U_BOOT_NODTB_DATA)] 745 self.assertEqual('nodtb with microcode' + pos_and_size + 746 ' somewhere in here', first) 747 748 def testPackUbootSingleMicrocode(self): 749 """Test that x86 microcode can be handled correctly with fdt_normal. 750 """ 751 self._RunPackUbootSingleMicrocode() 752 753 def testUBootImg(self): 754 """Test that u-boot.img can be put in a file""" 755 data = self._DoReadFile('36_u_boot_img.dts') 756 self.assertEqual(U_BOOT_IMG_DATA, data) 757 758 def testNoMicrocode(self): 759 """Test that a missing microcode region is detected""" 760 with self.assertRaises(ValueError) as e: 761 self._DoReadFile('37_x86_no_ucode.dts', True) 762 self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode " 763 "node found in ", str(e.exception)) 764 765 def testMicrocodeWithoutNode(self): 766 """Test that a missing u-boot-dtb-with-ucode node is detected""" 767 with self.assertRaises(ValueError) as e: 768 self._DoReadFile('38_x86_ucode_missing_node.dts', True) 769 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find " 770 "microcode region u-boot-dtb-with-ucode", str(e.exception)) 771 772 def testMicrocodeWithoutNode2(self): 773 """Test that a missing u-boot-ucode node is detected""" 774 with self.assertRaises(ValueError) as e: 775 self._DoReadFile('39_x86_ucode_missing_node2.dts', True) 776 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find " 777 "microcode region u-boot-ucode", str(e.exception)) 778 779 def testMicrocodeWithoutPtrInElf(self): 780 """Test that a U-Boot binary without the microcode symbol is detected""" 781 # ELF file without a '_dt_ucode_base_size' symbol 782 try: 783 with open(self.TestFile('u_boot_no_ucode_ptr')) as fd: 784 TestFunctional._MakeInputFile('u-boot', fd.read()) 785 786 with self.assertRaises(ValueError) as e: 787 self._RunPackUbootSingleMicrocode() 788 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate " 789 "_dt_ucode_base_size symbol in u-boot", str(e.exception)) 790 791 finally: 792 # Put the original file back 793 with open(self.TestFile('u_boot_ucode_ptr')) as fd: 794 TestFunctional._MakeInputFile('u-boot', fd.read()) 795 796 def testMicrocodeNotInImage(self): 797 """Test that microcode must be placed within the image""" 798 with self.assertRaises(ValueError) as e: 799 self._DoReadFile('40_x86_ucode_not_in_image.dts', True) 800 self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode " 801 "pointer _dt_ucode_base_size at fffffe14 is outside the " 802 "image ranging from 00000000 to 0000002e", str(e.exception)) 803 804 def testWithoutMicrocode(self): 805 """Test that we can cope with an image without microcode (e.g. qemu)""" 806 with open(self.TestFile('u_boot_no_ucode_ptr')) as fd: 807 TestFunctional._MakeInputFile('u-boot', fd.read()) 808 data, dtb = self._DoReadFileDtb('44_x86_optional_ucode.dts', True) 809 810 # Now check the device tree has no microcode 811 self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)]) 812 second = data[len(U_BOOT_NODTB_DATA):] 813 814 fdt_len = self.GetFdtLen(second) 815 self.assertEqual(dtb, second[:fdt_len]) 816 817 used_len = len(U_BOOT_NODTB_DATA) + fdt_len 818 third = data[used_len:] 819 self.assertEqual(chr(0) * (0x200 - used_len), third) 820 821 def testUnknownPosSize(self): 822 """Test that microcode must be placed within the image""" 823 with self.assertRaises(ValueError) as e: 824 self._DoReadFile('41_unknown_pos_size.dts', True) 825 self.assertIn("Image '/binman': Unable to set pos/size for unknown " 826 "entry 'invalid-entry'", str(e.exception)) 827 828 def testPackFsp(self): 829 """Test that an image with a FSP binary can be created""" 830 data = self._DoReadFile('42_intel-fsp.dts') 831 self.assertEqual(FSP_DATA, data[:len(FSP_DATA)]) 832 833 def testPackCmc(self): 834 """Test that an image with a CMC binary can be created""" 835 data = self._DoReadFile('43_intel-cmc.dts') 836 self.assertEqual(CMC_DATA, data[:len(CMC_DATA)]) 837 838 def testPackVbt(self): 839 """Test that an image with a VBT binary can be created""" 840 data = self._DoReadFile('46_intel-vbt.dts') 841 self.assertEqual(VBT_DATA, data[:len(VBT_DATA)]) 842 843 def testSplBssPad(self): 844 """Test that we can pad SPL's BSS with zeros""" 845 # ELF file with a '__bss_size' symbol 846 with open(self.TestFile('bss_data')) as fd: 847 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) 848 data = self._DoReadFile('47_spl_bss_pad.dts') 849 self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data) 850 851 with open(self.TestFile('u_boot_ucode_ptr')) as fd: 852 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) 853 with self.assertRaises(ValueError) as e: 854 data = self._DoReadFile('47_spl_bss_pad.dts') 855 self.assertIn('Expected __bss_size symbol in spl/u-boot-spl', 856 str(e.exception)) 857 858 def testPackStart16Spl(self): 859 """Test that an image with an x86 start16 region can be created""" 860 data = self._DoReadFile('48_x86-start16-spl.dts') 861 self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)]) 862 863 def testPackUbootSplMicrocode(self): 864 """Test that x86 microcode can be handled correctly in SPL 865 866 We expect to see the following in the image, in order: 867 u-boot-spl-nodtb.bin with a microcode pointer inserted at the 868 correct place 869 u-boot.dtb with the microcode removed 870 the microcode 871 """ 872 # ELF file with a '_dt_ucode_base_size' symbol 873 with open(self.TestFile('u_boot_ucode_ptr')) as fd: 874 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) 875 first, pos_and_size = self._RunMicrocodeTest('49_x86_ucode_spl.dts', 876 U_BOOT_SPL_NODTB_DATA) 877 self.assertEqual('splnodtb with microc' + pos_and_size + 878 'ter somewhere in here', first) 879 880 def testPackMrc(self): 881 """Test that an image with an MRC binary can be created""" 882 data = self._DoReadFile('50_intel_mrc.dts') 883 self.assertEqual(MRC_DATA, data[:len(MRC_DATA)]) 884 885 def testSplDtb(self): 886 """Test that an image with spl/u-boot-spl.dtb can be created""" 887 data = self._DoReadFile('51_u_boot_spl_dtb.dts') 888 self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)]) 889 890 def testSplNoDtb(self): 891 """Test that an image with spl/u-boot-spl-nodtb.bin can be created""" 892 data = self._DoReadFile('52_u_boot_spl_nodtb.dts') 893 self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)]) 894 895 def testSymbols(self): 896 """Test binman can assign symbols embedded in U-Boot""" 897 elf_fname = self.TestFile('u_boot_binman_syms') 898 syms = elf.GetSymbols(elf_fname, ['binman', 'image']) 899 addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start') 900 self.assertEqual(syms['_binman_u_boot_spl_prop_pos'].address, addr) 901 902 with open(self.TestFile('u_boot_binman_syms')) as fd: 903 TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) 904 data = self._DoReadFile('53_symbols.dts') 905 sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20) 906 expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) + 907 U_BOOT_DATA + 908 sym_values + U_BOOT_SPL_DATA[16:]) 909 self.assertEqual(expected, data) 910 911 912if __name__ == "__main__": 913 unittest.main() 914