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