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