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