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