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