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