1# 2# Copyright OpenEmbedded Contributors 3# 4# SPDX-License-Identifier: MIT 5# 6 7from oeqa.selftest.case import OESelftestTestCase 8from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars 9import os 10import re 11 12class FitImageTests(OESelftestTestCase): 13 14 def _setup_uboot_tools_native(self): 15 """build u-boot-tools-native and return ${RECIPE_SYSROOT_NATIVE}/${bindir}""" 16 bitbake("u-boot-tools-native -c addto_recipe_sysroot") 17 vars = get_bb_vars(['RECIPE_SYSROOT_NATIVE', 'bindir'], 'u-boot-tools-native') 18 return os.path.join(vars['RECIPE_SYSROOT_NATIVE'], vars['bindir']) 19 20 def _run_dumpimage(self, fitimage_path, uboot_tools_bindir): 21 dumpimage_path = os.path.join(uboot_tools_bindir, 'dumpimage') 22 return runCmd('%s -l %s' % (dumpimage_path, fitimage_path)) 23 24 def _verify_fit_image_signature(self, uboot_tools_bindir, fitimage_path, dtb_path, conf_name=None): 25 """Verify the signature of a fit contfiguration 26 27 The fit_check_sign utility from u-boot-tools-native is called. 28 uboot-fit_check_sign -f fitImage -k $dtb_name -c conf-$dtb_name 29 """ 30 fit_check_sign_path = os.path.join(uboot_tools_bindir, 'uboot-fit_check_sign') 31 cmd = '%s -f %s -k %s' % (fit_check_sign_path, fitimage_path, dtb_path) 32 if conf_name: 33 cmd += ' -c %s' % conf_name 34 result = runCmd(cmd) 35 self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) 36 self.assertIn("Signature check OK", result.output) 37 38 @staticmethod 39 def _find_string_in_bin_file(file_path, search_string): 40 """find stings in a binary file 41 42 Shell equivalent: strings "$1" | grep "$2" | wc -l 43 return number of matches 44 """ 45 found_positions = 0 46 with open(file_path, 'rb') as file: 47 byte = file.read(1) 48 current_position = 0 49 current_match = 0 50 while byte: 51 char = byte.decode('ascii', errors='ignore') 52 if char == search_string[current_match]: 53 current_match += 1 54 if current_match == len(search_string): 55 found_positions += 1 56 current_match = 0 57 else: 58 current_match = 0 59 current_position += 1 60 byte = file.read(1) 61 return found_positions 62 63 def _config_add_uboot_env(self, config): 64 """Generate an u-boot environment 65 66 Create a boot.cmd file that is packed into the FitImage as a source-able text file. 67 """ 68 fit_uenv_file = "boot.cmd" 69 test_files_dir = "test-files" 70 fit_uenv_path = os.path.join(self.builddir, test_files_dir, fit_uenv_file) 71 72 config += '# Add an u-boot script to the fitImage' + os.linesep 73 config += 'FIT_UBOOT_ENV = "%s"' % fit_uenv_file + os.linesep 74 config += 'FILESEXTRAPATHS:prepend := "${TOPDIR}/%s:"' % test_files_dir + os.linesep 75 config += 'SRC_URI:append:pn-linux-yocto = " file://${FIT_UBOOT_ENV}"' + os.linesep 76 77 if not os.path.isdir(test_files_dir): 78 os.mkdir(test_files_dir) 79 self.logger.debug("Writing to: %s" % fit_uenv_path) 80 with open(fit_uenv_path, "w") as f: 81 f.write('echo "hello world"') 82 83 return config 84 85 def _verify_fitimage_uboot_env(self, dumpimage_result): 86 """Check if the boot.cmd script is part of the fitImage""" 87 num_scr_images = len(re.findall(r"^ *Image +[0-9]+ +\(bootscr-boot\.cmd\)$", dumpimage_result.output, re.MULTILINE)) 88 self.assertEqual(1, num_scr_images, msg="Expected exactly 1 bootscr-boot.cmd image section in the fitImage") 89 90 def test_fit_image(self): 91 """ 92 Summary: Check if FIT image and Image Tree Source (its) are built 93 and the Image Tree Source has the correct fields. 94 Expected: 1. fitImage and fitImage-its can be built 95 2. The type, load address, entrypoint address and 96 default values of kernel and ramdisk are as expected 97 in the Image Tree Source. Not all the fields are tested, 98 only the key fields that wont vary between different 99 architectures. 100 Product: oe-core 101 Author: Usama Arif <usama.arif@arm.com> 102 """ 103 config = """ 104# Enable creation of fitImage 105KERNEL_IMAGETYPE = "Image" 106KERNEL_IMAGETYPES += " fitImage " 107KERNEL_CLASSES = " kernel-fitimage " 108 109# RAM disk variables including load address and entrypoint for kernel and RAM disk 110IMAGE_FSTYPES += "cpio.gz" 111INITRAMFS_IMAGE = "core-image-minimal" 112# core-image-minimal is used as initramfs here, drop the rootfs suffix 113IMAGE_NAME_SUFFIX:pn-core-image-minimal = "" 114UBOOT_RD_LOADADDRESS = "0x88000000" 115UBOOT_RD_ENTRYPOINT = "0x88000000" 116UBOOT_LOADADDRESS = "0x80080000" 117UBOOT_ENTRYPOINT = "0x80080000" 118FIT_DESC = "A model description" 119""" 120 config = self._config_add_uboot_env(config) 121 self.write_config(config) 122 123 # fitImage is created as part of linux recipe 124 image = "virtual/kernel" 125 bitbake(image) 126 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'INITRAMFS_IMAGE_NAME', 'KERNEL_FIT_LINK_NAME'], image) 127 128 fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], 129 "fitImage-its-%s-%s" % (bb_vars['INITRAMFS_IMAGE_NAME'], bb_vars['KERNEL_FIT_LINK_NAME'])) 130 fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], 131 "fitImage-%s-%s" % (bb_vars['INITRAMFS_IMAGE_NAME'], bb_vars['KERNEL_FIT_LINK_NAME'])) 132 133 self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) 134 self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) 135 136 # Check that the type, load address, entrypoint address and default 137 # values for kernel and ramdisk in Image Tree Source are as expected. 138 # The order of fields in the below array is important. Not all the 139 # fields are tested, only the key fields that wont vary between 140 # different architectures. 141 its_field_check = [ 142 'description = "A model description";', 143 'type = "kernel";', 144 'load = <0x80080000>;', 145 'entry = <0x80080000>;', 146 'type = "ramdisk";', 147 'load = <0x88000000>;', 148 'entry = <0x88000000>;', 149 'default = "conf-1";', 150 'kernel = "kernel-1";', 151 'ramdisk = "ramdisk-1";' 152 ] 153 154 with open(fitimage_its_path) as its_file: 155 field_index = 0 156 for line in its_file: 157 if field_index == len(its_field_check): 158 break 159 if its_field_check[field_index] in line: 160 field_index +=1 161 162 if field_index != len(its_field_check): # if its equal, the test passed 163 self.assertTrue(field_index == len(its_field_check), 164 "Fields in Image Tree Source File %s did not match, error in finding %s" 165 % (fitimage_its_path, its_field_check[field_index])) 166 167 uboot_tools_bindir = self._setup_uboot_tools_native() 168 dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) 169 self._verify_fitimage_uboot_env(dumpimage_result) 170 171 172 def test_sign_fit_image(self): 173 """ 174 Summary: Check if FIT image and Image Tree Source (its) are created 175 and signed correctly. 176 Expected: 1) its and FIT image are built successfully 177 2) Scanning the its file indicates signing is enabled 178 as requested by UBOOT_SIGN_ENABLE (using keys generated 179 via FIT_GENERATE_KEYS) 180 3) Dumping the FIT image indicates signature values 181 are present (including for images as enabled via 182 FIT_SIGN_INDIVIDUAL) 183 4) Examination of the do_assemble_fitimage runfile/logfile 184 indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN and 185 UBOOT_MKIMAGE_SIGN_ARGS are working as expected. 186 Product: oe-core 187 Author: Paul Eggleton <paul.eggleton@microsoft.com> based upon 188 work by Usama Arif <usama.arif@arm.com> 189 """ 190 a_comment = "a smart comment" 191 config = """ 192# Enable creation of fitImage 193MACHINE = "beaglebone-yocto" 194KERNEL_IMAGETYPES += " fitImage " 195KERNEL_CLASSES = " kernel-fitimage " 196UBOOT_SIGN_ENABLE = "1" 197FIT_GENERATE_KEYS = "1" 198UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 199UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" 200UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" 201FIT_SIGN_INDIVIDUAL = "1" 202UBOOT_MKIMAGE_SIGN_ARGS = "-c '%s'" 203""" % a_comment 204 205 config = self._config_add_uboot_env(config) 206 self.write_config(config) 207 208 # fitImage is created as part of linux recipe 209 image = "virtual/kernel" 210 bitbake(image) 211 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'KERNEL_FIT_LINK_NAME'], image) 212 213 fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], 214 "fitImage-its-%s" % (bb_vars['KERNEL_FIT_LINK_NAME'])) 215 fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], 216 "fitImage-%s.bin" % (bb_vars['KERNEL_FIT_LINK_NAME'])) 217 218 self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) 219 self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) 220 221 req_itspaths = [ 222 ['/', 'images', 'kernel-1'], 223 ['/', 'images', 'kernel-1', 'signature-1'], 224 ['/', 'images', 'fdt-am335x-boneblack.dtb'], 225 ['/', 'images', 'fdt-am335x-boneblack.dtb', 'signature-1'], 226 ['/', 'configurations', 'conf-am335x-boneblack.dtb'], 227 ['/', 'configurations', 'conf-am335x-boneblack.dtb', 'signature-1'], 228 ] 229 230 itspath = [] 231 itspaths = [] 232 linect = 0 233 sigs = {} 234 with open(fitimage_its_path) as its_file: 235 linect += 1 236 for line in its_file: 237 line = line.strip() 238 if line.endswith('};'): 239 itspath.pop() 240 elif line.endswith('{'): 241 itspath.append(line[:-1].strip()) 242 itspaths.append(itspath[:]) 243 elif itspath and itspath[-1] == 'signature-1': 244 itsdotpath = '.'.join(itspath) 245 if not itsdotpath in sigs: 246 sigs[itsdotpath] = {} 247 if not '=' in line or not line.endswith(';'): 248 self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line)) 249 key, value = line.split('=', 1) 250 sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') 251 252 for reqpath in req_itspaths: 253 if not reqpath in itspaths: 254 self.fail('Missing section in its file: %s' % reqpath) 255 256 reqsigvalues_image = { 257 'algo': '"sha256,rsa2048"', 258 'key-name-hint': '"img-oe-selftest"', 259 } 260 reqsigvalues_config = { 261 'algo': '"sha256,rsa2048"', 262 'key-name-hint': '"cfg-oe-selftest"', 263 'sign-images': '"kernel", "fdt", "bootscr"', 264 } 265 266 for itspath, values in sigs.items(): 267 if 'conf-' in itspath: 268 reqsigvalues = reqsigvalues_config 269 else: 270 reqsigvalues = reqsigvalues_image 271 for reqkey, reqvalue in reqsigvalues.items(): 272 value = values.get(reqkey, None) 273 if value is None: 274 self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath)) 275 self.assertEqual(value, reqvalue) 276 277 # Dump the image to see if it really got signed 278 uboot_tools_bindir = self._setup_uboot_tools_native() 279 dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) 280 in_signed = None 281 signed_sections = {} 282 for line in dumpimage_result.output.splitlines(): 283 if line.startswith((' Configuration', ' Image')): 284 in_signed = re.search(r'\((.*)\)', line).groups()[0] 285 elif re.match('^ *', line) in (' ', ''): 286 in_signed = None 287 elif in_signed: 288 if not in_signed in signed_sections: 289 signed_sections[in_signed] = {} 290 key, value = line.split(':', 1) 291 signed_sections[in_signed][key.strip()] = value.strip() 292 self.assertIn('kernel-1', signed_sections) 293 self.assertIn('fdt-am335x-boneblack.dtb', signed_sections) 294 self.assertIn('conf-am335x-boneblack.dtb', signed_sections) 295 for signed_section, values in signed_sections.items(): 296 value = values.get('Sign algo', None) 297 if signed_section.startswith("conf"): 298 self.assertEqual(value, 'sha256,rsa2048:cfg-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) 299 else: 300 self.assertEqual(value, 'sha256,rsa2048:img-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) 301 value = values.get('Sign value', None) 302 self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section) 303 304 # Check if the u-boot boot.scr script is in the fitImage 305 self._verify_fitimage_uboot_env(dumpimage_result) 306 307 # Search for the string passed to mkimage: 1 kernel + 3 DTBs + config per DTB = 7 sections 308 # Looks like mkimage supports to add a comment but does not support to read it back. 309 found_comments = FitImageTests._find_string_in_bin_file(fitimage_path, a_comment) 310 self.assertEqual(found_comments, 8, "Expected 8 signed and commented section in the fitImage.") 311 312 # Verify the signature for all configurations = DTBs 313 for dtb in ['am335x-bone.dtb', 'am335x-boneblack.dtb', 'am335x-bonegreen.dtb']: 314 self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, 315 os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], dtb), 'conf-' + dtb) 316 317 def test_uboot_fit_image(self): 318 """ 319 Summary: Check if Uboot FIT image and Image Tree Source 320 (its) are built and the Image Tree Source has the 321 correct fields. 322 Expected: 1. u-boot-fitImage and u-boot-its can be built 323 2. The type, load address, entrypoint address and 324 default values of U-boot image are correct in the 325 Image Tree Source. Not all the fields are tested, 326 only the key fields that wont vary between 327 different architectures. 328 Product: oe-core 329 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> 330 based on work by Usama Arif <usama.arif@arm.com> 331 """ 332 config = """ 333# We need at least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set 334MACHINE = "qemuarm" 335UBOOT_MACHINE = "am57xx_evm_defconfig" 336SPL_BINARY = "MLO" 337 338# Enable creation of the U-Boot fitImage 339UBOOT_FITIMAGE_ENABLE = "1" 340 341# (U-boot) fitImage properties 342UBOOT_LOADADDRESS = "0x80080000" 343UBOOT_ENTRYPOINT = "0x80080000" 344UBOOT_FIT_DESC = "A model description" 345 346# Enable creation of Kernel fitImage 347KERNEL_IMAGETYPES += " fitImage " 348KERNEL_CLASSES = " kernel-fitimage" 349UBOOT_SIGN_ENABLE = "1" 350FIT_GENERATE_KEYS = "1" 351UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 352UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" 353UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" 354FIT_SIGN_INDIVIDUAL = "1" 355""" 356 self.write_config(config) 357 358 # The U-Boot fitImage is created as part of the U-Boot recipe 359 bitbake("virtual/bootloader") 360 361 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') 362 machine = get_bb_var('MACHINE') 363 fitimage_its_path = os.path.join(deploy_dir_image, 364 "u-boot-its-%s" % (machine,)) 365 fitimage_path = os.path.join(deploy_dir_image, 366 "u-boot-fitImage-%s" % (machine,)) 367 368 self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) 369 self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) 370 371 # Check that the type, load address, entrypoint address and default 372 # values for kernel and ramdisk in Image Tree Source are as expected. 373 # The order of fields in the below array is important. Not all the 374 # fields are tested, only the key fields that wont vary between 375 # different architectures. 376 its_field_check = [ 377 'description = "A model description";', 378 'type = "standalone";', 379 'load = <0x80080000>;', 380 'entry = <0x80080000>;', 381 'default = "conf";', 382 'loadables = "uboot";', 383 'fdt = "fdt";' 384 ] 385 386 with open(fitimage_its_path) as its_file: 387 field_index = 0 388 for line in its_file: 389 if field_index == len(its_field_check): 390 break 391 if its_field_check[field_index] in line: 392 field_index +=1 393 394 if field_index != len(its_field_check): # if its equal, the test passed 395 self.assertTrue(field_index == len(its_field_check), 396 "Fields in Image Tree Source File %s did not match, error in finding %s" 397 % (fitimage_its_path, its_field_check[field_index])) 398 399 def test_uboot_sign_fit_image(self): 400 """ 401 Summary: Check if Uboot FIT image and Image Tree Source 402 (its) are built and the Image Tree Source has the 403 correct fields, in the scenario where the Kernel 404 is also creating/signing it's fitImage. 405 Expected: 1. u-boot-fitImage and u-boot-its can be built 406 2. The type, load address, entrypoint address and 407 default values of U-boot image are correct in the 408 Image Tree Source. Not all the fields are tested, 409 only the key fields that wont vary between 410 different architectures. 411 Product: oe-core 412 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> 413 based on work by Usama Arif <usama.arif@arm.com> 414 """ 415 config = """ 416# We need at least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set 417MACHINE = "qemuarm" 418UBOOT_MACHINE = "am57xx_evm_defconfig" 419SPL_BINARY = "MLO" 420 421# Enable creation of the U-Boot fitImage 422UBOOT_FITIMAGE_ENABLE = "1" 423 424# (U-boot) fitImage properties 425UBOOT_LOADADDRESS = "0x80080000" 426UBOOT_ENTRYPOINT = "0x80080000" 427UBOOT_FIT_DESC = "A model description" 428KERNEL_IMAGETYPES += " fitImage " 429KERNEL_CLASSES = " kernel-fitimage " 430UBOOT_SIGN_ENABLE = "1" 431FIT_GENERATE_KEYS = "1" 432UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 433UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" 434UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" 435FIT_SIGN_INDIVIDUAL = "1" 436UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'" 437""" 438 self.write_config(config) 439 440 # The U-Boot fitImage is created as part of the U-Boot recipe 441 bitbake("virtual/bootloader") 442 443 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') 444 machine = get_bb_var('MACHINE') 445 fitimage_its_path = os.path.join(deploy_dir_image, 446 "u-boot-its-%s" % (machine,)) 447 fitimage_path = os.path.join(deploy_dir_image, 448 "u-boot-fitImage-%s" % (machine,)) 449 450 self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) 451 self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) 452 453 # Check that the type, load address, entrypoint address and default 454 # values for kernel and ramdisk in Image Tree Source are as expected. 455 # The order of fields in the below array is important. Not all the 456 # fields are tested, only the key fields that wont vary between 457 # different architectures. 458 its_field_check = [ 459 'description = "A model description";', 460 'type = "standalone";', 461 'load = <0x80080000>;', 462 'entry = <0x80080000>;', 463 'default = "conf";', 464 'loadables = "uboot";', 465 'fdt = "fdt";' 466 ] 467 468 with open(fitimage_its_path) as its_file: 469 field_index = 0 470 for line in its_file: 471 if field_index == len(its_field_check): 472 break 473 if its_field_check[field_index] in line: 474 field_index +=1 475 476 if field_index != len(its_field_check): # if its equal, the test passed 477 self.assertTrue(field_index == len(its_field_check), 478 "Fields in Image Tree Source File %s did not match, error in finding %s" 479 % (fitimage_its_path, its_field_check[field_index])) 480 481 482 def test_sign_standalone_uboot_fit_image(self): 483 """ 484 Summary: Check if U-Boot FIT image and Image Tree Source (its) are 485 created and signed correctly for the scenario where only 486 the U-Boot proper fitImage is being created and signed. 487 Expected: 1) U-Boot its and FIT image are built successfully 488 2) Scanning the its file indicates signing is enabled 489 as requested by SPL_SIGN_ENABLE (using keys generated 490 via UBOOT_FIT_GENERATE_KEYS) 491 3) Dumping the FIT image indicates signature values 492 are present 493 4) Examination of the do_uboot_assemble_fitimage 494 runfile/logfile indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN 495 and SPL_MKIMAGE_SIGN_ARGS are working as expected. 496 Product: oe-core 497 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> based upon 498 work by Paul Eggleton <paul.eggleton@microsoft.com> and 499 Usama Arif <usama.arif@arm.com> 500 """ 501 a_comment = "a smart U-Boot comment" 502 config = """ 503# There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at 504# least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set 505MACHINE = "qemuarm" 506UBOOT_MACHINE = "am57xx_evm_defconfig" 507SPL_BINARY = "MLO" 508# The kernel-fitimage class is a dependency even if we're only 509# creating/signing the U-Boot fitImage 510KERNEL_CLASSES = " kernel-fitimage" 511# Enable creation and signing of the U-Boot fitImage 512UBOOT_FITIMAGE_ENABLE = "1" 513SPL_SIGN_ENABLE = "1" 514SPL_SIGN_KEYNAME = "spl-oe-selftest" 515SPL_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 516UBOOT_DTB_BINARY = "u-boot.dtb" 517UBOOT_ENTRYPOINT = "0x80000000" 518UBOOT_LOADADDRESS = "0x80000000" 519UBOOT_DTB_LOADADDRESS = "0x82000000" 520UBOOT_ARCH = "arm" 521SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 522SPL_MKIMAGE_SIGN_ARGS = "-c '%s'" 523UBOOT_EXTLINUX = "0" 524UBOOT_FIT_GENERATE_KEYS = "1" 525UBOOT_FIT_HASH_ALG = "sha256" 526""" % a_comment 527 528 self.write_config(config) 529 530 # The U-Boot fitImage is created as part of the U-Boot recipe 531 bitbake("virtual/bootloader") 532 533 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') 534 machine = get_bb_var('MACHINE') 535 fitimage_its_path = os.path.join(deploy_dir_image, 536 "u-boot-its-%s" % (machine,)) 537 fitimage_path = os.path.join(deploy_dir_image, 538 "u-boot-fitImage-%s" % (machine,)) 539 540 self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) 541 self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) 542 543 req_itspaths = [ 544 ['/', 'images', 'uboot'], 545 ['/', 'images', 'uboot', 'signature'], 546 ['/', 'images', 'fdt'], 547 ['/', 'images', 'fdt', 'signature'], 548 ] 549 550 itspath = [] 551 itspaths = [] 552 linect = 0 553 sigs = {} 554 with open(fitimage_its_path) as its_file: 555 linect += 1 556 for line in its_file: 557 line = line.strip() 558 if line.endswith('};'): 559 itspath.pop() 560 elif line.endswith('{'): 561 itspath.append(line[:-1].strip()) 562 itspaths.append(itspath[:]) 563 elif itspath and itspath[-1] == 'signature': 564 itsdotpath = '.'.join(itspath) 565 if not itsdotpath in sigs: 566 sigs[itsdotpath] = {} 567 if not '=' in line or not line.endswith(';'): 568 self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line)) 569 key, value = line.split('=', 1) 570 sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') 571 572 for reqpath in req_itspaths: 573 if not reqpath in itspaths: 574 self.fail('Missing section in its file: %s' % reqpath) 575 576 reqsigvalues_image = { 577 'algo': '"sha256,rsa2048"', 578 'key-name-hint': '"spl-oe-selftest"', 579 } 580 581 for itspath, values in sigs.items(): 582 reqsigvalues = reqsigvalues_image 583 for reqkey, reqvalue in reqsigvalues.items(): 584 value = values.get(reqkey, None) 585 if value is None: 586 self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath)) 587 self.assertEqual(value, reqvalue) 588 589 # Dump the image to see if it really got signed 590 uboot_tools_bindir = self._setup_uboot_tools_native() 591 dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) 592 in_signed = None 593 signed_sections = {} 594 for line in dumpimage_result.output.splitlines(): 595 if line.startswith((' Image')): 596 in_signed = re.search(r'\((.*)\)', line).groups()[0] 597 elif re.match(' \w', line): 598 in_signed = None 599 elif in_signed: 600 if not in_signed in signed_sections: 601 signed_sections[in_signed] = {} 602 key, value = line.split(':', 1) 603 signed_sections[in_signed][key.strip()] = value.strip() 604 self.assertIn('uboot', signed_sections) 605 self.assertIn('fdt', signed_sections) 606 for signed_section, values in signed_sections.items(): 607 value = values.get('Sign algo', None) 608 self.assertEqual(value, 'sha256,rsa2048:spl-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) 609 value = values.get('Sign value', None) 610 self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section) 611 612 # Check for SPL_MKIMAGE_SIGN_ARGS 613 # Looks like mkimage supports to add a comment but does not support to read it back. 614 found_comments = FitImageTests._find_string_in_bin_file(fitimage_path, a_comment) 615 self.assertEqual(found_comments, 2, "Expected 2 signed and commented section in the fitImage.") 616 617 # Verify the signature 618 self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, 619 os.path.join(deploy_dir_image, 'u-boot-spl.dtb')) 620 621 622 def test_sign_cascaded_uboot_fit_image(self): 623 """ 624 Summary: Check if U-Boot FIT image and Image Tree Source (its) are 625 created and signed correctly for the scenario where both 626 U-Boot proper and Kernel fitImages are being created and 627 signed. 628 Expected: 1) U-Boot its and FIT image are built successfully 629 2) Scanning the its file indicates signing is enabled 630 as requested by SPL_SIGN_ENABLE (using keys generated 631 via UBOOT_FIT_GENERATE_KEYS) 632 3) Dumping the FIT image indicates signature values 633 are present 634 4) Examination of the do_uboot_assemble_fitimage 635 runfile/logfile indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN 636 and SPL_MKIMAGE_SIGN_ARGS are working as expected. 637 Product: oe-core 638 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> based upon 639 work by Paul Eggleton <paul.eggleton@microsoft.com> and 640 Usama Arif <usama.arif@arm.com> 641 """ 642 a_comment = "a smart cascaded U-Boot comment" 643 config = """ 644# There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at 645# least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set 646MACHINE = "qemuarm" 647UBOOT_MACHINE = "am57xx_evm_defconfig" 648SPL_BINARY = "MLO" 649# Enable creation and signing of the U-Boot fitImage 650UBOOT_FITIMAGE_ENABLE = "1" 651SPL_SIGN_ENABLE = "1" 652SPL_SIGN_KEYNAME = "spl-cascaded-oe-selftest" 653SPL_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 654UBOOT_DTB_BINARY = "u-boot.dtb" 655UBOOT_ENTRYPOINT = "0x80000000" 656UBOOT_LOADADDRESS = "0x80000000" 657UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 658UBOOT_MKIMAGE_SIGN_ARGS = "-c '%s'" 659UBOOT_DTB_LOADADDRESS = "0x82000000" 660UBOOT_ARCH = "arm" 661SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 662SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded U-Boot comment'" 663UBOOT_EXTLINUX = "0" 664UBOOT_FIT_GENERATE_KEYS = "1" 665UBOOT_FIT_HASH_ALG = "sha256" 666KERNEL_IMAGETYPES += " fitImage " 667KERNEL_CLASSES = " kernel-fitimage " 668UBOOT_SIGN_ENABLE = "1" 669FIT_GENERATE_KEYS = "1" 670UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 671UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" 672UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" 673FIT_SIGN_INDIVIDUAL = "1" 674""" % a_comment 675 self.write_config(config) 676 677 # The U-Boot fitImage is created as part of the U-Boot recipe 678 bitbake("virtual/bootloader") 679 680 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') 681 machine = get_bb_var('MACHINE') 682 fitimage_its_path = os.path.join(deploy_dir_image, 683 "u-boot-its-%s" % (machine,)) 684 fitimage_path = os.path.join(deploy_dir_image, 685 "u-boot-fitImage-%s" % (machine,)) 686 687 self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) 688 self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) 689 690 req_itspaths = [ 691 ['/', 'images', 'uboot'], 692 ['/', 'images', 'uboot', 'signature'], 693 ['/', 'images', 'fdt'], 694 ['/', 'images', 'fdt', 'signature'], 695 ] 696 697 itspath = [] 698 itspaths = [] 699 linect = 0 700 sigs = {} 701 with open(fitimage_its_path) as its_file: 702 linect += 1 703 for line in its_file: 704 line = line.strip() 705 if line.endswith('};'): 706 itspath.pop() 707 elif line.endswith('{'): 708 itspath.append(line[:-1].strip()) 709 itspaths.append(itspath[:]) 710 elif itspath and itspath[-1] == 'signature': 711 itsdotpath = '.'.join(itspath) 712 if not itsdotpath in sigs: 713 sigs[itsdotpath] = {} 714 if not '=' in line or not line.endswith(';'): 715 self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line)) 716 key, value = line.split('=', 1) 717 sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') 718 719 for reqpath in req_itspaths: 720 if not reqpath in itspaths: 721 self.fail('Missing section in its file: %s' % reqpath) 722 723 reqsigvalues_image = { 724 'algo': '"sha256,rsa2048"', 725 'key-name-hint': '"spl-cascaded-oe-selftest"', 726 } 727 728 for itspath, values in sigs.items(): 729 reqsigvalues = reqsigvalues_image 730 for reqkey, reqvalue in reqsigvalues.items(): 731 value = values.get(reqkey, None) 732 if value is None: 733 self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath)) 734 self.assertEqual(value, reqvalue) 735 736 # Dump the image to see if it really got signed 737 uboot_tools_bindir = self._setup_uboot_tools_native() 738 dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) 739 in_signed = None 740 signed_sections = {} 741 for line in dumpimage_result.output.splitlines(): 742 if line.startswith((' Image')): 743 in_signed = re.search(r'\((.*)\)', line).groups()[0] 744 elif re.match(' \w', line): 745 in_signed = None 746 elif in_signed: 747 if not in_signed in signed_sections: 748 signed_sections[in_signed] = {} 749 key, value = line.split(':', 1) 750 signed_sections[in_signed][key.strip()] = value.strip() 751 self.assertIn('uboot', signed_sections) 752 self.assertIn('fdt', signed_sections) 753 for signed_section, values in signed_sections.items(): 754 value = values.get('Sign algo', None) 755 self.assertEqual(value, 'sha256,rsa2048:spl-cascaded-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) 756 value = values.get('Sign value', None) 757 self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section) 758 759 # Check for SPL_MKIMAGE_SIGN_ARGS 760 # Looks like mkimage supports to add a comment but does not support to read it back. 761 found_comments = FitImageTests._find_string_in_bin_file(fitimage_path, a_comment) 762 self.assertEqual(found_comments, 2, "Expected 2 signed and commented section in the fitImage.") 763 764 # Verify the signature 765 self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, 766 os.path.join(deploy_dir_image, 'u-boot-spl.dtb')) 767 768 769 def test_initramfs_bundle(self): 770 """ 771 Summary: Verifies the content of the initramfs bundle node in the FIT Image Tree Source (its) 772 The FIT settings are set by the test case. 773 The machine used is beaglebone-yocto. 774 Expected: 1. The ITS is generated with initramfs bundle support 775 2. All the fields in the kernel node are as expected (matching the 776 conf settings) 777 3. The kernel is included in all the available configurations and 778 its hash is included in the configuration signature 779 780 Product: oe-core 781 Author: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> 782 """ 783 784 config = """ 785DISTRO="poky" 786MACHINE = "beaglebone-yocto" 787INITRAMFS_IMAGE_BUNDLE = "1" 788INITRAMFS_IMAGE = "core-image-minimal-initramfs" 789INITRAMFS_SCRIPTS = "" 790UBOOT_MACHINE = "am335x_evm_defconfig" 791KERNEL_CLASSES = " kernel-fitimage " 792KERNEL_IMAGETYPES = "fitImage" 793UBOOT_SIGN_ENABLE = "1" 794UBOOT_SIGN_KEYNAME = "beaglebonekey" 795UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}" 796UBOOT_DTB_BINARY = "u-boot.dtb" 797UBOOT_ENTRYPOINT = "0x80000000" 798UBOOT_LOADADDRESS = "0x80000000" 799UBOOT_DTB_LOADADDRESS = "0x82000000" 800UBOOT_ARCH = "arm" 801UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 802UBOOT_MKIMAGE_KERNEL_TYPE = "kernel" 803UBOOT_EXTLINUX = "0" 804FIT_GENERATE_KEYS = "1" 805KERNEL_IMAGETYPE_REPLACEMENT = "zImage" 806FIT_KERNEL_COMP_ALG = "none" 807FIT_HASH_ALG = "sha256" 808""" 809 config = self._config_add_uboot_env(config) 810 self.write_config(config) 811 812 # fitImage is created as part of linux recipe 813 bitbake("virtual/kernel") 814 815 bb_vars = get_bb_vars([ 816 'DEPLOY_DIR_IMAGE', 817 'FIT_HASH_ALG', 818 'FIT_KERNEL_COMP_ALG', 819 'INITRAMFS_IMAGE', 820 'MACHINE', 821 'UBOOT_ARCH', 822 'UBOOT_ENTRYPOINT', 823 'UBOOT_LOADADDRESS', 824 'UBOOT_MKIMAGE_KERNEL_TYPE' 825 ], 826 'virtual/kernel') 827 fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], 828 "fitImage-its-%s-%s-%s" % (bb_vars['INITRAMFS_IMAGE'], bb_vars['MACHINE'], bb_vars['MACHINE'])) 829 fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],"fitImage") 830 831 self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) 832 self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) 833 834 its_file = open(fitimage_its_path) 835 836 its_lines = [line.strip() for line in its_file.readlines()] 837 838 exp_node_lines = [ 839 'kernel-1 {', 840 'description = "Linux kernel";', 841 'data = /incbin/("linux.bin");', 842 'type = "' + str(bb_vars['UBOOT_MKIMAGE_KERNEL_TYPE']) + '";', 843 'arch = "' + str(bb_vars['UBOOT_ARCH']) + '";', 844 'os = "linux";', 845 'compression = "' + str(bb_vars['FIT_KERNEL_COMP_ALG']) + '";', 846 'load = <' + str(bb_vars['UBOOT_LOADADDRESS']) + '>;', 847 'entry = <' + str(bb_vars['UBOOT_ENTRYPOINT']) + '>;', 848 'hash-1 {', 849 'algo = "' + str(bb_vars['FIT_HASH_ALG']) +'";', 850 '};', 851 '};' 852 ] 853 854 node_str = exp_node_lines[0] 855 856 print ("checking kernel node\n") 857 self.assertIn(node_str, its_lines) 858 859 node_start_idx = its_lines.index(node_str) 860 node = its_lines[node_start_idx:(node_start_idx + len(exp_node_lines))] 861 862 # Remove the absolute path. This refers to WORKDIR which is not always predictable. 863 re_data = re.compile(r'^data = /incbin/\(.*/linux\.bin"\);$') 864 node = [re.sub(re_data, 'data = /incbin/("linux.bin");', cfg_str) for cfg_str in node] 865 866 self.assertEqual(node, exp_node_lines, "kernel node does not match expectation") 867 868 rx_configs = re.compile("^conf-.*") 869 its_configs = list(filter(rx_configs.match, its_lines)) 870 871 for cfg_str in its_configs: 872 cfg_start_idx = its_lines.index(cfg_str) 873 line_idx = cfg_start_idx + 2 874 node_end = False 875 while node_end == False: 876 if its_lines[line_idx] == "};" and its_lines[line_idx-1] == "};" : 877 node_end = True 878 line_idx = line_idx + 1 879 880 node = its_lines[cfg_start_idx:line_idx] 881 print("checking configuration " + cfg_str.rstrip(" {")) 882 rx_desc_line = re.compile(r'^description = ".*Linux kernel.*') 883 self.assertEqual(len(list(filter(rx_desc_line.match, node))), 1, "kernel keyword not found in the description line") 884 885 self.assertIn('kernel = "kernel-1";', node) 886 887 rx_sign_line = re.compile(r'^sign-images = .*kernel.*') 888 self.assertEqual(len(list(filter(rx_sign_line.match, node))), 1, "kernel hash not signed") 889 890 # Verify the signature 891 uboot_tools_bindir = self._setup_uboot_tools_native() 892 self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], 'am335x-bone.dtb')) 893 894 # Check if the u-boot boot.scr script is in the fitImage 895 dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) 896 self._verify_fitimage_uboot_env(dumpimage_result) 897