blob: 7bc171e02dd312f08b0ebc34efcf3f578d8dc407 [file] [log] [blame]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001#
Patrick Williams92b42cb2022-09-03 06:53:57 -05002# Copyright OpenEmbedded Contributors
3#
Andrew Geisslerd1e89492021-02-12 15:35:20 -06004# SPDX-License-Identifier: MIT
5#
6
7from oeqa.selftest.case import OESelftestTestCase
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05008from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
Andrew Geisslerd1e89492021-02-12 15:35:20 -06009import os
Andrew Geisslerd1e89492021-02-12 15:35:20 -060010import re
11
12class FitImageTests(OESelftestTestCase):
13
14 def test_fit_image(self):
15 """
16 Summary: Check if FIT image and Image Tree Source (its) are built
17 and the Image Tree Source has the correct fields.
18 Expected: 1. fitImage and fitImage-its can be built
19 2. The type, load address, entrypoint address and
20 default values of kernel and ramdisk are as expected
21 in the Image Tree Source. Not all the fields are tested,
22 only the key fields that wont vary between different
23 architectures.
24 Product: oe-core
25 Author: Usama Arif <usama.arif@arm.com>
26 """
27 config = """
28# Enable creation of fitImage
29KERNEL_IMAGETYPE = "Image"
30KERNEL_IMAGETYPES += " fitImage "
31KERNEL_CLASSES = " kernel-fitimage "
32
33# RAM disk variables including load address and entrypoint for kernel and RAM disk
34IMAGE_FSTYPES += "cpio.gz"
35INITRAMFS_IMAGE = "core-image-minimal"
36UBOOT_RD_LOADADDRESS = "0x88000000"
37UBOOT_RD_ENTRYPOINT = "0x88000000"
38UBOOT_LOADADDRESS = "0x80080000"
39UBOOT_ENTRYPOINT = "0x80080000"
40FIT_DESC = "A model description"
41"""
42 self.write_config(config)
43
44 # fitImage is created as part of linux recipe
Andrew Geisslerfc113ea2023-03-31 09:59:46 -050045 image = "virtual/kernel"
46 bitbake(image)
47 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'INITRAMFS_IMAGE_NAME', 'KERNEL_FIT_LINK_NAME'], image)
Andrew Geisslerd1e89492021-02-12 15:35:20 -060048
Andrew Geisslerfc113ea2023-03-31 09:59:46 -050049 fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],
50 "fitImage-its-%s-%s" % (bb_vars['INITRAMFS_IMAGE_NAME'], bb_vars['KERNEL_FIT_LINK_NAME']))
51 fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],
52 "fitImage-%s-%s" % (bb_vars['INITRAMFS_IMAGE_NAME'], bb_vars['KERNEL_FIT_LINK_NAME']))
Andrew Geisslerd1e89492021-02-12 15:35:20 -060053
54 self.assertTrue(os.path.exists(fitimage_its_path),
55 "%s image tree source doesn't exist" % (fitimage_its_path))
56 self.assertTrue(os.path.exists(fitimage_path),
57 "%s FIT image doesn't exist" % (fitimage_path))
58
59 # Check that the type, load address, entrypoint address and default
60 # values for kernel and ramdisk in Image Tree Source are as expected.
61 # The order of fields in the below array is important. Not all the
62 # fields are tested, only the key fields that wont vary between
63 # different architectures.
64 its_field_check = [
65 'description = "A model description";',
66 'type = "kernel";',
67 'load = <0x80080000>;',
68 'entry = <0x80080000>;',
69 'type = "ramdisk";',
70 'load = <0x88000000>;',
71 'entry = <0x88000000>;',
Andrew Geissler90fd73c2021-03-05 15:25:55 -060072 'default = "conf-1";',
73 'kernel = "kernel-1";',
74 'ramdisk = "ramdisk-1";'
Andrew Geisslerd1e89492021-02-12 15:35:20 -060075 ]
76
77 with open(fitimage_its_path) as its_file:
78 field_index = 0
79 for line in its_file:
80 if field_index == len(its_field_check):
81 break
82 if its_field_check[field_index] in line:
83 field_index +=1
84
85 if field_index != len(its_field_check): # if its equal, the test passed
86 self.assertTrue(field_index == len(its_field_check),
87 "Fields in Image Tree Source File %s did not match, error in finding %s"
88 % (fitimage_its_path, its_field_check[field_index]))
89
90
91 def test_sign_fit_image(self):
92 """
93 Summary: Check if FIT image and Image Tree Source (its) are created
94 and signed correctly.
95 Expected: 1) its and FIT image are built successfully
96 2) Scanning the its file indicates signing is enabled
97 as requested by UBOOT_SIGN_ENABLE (using keys generated
98 via FIT_GENERATE_KEYS)
99 3) Dumping the FIT image indicates signature values
100 are present (including for images as enabled via
101 FIT_SIGN_INDIVIDUAL)
102 4) Examination of the do_assemble_fitimage runfile/logfile
103 indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN and
104 UBOOT_MKIMAGE_SIGN_ARGS are working as expected.
105 Product: oe-core
106 Author: Paul Eggleton <paul.eggleton@microsoft.com> based upon
107 work by Usama Arif <usama.arif@arm.com>
108 """
109 config = """
110# Enable creation of fitImage
111MACHINE = "beaglebone-yocto"
112KERNEL_IMAGETYPES += " fitImage "
113KERNEL_CLASSES = " kernel-fitimage test-mkimage-wrapper "
114UBOOT_SIGN_ENABLE = "1"
115FIT_GENERATE_KEYS = "1"
116UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500117UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
118UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600119FIT_SIGN_INDIVIDUAL = "1"
120UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
121"""
122 self.write_config(config)
123
124 # fitImage is created as part of linux recipe
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500125 image = "virtual/kernel"
126 bitbake(image)
127 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'KERNEL_FIT_LINK_NAME'], image)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600128
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500129 fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],
130 "fitImage-its-%s" % (bb_vars['KERNEL_FIT_LINK_NAME']))
131 fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],
132 "fitImage-%s.bin" % (bb_vars['KERNEL_FIT_LINK_NAME']))
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600133
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 = [
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600140 ['/', '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'],
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600146 ]
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[:])
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600161 elif itspath and itspath[-1] == 'signature-1':
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600162 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"',
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500176 'key-name-hint': '"img-oe-selftest"',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600177 }
178 reqsigvalues_config = {
179 'algo': '"sha256,rsa2048"',
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500180 'key-name-hint': '"cfg-oe-selftest"',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600181 'sign-images': '"kernel", "fdt"',
182 }
183
184 for itspath, values in sigs.items():
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600185 if 'conf-' in itspath:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600186 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()
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600213 self.assertIn('kernel-1', signed_sections)
214 self.assertIn('fdt-am335x-boneblack.dtb', signed_sections)
215 self.assertIn('conf-am335x-boneblack.dtb', signed_sections)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600216 for signed_section, values in signed_sections.items():
217 value = values.get('Sign algo', None)
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500218 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)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600222 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
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500237 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"
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500272UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
273UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500274FIT_SIGN_INDIVIDUAL = "1"
275"""
276 self.write_config(config)
277
Patrick Williams2390b1b2022-11-03 13:47:49 -0500278 # The U-Boot fitImage is created as part of the U-Boot recipe
279 bitbake("virtual/bootloader")
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500280
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 "
Patrick Williams2390b1b2022-11-03 13:47:49 -0500351KERNEL_CLASSES = " kernel-fitimage "
352INHERIT += "test-mkimage-wrapper"
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500353UBOOT_SIGN_ENABLE = "1"
354FIT_GENERATE_KEYS = "1"
355UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500356UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
357UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500358FIT_SIGN_INDIVIDUAL = "1"
359UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'"
360"""
361 self.write_config(config)
362
Patrick Williams2390b1b2022-11-03 13:47:49 -0500363 # The U-Boot fitImage is created as part of the U-Boot recipe
364 bitbake("virtual/bootloader")
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500365
366 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
367 machine = get_bb_var('MACHINE')
368 fitimage_its_path = os.path.join(deploy_dir_image,
369 "u-boot-its-%s" % (machine,))
370 fitimage_path = os.path.join(deploy_dir_image,
371 "u-boot-fitImage-%s" % (machine,))
372
373 self.assertTrue(os.path.exists(fitimage_its_path),
374 "%s image tree source doesn't exist" % (fitimage_its_path))
375 self.assertTrue(os.path.exists(fitimage_path),
376 "%s FIT image doesn't exist" % (fitimage_path))
377
378 # Check that the type, load address, entrypoint address and default
379 # values for kernel and ramdisk in Image Tree Source are as expected.
380 # The order of fields in the below array is important. Not all the
381 # fields are tested, only the key fields that wont vary between
382 # different architectures.
383 its_field_check = [
384 'description = "A model description";',
385 'type = "standalone";',
386 'load = <0x80080000>;',
387 'entry = <0x80080000>;',
388 'default = "conf";',
389 'loadables = "uboot";',
390 'fdt = "fdt";'
391 ]
392
393 with open(fitimage_its_path) as its_file:
394 field_index = 0
395 for line in its_file:
396 if field_index == len(its_field_check):
397 break
398 if its_field_check[field_index] in line:
399 field_index +=1
400
401 if field_index != len(its_field_check): # if its equal, the test passed
402 self.assertTrue(field_index == len(its_field_check),
403 "Fields in Image Tree Source File %s did not match, error in finding %s"
404 % (fitimage_its_path, its_field_check[field_index]))
405
406
407 def test_sign_standalone_uboot_fit_image(self):
408 """
409 Summary: Check if U-Boot FIT image and Image Tree Source (its) are
410 created and signed correctly for the scenario where only
411 the U-Boot proper fitImage is being created and signed.
412 Expected: 1) U-Boot its and FIT image are built successfully
413 2) Scanning the its file indicates signing is enabled
414 as requested by SPL_SIGN_ENABLE (using keys generated
415 via UBOOT_FIT_GENERATE_KEYS)
416 3) Dumping the FIT image indicates signature values
417 are present
418 4) Examination of the do_uboot_assemble_fitimage
419 runfile/logfile indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN
420 and SPL_MKIMAGE_SIGN_ARGS are working as expected.
421 Product: oe-core
422 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> based upon
423 work by Paul Eggleton <paul.eggleton@microsoft.com> and
424 Usama Arif <usama.arif@arm.com>
425 """
426 config = """
427# There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at
428# least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set
429MACHINE = "qemuarm"
430UBOOT_MACHINE = "am57xx_evm_defconfig"
431SPL_BINARY = "MLO"
432# The kernel-fitimage class is a dependency even if we're only
433# creating/signing the U-Boot fitImage
Patrick Williams2390b1b2022-11-03 13:47:49 -0500434KERNEL_CLASSES = " kernel-fitimage"
435INHERIT += "test-mkimage-wrapper"
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500436# Enable creation and signing of the U-Boot fitImage
437UBOOT_FITIMAGE_ENABLE = "1"
438SPL_SIGN_ENABLE = "1"
439SPL_SIGN_KEYNAME = "spl-oe-selftest"
440SPL_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
441UBOOT_DTB_BINARY = "u-boot.dtb"
442UBOOT_ENTRYPOINT = "0x80000000"
443UBOOT_LOADADDRESS = "0x80000000"
444UBOOT_DTB_LOADADDRESS = "0x82000000"
445UBOOT_ARCH = "arm"
446SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
447SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'"
448UBOOT_EXTLINUX = "0"
449UBOOT_FIT_GENERATE_KEYS = "1"
450UBOOT_FIT_HASH_ALG = "sha256"
451"""
452 self.write_config(config)
453
Patrick Williams2390b1b2022-11-03 13:47:49 -0500454 # The U-Boot fitImage is created as part of the U-Boot recipe
455 bitbake("virtual/bootloader")
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500456
457 image_type = "core-image-minimal"
458 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
459 machine = get_bb_var('MACHINE')
460 fitimage_its_path = os.path.join(deploy_dir_image,
461 "u-boot-its-%s" % (machine,))
462 fitimage_path = os.path.join(deploy_dir_image,
463 "u-boot-fitImage-%s" % (machine,))
464
465 self.assertTrue(os.path.exists(fitimage_its_path),
466 "%s image tree source doesn't exist" % (fitimage_its_path))
467 self.assertTrue(os.path.exists(fitimage_path),
468 "%s FIT image doesn't exist" % (fitimage_path))
469
470 req_itspaths = [
471 ['/', 'images', 'uboot'],
472 ['/', 'images', 'uboot', 'signature'],
473 ['/', 'images', 'fdt'],
474 ['/', 'images', 'fdt', 'signature'],
475 ]
476
477 itspath = []
478 itspaths = []
479 linect = 0
480 sigs = {}
481 with open(fitimage_its_path) as its_file:
482 linect += 1
483 for line in its_file:
484 line = line.strip()
485 if line.endswith('};'):
486 itspath.pop()
487 elif line.endswith('{'):
488 itspath.append(line[:-1].strip())
489 itspaths.append(itspath[:])
490 elif itspath and itspath[-1] == 'signature':
491 itsdotpath = '.'.join(itspath)
492 if not itsdotpath in sigs:
493 sigs[itsdotpath] = {}
494 if not '=' in line or not line.endswith(';'):
495 self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line))
496 key, value = line.split('=', 1)
497 sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';')
498
499 for reqpath in req_itspaths:
500 if not reqpath in itspaths:
501 self.fail('Missing section in its file: %s' % reqpath)
502
503 reqsigvalues_image = {
504 'algo': '"sha256,rsa2048"',
505 'key-name-hint': '"spl-oe-selftest"',
506 }
507
508 for itspath, values in sigs.items():
509 reqsigvalues = reqsigvalues_image
510 for reqkey, reqvalue in reqsigvalues.items():
511 value = values.get(reqkey, None)
512 if value is None:
513 self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath))
514 self.assertEqual(value, reqvalue)
515
516 # Dump the image to see if it really got signed
517 bitbake("u-boot-tools-native -c addto_recipe_sysroot")
518 result = runCmd('bitbake -e u-boot-tools-native | grep ^RECIPE_SYSROOT_NATIVE=')
519 recipe_sysroot_native = result.output.split('=')[1].strip('"')
520 dumpimage_path = os.path.join(recipe_sysroot_native, 'usr', 'bin', 'dumpimage')
521 result = runCmd('%s -l %s' % (dumpimage_path, fitimage_path))
522 in_signed = None
523 signed_sections = {}
524 for line in result.output.splitlines():
525 if line.startswith((' Image')):
526 in_signed = re.search('\((.*)\)', line).groups()[0]
527 elif re.match(' \w', line):
528 in_signed = None
529 elif in_signed:
530 if not in_signed in signed_sections:
531 signed_sections[in_signed] = {}
532 key, value = line.split(':', 1)
533 signed_sections[in_signed][key.strip()] = value.strip()
534 self.assertIn('uboot', signed_sections)
535 self.assertIn('fdt', signed_sections)
536 for signed_section, values in signed_sections.items():
537 value = values.get('Sign algo', None)
538 self.assertEqual(value, 'sha256,rsa2048:spl-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section)
539 value = values.get('Sign value', None)
540 self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section)
541
542 # Check for SPL_MKIMAGE_SIGN_ARGS
Patrick Williams2390b1b2022-11-03 13:47:49 -0500543 result = runCmd('bitbake -e virtual/bootloader | grep ^T=')
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500544 tempdir = result.output.split('=', 1)[1].strip().strip('')
545 result = runCmd('grep "a smart U-Boot comment" %s/run.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
546 self.assertEqual(result.status, 0, 'SPL_MKIMAGE_SIGN_ARGS value did not get used')
547
548 # Check for evidence of test-mkimage-wrapper class
549 result = runCmd('grep "### uboot-mkimage wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
550 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE did not work')
551 result = runCmd('grep "### uboot-mkimage signing wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
552 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN did not work')
553
554 def test_sign_cascaded_uboot_fit_image(self):
555 """
556 Summary: Check if U-Boot FIT image and Image Tree Source (its) are
557 created and signed correctly for the scenario where both
558 U-Boot proper and Kernel fitImages are being created and
559 signed.
560 Expected: 1) U-Boot its and FIT image are built successfully
561 2) Scanning the its file indicates signing is enabled
562 as requested by SPL_SIGN_ENABLE (using keys generated
563 via UBOOT_FIT_GENERATE_KEYS)
564 3) Dumping the FIT image indicates signature values
565 are present
566 4) Examination of the do_uboot_assemble_fitimage
567 runfile/logfile indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN
568 and SPL_MKIMAGE_SIGN_ARGS are working as expected.
569 Product: oe-core
570 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> based upon
571 work by Paul Eggleton <paul.eggleton@microsoft.com> and
572 Usama Arif <usama.arif@arm.com>
573 """
574 config = """
575# There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at
576# least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set
577MACHINE = "qemuarm"
578UBOOT_MACHINE = "am57xx_evm_defconfig"
579SPL_BINARY = "MLO"
580# Enable creation and signing of the U-Boot fitImage
581UBOOT_FITIMAGE_ENABLE = "1"
582SPL_SIGN_ENABLE = "1"
583SPL_SIGN_KEYNAME = "spl-cascaded-oe-selftest"
584SPL_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
585UBOOT_DTB_BINARY = "u-boot.dtb"
586UBOOT_ENTRYPOINT = "0x80000000"
587UBOOT_LOADADDRESS = "0x80000000"
588UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
589UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded Kernel comment'"
590UBOOT_DTB_LOADADDRESS = "0x82000000"
591UBOOT_ARCH = "arm"
592SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
593SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded U-Boot comment'"
594UBOOT_EXTLINUX = "0"
595UBOOT_FIT_GENERATE_KEYS = "1"
596UBOOT_FIT_HASH_ALG = "sha256"
597KERNEL_IMAGETYPES += " fitImage "
Patrick Williams2390b1b2022-11-03 13:47:49 -0500598KERNEL_CLASSES = " kernel-fitimage "
599INHERIT += "test-mkimage-wrapper"
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500600UBOOT_SIGN_ENABLE = "1"
601FIT_GENERATE_KEYS = "1"
602UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500603UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
604UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500605FIT_SIGN_INDIVIDUAL = "1"
606"""
607 self.write_config(config)
608
Patrick Williams2390b1b2022-11-03 13:47:49 -0500609 # The U-Boot fitImage is created as part of the U-Boot recipe
610 bitbake("virtual/bootloader")
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500611
612 image_type = "core-image-minimal"
613 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
614 machine = get_bb_var('MACHINE')
615 fitimage_its_path = os.path.join(deploy_dir_image,
616 "u-boot-its-%s" % (machine,))
617 fitimage_path = os.path.join(deploy_dir_image,
618 "u-boot-fitImage-%s" % (machine,))
619
620 self.assertTrue(os.path.exists(fitimage_its_path),
621 "%s image tree source doesn't exist" % (fitimage_its_path))
622 self.assertTrue(os.path.exists(fitimage_path),
623 "%s FIT image doesn't exist" % (fitimage_path))
624
625 req_itspaths = [
626 ['/', 'images', 'uboot'],
627 ['/', 'images', 'uboot', 'signature'],
628 ['/', 'images', 'fdt'],
629 ['/', 'images', 'fdt', 'signature'],
630 ]
631
632 itspath = []
633 itspaths = []
634 linect = 0
635 sigs = {}
636 with open(fitimage_its_path) as its_file:
637 linect += 1
638 for line in its_file:
639 line = line.strip()
640 if line.endswith('};'):
641 itspath.pop()
642 elif line.endswith('{'):
643 itspath.append(line[:-1].strip())
644 itspaths.append(itspath[:])
645 elif itspath and itspath[-1] == 'signature':
646 itsdotpath = '.'.join(itspath)
647 if not itsdotpath in sigs:
648 sigs[itsdotpath] = {}
649 if not '=' in line or not line.endswith(';'):
650 self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line))
651 key, value = line.split('=', 1)
652 sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';')
653
654 for reqpath in req_itspaths:
655 if not reqpath in itspaths:
656 self.fail('Missing section in its file: %s' % reqpath)
657
658 reqsigvalues_image = {
659 'algo': '"sha256,rsa2048"',
660 'key-name-hint': '"spl-cascaded-oe-selftest"',
661 }
662
663 for itspath, values in sigs.items():
664 reqsigvalues = reqsigvalues_image
665 for reqkey, reqvalue in reqsigvalues.items():
666 value = values.get(reqkey, None)
667 if value is None:
668 self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath))
669 self.assertEqual(value, reqvalue)
670
671 # Dump the image to see if it really got signed
672 bitbake("u-boot-tools-native -c addto_recipe_sysroot")
673 result = runCmd('bitbake -e u-boot-tools-native | grep ^RECIPE_SYSROOT_NATIVE=')
674 recipe_sysroot_native = result.output.split('=')[1].strip('"')
675 dumpimage_path = os.path.join(recipe_sysroot_native, 'usr', 'bin', 'dumpimage')
676 result = runCmd('%s -l %s' % (dumpimage_path, fitimage_path))
677 in_signed = None
678 signed_sections = {}
679 for line in result.output.splitlines():
680 if line.startswith((' Image')):
681 in_signed = re.search('\((.*)\)', line).groups()[0]
682 elif re.match(' \w', line):
683 in_signed = None
684 elif in_signed:
685 if not in_signed in signed_sections:
686 signed_sections[in_signed] = {}
687 key, value = line.split(':', 1)
688 signed_sections[in_signed][key.strip()] = value.strip()
689 self.assertIn('uboot', signed_sections)
690 self.assertIn('fdt', signed_sections)
691 for signed_section, values in signed_sections.items():
692 value = values.get('Sign algo', None)
693 self.assertEqual(value, 'sha256,rsa2048:spl-cascaded-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section)
694 value = values.get('Sign value', None)
695 self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section)
696
697 # Check for SPL_MKIMAGE_SIGN_ARGS
Patrick Williams2390b1b2022-11-03 13:47:49 -0500698 result = runCmd('bitbake -e virtual/bootloader | grep ^T=')
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500699 tempdir = result.output.split('=', 1)[1].strip().strip('')
700 result = runCmd('grep "a smart cascaded U-Boot comment" %s/run.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
701 self.assertEqual(result.status, 0, 'SPL_MKIMAGE_SIGN_ARGS value did not get used')
702
703 # Check for evidence of test-mkimage-wrapper class
704 result = runCmd('grep "### uboot-mkimage wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
705 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE did not work')
706 result = runCmd('grep "### uboot-mkimage signing wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
707 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN did not work')
708
709
710
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600711 def test_initramfs_bundle(self):
712 """
713 Summary: Verifies the content of the initramfs bundle node in the FIT Image Tree Source (its)
714 The FIT settings are set by the test case.
715 The machine used is beaglebone-yocto.
716 Expected: 1. The ITS is generated with initramfs bundle support
717 2. All the fields in the kernel node are as expected (matching the
718 conf settings)
719 3. The kernel is included in all the available configurations and
720 its hash is included in the configuration signature
721
722 Product: oe-core
723 Author: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
724 """
725
726 config = """
727DISTRO="poky"
728MACHINE = "beaglebone-yocto"
729INITRAMFS_IMAGE_BUNDLE = "1"
730INITRAMFS_IMAGE = "core-image-minimal-initramfs"
731INITRAMFS_SCRIPTS = ""
732UBOOT_MACHINE = "am335x_evm_defconfig"
733KERNEL_CLASSES = " kernel-fitimage "
734KERNEL_IMAGETYPES = "fitImage"
735UBOOT_SIGN_ENABLE = "1"
736UBOOT_SIGN_KEYNAME = "beaglebonekey"
737UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}"
738UBOOT_DTB_BINARY = "u-boot.dtb"
739UBOOT_ENTRYPOINT = "0x80000000"
740UBOOT_LOADADDRESS = "0x80000000"
741UBOOT_DTB_LOADADDRESS = "0x82000000"
742UBOOT_ARCH = "arm"
743UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
Andrew Geissler615f2f12022-07-15 14:00:58 -0500744UBOOT_MKIMAGE_KERNEL_TYPE = "kernel"
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600745UBOOT_EXTLINUX = "0"
746FIT_GENERATE_KEYS = "1"
747KERNEL_IMAGETYPE_REPLACEMENT = "zImage"
Andrew Geisslereff27472021-10-29 15:35:00 -0500748FIT_KERNEL_COMP_ALG = "none"
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600749FIT_HASH_ALG = "sha256"
750"""
751 self.write_config(config)
752
753 # fitImage is created as part of linux recipe
754 bitbake("virtual/kernel")
755
756 image_type = get_bb_var('INITRAMFS_IMAGE')
757 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
758 machine = get_bb_var('MACHINE')
759 fitimage_its_path = os.path.join(deploy_dir_image,
760 "fitImage-its-%s-%s-%s" % (image_type, machine, machine))
761 fitimage_path = os.path.join(deploy_dir_image,"fitImage")
762
763 self.assertTrue(os.path.exists(fitimage_its_path),
764 "%s image tree source doesn't exist" % (fitimage_its_path))
765 self.assertTrue(os.path.exists(fitimage_path),
766 "%s FIT image doesn't exist" % (fitimage_path))
767
768 kernel_load = str(get_bb_var('UBOOT_LOADADDRESS'))
769 kernel_entry = str(get_bb_var('UBOOT_ENTRYPOINT'))
Andrew Geissler615f2f12022-07-15 14:00:58 -0500770 kernel_type = str(get_bb_var('UBOOT_MKIMAGE_KERNEL_TYPE'))
Andrew Geisslereff27472021-10-29 15:35:00 -0500771 kernel_compression = str(get_bb_var('FIT_KERNEL_COMP_ALG'))
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600772 uboot_arch = str(get_bb_var('UBOOT_ARCH'))
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600773 fit_hash_alg = str(get_bb_var('FIT_HASH_ALG'))
774
775 its_file = open(fitimage_its_path)
776
777 its_lines = [line.strip() for line in its_file.readlines()]
778
779 exp_node_lines = [
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600780 'kernel-1 {',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600781 'description = "Linux kernel";',
Andrew Geisslereff27472021-10-29 15:35:00 -0500782 'data = /incbin/("linux.bin");',
Andrew Geissler615f2f12022-07-15 14:00:58 -0500783 'type = "' + kernel_type + '";',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600784 'arch = "' + uboot_arch + '";',
785 'os = "linux";',
Andrew Geisslereff27472021-10-29 15:35:00 -0500786 'compression = "' + kernel_compression + '";',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600787 'load = <' + kernel_load + '>;',
788 'entry = <' + kernel_entry + '>;',
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600789 'hash-1 {',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600790 'algo = "' + fit_hash_alg +'";',
791 '};',
792 '};'
793 ]
794
795 node_str = exp_node_lines[0]
796
797 test_passed = False
798
799 print ("checking kernel node\n")
800
801 if node_str in its_lines:
802 node_start_idx = its_lines.index(node_str)
803 node = its_lines[node_start_idx:(node_start_idx + len(exp_node_lines))]
804 if node == exp_node_lines:
805 print("kernel node verified")
806 else:
807 self.assertTrue(test_passed == True,"kernel node does not match expectation")
808
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600809 rx_configs = re.compile("^conf-.*")
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600810 its_configs = list(filter(rx_configs.match, its_lines))
811
812 for cfg_str in its_configs:
813 cfg_start_idx = its_lines.index(cfg_str)
814 line_idx = cfg_start_idx + 2
815 node_end = False
816 while node_end == False:
817 if its_lines[line_idx] == "};" and its_lines[line_idx-1] == "};" :
818 node_end = True
819 line_idx = line_idx + 1
820
821 node = its_lines[cfg_start_idx:line_idx]
822 print("checking configuration " + cfg_str.rstrip(" {"))
823 rx_desc_line = re.compile("^description.*1 Linux kernel.*")
824 if len(list(filter(rx_desc_line.match, node))) != 1:
825 self.assertTrue(test_passed == True,"kernel keyword not found in the description line")
826 break
827 else:
828 print("kernel keyword found in the description line")
829
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600830 if 'kernel = "kernel-1";' not in node:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600831 self.assertTrue(test_passed == True,"kernel line not found")
832 break
833 else:
834 print("kernel line found")
835
836 rx_sign_line = re.compile("^sign-images.*kernel.*")
837 if len(list(filter(rx_sign_line.match, node))) != 1:
838 self.assertTrue(test_passed == True,"kernel hash not signed")
839 break
840 else:
841 print("kernel hash signed")
842
843 test_passed = True
844 self.assertTrue(test_passed == True,"Initramfs bundle test success")