1#
2# Copyright (c) 2014, Intel Corporation.
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6# DESCRIPTION
7# This implements the 'bootimg-pcbios' source plugin class for 'wic'
8#
9# AUTHORS
10# Tom Zanussi <tom.zanussi (at] linux.intel.com>
11#
12
13import logging
14import os
15import re
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 BootimgPcbiosPlugin(SourcePlugin):
26    """
27    Create MBR boot partition and install syslinux on it.
28    """
29
30    name = 'bootimg-pcbios'
31
32    @classmethod
33    def _get_bootimg_dir(cls, bootimg_dir, dirname):
34        """
35        Check if dirname exists in default bootimg_dir or in STAGING_DIR.
36        """
37        staging_datadir = get_bitbake_var("STAGING_DATADIR")
38        for result in (bootimg_dir, staging_datadir):
39            if os.path.exists("%s/%s" % (result, dirname)):
40                return result
41
42        # STAGING_DATADIR is expanded with MLPREFIX if multilib is enabled
43        # but dependency syslinux is still populated to original STAGING_DATADIR
44        nonarch_datadir = re.sub('/[^/]*recipe-sysroot', '/recipe-sysroot', staging_datadir)
45        if os.path.exists(os.path.join(nonarch_datadir, dirname)):
46            return nonarch_datadir
47
48        raise WicError("Couldn't find correct bootimg_dir, exiting")
49
50    @classmethod
51    def do_install_disk(cls, disk, disk_name, creator, workdir, oe_builddir,
52                        bootimg_dir, kernel_dir, native_sysroot):
53        """
54        Called after all partitions have been prepared and assembled into a
55        disk image.  In this case, we install the MBR.
56        """
57        bootimg_dir = cls._get_bootimg_dir(bootimg_dir, 'syslinux')
58        mbrfile = "%s/syslinux/" % bootimg_dir
59        if creator.ptable_format == 'msdos':
60            mbrfile += "mbr.bin"
61        elif creator.ptable_format == 'gpt':
62            mbrfile += "gptmbr.bin"
63        else:
64            raise WicError("Unsupported partition table: %s" %
65                           creator.ptable_format)
66
67        if not os.path.exists(mbrfile):
68            raise WicError("Couldn't find %s.  If using the -e option, do you "
69                           "have the right MACHINE set in local.conf?  If not, "
70                           "is the bootimg_dir path correct?" % mbrfile)
71
72        full_path = creator._full_path(workdir, disk_name, "direct")
73        logger.debug("Installing MBR on disk %s as %s with size %s bytes",
74                     disk_name, full_path, disk.min_size)
75
76        dd_cmd = "dd if=%s of=%s conv=notrunc" % (mbrfile, full_path)
77        exec_cmd(dd_cmd, native_sysroot)
78
79    @classmethod
80    def do_configure_partition(cls, part, source_params, creator, cr_workdir,
81                               oe_builddir, bootimg_dir, kernel_dir,
82                               native_sysroot):
83        """
84        Called before do_prepare_partition(), creates syslinux config
85        """
86        hdddir = "%s/hdd/boot" % cr_workdir
87
88        install_cmd = "install -d %s" % hdddir
89        exec_cmd(install_cmd)
90
91        bootloader = creator.ks.bootloader
92
93        custom_cfg = None
94        if bootloader.configfile:
95            custom_cfg = get_custom_config(bootloader.configfile)
96            if custom_cfg:
97                # Use a custom configuration for grub
98                syslinux_conf = custom_cfg
99                logger.debug("Using custom configuration file %s "
100                             "for syslinux.cfg", bootloader.configfile)
101            else:
102                raise WicError("configfile is specified but failed to "
103                               "get it from %s." % bootloader.configfile)
104
105        if not custom_cfg:
106            # Create syslinux configuration using parameters from wks file
107            splash = os.path.join(cr_workdir, "/hdd/boot/splash.jpg")
108            if os.path.exists(splash):
109                splashline = "menu background splash.jpg"
110            else:
111                splashline = ""
112
113            syslinux_conf = ""
114            syslinux_conf += "PROMPT 0\n"
115            syslinux_conf += "TIMEOUT " + str(bootloader.timeout) + "\n"
116            syslinux_conf += "\n"
117            syslinux_conf += "ALLOWOPTIONS 1\n"
118            syslinux_conf += "SERIAL 0 115200\n"
119            syslinux_conf += "\n"
120            if splashline:
121                syslinux_conf += "%s\n" % splashline
122            syslinux_conf += "DEFAULT boot\n"
123            syslinux_conf += "LABEL boot\n"
124
125            kernel = "/" + get_bitbake_var("KERNEL_IMAGETYPE")
126            syslinux_conf += "KERNEL " + kernel + "\n"
127
128            syslinux_conf += "APPEND label=boot root=%s %s\n" % \
129                             (creator.rootdev, bootloader.append)
130
131        logger.debug("Writing syslinux config %s/hdd/boot/syslinux.cfg",
132                     cr_workdir)
133        cfg = open("%s/hdd/boot/syslinux.cfg" % cr_workdir, "w")
134        cfg.write(syslinux_conf)
135        cfg.close()
136
137    @classmethod
138    def do_prepare_partition(cls, part, source_params, creator, cr_workdir,
139                             oe_builddir, bootimg_dir, kernel_dir,
140                             rootfs_dir, native_sysroot):
141        """
142        Called to do the actual content population for a partition i.e. it
143        'prepares' the partition to be incorporated into the image.
144        In this case, prepare content for legacy bios boot partition.
145        """
146        bootimg_dir = cls._get_bootimg_dir(bootimg_dir, 'syslinux')
147
148        staging_kernel_dir = kernel_dir
149
150        hdddir = "%s/hdd/boot" % cr_workdir
151
152        kernel = get_bitbake_var("KERNEL_IMAGETYPE")
153        if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
154            if get_bitbake_var("INITRAMFS_IMAGE"):
155                kernel = "%s-%s.bin" % \
156                    (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
157
158        cmds = ("install -m 0644 %s/%s %s/%s" %
159                (staging_kernel_dir, kernel, hdddir, get_bitbake_var("KERNEL_IMAGETYPE")),
160                "install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" %
161                (bootimg_dir, hdddir),
162                "install -m 0644 %s/syslinux/vesamenu.c32 %s/vesamenu.c32" %
163                (bootimg_dir, hdddir),
164                "install -m 444 %s/syslinux/libcom32.c32 %s/libcom32.c32" %
165                (bootimg_dir, hdddir),
166                "install -m 444 %s/syslinux/libutil.c32 %s/libutil.c32" %
167                (bootimg_dir, hdddir))
168
169        for install_cmd in cmds:
170            exec_cmd(install_cmd)
171
172        du_cmd = "du -bks %s" % hdddir
173        out = exec_cmd(du_cmd)
174        blocks = int(out.split()[0])
175
176        extra_blocks = part.get_extra_block_count(blocks)
177
178        if extra_blocks < BOOTDD_EXTRA_SPACE:
179            extra_blocks = BOOTDD_EXTRA_SPACE
180
181        blocks += extra_blocks
182
183        logger.debug("Added %d extra blocks to %s to get to %d total blocks",
184                     extra_blocks, part.mountpoint, blocks)
185
186        # dosfs image, created by mkdosfs
187        bootimg = "%s/boot%s.img" % (cr_workdir, part.lineno)
188
189        label = part.label if part.label else "boot"
190
191        dosfs_cmd = "mkdosfs -n %s -i %s -S 512 -C %s %d" % \
192                    (label, part.fsuuid, bootimg, blocks)
193        exec_native_cmd(dosfs_cmd, native_sysroot)
194
195        mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir)
196        exec_native_cmd(mcopy_cmd, native_sysroot)
197
198        syslinux_cmd = "syslinux %s" % bootimg
199        exec_native_cmd(syslinux_cmd, native_sysroot)
200
201        chmod_cmd = "chmod 644 %s" % bootimg
202        exec_cmd(chmod_cmd)
203
204        du_cmd = "du -Lbks %s" % bootimg
205        out = exec_cmd(du_cmd)
206        bootimg_size = out.split()[0]
207
208        part.size = int(bootimg_size)
209        part.source_file = bootimg
210