1#
2# Copyright (c) 2014, Intel Corporation.
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6# DESCRIPTION
7# This implements the 'bootimg-efi' source plugin class for 'wic'
8#
9# AUTHORS
10# Tom Zanussi <tom.zanussi (at] linux.intel.com>
11#
12
13import logging
14import os
15import shutil
16
17from wic import WicError
18from wic.engine import get_custom_config
19from wic.pluginbase import SourcePlugin
20from wic.misc import (exec_cmd, exec_native_cmd,
21                      get_bitbake_var, BOOTDD_EXTRA_SPACE)
22
23logger = logging.getLogger('wic')
24
25class BootimgEFIPlugin(SourcePlugin):
26    """
27    Create EFI boot partition.
28    This plugin supports GRUB 2 and systemd-boot bootloaders.
29    """
30
31    name = 'bootimg-efi'
32
33    @classmethod
34    def do_configure_grubefi(cls, hdddir, creator, cr_workdir, source_params):
35        """
36        Create loader-specific (grub-efi) config
37        """
38        configfile = creator.ks.bootloader.configfile
39        custom_cfg = None
40        if configfile:
41            custom_cfg = get_custom_config(configfile)
42            if custom_cfg:
43                # Use a custom configuration for grub
44                grubefi_conf = custom_cfg
45                logger.debug("Using custom configuration file "
46                             "%s for grub.cfg", configfile)
47            else:
48                raise WicError("configfile is specified but failed to "
49                               "get it from %s." % configfile)
50
51        initrd = source_params.get('initrd')
52
53        if initrd:
54            bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
55            if not bootimg_dir:
56                raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
57
58            cp_cmd = "cp %s/%s %s" % (bootimg_dir, initrd, hdddir)
59            exec_cmd(cp_cmd, True)
60        else:
61            logger.debug("Ignoring missing initrd")
62
63        if not custom_cfg:
64            # Create grub configuration using parameters from wks file
65            bootloader = creator.ks.bootloader
66            title = source_params.get('title')
67
68            grubefi_conf = ""
69            grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n"
70            grubefi_conf += "default=boot\n"
71            grubefi_conf += "timeout=%s\n" % bootloader.timeout
72            grubefi_conf += "menuentry '%s'{\n" % (title if title else "boot")
73
74            kernel = get_bitbake_var("KERNEL_IMAGETYPE")
75            if not kernel:
76                kernel = "bzImage"
77
78            label = source_params.get('label')
79            label_conf = "root=%s" % creator.rootdev
80            if label:
81                label_conf = "LABEL=%s" % label
82
83            grubefi_conf += "linux /%s %s rootwait %s\n" \
84                % (kernel, label_conf, bootloader.append)
85
86            if initrd:
87               grubefi_conf += "initrd /%s\n" % initrd
88
89            grubefi_conf += "}\n"
90
91        logger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg",
92                     cr_workdir)
93        cfg = open("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, "w")
94        cfg.write(grubefi_conf)
95        cfg.close()
96
97    @classmethod
98    def do_configure_systemdboot(cls, hdddir, creator, cr_workdir, source_params):
99        """
100        Create loader-specific systemd-boot/gummiboot config
101        """
102        install_cmd = "install -d %s/loader" % hdddir
103        exec_cmd(install_cmd)
104
105        install_cmd = "install -d %s/loader/entries" % hdddir
106        exec_cmd(install_cmd)
107
108        bootloader = creator.ks.bootloader
109
110        loader_conf = ""
111        loader_conf += "default boot\n"
112        loader_conf += "timeout %d\n" % bootloader.timeout
113
114        initrd = source_params.get('initrd')
115
116        if initrd:
117            # obviously we need to have a common common deploy var
118            bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
119            if not bootimg_dir:
120                raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
121
122            cp_cmd = "cp %s/%s %s" % (bootimg_dir, initrd, hdddir)
123            exec_cmd(cp_cmd, True)
124        else:
125            logger.debug("Ignoring missing initrd")
126
127        logger.debug("Writing systemd-boot config "
128                     "%s/hdd/boot/loader/loader.conf", cr_workdir)
129        cfg = open("%s/hdd/boot/loader/loader.conf" % cr_workdir, "w")
130        cfg.write(loader_conf)
131        cfg.close()
132
133        configfile = creator.ks.bootloader.configfile
134        custom_cfg = None
135        if configfile:
136            custom_cfg = get_custom_config(configfile)
137            if custom_cfg:
138                # Use a custom configuration for systemd-boot
139                boot_conf = custom_cfg
140                logger.debug("Using custom configuration file "
141                             "%s for systemd-boots's boot.conf", configfile)
142            else:
143                raise WicError("configfile is specified but failed to "
144                               "get it from %s.", configfile)
145
146        if not custom_cfg:
147            # Create systemd-boot configuration using parameters from wks file
148            kernel = get_bitbake_var("KERNEL_IMAGETYPE")
149            if not kernel:
150                kernel = "bzImage"
151
152            title = source_params.get('title')
153
154            boot_conf = ""
155            boot_conf += "title %s\n" % (title if title else "boot")
156            boot_conf += "linux /%s\n" % kernel
157
158            label = source_params.get('label')
159            label_conf = "LABEL=Boot root=%s" % creator.rootdev
160            if label:
161                label_conf = "LABEL=%s" % label
162
163            boot_conf += "options %s %s\n" % \
164                             (label_conf, bootloader.append)
165
166            if initrd:
167                boot_conf += "initrd /%s\n" % initrd
168
169        logger.debug("Writing systemd-boot config "
170                     "%s/hdd/boot/loader/entries/boot.conf", cr_workdir)
171        cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w")
172        cfg.write(boot_conf)
173        cfg.close()
174
175
176    @classmethod
177    def do_configure_partition(cls, part, source_params, creator, cr_workdir,
178                               oe_builddir, bootimg_dir, kernel_dir,
179                               native_sysroot):
180        """
181        Called before do_prepare_partition(), creates loader-specific config
182        """
183        hdddir = "%s/hdd/boot" % cr_workdir
184
185        install_cmd = "install -d %s/EFI/BOOT" % hdddir
186        exec_cmd(install_cmd)
187
188        try:
189            if source_params['loader'] == 'grub-efi':
190                cls.do_configure_grubefi(hdddir, creator, cr_workdir, source_params)
191            elif source_params['loader'] == 'systemd-boot':
192                cls.do_configure_systemdboot(hdddir, creator, cr_workdir, source_params)
193            else:
194                raise WicError("unrecognized bootimg-efi loader: %s" % source_params['loader'])
195        except KeyError:
196            raise WicError("bootimg-efi requires a loader, none specified")
197
198
199    @classmethod
200    def do_prepare_partition(cls, part, source_params, creator, cr_workdir,
201                             oe_builddir, bootimg_dir, kernel_dir,
202                             rootfs_dir, native_sysroot):
203        """
204        Called to do the actual content population for a partition i.e. it
205        'prepares' the partition to be incorporated into the image.
206        In this case, prepare content for an EFI (grub) boot partition.
207        """
208        if not kernel_dir:
209            kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
210            if not kernel_dir:
211                raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
212
213        staging_kernel_dir = kernel_dir
214
215        hdddir = "%s/hdd/boot" % cr_workdir
216
217        kernel = get_bitbake_var("KERNEL_IMAGETYPE")
218        if not kernel:
219            kernel = "bzImage"
220
221        install_cmd = "install -m 0644 %s/%s %s/%s" % \
222            (staging_kernel_dir, kernel, hdddir, kernel)
223        exec_cmd(install_cmd)
224
225
226        try:
227            if source_params['loader'] == 'grub-efi':
228                shutil.copyfile("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir,
229                                "%s/grub.cfg" % cr_workdir)
230                for mod in [x for x in os.listdir(kernel_dir) if x.startswith("grub-efi-")]:
231                    cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[9:])
232                    exec_cmd(cp_cmd, True)
233                shutil.move("%s/grub.cfg" % cr_workdir,
234                            "%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir)
235            elif source_params['loader'] == 'systemd-boot':
236                for mod in [x for x in os.listdir(kernel_dir) if x.startswith("systemd-")]:
237                    cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[8:])
238                    exec_cmd(cp_cmd, True)
239            else:
240                raise WicError("unrecognized bootimg-efi loader: %s" %
241                               source_params['loader'])
242        except KeyError:
243            raise WicError("bootimg-efi requires a loader, none specified")
244
245        startup = os.path.join(kernel_dir, "startup.nsh")
246        if os.path.exists(startup):
247            cp_cmd = "cp %s %s/" % (startup, hdddir)
248            exec_cmd(cp_cmd, True)
249
250        du_cmd = "du -bks %s" % hdddir
251        out = exec_cmd(du_cmd)
252        blocks = int(out.split()[0])
253
254        extra_blocks = part.get_extra_block_count(blocks)
255
256        if extra_blocks < BOOTDD_EXTRA_SPACE:
257            extra_blocks = BOOTDD_EXTRA_SPACE
258
259        blocks += extra_blocks
260
261        logger.debug("Added %d extra blocks to %s to get to %d total blocks",
262                     extra_blocks, part.mountpoint, blocks)
263
264        # dosfs image, created by mkdosfs
265        bootimg = "%s/boot.img" % cr_workdir
266
267        label = part.label if part.label else "ESP"
268
269        dosfs_cmd = "mkdosfs -n %s -i %s -C %s %d" % \
270                    (label, part.fsuuid, bootimg, blocks)
271        exec_native_cmd(dosfs_cmd, native_sysroot)
272
273        mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir)
274        exec_native_cmd(mcopy_cmd, native_sysroot)
275
276        chmod_cmd = "chmod 644 %s" % bootimg
277        exec_cmd(chmod_cmd)
278
279        du_cmd = "du -Lbks %s" % bootimg
280        out = exec_cmd(du_cmd)
281        bootimg_size = out.split()[0]
282
283        part.size = int(bootimg_size)
284        part.source_file = bootimg
285