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