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