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