blob: 14267dbaaa3a2f803b327f47871bc3b10b13acb0 [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
Patrick Williams45852732022-04-02 08:58:32 -05008from oeqa.utils.commands import runCmd, bitbake, get_bb_var
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
45 bitbake("virtual/kernel")
46
47 image_type = "core-image-minimal"
48 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
49 machine = get_bb_var('MACHINE')
50 fitimage_its_path = os.path.join(deploy_dir_image,
51 "fitImage-its-%s-%s-%s" % (image_type, machine, machine))
52 fitimage_path = os.path.join(deploy_dir_image,
53 "fitImage-%s-%s-%s" % (image_type, machine, machine))
54
55 self.assertTrue(os.path.exists(fitimage_its_path),
56 "%s image tree source doesn't exist" % (fitimage_its_path))
57 self.assertTrue(os.path.exists(fitimage_path),
58 "%s FIT image doesn't exist" % (fitimage_path))
59
60 # Check that the type, load address, entrypoint address and default
61 # values for kernel and ramdisk in Image Tree Source are as expected.
62 # The order of fields in the below array is important. Not all the
63 # fields are tested, only the key fields that wont vary between
64 # different architectures.
65 its_field_check = [
66 'description = "A model description";',
67 'type = "kernel";',
68 'load = <0x80080000>;',
69 'entry = <0x80080000>;',
70 'type = "ramdisk";',
71 'load = <0x88000000>;',
72 'entry = <0x88000000>;',
Andrew Geissler90fd73c2021-03-05 15:25:55 -060073 'default = "conf-1";',
74 'kernel = "kernel-1";',
75 'ramdisk = "ramdisk-1";'
Andrew Geisslerd1e89492021-02-12 15:35:20 -060076 ]
77
78 with open(fitimage_its_path) as its_file:
79 field_index = 0
80 for line in its_file:
81 if field_index == len(its_field_check):
82 break
83 if its_field_check[field_index] in line:
84 field_index +=1
85
86 if field_index != len(its_field_check): # if its equal, the test passed
87 self.assertTrue(field_index == len(its_field_check),
88 "Fields in Image Tree Source File %s did not match, error in finding %s"
89 % (fitimage_its_path, its_field_check[field_index]))
90
91
92 def test_sign_fit_image(self):
93 """
94 Summary: Check if FIT image and Image Tree Source (its) are created
95 and signed correctly.
96 Expected: 1) its and FIT image are built successfully
97 2) Scanning the its file indicates signing is enabled
98 as requested by UBOOT_SIGN_ENABLE (using keys generated
99 via FIT_GENERATE_KEYS)
100 3) Dumping the FIT image indicates signature values
101 are present (including for images as enabled via
102 FIT_SIGN_INDIVIDUAL)
103 4) Examination of the do_assemble_fitimage runfile/logfile
104 indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN and
105 UBOOT_MKIMAGE_SIGN_ARGS are working as expected.
106 Product: oe-core
107 Author: Paul Eggleton <paul.eggleton@microsoft.com> based upon
108 work by Usama Arif <usama.arif@arm.com>
109 """
110 config = """
111# Enable creation of fitImage
112MACHINE = "beaglebone-yocto"
113KERNEL_IMAGETYPES += " fitImage "
114KERNEL_CLASSES = " kernel-fitimage test-mkimage-wrapper "
115UBOOT_SIGN_ENABLE = "1"
116FIT_GENERATE_KEYS = "1"
117UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500118UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
119UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600120FIT_SIGN_INDIVIDUAL = "1"
121UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
122"""
123 self.write_config(config)
124
125 # fitImage is created as part of linux recipe
126 bitbake("virtual/kernel")
127
128 image_type = "core-image-minimal"
129 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
130 machine = get_bb_var('MACHINE')
131 fitimage_its_path = os.path.join(deploy_dir_image,
132 "fitImage-its-%s" % (machine,))
133 fitimage_path = os.path.join(deploy_dir_image,
134 "fitImage-%s.bin" % (machine,))
135
136 self.assertTrue(os.path.exists(fitimage_its_path),
137 "%s image tree source doesn't exist" % (fitimage_its_path))
138 self.assertTrue(os.path.exists(fitimage_path),
139 "%s FIT image doesn't exist" % (fitimage_path))
140
141 req_itspaths = [
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600142 ['/', 'images', 'kernel-1'],
143 ['/', 'images', 'kernel-1', 'signature-1'],
144 ['/', 'images', 'fdt-am335x-boneblack.dtb'],
145 ['/', 'images', 'fdt-am335x-boneblack.dtb', 'signature-1'],
146 ['/', 'configurations', 'conf-am335x-boneblack.dtb'],
147 ['/', 'configurations', 'conf-am335x-boneblack.dtb', 'signature-1'],
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600148 ]
149
150 itspath = []
151 itspaths = []
152 linect = 0
153 sigs = {}
154 with open(fitimage_its_path) as its_file:
155 linect += 1
156 for line in its_file:
157 line = line.strip()
158 if line.endswith('};'):
159 itspath.pop()
160 elif line.endswith('{'):
161 itspath.append(line[:-1].strip())
162 itspaths.append(itspath[:])
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600163 elif itspath and itspath[-1] == 'signature-1':
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600164 itsdotpath = '.'.join(itspath)
165 if not itsdotpath in sigs:
166 sigs[itsdotpath] = {}
167 if not '=' in line or not line.endswith(';'):
168 self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line))
169 key, value = line.split('=', 1)
170 sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';')
171
172 for reqpath in req_itspaths:
173 if not reqpath in itspaths:
174 self.fail('Missing section in its file: %s' % reqpath)
175
176 reqsigvalues_image = {
177 'algo': '"sha256,rsa2048"',
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500178 'key-name-hint': '"img-oe-selftest"',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600179 }
180 reqsigvalues_config = {
181 'algo': '"sha256,rsa2048"',
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500182 'key-name-hint': '"cfg-oe-selftest"',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600183 'sign-images': '"kernel", "fdt"',
184 }
185
186 for itspath, values in sigs.items():
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600187 if 'conf-' in itspath:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600188 reqsigvalues = reqsigvalues_config
189 else:
190 reqsigvalues = reqsigvalues_image
191 for reqkey, reqvalue in reqsigvalues.items():
192 value = values.get(reqkey, None)
193 if value is None:
194 self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath))
195 self.assertEqual(value, reqvalue)
196
197 # Dump the image to see if it really got signed
198 bitbake("u-boot-tools-native -c addto_recipe_sysroot")
199 result = runCmd('bitbake -e u-boot-tools-native | grep ^RECIPE_SYSROOT_NATIVE=')
200 recipe_sysroot_native = result.output.split('=')[1].strip('"')
201 dumpimage_path = os.path.join(recipe_sysroot_native, 'usr', 'bin', 'dumpimage')
202 result = runCmd('%s -l %s' % (dumpimage_path, fitimage_path))
203 in_signed = None
204 signed_sections = {}
205 for line in result.output.splitlines():
206 if line.startswith((' Configuration', ' Image')):
207 in_signed = re.search('\((.*)\)', line).groups()[0]
208 elif re.match('^ *', line) in (' ', ''):
209 in_signed = None
210 elif in_signed:
211 if not in_signed in signed_sections:
212 signed_sections[in_signed] = {}
213 key, value = line.split(':', 1)
214 signed_sections[in_signed][key.strip()] = value.strip()
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600215 self.assertIn('kernel-1', signed_sections)
216 self.assertIn('fdt-am335x-boneblack.dtb', signed_sections)
217 self.assertIn('conf-am335x-boneblack.dtb', signed_sections)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600218 for signed_section, values in signed_sections.items():
219 value = values.get('Sign algo', None)
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500220 if signed_section.startswith("conf"):
221 self.assertEqual(value, 'sha256,rsa2048:cfg-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section)
222 else:
223 self.assertEqual(value, 'sha256,rsa2048:img-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600224 value = values.get('Sign value', None)
225 self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section)
226
227 # Check for UBOOT_MKIMAGE_SIGN_ARGS
228 result = runCmd('bitbake -e virtual/kernel | grep ^T=')
229 tempdir = result.output.split('=', 1)[1].strip().strip('')
230 result = runCmd('grep "a smart comment" %s/run.do_assemble_fitimage' % tempdir, ignore_status=True)
231 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN_ARGS value did not get used')
232
233 # Check for evidence of test-mkimage-wrapper class
234 result = runCmd('grep "### uboot-mkimage wrapper message" %s/log.do_assemble_fitimage' % tempdir, ignore_status=True)
235 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE did not work')
236 result = runCmd('grep "### uboot-mkimage signing wrapper message" %s/log.do_assemble_fitimage' % tempdir, ignore_status=True)
237 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN did not work')
238
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500239 def test_uboot_fit_image(self):
240 """
241 Summary: Check if Uboot FIT image and Image Tree Source
242 (its) are built and the Image Tree Source has the
243 correct fields.
244 Expected: 1. u-boot-fitImage and u-boot-its can be built
245 2. The type, load address, entrypoint address and
246 default values of U-boot image are correct in the
247 Image Tree Source. Not all the fields are tested,
248 only the key fields that wont vary between
249 different architectures.
250 Product: oe-core
251 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com>
252 based on work by Usama Arif <usama.arif@arm.com>
253 """
254 config = """
255# We need at least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set
256MACHINE = "qemuarm"
257UBOOT_MACHINE = "am57xx_evm_defconfig"
258SPL_BINARY = "MLO"
259
260# Enable creation of the U-Boot fitImage
261UBOOT_FITIMAGE_ENABLE = "1"
262
263# (U-boot) fitImage properties
264UBOOT_LOADADDRESS = "0x80080000"
265UBOOT_ENTRYPOINT = "0x80080000"
266UBOOT_FIT_DESC = "A model description"
267
268# Enable creation of Kernel fitImage
269KERNEL_IMAGETYPES += " fitImage "
270KERNEL_CLASSES = " kernel-fitimage"
271UBOOT_SIGN_ENABLE = "1"
272FIT_GENERATE_KEYS = "1"
273UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500274UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
275UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500276FIT_SIGN_INDIVIDUAL = "1"
277"""
278 self.write_config(config)
279
280 # The U-Boot fitImage is created as part of linux recipe
281 bitbake("virtual/kernel")
282
283 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
284 machine = get_bb_var('MACHINE')
285 fitimage_its_path = os.path.join(deploy_dir_image,
286 "u-boot-its-%s" % (machine,))
287 fitimage_path = os.path.join(deploy_dir_image,
288 "u-boot-fitImage-%s" % (machine,))
289
290 self.assertTrue(os.path.exists(fitimage_its_path),
291 "%s image tree source doesn't exist" % (fitimage_its_path))
292 self.assertTrue(os.path.exists(fitimage_path),
293 "%s FIT image doesn't exist" % (fitimage_path))
294
295 # Check that the type, load address, entrypoint address and default
296 # values for kernel and ramdisk in Image Tree Source are as expected.
297 # The order of fields in the below array is important. Not all the
298 # fields are tested, only the key fields that wont vary between
299 # different architectures.
300 its_field_check = [
301 'description = "A model description";',
302 'type = "standalone";',
303 'load = <0x80080000>;',
304 'entry = <0x80080000>;',
305 'default = "conf";',
306 'loadables = "uboot";',
307 'fdt = "fdt";'
308 ]
309
310 with open(fitimage_its_path) as its_file:
311 field_index = 0
312 for line in its_file:
313 if field_index == len(its_field_check):
314 break
315 if its_field_check[field_index] in line:
316 field_index +=1
317
318 if field_index != len(its_field_check): # if its equal, the test passed
319 self.assertTrue(field_index == len(its_field_check),
320 "Fields in Image Tree Source File %s did not match, error in finding %s"
321 % (fitimage_its_path, its_field_check[field_index]))
322
323 def test_uboot_sign_fit_image(self):
324 """
325 Summary: Check if Uboot FIT image and Image Tree Source
326 (its) are built and the Image Tree Source has the
327 correct fields, in the scenario where the Kernel
328 is also creating/signing it's fitImage.
329 Expected: 1. u-boot-fitImage and u-boot-its can be built
330 2. The type, load address, entrypoint address and
331 default values of U-boot image are correct in the
332 Image Tree Source. Not all the fields are tested,
333 only the key fields that wont vary between
334 different architectures.
335 Product: oe-core
336 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com>
337 based on work by Usama Arif <usama.arif@arm.com>
338 """
339 config = """
340# We need at least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set
341MACHINE = "qemuarm"
342UBOOT_MACHINE = "am57xx_evm_defconfig"
343SPL_BINARY = "MLO"
344
345# Enable creation of the U-Boot fitImage
346UBOOT_FITIMAGE_ENABLE = "1"
347
348# (U-boot) fitImage properties
349UBOOT_LOADADDRESS = "0x80080000"
350UBOOT_ENTRYPOINT = "0x80080000"
351UBOOT_FIT_DESC = "A model description"
352KERNEL_IMAGETYPES += " fitImage "
353KERNEL_CLASSES = " kernel-fitimage test-mkimage-wrapper "
354UBOOT_SIGN_ENABLE = "1"
355FIT_GENERATE_KEYS = "1"
356UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500357UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
358UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500359FIT_SIGN_INDIVIDUAL = "1"
360UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'"
361"""
362 self.write_config(config)
363
364 # The U-Boot fitImage is created as part of linux recipe
365 bitbake("virtual/kernel")
366
367 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
368 machine = get_bb_var('MACHINE')
369 fitimage_its_path = os.path.join(deploy_dir_image,
370 "u-boot-its-%s" % (machine,))
371 fitimage_path = os.path.join(deploy_dir_image,
372 "u-boot-fitImage-%s" % (machine,))
373
374 self.assertTrue(os.path.exists(fitimage_its_path),
375 "%s image tree source doesn't exist" % (fitimage_its_path))
376 self.assertTrue(os.path.exists(fitimage_path),
377 "%s FIT image doesn't exist" % (fitimage_path))
378
379 # Check that the type, load address, entrypoint address and default
380 # values for kernel and ramdisk in Image Tree Source are as expected.
381 # The order of fields in the below array is important. Not all the
382 # fields are tested, only the key fields that wont vary between
383 # different architectures.
384 its_field_check = [
385 'description = "A model description";',
386 'type = "standalone";',
387 'load = <0x80080000>;',
388 'entry = <0x80080000>;',
389 'default = "conf";',
390 'loadables = "uboot";',
391 'fdt = "fdt";'
392 ]
393
394 with open(fitimage_its_path) as its_file:
395 field_index = 0
396 for line in its_file:
397 if field_index == len(its_field_check):
398 break
399 if its_field_check[field_index] in line:
400 field_index +=1
401
402 if field_index != len(its_field_check): # if its equal, the test passed
403 self.assertTrue(field_index == len(its_field_check),
404 "Fields in Image Tree Source File %s did not match, error in finding %s"
405 % (fitimage_its_path, its_field_check[field_index]))
406
407
408 def test_sign_standalone_uboot_fit_image(self):
409 """
410 Summary: Check if U-Boot FIT image and Image Tree Source (its) are
411 created and signed correctly for the scenario where only
412 the U-Boot proper fitImage is being created and signed.
413 Expected: 1) U-Boot its and FIT image are built successfully
414 2) Scanning the its file indicates signing is enabled
415 as requested by SPL_SIGN_ENABLE (using keys generated
416 via UBOOT_FIT_GENERATE_KEYS)
417 3) Dumping the FIT image indicates signature values
418 are present
419 4) Examination of the do_uboot_assemble_fitimage
420 runfile/logfile indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN
421 and SPL_MKIMAGE_SIGN_ARGS are working as expected.
422 Product: oe-core
423 Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> based upon
424 work by Paul Eggleton <paul.eggleton@microsoft.com> and
425 Usama Arif <usama.arif@arm.com>
426 """
427 config = """
428# There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at
429# least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set
430MACHINE = "qemuarm"
431UBOOT_MACHINE = "am57xx_evm_defconfig"
432SPL_BINARY = "MLO"
433# The kernel-fitimage class is a dependency even if we're only
434# creating/signing the U-Boot fitImage
435KERNEL_CLASSES = " kernel-fitimage test-mkimage-wrapper "
436# 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
454 # The U-Boot fitImage is created as part of linux recipe
455 bitbake("virtual/kernel")
456
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
543 result = runCmd('bitbake -e virtual/kernel | grep ^T=')
544 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 "
598KERNEL_CLASSES = " kernel-fitimage test-mkimage-wrapper "
599UBOOT_SIGN_ENABLE = "1"
600FIT_GENERATE_KEYS = "1"
601UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500602UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
603UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
Andrew Geissler3b8a17c2021-04-15 15:55:55 -0500604FIT_SIGN_INDIVIDUAL = "1"
605"""
606 self.write_config(config)
607
608 # The U-Boot fitImage is created as part of linux recipe
609 bitbake("virtual/kernel")
610
611 image_type = "core-image-minimal"
612 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
613 machine = get_bb_var('MACHINE')
614 fitimage_its_path = os.path.join(deploy_dir_image,
615 "u-boot-its-%s" % (machine,))
616 fitimage_path = os.path.join(deploy_dir_image,
617 "u-boot-fitImage-%s" % (machine,))
618
619 self.assertTrue(os.path.exists(fitimage_its_path),
620 "%s image tree source doesn't exist" % (fitimage_its_path))
621 self.assertTrue(os.path.exists(fitimage_path),
622 "%s FIT image doesn't exist" % (fitimage_path))
623
624 req_itspaths = [
625 ['/', 'images', 'uboot'],
626 ['/', 'images', 'uboot', 'signature'],
627 ['/', 'images', 'fdt'],
628 ['/', 'images', 'fdt', 'signature'],
629 ]
630
631 itspath = []
632 itspaths = []
633 linect = 0
634 sigs = {}
635 with open(fitimage_its_path) as its_file:
636 linect += 1
637 for line in its_file:
638 line = line.strip()
639 if line.endswith('};'):
640 itspath.pop()
641 elif line.endswith('{'):
642 itspath.append(line[:-1].strip())
643 itspaths.append(itspath[:])
644 elif itspath and itspath[-1] == 'signature':
645 itsdotpath = '.'.join(itspath)
646 if not itsdotpath in sigs:
647 sigs[itsdotpath] = {}
648 if not '=' in line or not line.endswith(';'):
649 self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line))
650 key, value = line.split('=', 1)
651 sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';')
652
653 for reqpath in req_itspaths:
654 if not reqpath in itspaths:
655 self.fail('Missing section in its file: %s' % reqpath)
656
657 reqsigvalues_image = {
658 'algo': '"sha256,rsa2048"',
659 'key-name-hint': '"spl-cascaded-oe-selftest"',
660 }
661
662 for itspath, values in sigs.items():
663 reqsigvalues = reqsigvalues_image
664 for reqkey, reqvalue in reqsigvalues.items():
665 value = values.get(reqkey, None)
666 if value is None:
667 self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath))
668 self.assertEqual(value, reqvalue)
669
670 # Dump the image to see if it really got signed
671 bitbake("u-boot-tools-native -c addto_recipe_sysroot")
672 result = runCmd('bitbake -e u-boot-tools-native | grep ^RECIPE_SYSROOT_NATIVE=')
673 recipe_sysroot_native = result.output.split('=')[1].strip('"')
674 dumpimage_path = os.path.join(recipe_sysroot_native, 'usr', 'bin', 'dumpimage')
675 result = runCmd('%s -l %s' % (dumpimage_path, fitimage_path))
676 in_signed = None
677 signed_sections = {}
678 for line in result.output.splitlines():
679 if line.startswith((' Image')):
680 in_signed = re.search('\((.*)\)', line).groups()[0]
681 elif re.match(' \w', line):
682 in_signed = None
683 elif in_signed:
684 if not in_signed in signed_sections:
685 signed_sections[in_signed] = {}
686 key, value = line.split(':', 1)
687 signed_sections[in_signed][key.strip()] = value.strip()
688 self.assertIn('uboot', signed_sections)
689 self.assertIn('fdt', signed_sections)
690 for signed_section, values in signed_sections.items():
691 value = values.get('Sign algo', None)
692 self.assertEqual(value, 'sha256,rsa2048:spl-cascaded-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section)
693 value = values.get('Sign value', None)
694 self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section)
695
696 # Check for SPL_MKIMAGE_SIGN_ARGS
697 result = runCmd('bitbake -e virtual/kernel | grep ^T=')
698 tempdir = result.output.split('=', 1)[1].strip().strip('')
699 result = runCmd('grep "a smart cascaded U-Boot comment" %s/run.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
700 self.assertEqual(result.status, 0, 'SPL_MKIMAGE_SIGN_ARGS value did not get used')
701
702 # Check for evidence of test-mkimage-wrapper class
703 result = runCmd('grep "### uboot-mkimage wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
704 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE did not work')
705 result = runCmd('grep "### uboot-mkimage signing wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
706 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN did not work')
707
708
709
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600710 def test_initramfs_bundle(self):
711 """
712 Summary: Verifies the content of the initramfs bundle node in the FIT Image Tree Source (its)
713 The FIT settings are set by the test case.
714 The machine used is beaglebone-yocto.
715 Expected: 1. The ITS is generated with initramfs bundle support
716 2. All the fields in the kernel node are as expected (matching the
717 conf settings)
718 3. The kernel is included in all the available configurations and
719 its hash is included in the configuration signature
720
721 Product: oe-core
722 Author: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
723 """
724
725 config = """
726DISTRO="poky"
727MACHINE = "beaglebone-yocto"
728INITRAMFS_IMAGE_BUNDLE = "1"
729INITRAMFS_IMAGE = "core-image-minimal-initramfs"
730INITRAMFS_SCRIPTS = ""
731UBOOT_MACHINE = "am335x_evm_defconfig"
732KERNEL_CLASSES = " kernel-fitimage "
733KERNEL_IMAGETYPES = "fitImage"
734UBOOT_SIGN_ENABLE = "1"
735UBOOT_SIGN_KEYNAME = "beaglebonekey"
736UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}"
737UBOOT_DTB_BINARY = "u-boot.dtb"
738UBOOT_ENTRYPOINT = "0x80000000"
739UBOOT_LOADADDRESS = "0x80000000"
740UBOOT_DTB_LOADADDRESS = "0x82000000"
741UBOOT_ARCH = "arm"
742UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
Andrew Geissler615f2f12022-07-15 14:00:58 -0500743UBOOT_MKIMAGE_KERNEL_TYPE = "kernel"
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600744UBOOT_EXTLINUX = "0"
745FIT_GENERATE_KEYS = "1"
746KERNEL_IMAGETYPE_REPLACEMENT = "zImage"
Andrew Geisslereff27472021-10-29 15:35:00 -0500747FIT_KERNEL_COMP_ALG = "none"
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600748FIT_HASH_ALG = "sha256"
749"""
750 self.write_config(config)
751
752 # fitImage is created as part of linux recipe
753 bitbake("virtual/kernel")
754
755 image_type = get_bb_var('INITRAMFS_IMAGE')
756 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
757 machine = get_bb_var('MACHINE')
758 fitimage_its_path = os.path.join(deploy_dir_image,
759 "fitImage-its-%s-%s-%s" % (image_type, machine, machine))
760 fitimage_path = os.path.join(deploy_dir_image,"fitImage")
761
762 self.assertTrue(os.path.exists(fitimage_its_path),
763 "%s image tree source doesn't exist" % (fitimage_its_path))
764 self.assertTrue(os.path.exists(fitimage_path),
765 "%s FIT image doesn't exist" % (fitimage_path))
766
767 kernel_load = str(get_bb_var('UBOOT_LOADADDRESS'))
768 kernel_entry = str(get_bb_var('UBOOT_ENTRYPOINT'))
Andrew Geissler615f2f12022-07-15 14:00:58 -0500769 kernel_type = str(get_bb_var('UBOOT_MKIMAGE_KERNEL_TYPE'))
Andrew Geisslereff27472021-10-29 15:35:00 -0500770 kernel_compression = str(get_bb_var('FIT_KERNEL_COMP_ALG'))
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600771 uboot_arch = str(get_bb_var('UBOOT_ARCH'))
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600772 fit_hash_alg = str(get_bb_var('FIT_HASH_ALG'))
773
774 its_file = open(fitimage_its_path)
775
776 its_lines = [line.strip() for line in its_file.readlines()]
777
778 exp_node_lines = [
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600779 'kernel-1 {',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600780 'description = "Linux kernel";',
Andrew Geisslereff27472021-10-29 15:35:00 -0500781 'data = /incbin/("linux.bin");',
Andrew Geissler615f2f12022-07-15 14:00:58 -0500782 'type = "' + kernel_type + '";',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600783 'arch = "' + uboot_arch + '";',
784 'os = "linux";',
Andrew Geisslereff27472021-10-29 15:35:00 -0500785 'compression = "' + kernel_compression + '";',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600786 'load = <' + kernel_load + '>;',
787 'entry = <' + kernel_entry + '>;',
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600788 'hash-1 {',
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600789 'algo = "' + fit_hash_alg +'";',
790 '};',
791 '};'
792 ]
793
794 node_str = exp_node_lines[0]
795
796 test_passed = False
797
798 print ("checking kernel node\n")
799
800 if node_str in its_lines:
801 node_start_idx = its_lines.index(node_str)
802 node = its_lines[node_start_idx:(node_start_idx + len(exp_node_lines))]
803 if node == exp_node_lines:
804 print("kernel node verified")
805 else:
806 self.assertTrue(test_passed == True,"kernel node does not match expectation")
807
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600808 rx_configs = re.compile("^conf-.*")
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600809 its_configs = list(filter(rx_configs.match, its_lines))
810
811 for cfg_str in its_configs:
812 cfg_start_idx = its_lines.index(cfg_str)
813 line_idx = cfg_start_idx + 2
814 node_end = False
815 while node_end == False:
816 if its_lines[line_idx] == "};" and its_lines[line_idx-1] == "};" :
817 node_end = True
818 line_idx = line_idx + 1
819
820 node = its_lines[cfg_start_idx:line_idx]
821 print("checking configuration " + cfg_str.rstrip(" {"))
822 rx_desc_line = re.compile("^description.*1 Linux kernel.*")
823 if len(list(filter(rx_desc_line.match, node))) != 1:
824 self.assertTrue(test_passed == True,"kernel keyword not found in the description line")
825 break
826 else:
827 print("kernel keyword found in the description line")
828
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600829 if 'kernel = "kernel-1";' not in node:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600830 self.assertTrue(test_passed == True,"kernel line not found")
831 break
832 else:
833 print("kernel line found")
834
835 rx_sign_line = re.compile("^sign-images.*kernel.*")
836 if len(list(filter(rx_sign_line.match, node))) != 1:
837 self.assertTrue(test_passed == True,"kernel hash not signed")
838 break
839 else:
840 print("kernel hash signed")
841
842 test_passed = True
843 self.assertTrue(test_passed == True,"Initramfs bundle test success")