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