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_vars 9import os 10import re 11import shlex 12import logging 13import pprint 14 15class FitImageTestCase(OESelftestTestCase): 16 """Test functions usable for testing kernel-fitimage.bbclass and uboot-sign.bbclass 17 18 A brief summary showing the structure of a test case: 19 20 self._test_fitimage() 21 # Generate a local.conf file and bitbake the bootloader or the kernel 22 self._bitbake_fit_image() 23 24 # Check if the its file contains the expected paths and attributes. 25 # The _get_req_* functions are implemented by more specific chield classes. 26 self._check_its_file() 27 req_its_paths = self._get_req_its_paths() 28 req_sigvalues_config = self._get_req_sigvalues_config() 29 req_sigvalues_image = self._get_req_sigvalues_image() 30 # Compare the its file against req_its_paths, req_sigvalues_config, req_sigvalues_image 31 32 # Call the dumpimage utiliy and check that it prints all the expected paths and attributes 33 # The _get_req_* functions are implemented by more specific chield classes. 34 self._check_fitimage() 35 self._get_req_sections() 36 # Compare the output of the dumpimage utility against 37 """ 38 39 MKIMAGE_HASH_LENGTHS = { 'sha256': 64, 'sha384': 96, 'sha512': 128 } 40 MKIMAGE_SIGNATURE_LENGTHS = { 'rsa2048': 512 } 41 42 def _gen_signing_key(self, bb_vars): 43 """Generate a key pair and a singing certificate 44 45 Generate a UBOOT_SIGN_KEYNAME in the UBOOT_SIGN_KEYDIR similar to what 46 the FIT_GENERATE_KEYS feature does. However, having a static key is 47 probably a more realistic use case than generating a random key with 48 each clean build. So this needs to be tested as well. 49 The FIT_GENERATE_KEYS generates 2 keys: The UBOOT_SIGN_KEYNAME and the 50 UBOOT_SIGN_IMG_KEYNAME. The UBOOT_SIGN_IMG_KEYNAME is used by the 51 FIT_SIGN_INDIVIDUAL feature only. Testing if everything is working if 52 there is only one key available is important as well. Therefore this 53 function generates only the keys which are really needed, not just two. 54 """ 55 56 # Define some variables which are usually defined by the kernel-fitimage.bbclass. 57 # But for testing purpose check if the uboot-sign.bbclass is independent from 58 # the kernel-fitimage.bbclass 59 fit_sign_numbits = bb_vars.get('FIT_SIGN_NUMBITS', "2048") 60 fit_key_genrsa_args = bb_vars.get('FIT_KEY_GENRSA_ARGS', "-F4") 61 fit_key_req_args = bb_vars.get('FIT_KEY_REQ_ARGS', "-batch -new") 62 fit_key_sign_pkcs = bb_vars.get('FIT_KEY_SIGN_PKCS', "-x509") 63 64 uboot_sign_keydir = bb_vars['UBOOT_SIGN_KEYDIR'] 65 sign_keys = [bb_vars['UBOOT_SIGN_KEYNAME']] 66 if bb_vars['FIT_SIGN_INDIVIDUAL'] == "1": 67 sign_keys.append(bb_vars['UBOOT_SIGN_IMG_KEYNAME']) 68 for sign_key in sign_keys: 69 sing_key_path = os.path.join(uboot_sign_keydir, sign_key) 70 if not os.path.isdir(uboot_sign_keydir): 71 os.makedirs(uboot_sign_keydir) 72 openssl_bindir = FitImageTestCase._setup_native('openssl-native') 73 openssl_path = os.path.join(openssl_bindir, 'openssl') 74 runCmd("%s genrsa %s -out %s.key %s" % ( 75 openssl_path, 76 fit_key_genrsa_args, 77 sing_key_path, 78 fit_sign_numbits 79 )) 80 runCmd("%s req %s %s -key %s.key -out %s.crt" % ( 81 openssl_path, 82 fit_key_req_args, 83 fit_key_sign_pkcs, 84 sing_key_path, 85 sing_key_path 86 )) 87 88 @staticmethod 89 def _gen_random_file(file_path, num_bytes=65536): 90 with open(file_path, 'wb') as file_out: 91 file_out.write(os.urandom(num_bytes)) 92 93 @staticmethod 94 def _setup_native(native_recipe): 95 """Build a native recipe and return the path to its bindir in RECIPE_SYSROOT_NATIVE""" 96 bitbake(native_recipe + " -c addto_recipe_sysroot") 97 vars = get_bb_vars(['RECIPE_SYSROOT_NATIVE', 'bindir'], native_recipe) 98 return os.path.join(vars['RECIPE_SYSROOT_NATIVE'], vars['bindir']) 99 100 def _verify_fit_image_signature(self, uboot_tools_bindir, fitimage_path, dtb_path, conf_name=None): 101 """Verify the signature of a fit configuration 102 103 The fit_check_sign utility from u-boot-tools-native is called. 104 uboot-fit_check_sign -f fitImage -k $dtb_path -c conf-$dtb_name 105 dtb_path refers to a binary device tree containing the public key. 106 """ 107 fit_check_sign_path = os.path.join(uboot_tools_bindir, 'uboot-fit_check_sign') 108 cmd = '%s -f %s -k %s' % (fit_check_sign_path, fitimage_path, dtb_path) 109 if conf_name: 110 cmd += ' -c %s' % conf_name 111 result = runCmd(cmd) 112 self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) 113 self.assertIn("Signature check OK", result.output) 114 115 def _verify_dtb_property(self, dtc_bindir, dtb_path, node_path, property_name, req_property, absent=False): 116 """Verify device tree properties 117 118 The fdtget utility from dtc-native is called and the property is compared. 119 """ 120 fdtget_path = os.path.join(dtc_bindir, 'fdtget') 121 cmd = '%s %s %s %s' % (fdtget_path, dtb_path, node_path, property_name) 122 if absent: 123 result = runCmd(cmd, ignore_status=True) 124 self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) 125 self.assertIn("FDT_ERR_NOTFOUND", result.output) 126 else: 127 result = runCmd(cmd) 128 self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) 129 self.assertEqual(req_property, result.output.strip()) 130 131 @staticmethod 132 def _find_string_in_bin_file(file_path, search_string): 133 """find strings in a binary file 134 135 Shell equivalent: strings "$1" | grep "$2" | wc -l 136 return number of matches 137 """ 138 found_positions = 0 139 with open(file_path, 'rb') as file: 140 content = file.read().decode('ascii', errors='ignore') 141 found_positions = content.count(search_string) 142 return found_positions 143 144 @staticmethod 145 def _get_uboot_mkimage_sign_args(uboot_mkimage_sign_args): 146 """Retrive the string passed via -c to the mkimage command 147 148 Example: If a build configutation defines 149 UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" 150 this function returns "a smart comment" 151 """ 152 a_comment = None 153 if uboot_mkimage_sign_args: 154 mkimage_args = shlex.split(uboot_mkimage_sign_args) 155 try: 156 c_index = mkimage_args.index('-c') 157 a_comment = mkimage_args[c_index+1] 158 except ValueError: 159 pass 160 return a_comment 161 162 @staticmethod 163 def _get_dtb_files(bb_vars): 164 kernel_devicetree = bb_vars.get('KERNEL_DEVICETREE') 165 if kernel_devicetree: 166 return [os.path.basename(dtb) for dtb in kernel_devicetree.split()] 167 return [] 168 169 def _is_req_dict_in_dict(self, found_dict, req_dict): 170 """ 171 Check if all key-value pairs in the required dictionary are present in the found dictionary. 172 173 This function recursively checks if the required dictionary (`req_dict`) is a subset of the found dictionary (`found_dict`). 174 It supports nested dictionaries, strings, lists, and sets as values. 175 176 Args: 177 found_dict (dict): The dictionary to search within. 178 req_dict (dict): The dictionary containing the required key-value pairs. 179 """ 180 for key, value in req_dict.items(): 181 self.assertIn(key, found_dict) 182 if isinstance(value, dict): 183 self._is_req_dict_in_dict(found_dict[key], value) 184 elif isinstance(value, str): 185 self.assertIn(value, found_dict[key]) 186 elif isinstance(value, list): 187 self.assertLessEqual(set(value), set(found_dict[key])) 188 elif isinstance(value, set): 189 self.assertLessEqual(value, found_dict[key]) 190 else: 191 self.assertEqual(value, found_dict[key]) 192 193 def _check_its_file(self, bb_vars, its_file_path): 194 """Check if the its file contains the expected sections and fields""" 195 # print the its file for debugging 196 if logging.DEBUG >= self.logger.level: 197 with open(its_file_path) as its_file: 198 self.logger.debug("its file: %s" % its_file.read()) 199 200 # Generate a list of expected paths in the its file 201 req_its_paths = self._get_req_its_paths(bb_vars) 202 self.logger.debug("req_its_paths:\n%s\n" % pprint.pformat(req_its_paths, indent=4)) 203 204 # Generate a dict of expected configuration signature nodes 205 req_sigvalues_config = self._get_req_sigvalues_config(bb_vars) 206 self.logger.debug("req_sigvalues_config:\n%s\n" % pprint.pformat(req_sigvalues_config, indent=4)) 207 208 # Generate a dict of expected image signature nodes 209 req_sigvalues_image = self._get_req_sigvalues_image(bb_vars) 210 self.logger.debug("req_sigvalues_image:\n%s\n" % pprint.pformat(req_sigvalues_image, indent=4)) 211 212 # Parse the its file for paths and signatures 213 its_path = [] 214 its_paths = [] 215 linect = 0 216 sigs = {} 217 with open(its_file_path) as its_file: 218 for line in its_file: 219 linect += 1 220 line = line.strip() 221 if line.endswith('};'): 222 its_path.pop() 223 elif line.endswith('{'): 224 its_path.append(line[:-1].strip()) 225 its_paths.append(its_path[:]) 226 # kernel-fitimage uses signature-1, uboot-sign uses signature 227 elif its_path and (its_path[-1] == 'signature-1' or its_path[-1] == 'signature'): 228 itsdotpath = '.'.join(its_path) 229 if not itsdotpath in sigs: 230 sigs[itsdotpath] = {} 231 if not '=' in line or not line.endswith(';'): 232 self.fail('Unexpected formatting in %s sigs section line %d:%s' % (its_file_path, linect, line)) 233 key, value = line.split('=', 1) 234 sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') 235 236 # Check if all expected paths are found in the its file 237 self.logger.debug("itspaths:\n%s\n" % pprint.pformat(its_paths, indent=4)) 238 for req_path in req_its_paths: 239 if not req_path in its_paths: 240 self.fail('Missing path in its file: %s (%s)' % (req_path, its_file_path)) 241 242 # Check if all the expected singnature nodes (images and configurations) are found 243 self.logger.debug("sigs:\n%s\n" % pprint.pformat(sigs, indent=4)) 244 if req_sigvalues_config or req_sigvalues_image: 245 for its_path, values in sigs.items(): 246 if 'conf-' in its_path: 247 reqsigvalues = req_sigvalues_config 248 else: 249 reqsigvalues = req_sigvalues_image 250 for reqkey, reqvalue in reqsigvalues.items(): 251 value = values.get(reqkey, None) 252 if value is None: 253 self.fail('Missing key "%s" in its file signature section %s (%s)' % (reqkey, its_path, its_file_path)) 254 self.assertEqual(value, reqvalue) 255 256 # Generate a list of expected fields in the its file 257 req_its_fields = self._get_req_its_fields(bb_vars) 258 self.logger.debug("req_its_fields:\n%s\n" % pprint.pformat(req_its_fields, indent=4)) 259 260 # Check if all expected fields are in the its file 261 if req_its_fields: 262 field_index = 0 263 field_index_last = len(req_its_fields) - 1 264 with open(its_file_path) as its_file: 265 for line in its_file: 266 if req_its_fields[field_index] in line: 267 if field_index < field_index_last: 268 field_index +=1 269 else: 270 break 271 self.assertEqual(field_index, field_index_last, 272 "Fields in Image Tree Source File %s did not match, error in finding %s" 273 % (its_file_path, req_its_fields[field_index])) 274 275 def _check_fitimage(self, bb_vars, fitimage_path, uboot_tools_bindir): 276 """Run dumpimage on the final FIT image and parse the output into a dict""" 277 dumpimage_path = os.path.join(uboot_tools_bindir, 'dumpimage') 278 cmd = '%s -l %s' % (dumpimage_path, fitimage_path) 279 self.logger.debug("Analyzing output from dumpimage: %s" % cmd) 280 dumpimage_result = runCmd(cmd) 281 in_section = None 282 sections = {} 283 self.logger.debug("dumpimage output: %s" % dumpimage_result.output) 284 for line in dumpimage_result.output.splitlines(): 285 # Find potentially hashed and signed sections 286 if line.startswith((' Configuration', ' Image')): 287 in_section = re.search(r'\((.*)\)', line).groups()[0] 288 # Key value lines start with two spaces otherwise the section ended 289 elif not line.startswith(" "): 290 in_section = None 291 # Handle key value lines of this section 292 elif in_section: 293 if not in_section in sections: 294 sections[in_section] = {} 295 try: 296 key, value = line.split(':', 1) 297 key = key.strip() 298 value = value.strip() 299 except ValueError as val_err: 300 # Handle multiple entries as e.g. for Loadables as a list 301 if key and line.startswith(" "): 302 value = sections[in_section][key] + "," + line.strip() 303 else: 304 raise ValueError(f"Error processing line: '{line}'. Original error: {val_err}") 305 sections[in_section][key] = value 306 307 # Check if the requested dictionary is a subset of the parsed dictionary 308 req_sections, num_signatures = self._get_req_sections(bb_vars) 309 self.logger.debug("req_sections: \n%s\n" % pprint.pformat(req_sections, indent=4)) 310 self.logger.debug("dumpimage sections: \n%s\n" % pprint.pformat(sections, indent=4)) 311 self._is_req_dict_in_dict(sections, req_sections) 312 313 # Call the signing related checks if the function is provided by a inherited class 314 self._check_signing(bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path) 315 316 def _get_req_its_paths(self, bb_vars): 317 self.logger.error("This function needs to be implemented") 318 return [] 319 320 def _get_req_its_fields(self, bb_vars): 321 self.logger.error("This function needs to be implemented") 322 return [] 323 324 def _get_req_sigvalues_config(self, bb_vars): 325 self.logger.error("This function needs to be implemented") 326 return {} 327 328 def _get_req_sigvalues_image(self, bb_vars): 329 self.logger.error("This function needs to be implemented") 330 return {} 331 332 def _get_req_sections(self, bb_vars): 333 self.logger.error("This function needs to be implemented") 334 return ({}, 0) 335 336 def _check_signing(self, bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path): 337 """Verify the signatures in the FIT image.""" 338 self.fail("Function needs to be implemented by inheriting classes") 339 340 def _bitbake_fit_image(self, bb_vars): 341 """Bitbake the FIT image and return the paths to the its file and the FIT image""" 342 self.fail("Function needs to be implemented by inheriting classes") 343 344 def _test_fitimage(self, bb_vars): 345 """Check if the its file and the FIT image are created and signed correctly""" 346 fitimage_its_path, fitimage_path = self._bitbake_fit_image(bb_vars) 347 self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) 348 self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) 349 350 self.logger.debug("Checking its: %s" % fitimage_its_path) 351 self._check_its_file(bb_vars, fitimage_its_path) 352 353 # Setup u-boot-tools-native 354 uboot_tools_bindir = FitImageTestCase._setup_native('u-boot-tools-native') 355 356 # Verify the FIT image 357 self._check_fitimage(bb_vars, fitimage_path, uboot_tools_bindir) 358 359 360class KernelFitImageTests(FitImageTestCase): 361 """Test cases for the kernel-fitimage bbclass""" 362 363 def _fit_get_bb_vars(self, additional_vars=[]): 364 """Retrieve BitBake variables specific to the test case. 365 366 Call the get_bb_vars function once and get all variables needed by the test case. 367 """ 368 internal_used = { 369 'DEPLOY_DIR_IMAGE', 370 'FIT_DESC', 371 'FIT_HASH_ALG', 372 'FIT_KERNEL_COMP_ALG', 373 'FIT_SIGN_ALG', 374 'FIT_SIGN_INDIVIDUAL', 375 'FIT_UBOOT_ENV', 376 'INITRAMFS_IMAGE_BUNDLE', 377 'INITRAMFS_IMAGE_NAME', 378 'INITRAMFS_IMAGE', 379 'KERNEL_DEVICETREE', 380 'KERNEL_FIT_LINK_NAME', 381 'MACHINE', 382 'UBOOT_ARCH', 383 'UBOOT_ENTRYPOINT', 384 'UBOOT_LOADADDRESS', 385 'UBOOT_MKIMAGE_KERNEL_TYPE', 386 'UBOOT_MKIMAGE_SIGN_ARGS', 387 'UBOOT_RD_ENTRYPOINT', 388 'UBOOT_RD_LOADADDRESS', 389 'UBOOT_SIGN_ENABLE', 390 'UBOOT_SIGN_IMG_KEYNAME', 391 'UBOOT_SIGN_KEYDIR', 392 'UBOOT_SIGN_KEYNAME', 393 } 394 bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), "virtual/kernel") 395 self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4)) 396 return bb_vars 397 398 def _config_add_uboot_env(self, config): 399 """Generate an u-boot environment 400 401 Create a boot.cmd file that is packed into the FIT image as a source-able text file. 402 Updates the configuration to include the boot.cmd file. 403 """ 404 fit_uenv_file = "boot.cmd" 405 test_files_dir = "test-files" 406 fit_uenv_path = os.path.join(self.builddir, test_files_dir, fit_uenv_file) 407 408 config += '# Add an u-boot script to the fitImage' + os.linesep 409 config += 'FIT_UBOOT_ENV = "%s"' % fit_uenv_file + os.linesep 410 config += 'FILESEXTRAPATHS:prepend := "${TOPDIR}/%s:"' % test_files_dir + os.linesep 411 config += 'SRC_URI:append:pn-linux-yocto = " file://${FIT_UBOOT_ENV}"' + os.linesep 412 413 if not os.path.isdir(test_files_dir): 414 os.makedirs(test_files_dir) 415 self.logger.debug("Writing to: %s" % fit_uenv_path) 416 with open(fit_uenv_path, "w") as f: 417 f.write('echo "hello world"') 418 419 return config 420 421 def _bitbake_fit_image(self, bb_vars): 422 """Bitbake the kernel and return the paths to the its file and the FIT image""" 423 bitbake("virtual/kernel") 424 425 # Find the right its file and the final fitImage and check if both files are available 426 deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE'] 427 initramfs_image = bb_vars['INITRAMFS_IMAGE'] 428 initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] 429 initramfs_image_name = bb_vars['INITRAMFS_IMAGE_NAME'] 430 kernel_fit_link_name = bb_vars['KERNEL_FIT_LINK_NAME'] 431 if not initramfs_image and initramfs_image_bundle != "1": 432 fitimage_its_name = "fitImage-its-%s" % kernel_fit_link_name 433 fitimage_name = "fitImage" 434 elif initramfs_image and initramfs_image_bundle != "1": 435 fitimage_its_name = "fitImage-its-%s-%s" % (initramfs_image_name, kernel_fit_link_name) 436 fitimage_name = "fitImage-%s-%s" % (initramfs_image_name, kernel_fit_link_name) 437 elif initramfs_image and initramfs_image_bundle == "1": 438 fitimage_its_name = "fitImage-its-%s-%s" % (initramfs_image_name, kernel_fit_link_name) 439 fitimage_name = "fitImage" # or fitImage-${KERNEL_IMAGE_LINK_NAME}${KERNEL_IMAGE_BIN_EXT} 440 else: 441 self.fail('Invalid configuration: INITRAMFS_IMAGE_BUNDLE = "1" and not INITRAMFS_IMAGE') 442 fitimage_its_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_its_name)) 443 fitimage_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_name)) 444 return (fitimage_its_path, fitimage_path) 445 446 def _get_req_its_paths(self, bb_vars): 447 """Generate a list of expected paths in the its file 448 449 Example: 450 [ 451 ['/', 'images', 'kernel-1', 'hash-1'], 452 ['/', 'images', 'kernel-1', 'signature-1'], 453 ] 454 """ 455 dtb_files = FitImageTestCase._get_dtb_files(bb_vars) 456 fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL'] 457 fit_uboot_env = bb_vars['FIT_UBOOT_ENV'] 458 initramfs_image = bb_vars['INITRAMFS_IMAGE'] 459 initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] 460 uboot_sign_enable = bb_vars.get('UBOOT_SIGN_ENABLE') 461 462 # image nodes 463 images = [ 'kernel-1' ] 464 if dtb_files: 465 images += [ 'fdt-' + dtb for dtb in dtb_files ] 466 if fit_uboot_env: 467 images.append('bootscr-' + fit_uboot_env) 468 if bb_vars['MACHINE'] == "qemux86-64": # Not really the right if 469 images.append('setup-1') 470 if initramfs_image and initramfs_image_bundle != "1": 471 images.append('ramdisk-1') 472 473 # configuration nodes 474 if dtb_files: 475 configurations = [ 'conf-' + conf for conf in dtb_files ] 476 else: 477 configurations = [ 'conf-1' ] 478 479 # Create a list of paths for all image and configuration nodes 480 req_its_paths = [] 481 for image in images: 482 req_its_paths.append(['/', 'images', image, 'hash-1']) 483 if uboot_sign_enable == "1" and fit_sign_individual == "1": 484 req_its_paths.append(['/', 'images', image, 'signature-1']) 485 for configuration in configurations: 486 req_its_paths.append(['/', 'configurations', configuration, 'hash-1']) 487 if uboot_sign_enable == "1": 488 req_its_paths.append(['/', 'configurations', configuration, 'signature-1']) 489 return req_its_paths 490 491 def _get_req_its_fields(self, bb_vars): 492 initramfs_image = bb_vars['INITRAMFS_IMAGE'] 493 initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] 494 uboot_rd_loadaddress = bb_vars.get('UBOOT_RD_LOADADDRESS') 495 uboot_rd_entrypoint = bb_vars.get('UBOOT_RD_ENTRYPOINT') 496 497 its_field_check = [ 498 'description = "%s";' % bb_vars['FIT_DESC'], 499 'description = "Linux kernel";', 500 'data = /incbin/("linux.bin");', 501 'type = "' + str(bb_vars['UBOOT_MKIMAGE_KERNEL_TYPE']) + '";', 502 'arch = "' + str(bb_vars['UBOOT_ARCH']) + '";', 503 'os = "linux";', 504 # 'compression = "' + str(bb_vars['FIT_KERNEL_COMP_ALG']) + '";', defined based on files in TMPDIR, not ideal... 505 'load = <' + str(bb_vars['UBOOT_LOADADDRESS']) + '>;', 506 'entry = <' + str(bb_vars['UBOOT_ENTRYPOINT']) + '>;', 507 ] 508 if initramfs_image and initramfs_image_bundle != "1": 509 its_field_check.append('type = "ramdisk";') 510 if uboot_rd_loadaddress: 511 its_field_check.append("load = <%s>;" % uboot_rd_loadaddress) 512 if uboot_rd_entrypoint: 513 its_field_check.append("entry = <%s>;" % uboot_rd_entrypoint) 514 its_field_check += [ 515 # 'default = "conf-1";', needs more work 516 'kernel = "kernel-1";', 517 ] 518 if initramfs_image and initramfs_image_bundle != "1": 519 its_field_check.append('ramdisk = "ramdisk-1";') 520 521 return its_field_check 522 523 def _get_req_sigvalues_config(self, bb_vars): 524 """Generate a dictionary of expected configuration signature nodes""" 525 if bb_vars.get('UBOOT_SIGN_ENABLE') != "1": 526 return {} 527 sign_images = '"kernel", "fdt"' 528 if bb_vars['INITRAMFS_IMAGE'] and bb_vars['INITRAMFS_IMAGE_BUNDLE'] != "1": 529 sign_images += ', "ramdisk"' 530 if bb_vars['FIT_UBOOT_ENV']: 531 sign_images += ', "bootscr"' 532 req_sigvalues_config = { 533 'algo': '"%s,%s"' % (bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG']), 534 'key-name-hint': '"%s"' % bb_vars['UBOOT_SIGN_KEYNAME'], 535 'sign-images': sign_images, 536 } 537 return req_sigvalues_config 538 539 def _get_req_sigvalues_image(self, bb_vars): 540 """Generate a dictionary of expected image signature nodes""" 541 if bb_vars['FIT_SIGN_INDIVIDUAL'] != "1": 542 return {} 543 req_sigvalues_image = { 544 'algo': '"%s,%s"' % (bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG']), 545 'key-name-hint': '"%s"' % bb_vars['UBOOT_SIGN_IMG_KEYNAME'], 546 } 547 return req_sigvalues_image 548 549 def _get_req_sections(self, bb_vars): 550 """Generate a dictionary of expected sections in the output of dumpimage""" 551 dtb_files = FitImageTestCase._get_dtb_files(bb_vars) 552 fit_hash_alg = bb_vars['FIT_HASH_ALG'] 553 fit_sign_alg = bb_vars['FIT_SIGN_ALG'] 554 fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL'] 555 fit_uboot_env = bb_vars['FIT_UBOOT_ENV'] 556 initramfs_image = bb_vars['INITRAMFS_IMAGE'] 557 initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] 558 uboot_sign_enable = bb_vars['UBOOT_SIGN_ENABLE'] 559 uboot_sign_img_keyname = bb_vars['UBOOT_SIGN_IMG_KEYNAME'] 560 uboot_sign_keyname = bb_vars['UBOOT_SIGN_KEYNAME'] 561 num_signatures = 0 562 req_sections = { 563 "kernel-1": { 564 "Type": "Kernel Image", 565 "OS": "Linux", 566 "Load Address": bb_vars['UBOOT_LOADADDRESS'], 567 "Entry Point": bb_vars['UBOOT_ENTRYPOINT'], 568 } 569 } 570 # Create one section per DTB 571 for dtb in dtb_files: 572 req_sections['fdt-' + dtb] = { 573 "Type": "Flat Device Tree", 574 } 575 # Add a script section if there is a script 576 if fit_uboot_env: 577 req_sections['bootscr-' + fit_uboot_env] = { "Type": "Script" } 578 # Add the initramfs 579 if initramfs_image and initramfs_image_bundle != "1": 580 req_sections['ramdisk-1'] = { 581 "Type": "RAMDisk Image", 582 "Load Address": bb_vars['UBOOT_RD_LOADADDRESS'], 583 "Entry Point": bb_vars['UBOOT_RD_ENTRYPOINT'] 584 } 585 # Create a configuration section for each DTB 586 if dtb_files: 587 for dtb in dtb_files: 588 req_sections['conf-' + dtb] = { 589 "Kernel": "kernel-1", 590 "FDT": 'fdt-' + dtb, 591 } 592 if initramfs_image and initramfs_image_bundle != "1": 593 req_sections['conf-' + dtb]['Init Ramdisk'] = "ramdisk-1" 594 else: 595 req_sections['conf-1'] = { 596 "Kernel": "kernel-1" 597 } 598 if initramfs_image and initramfs_image_bundle != "1": 599 req_sections['conf-1']['Init Ramdisk'] = "ramdisk-1" 600 601 # Add signing related properties if needed 602 if uboot_sign_enable == "1": 603 for section in req_sections: 604 req_sections[section]['Hash algo'] = fit_hash_alg 605 if section.startswith('conf-'): 606 req_sections[section]['Hash value'] = "unavailable" 607 req_sections[section]['Sign algo'] = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_keyname) 608 num_signatures += 1 609 elif fit_sign_individual == "1": 610 req_sections[section]['Sign algo'] = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_img_keyname) 611 num_signatures += 1 612 return (req_sections, num_signatures) 613 614 def _check_signing(self, bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path): 615 """Verify the signature nodes in the FIT image""" 616 if bb_vars['UBOOT_SIGN_ENABLE'] == "1": 617 self.logger.debug("Verifying signatures in the FIT image") 618 else: 619 self.logger.debug("FIT image is not signed. Signature verification is not needed.") 620 return 621 622 fit_hash_alg = bb_vars['FIT_HASH_ALG'] 623 fit_sign_alg = bb_vars['FIT_SIGN_ALG'] 624 uboot_sign_keyname = bb_vars['UBOOT_SIGN_KEYNAME'] 625 uboot_sign_img_keyname = bb_vars['UBOOT_SIGN_IMG_KEYNAME'] 626 deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE'] 627 fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL'] 628 fit_hash_alg_len = FitImageTestCase.MKIMAGE_HASH_LENGTHS[fit_hash_alg] 629 fit_sign_alg_len = FitImageTestCase.MKIMAGE_SIGNATURE_LENGTHS[fit_sign_alg] 630 for section, values in sections.items(): 631 # Configuration nodes are always signed with UBOOT_SIGN_KEYNAME (if UBOOT_SIGN_ENABLE = "1") 632 if section.startswith("conf"): 633 sign_algo = values.get('Sign algo', None) 634 req_sign_algo = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_keyname) 635 self.assertEqual(sign_algo, req_sign_algo, 'Signature algorithm for %s not expected value' % section) 636 sign_value = values.get('Sign value', None) 637 self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section) 638 dtb_path = os.path.join(deploy_dir_image, section.replace('conf-', '')) 639 self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, dtb_path, section) 640 else: 641 # Image nodes always need a hash which gets indirectly signed by the config signature 642 hash_algo = values.get('Hash algo', None) 643 self.assertEqual(hash_algo, fit_hash_alg) 644 hash_value = values.get('Hash value', None) 645 self.assertEqual(len(hash_value), fit_hash_alg_len, 'Hash value for section %s not expected length' % section) 646 # Optionally, if FIT_SIGN_INDIVIDUAL = 1 also the image nodes have a signature (which is redundant but possible) 647 if fit_sign_individual == "1": 648 sign_algo = values.get('Sign algo', None) 649 req_sign_algo = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_img_keyname) 650 self.assertEqual(sign_algo, req_sign_algo, 'Signature algorithm for %s not expected value' % section) 651 sign_value = values.get('Sign value', None) 652 self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section) 653 654 # Search for the string passed to mkimage in each signed section of the FIT image. 655 # Looks like mkimage supports to add a comment but does not support to read it back. 656 a_comment = FitImageTestCase._get_uboot_mkimage_sign_args(bb_vars['UBOOT_MKIMAGE_SIGN_ARGS']) 657 self.logger.debug("a_comment: %s" % a_comment) 658 if a_comment: 659 found_comments = FitImageTestCase._find_string_in_bin_file(fitimage_path, a_comment) 660 self.assertEqual(found_comments, num_signatures, "Expected %d signed and commented (%s) sections in the fitImage." % 661 (num_signatures, a_comment)) 662 663 664 def test_fit_image(self): 665 """ 666 Summary: Check if FIT image and Image Tree Source (its) are built 667 and the Image Tree Source has the correct fields. 668 Expected: 1. fitImage and fitImage-its can be built 669 2. The type, load address, entrypoint address and 670 default values of kernel and ramdisk are as expected 671 in the Image Tree Source. Not all the fields are tested, 672 only the key fields that wont vary between different 673 architectures. 674 Product: oe-core 675 Author: Usama Arif <usama.arif@arm.com> 676 """ 677 config = """ 678# Enable creation of fitImage 679KERNEL_IMAGETYPE = "Image" 680KERNEL_IMAGETYPES += " fitImage " 681KERNEL_CLASSES = " kernel-fitimage " 682 683# RAM disk variables including load address and entrypoint for kernel and RAM disk 684IMAGE_FSTYPES += "cpio.gz" 685INITRAMFS_IMAGE = "core-image-minimal" 686# core-image-minimal is used as initramfs here, drop the rootfs suffix 687IMAGE_NAME_SUFFIX:pn-core-image-minimal = "" 688UBOOT_RD_LOADADDRESS = "0x88000000" 689UBOOT_RD_ENTRYPOINT = "0x88000000" 690UBOOT_LOADADDRESS = "0x80080000" 691UBOOT_ENTRYPOINT = "0x80080000" 692FIT_DESC = "A model description" 693""" 694 self.write_config(config) 695 bb_vars = self._fit_get_bb_vars() 696 self._test_fitimage(bb_vars) 697 698 699 def test_sign_fit_image_configurations(self): 700 """ 701 Summary: Check if FIT image and Image Tree Source (its) are created 702 and the configuration nodes are signed correctly. 703 Expected: 1) its and FIT image are built successfully 704 2) Scanning the its file indicates signing is enabled 705 as requested by UBOOT_SIGN_ENABLE (using 1 key 706 generated by the test not via FIT_GENERATE_KEYS) 707 3) Dumping the FIT image indicates signature values 708 are present (only for the configuration nodes as 709 FIT_SIGN_INDIVIDUAL is disabled) 710 4) Verify the FIT image contains the comments passed via 711 UBOOT_MKIMAGE_SIGN_ARGS once per configuration node. 712 """ 713 # Generate a configuration section which gets included into the local.conf file 714 config = """ 715# Enable creation of fitImage 716MACHINE = "beaglebone-yocto" 717KERNEL_IMAGETYPES += " fitImage " 718KERNEL_CLASSES = " kernel-fitimage " 719UBOOT_SIGN_ENABLE = "1" 720UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 721UBOOT_SIGN_KEYNAME = "dev" 722UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" 723""" 724 config = self._config_add_uboot_env(config) 725 self.write_config(config) 726 727 # Retrieve some variables from bitbake 728 bb_vars = self._fit_get_bb_vars([ 729 'FIT_KEY_GENRSA_ARGS', 730 'FIT_KEY_REQ_ARGS', 731 'FIT_KEY_SIGN_PKCS', 732 'FIT_SIGN_NUMBITS', 733 'UBOOT_SIGN_KEYDIR', 734 ]) 735 736 # Do not use the random keys generated by FIT_GENERATE_KEYS. 737 # Using a static key is probably a more realistic scenario. 738 self._gen_signing_key(bb_vars) 739 740 self._test_fitimage(bb_vars) 741 742 def test_sign_fit_image_individual(self): 743 """ 744 Summary: Check if FIT image and Image Tree Source (its) are created 745 and all nodes are signed correctly. 746 Expected: 1) its and FIT image are built successfully 747 2) Scanning the its file indicates signing is enabled 748 as requested by UBOOT_SIGN_ENABLE (using 2 keys 749 generated via FIT_GENERATE_KEYS) 750 3) Dumping the FIT image indicates signature values 751 are present (including for images as enabled via 752 FIT_SIGN_INDIVIDUAL) 753 4) Verify the FIT image contains the comments passed via 754 UBOOT_MKIMAGE_SIGN_ARGS once per image and per 755 configuration node. 756 Note: This test is mostly for backward compatibility. 757 The recommended approach is to sign the configuration nodes 758 which include also the hashes of all the images. Signing 759 all the images individually is therefore redundant. 760 Product: oe-core 761 Author: Paul Eggleton <paul.eggleton@microsoft.com> based upon 762 work by Usama Arif <usama.arif@arm.com> 763 """ 764 # Generate a configuration section which gets included into the local.conf file 765 config = """ 766# Enable creation of fitImage 767MACHINE = "beaglebone-yocto" 768KERNEL_IMAGETYPES += " fitImage " 769KERNEL_CLASSES = " kernel-fitimage " 770UBOOT_SIGN_ENABLE = "1" 771FIT_GENERATE_KEYS = "1" 772UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 773UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" 774UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" 775FIT_SIGN_INDIVIDUAL = "1" 776UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" 777""" 778 config = self._config_add_uboot_env(config) 779 self.write_config(config) 780 bb_vars = self._fit_get_bb_vars() 781 self._test_fitimage(bb_vars) 782 783 def test_fit_image_sign_initramfs(self): 784 """ 785 Summary: Verifies the content of the initramfs node in the FIT Image Tree Source (its) 786 The FIT settings are set by the test case. 787 The machine used is beaglebone-yocto. 788 Expected: 1. The ITS is generated with initramfs support 789 2. All the fields in the kernel node are as expected (matching the 790 conf settings) 791 3. The kernel is included in all the available configurations and 792 its hash is included in the configuration signature 793 794 Product: oe-core 795 Author: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> 796 """ 797 798 config = """ 799DISTRO="poky" 800MACHINE = "beaglebone-yocto" 801INITRAMFS_IMAGE = "core-image-minimal-initramfs" 802INITRAMFS_SCRIPTS = "" 803UBOOT_MACHINE = "am335x_evm_defconfig" 804KERNEL_CLASSES = " kernel-fitimage " 805KERNEL_IMAGETYPES = "fitImage" 806UBOOT_SIGN_ENABLE = "1" 807UBOOT_SIGN_KEYNAME = "beaglebonekey" 808UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}" 809UBOOT_DTB_BINARY = "u-boot.dtb" 810UBOOT_ENTRYPOINT = "0x80000000" 811UBOOT_LOADADDRESS = "0x80000000" 812UBOOT_RD_LOADADDRESS = "0x88000000" 813UBOOT_RD_ENTRYPOINT = "0x88000000" 814UBOOT_DTB_LOADADDRESS = "0x82000000" 815UBOOT_ARCH = "arm" 816UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 817UBOOT_MKIMAGE_KERNEL_TYPE = "kernel" 818UBOOT_EXTLINUX = "0" 819FIT_GENERATE_KEYS = "1" 820KERNEL_IMAGETYPE_REPLACEMENT = "zImage" 821FIT_KERNEL_COMP_ALG = "none" 822FIT_HASH_ALG = "sha256" 823""" 824 config = self._config_add_uboot_env(config) 825 self.write_config(config) 826 827 # Retrieve some variables from bitbake 828 bb_vars = self._fit_get_bb_vars([ 829 'FIT_KEY_GENRSA_ARGS', 830 'FIT_KEY_REQ_ARGS', 831 'FIT_KEY_SIGN_PKCS', 832 'FIT_SIGN_NUMBITS', 833 'UBOOT_SIGN_KEYDIR', 834 ]) 835 836 # Do not use the random keys generated by FIT_GENERATE_KEYS. 837 # Using a static key is probably a more realistic scenario. 838 self._gen_signing_key(bb_vars) 839 840 self._test_fitimage(bb_vars) 841 842 def test_fit_image_sign_initramfs_bundle(self): 843 """ 844 Summary: Verifies the content of the initramfs bundle node in the FIT Image Tree Source (its) 845 The FIT settings are set by the test case. 846 The machine used is beaglebone-yocto. 847 Expected: 1. The ITS is generated with initramfs bundle support 848 2. All the fields in the kernel node are as expected (matching the 849 conf settings) 850 3. The kernel is included in all the available configurations and 851 its hash is included in the configuration signature 852 853 Product: oe-core 854 Author: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> 855 """ 856 857 config = """ 858DISTRO="poky" 859MACHINE = "beaglebone-yocto" 860INITRAMFS_IMAGE_BUNDLE = "1" 861INITRAMFS_IMAGE = "core-image-minimal-initramfs" 862INITRAMFS_SCRIPTS = "" 863UBOOT_MACHINE = "am335x_evm_defconfig" 864KERNEL_CLASSES = " kernel-fitimage " 865KERNEL_IMAGETYPES = "fitImage" 866UBOOT_SIGN_ENABLE = "1" 867UBOOT_SIGN_KEYNAME = "beaglebonekey" 868UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}" 869UBOOT_DTB_BINARY = "u-boot.dtb" 870UBOOT_ENTRYPOINT = "0x80000000" 871UBOOT_LOADADDRESS = "0x80000000" 872UBOOT_DTB_LOADADDRESS = "0x82000000" 873UBOOT_ARCH = "arm" 874UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 875UBOOT_MKIMAGE_KERNEL_TYPE = "kernel" 876UBOOT_EXTLINUX = "0" 877FIT_GENERATE_KEYS = "1" 878KERNEL_IMAGETYPE_REPLACEMENT = "zImage" 879FIT_KERNEL_COMP_ALG = "none" 880FIT_HASH_ALG = "sha256" 881""" 882 config = self._config_add_uboot_env(config) 883 self.write_config(config) 884 bb_vars = self._fit_get_bb_vars() 885 self._test_fitimage(bb_vars) 886 887 888class UBootFitImageTests(FitImageTestCase): 889 """Test cases for the uboot-sign bbclass""" 890 891 def _fit_get_bb_vars(self, additional_vars=[]): 892 """Get bb_vars as needed by _test_sign_fit_image 893 894 Call the get_bb_vars function once and get all variables needed by the test case. 895 """ 896 internal_used = { 897 'DEPLOY_DIR_IMAGE', 898 'FIT_HASH_ALG', 899 'FIT_KEY_GENRSA_ARGS', 900 'FIT_KEY_REQ_ARGS', 901 'FIT_KEY_SIGN_PKCS', 902 'FIT_SIGN_ALG', 903 'FIT_SIGN_INDIVIDUAL', 904 'FIT_SIGN_NUMBITS', 905 'MACHINE', 906 'SPL_MKIMAGE_SIGN_ARGS', 907 'SPL_SIGN_ENABLE', 908 'SPL_SIGN_KEYNAME', 909 'UBOOT_ARCH', 910 'UBOOT_DTB_BINARY', 911 'UBOOT_DTB_IMAGE', 912 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT', 913 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS', 914 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE', 915 'UBOOT_FIT_CONF_USER_LOADABLES', 916 'UBOOT_FIT_DESC', 917 'UBOOT_FIT_HASH_ALG', 918 'UBOOT_FIT_SIGN_ALG', 919 'UBOOT_FIT_TEE_ENTRYPOINT', 920 'UBOOT_FIT_TEE_LOADADDRESS', 921 'UBOOT_FIT_TEE', 922 'UBOOT_FIT_UBOOT_ENTRYPOINT', 923 'UBOOT_FIT_UBOOT_LOADADDRESS', 924 'UBOOT_FIT_USER_SETTINGS', 925 'UBOOT_FITIMAGE_ENABLE', 926 'UBOOT_NODTB_BINARY', 927 'UBOOT_SIGN_ENABLE', 928 'UBOOT_SIGN_IMG_KEYNAME', 929 'UBOOT_SIGN_KEYDIR', 930 'UBOOT_SIGN_KEYNAME', 931 } 932 bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), "virtual/bootloader") 933 self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4)) 934 return bb_vars 935 936 def _bitbake_fit_image(self, bb_vars): 937 """Bitbake the bootloader and return the paths to the its file and the FIT image""" 938 bitbake("virtual/bootloader") 939 940 deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE'] 941 machine = bb_vars['MACHINE'] 942 fitimage_its_path = os.path.join(deploy_dir_image, "u-boot-its-%s" % machine) 943 fitimage_path = os.path.join(deploy_dir_image, "u-boot-fitImage-%s" % machine) 944 return (fitimage_its_path, fitimage_path) 945 946 def _get_req_its_paths(self, bb_vars): 947 # image nodes 948 images = [ 'uboot', 'fdt', ] 949 if bb_vars['UBOOT_FIT_TEE'] == "1": 950 images.append('tee') 951 if bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE'] == "1": 952 images.append('atf') 953 # if bb_vars['UBOOT_FIT_USER_SETTINGS']: 954 955 # configuration nodes 956 configurations = [ 'conf'] 957 958 # Create a list of paths for all image and configuration nodes 959 req_its_paths = [] 960 for image in images: 961 req_its_paths.append(['/', 'images', image]) 962 if bb_vars['SPL_SIGN_ENABLE'] == "1": 963 req_its_paths.append(['/', 'images', image, 'signature']) 964 for configuration in configurations: 965 req_its_paths.append(['/', 'configurations', configuration]) 966 return req_its_paths 967 968 def _get_req_its_fields(self, bb_vars): 969 loadables = ["uboot"] 970 its_field_check = [ 971 'description = "%s";' % bb_vars['UBOOT_FIT_DESC'], 972 'description = "U-Boot image";', 973 'data = /incbin/("%s");' % bb_vars['UBOOT_NODTB_BINARY'], 974 'type = "standalone";', 975 'os = "u-boot";', 976 'arch = "%s";' % bb_vars['UBOOT_ARCH'], 977 'compression = "none";', 978 'load = <%s>;' % bb_vars['UBOOT_FIT_UBOOT_LOADADDRESS'], 979 'entry = <%s>;' % bb_vars['UBOOT_FIT_UBOOT_ENTRYPOINT'], 980 'description = "U-Boot FDT";', 981 'data = /incbin/("%s");' % bb_vars['UBOOT_DTB_BINARY'], 982 'type = "flat_dt";', 983 'arch = "%s";' % bb_vars['UBOOT_ARCH'], 984 'compression = "none";', 985 ] 986 if bb_vars['UBOOT_FIT_TEE'] == "1": 987 its_field_check += [ 988 'description = "Trusted Execution Environment";', 989 'data = /incbin/("%s");' % bb_vars['UBOOT_FIT_TEE_IMAGE'], 990 'type = "tee";', 991 'arch = "%s";' % bb_vars['UBOOT_ARCH'], 992 'os = "tee";', 993 'load = <%s>;' % bb_vars['UBOOT_FIT_TEE_LOADADDRESS'], 994 'entry = <%s>;' % bb_vars['UBOOT_FIT_TEE_ENTRYPOINT'], 995 'compression = "none";', 996 ] 997 loadables.insert(0, "tee") 998 if bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE'] == "1": 999 its_field_check += [ 1000 'description = "ARM Trusted Firmware";', 1001 'data = /incbin/("%s");' % bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE'], 1002 'type = "firmware";', 1003 'arch = "%s";' % bb_vars['UBOOT_ARCH'], 1004 'os = "arm-trusted-firmware";', 1005 'load = <%s>;' % bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS'], 1006 'entry = <%s>;' % bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT'], 1007 'compression = "none";', 1008 ] 1009 loadables.insert(0, "atf") 1010 its_field_check += [ 1011 'default = "conf";', 1012 'description = "Boot with signed U-Boot FIT";', 1013 'loadables = "%s";' % '", "'.join(loadables), 1014 'fdt = "fdt";', 1015 ] 1016 return its_field_check 1017 1018 def _get_req_sigvalues_config(self, bb_vars): 1019 # COnfigurations are not signed by uboot-sign 1020 return {} 1021 1022 def _get_req_sigvalues_image(self, bb_vars): 1023 if bb_vars['SPL_SIGN_ENABLE'] != "1": 1024 return {} 1025 req_sigvalues_image = { 1026 'algo': '"%s,%s"' % (bb_vars['UBOOT_FIT_HASH_ALG'], bb_vars['UBOOT_FIT_SIGN_ALG']), 1027 'key-name-hint': '"%s"' % bb_vars['SPL_SIGN_KEYNAME'], 1028 } 1029 return req_sigvalues_image 1030 1031 def _get_req_sections(self, bb_vars): 1032 """Generate the expected output of dumpimage for beaglebone targets 1033 1034 The dict generated by this function is supposed to be compared against 1035 the dict which is generated by the _dump_fitimage function. 1036 """ 1037 loadables = ['uboot'] 1038 req_sections = { 1039 "uboot": { 1040 "Type": "Standalone Program", 1041 "Load Address": bb_vars['UBOOT_FIT_UBOOT_LOADADDRESS'], 1042 "Entry Point": bb_vars['UBOOT_FIT_UBOOT_ENTRYPOINT'], 1043 }, 1044 "fdt": { 1045 "Type": "Flat Device Tree", 1046 } 1047 } 1048 if bb_vars['UBOOT_FIT_TEE'] == "1": 1049 loadables.insert(0, "tee") 1050 req_sections['tee'] = { 1051 "Type": "Trusted Execution Environment Image", 1052 # "Load Address": bb_vars['UBOOT_FIT_TEE_LOADADDRESS'], not printed by mkimage? 1053 # "Entry Point": bb_vars['UBOOT_FIT_TEE_ENTRYPOINT'], not printed by mkimage? 1054 } 1055 if bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE'] == "1": 1056 loadables.insert(0, "atf") 1057 req_sections['atf'] = { 1058 "Type": "Firmware", 1059 "Load Address": bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS'], 1060 # "Entry Point": bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT'], not printed by mkimage? 1061 } 1062 req_sections["conf"] = { 1063 "Kernel": "unavailable", 1064 "FDT": "fdt", 1065 "Loadables": ','.join(loadables), 1066 } 1067 1068 # Add signing related properties if needed 1069 uboot_fit_hash_alg = bb_vars['UBOOT_FIT_HASH_ALG'] 1070 uboot_fit_sign_alg = bb_vars['UBOOT_FIT_SIGN_ALG'] 1071 spl_sign_enable = bb_vars['SPL_SIGN_ENABLE'] 1072 spl_sign_keyname = bb_vars['SPL_SIGN_KEYNAME'] 1073 num_signatures = 0 1074 if spl_sign_enable == "1": 1075 for section in req_sections: 1076 if not section.startswith('conf'): 1077 req_sections[section]['Sign algo'] = "%s,%s:%s" % \ 1078 (uboot_fit_hash_alg, uboot_fit_sign_alg, spl_sign_keyname) 1079 num_signatures += 1 1080 return (req_sections, num_signatures) 1081 1082 def _check_signing(self, bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path): 1083 if bb_vars['UBOOT_FITIMAGE_ENABLE'] == '1' and bb_vars['SPL_SIGN_ENABLE'] == "1": 1084 self.logger.debug("Verifying signatures in the FIT image") 1085 else: 1086 self.logger.debug("FIT image is not signed. Signature verification is not needed.") 1087 return 1088 1089 uboot_fit_hash_alg = bb_vars['UBOOT_FIT_HASH_ALG'] 1090 uboot_fit_sign_alg = bb_vars['UBOOT_FIT_SIGN_ALG'] 1091 spl_sign_keyname = bb_vars['SPL_SIGN_KEYNAME'] 1092 fit_sign_alg_len = FitImageTestCase.MKIMAGE_SIGNATURE_LENGTHS[uboot_fit_sign_alg] 1093 for section, values in sections.items(): 1094 # Configuration nodes are always signed with UBOOT_SIGN_KEYNAME (if UBOOT_SIGN_ENABLE = "1") 1095 if section.startswith("conf"): 1096 # uboot-sign does not sign configuration nodes 1097 pass 1098 else: 1099 # uboot-sign does not add hash nodes, only image signatures 1100 sign_algo = values.get('Sign algo', None) 1101 req_sign_algo = "%s,%s:%s" % (uboot_fit_hash_alg, uboot_fit_sign_alg, spl_sign_keyname) 1102 self.assertEqual(sign_algo, req_sign_algo, 'Signature algorithm for %s not expected value' % section) 1103 sign_value = values.get('Sign value', None) 1104 self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section) 1105 1106 # Search for the string passed to mkimage in each signed section of the FIT image. 1107 # Looks like mkimage supports to add a comment but does not support to read it back. 1108 a_comment = FitImageTestCase._get_uboot_mkimage_sign_args(bb_vars['SPL_MKIMAGE_SIGN_ARGS']) 1109 self.logger.debug("a_comment: %s" % a_comment) 1110 if a_comment: 1111 found_comments = FitImageTestCase._find_string_in_bin_file(fitimage_path, a_comment) 1112 self.assertEqual(found_comments, num_signatures, "Expected %d signed and commented (%s) sections in the fitImage." % 1113 (num_signatures, a_comment)) 1114 1115 def _check_kernel_dtb(self, bb_vars): 1116 """ 1117 Check if the device-tree from U-Boot has the kernel public key(s). 1118 1119 The concat_dtb function of the uboot-sign.bbclass injects the public keys 1120 which are required for verifying the kernel at run-time into the DTB from 1121 U-Boot. The following example is from a build with FIT_SIGN_INDIVIDUAL 1122 set to "1". If it is set to "0" the key-the-kernel-image-key node is not 1123 present. 1124 / { 1125 ... 1126 signature { 1127 key-the-kernel-image-key { 1128 required = "image"; 1129 algo = "sha256,rsa2048"; 1130 ... 1131 }; 1132 key-the-kernel-config-key { 1133 required = "conf"; 1134 algo = "sha256,rsa2048"; 1135 ... 1136 }; 1137 }; 1138 """ 1139 # Setup u-boot-tools-native 1140 dtc_bindir = FitImageTestCase._setup_native('dtc-native') 1141 1142 # Check if 1 or 2 signature sections are in the DTB. 1143 uboot_dtb_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], bb_vars['UBOOT_DTB_IMAGE']) 1144 algo = "%s,%s" % (bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG']) 1145 if bb_vars['FIT_SIGN_INDIVIDUAL'] == "1": 1146 uboot_sign_img_keyname = bb_vars['UBOOT_SIGN_IMG_KEYNAME'] 1147 key_dtb_path = "/signature/key-" + uboot_sign_img_keyname 1148 self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "required", "image") 1149 self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "algo", algo) 1150 self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "key-name-hint", uboot_sign_img_keyname) 1151 1152 uboot_sign_keyname = bb_vars['UBOOT_SIGN_KEYNAME'] 1153 key_dtb_path = "/signature/key-" + uboot_sign_keyname 1154 self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "required", "conf") 1155 self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "algo", algo) 1156 self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "key-name-hint", uboot_sign_keyname) 1157 1158 1159 def test_uboot_fit_image(self): 1160 """ 1161 Summary: Check if Uboot FIT image and Image Tree Source 1162 (its) are built and the Image Tree Source has the 1163 correct fields. 1164 Expected: 1. u-boot-fitImage and u-boot-its can be built 1165 2. The type, load address, entrypoint address and 1166 default values of U-boot image are correct in the 1167 Image Tree Source. Not all the fields are tested, 1168 only the key fields that wont vary between 1169 different architectures. 1170 Product: oe-core 1171 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> 1172 based on work by Usama Arif <usama.arif@arm.com> 1173 """ 1174 config = """ 1175# We need at least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set 1176MACHINE = "qemuarm" 1177UBOOT_MACHINE = "am57xx_evm_defconfig" 1178SPL_BINARY = "MLO" 1179 1180# Enable creation of the U-Boot fitImage 1181UBOOT_FITIMAGE_ENABLE = "1" 1182 1183# (U-boot) fitImage properties 1184UBOOT_LOADADDRESS = "0x80080000" 1185UBOOT_ENTRYPOINT = "0x80080000" 1186UBOOT_FIT_DESC = "A model description" 1187""" 1188 self.write_config(config) 1189 bb_vars = self._fit_get_bb_vars() 1190 self._test_fitimage(bb_vars) 1191 1192 1193 def test_sign_standalone_uboot_fit_image(self): 1194 """ 1195 Summary: Check if U-Boot FIT image and Image Tree Source (its) are 1196 created and signed correctly for the scenario where only 1197 the U-Boot proper fitImage is being created and signed. 1198 Expected: 1) U-Boot its and FIT image are built successfully 1199 2) Scanning the its file indicates signing is enabled 1200 as requested by SPL_SIGN_ENABLE (using keys generated 1201 via UBOOT_FIT_GENERATE_KEYS) 1202 3) Dumping the FIT image indicates signature values 1203 are present 1204 4) Examination of the do_uboot_assemble_fitimage 1205 runfile/logfile indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN 1206 and SPL_MKIMAGE_SIGN_ARGS are working as expected. 1207 Product: oe-core 1208 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> based upon 1209 work by Paul Eggleton <paul.eggleton@microsoft.com> and 1210 Usama Arif <usama.arif@arm.com> 1211 """ 1212 config = """ 1213# There's no U-boot defconfig with CONFIG_FIT_SIGNATURE yet, so we need at 1214# least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set 1215MACHINE = "qemuarm" 1216UBOOT_MACHINE = "am57xx_evm_defconfig" 1217SPL_BINARY = "MLO" 1218# Enable creation and signing of the U-Boot fitImage 1219UBOOT_FITIMAGE_ENABLE = "1" 1220SPL_SIGN_ENABLE = "1" 1221SPL_SIGN_KEYNAME = "spl-oe-selftest" 1222SPL_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 1223UBOOT_DTB_BINARY = "u-boot.dtb" 1224UBOOT_ENTRYPOINT = "0x80000000" 1225UBOOT_LOADADDRESS = "0x80000000" 1226UBOOT_DTB_LOADADDRESS = "0x82000000" 1227UBOOT_ARCH = "arm" 1228SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 1229SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'" 1230UBOOT_EXTLINUX = "0" 1231UBOOT_FIT_GENERATE_KEYS = "1" 1232UBOOT_FIT_HASH_ALG = "sha256" 1233""" 1234 self.write_config(config) 1235 bb_vars = self._fit_get_bb_vars() 1236 self._test_fitimage(bb_vars) 1237 1238 1239 def test_sign_cascaded_uboot_fit_image(self): 1240 """ 1241 Summary: Check if U-Boot FIT image and Image Tree Source (its) are 1242 created and signed correctly for the scenario where both 1243 U-Boot proper and Kernel fitImages are being created and 1244 signed. 1245 Expected: 1) U-Boot its and FIT image are built successfully 1246 2) Scanning the its file indicates signing is enabled 1247 as requested by SPL_SIGN_ENABLE (using keys generated 1248 via UBOOT_FIT_GENERATE_KEYS) 1249 3) Dumping the FIT image indicates signature values 1250 are present 1251 4) Examination of the do_uboot_assemble_fitimage that 1252 UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN and SPL_MKIMAGE_SIGN_ARGS 1253 are working as expected. 1254 Product: oe-core 1255 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> based upon 1256 work by Paul Eggleton <paul.eggleton@microsoft.com> and 1257 Usama Arif <usama.arif@arm.com> 1258 """ 1259 config = """ 1260# There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at 1261# least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set 1262MACHINE = "qemuarm" 1263UBOOT_MACHINE = "am57xx_evm_defconfig" 1264SPL_BINARY = "MLO" 1265# Enable creation and signing of the U-Boot fitImage 1266UBOOT_FITIMAGE_ENABLE = "1" 1267SPL_SIGN_ENABLE = "1" 1268SPL_SIGN_KEYNAME = "spl-cascaded-oe-selftest" 1269SPL_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 1270UBOOT_DTB_BINARY = "u-boot.dtb" 1271UBOOT_ENTRYPOINT = "0x80000000" 1272UBOOT_LOADADDRESS = "0x80000000" 1273UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 1274UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded U-Boot comment'" 1275UBOOT_DTB_LOADADDRESS = "0x82000000" 1276UBOOT_ARCH = "arm" 1277SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 1278SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded U-Boot comment'" 1279UBOOT_EXTLINUX = "0" 1280UBOOT_FIT_GENERATE_KEYS = "1" 1281UBOOT_FIT_HASH_ALG = "sha256" 1282UBOOT_SIGN_ENABLE = "1" 1283UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 1284UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" 1285""" 1286 self.write_config(config) 1287 bb_vars = self._fit_get_bb_vars() 1288 1289 # Using a static key. FIT_GENERATE_KEYS = "1" does not work without kernel-fitimage.bbclass 1290 self._gen_signing_key(bb_vars) 1291 1292 self._test_fitimage(bb_vars) 1293 self._check_kernel_dtb(bb_vars) 1294 1295 def test_uboot_atf_tee_fit_image(self): 1296 """ 1297 Summary: Check if U-boot FIT image and Image Tree Source 1298 (its) are built and the Image Tree Source has the 1299 correct fields. 1300 Expected: 1. Create atf and tee dummy images 1301 2. Both u-boot-fitImage and u-boot-its can be built 1302 3. The os, load address, entrypoint address and 1303 default values of U-boot, ATF and TEE images are 1304 correct in the Image Tree Source. Not all the 1305 fields are tested, only the key fields that wont 1306 vary between different architectures. 1307 Product: oe-core 1308 Author: Jamin Lin <jamin_lin@aspeedtech.com> 1309 """ 1310 config = """ 1311# We need at least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set 1312MACHINE = "qemuarm" 1313UBOOT_MACHINE = "am57xx_evm_defconfig" 1314SPL_BINARY = "MLO" 1315 1316# Enable creation of the U-Boot fitImage 1317UBOOT_FITIMAGE_ENABLE = "1" 1318 1319# (U-boot) fitImage properties 1320UBOOT_LOADADDRESS = "0x80080000" 1321UBOOT_ENTRYPOINT = "0x80080000" 1322UBOOT_FIT_DESC = "A model description" 1323 1324# Enable creation of the TEE fitImage 1325UBOOT_FIT_TEE = "1" 1326 1327# TEE fitImage properties 1328UBOOT_FIT_TEE_IMAGE = "${TOPDIR}/tee-dummy.bin" 1329UBOOT_FIT_TEE_LOADADDRESS = "0x80180000" 1330UBOOT_FIT_TEE_ENTRYPOINT = "0x80180000" 1331 1332# Enable creation of the ATF fitImage 1333UBOOT_FIT_ARM_TRUSTED_FIRMWARE = "1" 1334 1335# ATF fitImage properties 1336UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE = "${TOPDIR}/atf-dummy.bin" 1337UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS = "0x80280000" 1338UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT = "0x80280000" 1339""" 1340 self.write_config(config) 1341 1342 bb_vars = self._fit_get_bb_vars([ 1343 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE', 1344 'UBOOT_FIT_TEE_IMAGE', 1345 ]) 1346 1347 # Create an ATF dummy image 1348 dummy_atf = os.path.join(self.builddir, bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE']) 1349 FitImageTestCase._gen_random_file(dummy_atf) 1350 1351 # Create a TEE dummy image 1352 dummy_tee = os.path.join(self.builddir, bb_vars['UBOOT_FIT_TEE_IMAGE']) 1353 FitImageTestCase._gen_random_file(dummy_tee) 1354 1355 self._test_fitimage(bb_vars) 1356 1357 def test_sign_standalone_uboot_atf_tee_fit_image(self): 1358 """ 1359 Summary: Check if U-Boot FIT image and Image Tree Source (its) are 1360 created and signed correctly for the scenario where only 1361 the U-Boot proper fitImage is being created and signed. 1362 Expected: 1. Create atf and tee dummy images 1363 2. U-Boot its and FIT image are built successfully 1364 3. Scanning the its file indicates signing is enabled 1365 as requested by SPL_SIGN_ENABLE (using keys generated 1366 via UBOOT_FIT_GENERATE_KEYS) 1367 4. Dumping the FIT image indicates signature values 1368 are present 1369 5. Examination of the do_uboot_assemble_fitimage 1370 runfile/logfile indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN 1371 and SPL_MKIMAGE_SIGN_ARGS are working as expected. 1372 Product: oe-core 1373 Author: Jamin Lin <jamin_lin@aspeedtech.com> 1374 """ 1375 config = """ 1376# There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at 1377# least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set 1378MACHINE = "qemuarm" 1379UBOOT_MACHINE = "am57xx_evm_defconfig" 1380SPL_BINARY = "MLO" 1381# Enable creation and signing of the U-Boot fitImage 1382UBOOT_FITIMAGE_ENABLE = "1" 1383SPL_SIGN_ENABLE = "1" 1384SPL_SIGN_KEYNAME = "spl-oe-selftest" 1385SPL_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 1386UBOOT_DTB_BINARY = "u-boot.dtb" 1387UBOOT_ENTRYPOINT = "0x80000000" 1388UBOOT_LOADADDRESS = "0x80000000" 1389UBOOT_ARCH = "arm" 1390SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 1391SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot ATF TEE comment'" 1392UBOOT_EXTLINUX = "0" 1393UBOOT_FIT_GENERATE_KEYS = "1" 1394UBOOT_FIT_HASH_ALG = "sha256" 1395 1396# Enable creation of the TEE fitImage 1397UBOOT_FIT_TEE = "1" 1398 1399# TEE fitImage properties 1400UBOOT_FIT_TEE_IMAGE = "${TOPDIR}/tee-dummy.bin" 1401UBOOT_FIT_TEE_LOADADDRESS = "0x80180000" 1402UBOOT_FIT_TEE_ENTRYPOINT = "0x80180000" 1403 1404# Enable creation of the ATF fitImage 1405UBOOT_FIT_ARM_TRUSTED_FIRMWARE = "1" 1406 1407# ATF fitImage properties 1408UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE = "${TOPDIR}/atf-dummy.bin" 1409UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS = "0x80280000" 1410UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT = "0x80280000" 1411""" 1412 self.write_config(config) 1413 1414 bb_vars = self._fit_get_bb_vars([ 1415 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE', 1416 'UBOOT_FIT_TEE_IMAGE', 1417 ]) 1418 1419 # Create an ATF dummy image 1420 dummy_atf = os.path.join(self.builddir, bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE']) 1421 FitImageTestCase._gen_random_file(dummy_atf) 1422 1423 # Create a TEE dummy image 1424 dummy_tee = os.path.join(self.builddir, bb_vars['UBOOT_FIT_TEE_IMAGE']) 1425 FitImageTestCase._gen_random_file(dummy_tee) 1426 1427 self._test_fitimage(bb_vars) 1428 1429 1430 def test_sign_uboot_kernel_individual(self): 1431 """ 1432 Summary: Check if the device-tree from U-Boot has two public keys 1433 for verifying the kernel FIT image created by the 1434 kernel-fitimage.bbclass included. 1435 This test sets: FIT_SIGN_INDIVIDUAL = "1" 1436 Expected: There must be two signature nodes. One is required for 1437 the individual image nodes, the other is required for the 1438 verification of the configuration section. 1439 """ 1440 config = """ 1441# Enable creation of fitImage 1442MACHINE = "beaglebone-yocto" 1443UBOOT_SIGN_ENABLE = "1" 1444UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 1445UBOOT_SIGN_KEYNAME = "the-kernel-config-key" 1446UBOOT_SIGN_IMG_KEYNAME = "the-kernel-image-key" 1447UBOOT_MKIMAGE_DTCOPTS="-I dts -O dtb -p 2000" 1448FIT_SIGN_INDIVIDUAL = "1" 1449""" 1450 self.write_config(config) 1451 bb_vars = self._fit_get_bb_vars() 1452 1453 # Using a static key. FIT_GENERATE_KEYS = "1" does not work without kernel-fitimage.bbclass 1454 self._gen_signing_key(bb_vars) 1455 1456 bitbake("virtual/bootloader") 1457 1458 # Just check the DTB of u-boot since there is no u-boot FIT image 1459 self._check_kernel_dtb(bb_vars) 1460