blob: 0958036a6fc670aea9b768b44f2d3e2b34412bd9 [file] [log] [blame]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001#
2# SPDX-License-Identifier: MIT
3#
4
5from oeqa.selftest.case import OESelftestTestCase
6from oeqa.utils.commands import runCmd, bitbake, get_bb_var, runqemu
7import os
8import json
9import re
10
11class FitImageTests(OESelftestTestCase):
12
13 def test_fit_image(self):
14 """
15 Summary: Check if FIT image and Image Tree Source (its) are built
16 and the Image Tree Source has the correct fields.
17 Expected: 1. fitImage and fitImage-its can be built
18 2. The type, load address, entrypoint address and
19 default values of kernel and ramdisk are as expected
20 in the Image Tree Source. Not all the fields are tested,
21 only the key fields that wont vary between different
22 architectures.
23 Product: oe-core
24 Author: Usama Arif <usama.arif@arm.com>
25 """
26 config = """
27# Enable creation of fitImage
28KERNEL_IMAGETYPE = "Image"
29KERNEL_IMAGETYPES += " fitImage "
30KERNEL_CLASSES = " kernel-fitimage "
31
32# RAM disk variables including load address and entrypoint for kernel and RAM disk
33IMAGE_FSTYPES += "cpio.gz"
34INITRAMFS_IMAGE = "core-image-minimal"
35UBOOT_RD_LOADADDRESS = "0x88000000"
36UBOOT_RD_ENTRYPOINT = "0x88000000"
37UBOOT_LOADADDRESS = "0x80080000"
38UBOOT_ENTRYPOINT = "0x80080000"
39FIT_DESC = "A model description"
40"""
41 self.write_config(config)
42
43 # fitImage is created as part of linux recipe
44 bitbake("virtual/kernel")
45
46 image_type = "core-image-minimal"
47 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
48 machine = get_bb_var('MACHINE')
49 fitimage_its_path = os.path.join(deploy_dir_image,
50 "fitImage-its-%s-%s-%s" % (image_type, machine, machine))
51 fitimage_path = os.path.join(deploy_dir_image,
52 "fitImage-%s-%s-%s" % (image_type, machine, machine))
53
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>;',
72 'default = "conf@1";',
73 'kernel = "kernel@1";',
74 'ramdisk = "ramdisk@1";'
75 ]
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"
117UBOOT_SIGN_KEYNAME = "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': '"oe-selftest"',
177 }
178 reqsigvalues_config = {
179 'algo': '"sha256,rsa2048"',
180 'key-name-hint': '"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 self.assertEqual(value, 'sha256,rsa2048:oe-selftest', 'Signature algorithm for %s not expected value' % signed_section)
219 value = values.get('Sign value', None)
220 self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section)
221
222 # Check for UBOOT_MKIMAGE_SIGN_ARGS
223 result = runCmd('bitbake -e virtual/kernel | grep ^T=')
224 tempdir = result.output.split('=', 1)[1].strip().strip('')
225 result = runCmd('grep "a smart comment" %s/run.do_assemble_fitimage' % tempdir, ignore_status=True)
226 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN_ARGS value did not get used')
227
228 # Check for evidence of test-mkimage-wrapper class
229 result = runCmd('grep "### uboot-mkimage wrapper message" %s/log.do_assemble_fitimage' % tempdir, ignore_status=True)
230 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE did not work')
231 result = runCmd('grep "### uboot-mkimage signing wrapper message" %s/log.do_assemble_fitimage' % tempdir, ignore_status=True)
232 self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN did not work')
233
234 def test_initramfs_bundle(self):
235 """
236 Summary: Verifies the content of the initramfs bundle node in the FIT Image Tree Source (its)
237 The FIT settings are set by the test case.
238 The machine used is beaglebone-yocto.
239 Expected: 1. The ITS is generated with initramfs bundle support
240 2. All the fields in the kernel node are as expected (matching the
241 conf settings)
242 3. The kernel is included in all the available configurations and
243 its hash is included in the configuration signature
244
245 Product: oe-core
246 Author: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
247 """
248
249 config = """
250DISTRO="poky"
251MACHINE = "beaglebone-yocto"
252INITRAMFS_IMAGE_BUNDLE = "1"
253INITRAMFS_IMAGE = "core-image-minimal-initramfs"
254INITRAMFS_SCRIPTS = ""
255UBOOT_MACHINE = "am335x_evm_defconfig"
256KERNEL_CLASSES = " kernel-fitimage "
257KERNEL_IMAGETYPES = "fitImage"
258UBOOT_SIGN_ENABLE = "1"
259UBOOT_SIGN_KEYNAME = "beaglebonekey"
260UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}"
261UBOOT_DTB_BINARY = "u-boot.dtb"
262UBOOT_ENTRYPOINT = "0x80000000"
263UBOOT_LOADADDRESS = "0x80000000"
264UBOOT_DTB_LOADADDRESS = "0x82000000"
265UBOOT_ARCH = "arm"
266UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
267UBOOT_EXTLINUX = "0"
268FIT_GENERATE_KEYS = "1"
269KERNEL_IMAGETYPE_REPLACEMENT = "zImage"
270FIT_HASH_ALG = "sha256"
271"""
272 self.write_config(config)
273
274 # fitImage is created as part of linux recipe
275 bitbake("virtual/kernel")
276
277 image_type = get_bb_var('INITRAMFS_IMAGE')
278 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
279 machine = get_bb_var('MACHINE')
280 fitimage_its_path = os.path.join(deploy_dir_image,
281 "fitImage-its-%s-%s-%s" % (image_type, machine, machine))
282 fitimage_path = os.path.join(deploy_dir_image,"fitImage")
283
284 self.assertTrue(os.path.exists(fitimage_its_path),
285 "%s image tree source doesn't exist" % (fitimage_its_path))
286 self.assertTrue(os.path.exists(fitimage_path),
287 "%s FIT image doesn't exist" % (fitimage_path))
288
289 kernel_load = str(get_bb_var('UBOOT_LOADADDRESS'))
290 kernel_entry = str(get_bb_var('UBOOT_ENTRYPOINT'))
291 initramfs_bundle_format = str(get_bb_var('KERNEL_IMAGETYPE_REPLACEMENT'))
292 uboot_arch = str(get_bb_var('UBOOT_ARCH'))
293 initramfs_bundle = "arch/" + uboot_arch + "/boot/" + initramfs_bundle_format + ".initramfs"
294 fit_hash_alg = str(get_bb_var('FIT_HASH_ALG'))
295
296 its_file = open(fitimage_its_path)
297
298 its_lines = [line.strip() for line in its_file.readlines()]
299
300 exp_node_lines = [
301 'kernel@1 {',
302 'description = "Linux kernel";',
303 'data = /incbin/("' + initramfs_bundle + '");',
304 'type = "kernel";',
305 'arch = "' + uboot_arch + '";',
306 'os = "linux";',
307 'compression = "none";',
308 'load = <' + kernel_load + '>;',
309 'entry = <' + kernel_entry + '>;',
310 'hash@1 {',
311 'algo = "' + fit_hash_alg +'";',
312 '};',
313 '};'
314 ]
315
316 node_str = exp_node_lines[0]
317
318 test_passed = False
319
320 print ("checking kernel node\n")
321
322 if node_str in its_lines:
323 node_start_idx = its_lines.index(node_str)
324 node = its_lines[node_start_idx:(node_start_idx + len(exp_node_lines))]
325 if node == exp_node_lines:
326 print("kernel node verified")
327 else:
328 self.assertTrue(test_passed == True,"kernel node does not match expectation")
329
330 rx_configs = re.compile("^conf@.*")
331 its_configs = list(filter(rx_configs.match, its_lines))
332
333 for cfg_str in its_configs:
334 cfg_start_idx = its_lines.index(cfg_str)
335 line_idx = cfg_start_idx + 2
336 node_end = False
337 while node_end == False:
338 if its_lines[line_idx] == "};" and its_lines[line_idx-1] == "};" :
339 node_end = True
340 line_idx = line_idx + 1
341
342 node = its_lines[cfg_start_idx:line_idx]
343 print("checking configuration " + cfg_str.rstrip(" {"))
344 rx_desc_line = re.compile("^description.*1 Linux kernel.*")
345 if len(list(filter(rx_desc_line.match, node))) != 1:
346 self.assertTrue(test_passed == True,"kernel keyword not found in the description line")
347 break
348 else:
349 print("kernel keyword found in the description line")
350
351 if 'kernel = "kernel@1";' not in node:
352 self.assertTrue(test_passed == True,"kernel line not found")
353 break
354 else:
355 print("kernel line found")
356
357 rx_sign_line = re.compile("^sign-images.*kernel.*")
358 if len(list(filter(rx_sign_line.match, node))) != 1:
359 self.assertTrue(test_passed == True,"kernel hash not signed")
360 break
361 else:
362 print("kernel hash signed")
363
364 test_passed = True
365 self.assertTrue(test_passed == True,"Initramfs bundle test success")