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 16import re 17 18from glob import glob 19 20from wic import WicError 21from wic.engine import get_custom_config 22from wic.pluginbase import SourcePlugin 23from wic.misc import (exec_cmd, exec_native_cmd, 24 get_bitbake_var, BOOTDD_EXTRA_SPACE) 25 26logger = logging.getLogger('wic') 27 28class BootimgEFIPlugin(SourcePlugin): 29 """ 30 Create EFI boot partition. 31 This plugin supports GRUB 2 and systemd-boot bootloaders. 32 """ 33 34 name = 'bootimg-efi' 35 36 @classmethod 37 def do_configure_grubefi(cls, hdddir, creator, cr_workdir, source_params): 38 """ 39 Create loader-specific (grub-efi) config 40 """ 41 configfile = creator.ks.bootloader.configfile 42 custom_cfg = None 43 if configfile: 44 custom_cfg = get_custom_config(configfile) 45 if custom_cfg: 46 # Use a custom configuration for grub 47 grubefi_conf = custom_cfg 48 logger.debug("Using custom configuration file " 49 "%s for grub.cfg", configfile) 50 else: 51 raise WicError("configfile is specified but failed to " 52 "get it from %s." % configfile) 53 54 initrd = source_params.get('initrd') 55 56 if initrd: 57 bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE") 58 if not bootimg_dir: 59 raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting") 60 61 initrds = initrd.split(';') 62 for rd in initrds: 63 cp_cmd = "cp %s/%s %s" % (bootimg_dir, rd, hdddir) 64 exec_cmd(cp_cmd, True) 65 else: 66 logger.debug("Ignoring missing initrd") 67 68 if not custom_cfg: 69 # Create grub configuration using parameters from wks file 70 bootloader = creator.ks.bootloader 71 title = source_params.get('title') 72 73 grubefi_conf = "" 74 grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n" 75 grubefi_conf += "default=boot\n" 76 grubefi_conf += "timeout=%s\n" % bootloader.timeout 77 grubefi_conf += "menuentry '%s'{\n" % (title if title else "boot") 78 79 kernel = get_bitbake_var("KERNEL_IMAGETYPE") 80 if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1": 81 if get_bitbake_var("INITRAMFS_IMAGE"): 82 kernel = "%s-%s.bin" % \ 83 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) 84 85 label = source_params.get('label') 86 label_conf = "root=%s" % creator.rootdev 87 if label: 88 label_conf = "LABEL=%s" % label 89 90 grubefi_conf += "linux /%s %s rootwait %s\n" \ 91 % (kernel, label_conf, bootloader.append) 92 93 if initrd: 94 initrds = initrd.split(';') 95 grubefi_conf += "initrd" 96 for rd in initrds: 97 grubefi_conf += " /%s" % rd 98 grubefi_conf += "\n" 99 100 grubefi_conf += "}\n" 101 102 logger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg", 103 cr_workdir) 104 cfg = open("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, "w") 105 cfg.write(grubefi_conf) 106 cfg.close() 107 108 @classmethod 109 def do_configure_systemdboot(cls, hdddir, creator, cr_workdir, source_params): 110 """ 111 Create loader-specific systemd-boot/gummiboot config 112 """ 113 install_cmd = "install -d %s/loader" % hdddir 114 exec_cmd(install_cmd) 115 116 install_cmd = "install -d %s/loader/entries" % hdddir 117 exec_cmd(install_cmd) 118 119 bootloader = creator.ks.bootloader 120 121 loader_conf = "" 122 loader_conf += "default boot\n" 123 loader_conf += "timeout %d\n" % bootloader.timeout 124 125 initrd = source_params.get('initrd') 126 127 if initrd: 128 # obviously we need to have a common common deploy var 129 bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE") 130 if not bootimg_dir: 131 raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting") 132 133 initrds = initrd.split(';') 134 for rd in initrds: 135 cp_cmd = "cp %s/%s %s" % (bootimg_dir, rd, hdddir) 136 exec_cmd(cp_cmd, True) 137 else: 138 logger.debug("Ignoring missing initrd") 139 140 logger.debug("Writing systemd-boot config " 141 "%s/hdd/boot/loader/loader.conf", cr_workdir) 142 cfg = open("%s/hdd/boot/loader/loader.conf" % cr_workdir, "w") 143 cfg.write(loader_conf) 144 cfg.close() 145 146 configfile = creator.ks.bootloader.configfile 147 custom_cfg = None 148 if configfile: 149 custom_cfg = get_custom_config(configfile) 150 if custom_cfg: 151 # Use a custom configuration for systemd-boot 152 boot_conf = custom_cfg 153 logger.debug("Using custom configuration file " 154 "%s for systemd-boots's boot.conf", configfile) 155 else: 156 raise WicError("configfile is specified but failed to " 157 "get it from %s.", configfile) 158 159 if not custom_cfg: 160 # Create systemd-boot configuration using parameters from wks file 161 kernel = get_bitbake_var("KERNEL_IMAGETYPE") 162 if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1": 163 if get_bitbake_var("INITRAMFS_IMAGE"): 164 kernel = "%s-%s.bin" % \ 165 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) 166 167 title = source_params.get('title') 168 169 boot_conf = "" 170 boot_conf += "title %s\n" % (title if title else "boot") 171 boot_conf += "linux /%s\n" % kernel 172 173 label = source_params.get('label') 174 label_conf = "LABEL=Boot root=%s" % creator.rootdev 175 if label: 176 label_conf = "LABEL=%s" % label 177 178 boot_conf += "options %s %s\n" % \ 179 (label_conf, bootloader.append) 180 181 if initrd: 182 initrds = initrd.split(';') 183 for rd in initrds: 184 boot_conf += "initrd /%s\n" % rd 185 186 logger.debug("Writing systemd-boot config " 187 "%s/hdd/boot/loader/entries/boot.conf", cr_workdir) 188 cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w") 189 cfg.write(boot_conf) 190 cfg.close() 191 192 193 @classmethod 194 def do_configure_partition(cls, part, source_params, creator, cr_workdir, 195 oe_builddir, bootimg_dir, kernel_dir, 196 native_sysroot): 197 """ 198 Called before do_prepare_partition(), creates loader-specific config 199 """ 200 hdddir = "%s/hdd/boot" % cr_workdir 201 202 install_cmd = "install -d %s/EFI/BOOT" % hdddir 203 exec_cmd(install_cmd) 204 205 try: 206 if source_params['loader'] == 'grub-efi': 207 cls.do_configure_grubefi(hdddir, creator, cr_workdir, source_params) 208 elif source_params['loader'] == 'systemd-boot': 209 cls.do_configure_systemdboot(hdddir, creator, cr_workdir, source_params) 210 else: 211 raise WicError("unrecognized bootimg-efi loader: %s" % source_params['loader']) 212 except KeyError: 213 raise WicError("bootimg-efi requires a loader, none specified") 214 215 if get_bitbake_var("IMAGE_BOOT_FILES") is None: 216 logger.debug('No boot files defined in IMAGE_BOOT_FILES') 217 else: 218 boot_files = None 219 for (fmt, id) in (("_uuid-%s", part.uuid), ("_label-%s", part.label), (None, None)): 220 if fmt: 221 var = fmt % id 222 else: 223 var = "" 224 225 boot_files = get_bitbake_var("IMAGE_BOOT_FILES" + var) 226 if boot_files: 227 break 228 229 logger.debug('Boot files: %s', boot_files) 230 231 # list of tuples (src_name, dst_name) 232 deploy_files = [] 233 for src_entry in re.findall(r'[\w;\-\./\*]+', boot_files): 234 if ';' in src_entry: 235 dst_entry = tuple(src_entry.split(';')) 236 if not dst_entry[0] or not dst_entry[1]: 237 raise WicError('Malformed boot file entry: %s' % src_entry) 238 else: 239 dst_entry = (src_entry, src_entry) 240 241 logger.debug('Destination entry: %r', dst_entry) 242 deploy_files.append(dst_entry) 243 244 cls.install_task = []; 245 for deploy_entry in deploy_files: 246 src, dst = deploy_entry 247 if '*' in src: 248 # by default install files under their basename 249 entry_name_fn = os.path.basename 250 if dst != src: 251 # unless a target name was given, then treat name 252 # as a directory and append a basename 253 entry_name_fn = lambda name: \ 254 os.path.join(dst, 255 os.path.basename(name)) 256 257 srcs = glob(os.path.join(kernel_dir, src)) 258 259 logger.debug('Globbed sources: %s', ', '.join(srcs)) 260 for entry in srcs: 261 src = os.path.relpath(entry, kernel_dir) 262 entry_dst_name = entry_name_fn(entry) 263 cls.install_task.append((src, entry_dst_name)) 264 else: 265 cls.install_task.append((src, dst)) 266 267 @classmethod 268 def do_prepare_partition(cls, part, source_params, creator, cr_workdir, 269 oe_builddir, bootimg_dir, kernel_dir, 270 rootfs_dir, native_sysroot): 271 """ 272 Called to do the actual content population for a partition i.e. it 273 'prepares' the partition to be incorporated into the image. 274 In this case, prepare content for an EFI (grub) boot partition. 275 """ 276 if not kernel_dir: 277 kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE") 278 if not kernel_dir: 279 raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting") 280 281 staging_kernel_dir = kernel_dir 282 283 hdddir = "%s/hdd/boot" % cr_workdir 284 285 kernel = get_bitbake_var("KERNEL_IMAGETYPE") 286 if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1": 287 if get_bitbake_var("INITRAMFS_IMAGE"): 288 kernel = "%s-%s.bin" % \ 289 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) 290 291 install_cmd = "install -m 0644 %s/%s %s/%s" % \ 292 (staging_kernel_dir, kernel, hdddir, kernel) 293 exec_cmd(install_cmd) 294 295 if get_bitbake_var("IMAGE_BOOT_FILES"): 296 for src_path, dst_path in cls.install_task: 297 install_cmd = "install -m 0644 -D %s %s" \ 298 % (os.path.join(kernel_dir, src_path), 299 os.path.join(hdddir, dst_path)) 300 exec_cmd(install_cmd) 301 302 try: 303 if source_params['loader'] == 'grub-efi': 304 shutil.copyfile("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, 305 "%s/grub.cfg" % cr_workdir) 306 for mod in [x for x in os.listdir(kernel_dir) if x.startswith("grub-efi-")]: 307 cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[9:]) 308 exec_cmd(cp_cmd, True) 309 shutil.move("%s/grub.cfg" % cr_workdir, 310 "%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir) 311 elif source_params['loader'] == 'systemd-boot': 312 for mod in [x for x in os.listdir(kernel_dir) if x.startswith("systemd-")]: 313 cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[8:]) 314 exec_cmd(cp_cmd, True) 315 else: 316 raise WicError("unrecognized bootimg-efi loader: %s" % 317 source_params['loader']) 318 except KeyError: 319 raise WicError("bootimg-efi requires a loader, none specified") 320 321 startup = os.path.join(kernel_dir, "startup.nsh") 322 if os.path.exists(startup): 323 cp_cmd = "cp %s %s/" % (startup, hdddir) 324 exec_cmd(cp_cmd, True) 325 326 du_cmd = "du -bks %s" % hdddir 327 out = exec_cmd(du_cmd) 328 blocks = int(out.split()[0]) 329 330 extra_blocks = part.get_extra_block_count(blocks) 331 332 if extra_blocks < BOOTDD_EXTRA_SPACE: 333 extra_blocks = BOOTDD_EXTRA_SPACE 334 335 blocks += extra_blocks 336 337 logger.debug("Added %d extra blocks to %s to get to %d total blocks", 338 extra_blocks, part.mountpoint, blocks) 339 340 # dosfs image, created by mkdosfs 341 bootimg = "%s/boot.img" % cr_workdir 342 343 label = part.label if part.label else "ESP" 344 345 dosfs_cmd = "mkdosfs -n %s -i %s -C %s %d" % \ 346 (label, part.fsuuid, bootimg, blocks) 347 exec_native_cmd(dosfs_cmd, native_sysroot) 348 349 mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir) 350 exec_native_cmd(mcopy_cmd, native_sysroot) 351 352 chmod_cmd = "chmod 644 %s" % bootimg 353 exec_cmd(chmod_cmd) 354 355 du_cmd = "du -Lbks %s" % bootimg 356 out = exec_cmd(du_cmd) 357 bootimg_size = out.split()[0] 358 359 part.size = int(bootimg_size) 360 part.source_file = bootimg 361