summaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic/plugins/source/bootimg_efi.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/wic/plugins/source/bootimg_efi.py')
-rw-r--r--scripts/lib/wic/plugins/source/bootimg_efi.py435
1 files changed, 435 insertions, 0 deletions
diff --git a/scripts/lib/wic/plugins/source/bootimg_efi.py b/scripts/lib/wic/plugins/source/bootimg_efi.py
new file mode 100644
index 0000000000..cf16705a28
--- /dev/null
+++ b/scripts/lib/wic/plugins/source/bootimg_efi.py
@@ -0,0 +1,435 @@
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 tempfile
16import shutil
17import re
18
19from glob import glob
20
21from wic import WicError
22from wic.engine import get_custom_config
23from wic.pluginbase import SourcePlugin
24from wic.misc import (exec_cmd, exec_native_cmd,
25 get_bitbake_var, BOOTDD_EXTRA_SPACE)
26
27logger = logging.getLogger('wic')
28
29class BootimgEFIPlugin(SourcePlugin):
30 """
31 Create EFI boot partition.
32 This plugin supports GRUB 2 and systemd-boot bootloaders.
33 """
34
35 name = 'bootimg_efi'
36
37 @classmethod
38 def _copy_additional_files(cls, hdddir, initrd, dtb):
39 bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
40 if not bootimg_dir:
41 raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
42
43 if initrd:
44 initrds = initrd.split(';')
45 for rd in initrds:
46 cp_cmd = "cp -v -p %s/%s %s" % (bootimg_dir, rd, hdddir)
47 out = exec_cmd(cp_cmd, True)
48 logger.debug("initrd files:\n%s" % (out))
49 else:
50 logger.debug("Ignoring missing initrd")
51
52 if dtb:
53 if ';' in dtb:
54 raise WicError("Only one DTB supported, exiting")
55 cp_cmd = "cp -v -p %s/%s %s" % (bootimg_dir, dtb, hdddir)
56 out = exec_cmd(cp_cmd, True)
57 logger.debug("dtb files:\n%s" % (out))
58
59 @classmethod
60 def do_configure_grubefi(cls, hdddir, creator, cr_workdir, source_params):
61 """
62 Create loader-specific (grub-efi) config
63 """
64 configfile = creator.ks.bootloader.configfile
65 custom_cfg = None
66 if configfile:
67 custom_cfg = get_custom_config(configfile)
68 if custom_cfg:
69 # Use a custom configuration for grub
70 grubefi_conf = custom_cfg
71 logger.debug("Using custom configuration file "
72 "%s for grub.cfg", configfile)
73 else:
74 raise WicError("configfile is specified but failed to "
75 "get it from %s." % configfile)
76
77 initrd = source_params.get('initrd')
78 dtb = source_params.get('dtb')
79
80 cls._copy_additional_files(hdddir, initrd, dtb)
81
82 if not custom_cfg:
83 # Create grub configuration using parameters from wks file
84 bootloader = creator.ks.bootloader
85 title = source_params.get('title')
86
87 grubefi_conf = ""
88 grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n"
89 grubefi_conf += "default=boot\n"
90 grubefi_conf += "timeout=%s\n" % bootloader.timeout
91 grubefi_conf += "menuentry '%s'{\n" % (title if title else "boot")
92
93 kernel = get_bitbake_var("KERNEL_IMAGETYPE")
94 if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
95 if get_bitbake_var("INITRAMFS_IMAGE"):
96 kernel = "%s-%s.bin" % \
97 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
98
99 label = source_params.get('label')
100 label_conf = "root=%s" % creator.rootdev
101 if label:
102 label_conf = "LABEL=%s" % label
103
104 grubefi_conf += "linux /%s %s rootwait %s\n" \
105 % (kernel, label_conf, bootloader.append)
106
107 if initrd:
108 initrds = initrd.split(';')
109 grubefi_conf += "initrd"
110 for rd in initrds:
111 grubefi_conf += " /%s" % rd
112 grubefi_conf += "\n"
113
114 if dtb:
115 grubefi_conf += "devicetree /%s\n" % dtb
116
117 grubefi_conf += "}\n"
118
119 logger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg",
120 cr_workdir)
121 cfg = open("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, "w")
122 cfg.write(grubefi_conf)
123 cfg.close()
124
125 @classmethod
126 def do_configure_systemdboot(cls, hdddir, creator, cr_workdir, source_params):
127 """
128 Create loader-specific systemd-boot/gummiboot config. Unified Kernel Image (uki)
129 support is done in image recipe with uki.bbclass and only systemd-boot loader config
130 and ESP partition structure is created here.
131 """
132 # detect uki.bbclass usage
133 image_classes = get_bitbake_var("IMAGE_CLASSES").split()
134 unified_image = False
135 if "uki" in image_classes:
136 unified_image = True
137
138 install_cmd = "install -d %s/loader" % hdddir
139 exec_cmd(install_cmd)
140
141 install_cmd = "install -d %s/loader/entries" % hdddir
142 exec_cmd(install_cmd)
143
144 bootloader = creator.ks.bootloader
145 loader_conf = ""
146
147 # 5 seconds is a sensible default timeout
148 loader_conf += "timeout %d\n" % (bootloader.timeout or 5)
149
150 logger.debug("Writing systemd-boot config "
151 "%s/hdd/boot/loader/loader.conf", cr_workdir)
152 cfg = open("%s/hdd/boot/loader/loader.conf" % cr_workdir, "w")
153 cfg.write(loader_conf)
154 logger.debug("loader.conf:\n%s" % (loader_conf))
155 cfg.close()
156
157 initrd = source_params.get('initrd')
158 dtb = source_params.get('dtb')
159 if not unified_image:
160 cls._copy_additional_files(hdddir, initrd, dtb)
161
162 configfile = creator.ks.bootloader.configfile
163 custom_cfg = None
164 boot_conf = ""
165 if configfile:
166 custom_cfg = get_custom_config(configfile)
167 if custom_cfg:
168 # Use a custom configuration for systemd-boot
169 boot_conf = custom_cfg
170 logger.debug("Using custom configuration file "
171 "%s for systemd-boots's boot.conf", configfile)
172 else:
173 raise WicError("configfile is specified but failed to "
174 "get it from %s.", configfile)
175 else:
176 # Create systemd-boot configuration using parameters from wks file
177 kernel = get_bitbake_var("KERNEL_IMAGETYPE")
178 if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
179 if get_bitbake_var("INITRAMFS_IMAGE"):
180 kernel = "%s-%s.bin" % \
181 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
182
183 title = source_params.get('title')
184
185 boot_conf += "title %s\n" % (title if title else "boot")
186 boot_conf += "linux /%s\n" % kernel
187
188 label = source_params.get('label')
189 label_conf = "LABEL=Boot root=%s" % creator.rootdev
190 if label:
191 label_conf = "LABEL=%s" % label
192
193 boot_conf += "options %s %s\n" % \
194 (label_conf, bootloader.append)
195
196 if initrd:
197 initrds = initrd.split(';')
198 for rd in initrds:
199 boot_conf += "initrd /%s\n" % rd
200
201 if dtb:
202 boot_conf += "devicetree /%s\n" % dtb
203
204 if not unified_image:
205 logger.debug("Writing systemd-boot config "
206 "%s/hdd/boot/loader/entries/boot.conf", cr_workdir)
207 cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w")
208 cfg.write(boot_conf)
209 logger.debug("boot.conf:\n%s" % (boot_conf))
210 cfg.close()
211
212
213 @classmethod
214 def do_configure_partition(cls, part, source_params, creator, cr_workdir,
215 oe_builddir, bootimg_dir, kernel_dir,
216 native_sysroot):
217 """
218 Called before do_prepare_partition(), creates loader-specific config
219 """
220 hdddir = "%s/hdd/boot" % cr_workdir
221
222 install_cmd = "install -d %s/EFI/BOOT" % hdddir
223 exec_cmd(install_cmd)
224
225 try:
226 if source_params['loader'] == 'grub-efi':
227 cls.do_configure_grubefi(hdddir, creator, cr_workdir, source_params)
228 elif source_params['loader'] == 'systemd-boot':
229 cls.do_configure_systemdboot(hdddir, creator, cr_workdir, source_params)
230 elif source_params['loader'] == 'uefi-kernel':
231 pass
232 else:
233 raise WicError("unrecognized bootimg_efi loader: %s" % source_params['loader'])
234 except KeyError:
235 raise WicError("bootimg_efi requires a loader, none specified")
236
237 if get_bitbake_var("IMAGE_EFI_BOOT_FILES") is None:
238 logger.debug('No boot files defined in IMAGE_EFI_BOOT_FILES')
239 else:
240 boot_files = None
241 for (fmt, id) in (("_uuid-%s", part.uuid), ("_label-%s", part.label), (None, None)):
242 if fmt:
243 var = fmt % id
244 else:
245 var = ""
246
247 boot_files = get_bitbake_var("IMAGE_EFI_BOOT_FILES" + var)
248 if boot_files:
249 break
250
251 logger.debug('Boot files: %s', boot_files)
252
253 # list of tuples (src_name, dst_name)
254 deploy_files = []
255 for src_entry in re.findall(r'[\w;\-\.\+/\*]+', boot_files):
256 if ';' in src_entry:
257 dst_entry = tuple(src_entry.split(';'))
258 if not dst_entry[0] or not dst_entry[1]:
259 raise WicError('Malformed boot file entry: %s' % src_entry)
260 else:
261 dst_entry = (src_entry, src_entry)
262
263 logger.debug('Destination entry: %r', dst_entry)
264 deploy_files.append(dst_entry)
265
266 cls.install_task = [];
267 for deploy_entry in deploy_files:
268 src, dst = deploy_entry
269 if '*' in src:
270 # by default install files under their basename
271 entry_name_fn = os.path.basename
272 if dst != src:
273 # unless a target name was given, then treat name
274 # as a directory and append a basename
275 entry_name_fn = lambda name: \
276 os.path.join(dst,
277 os.path.basename(name))
278
279 srcs = glob(os.path.join(kernel_dir, src))
280
281 logger.debug('Globbed sources: %s', ', '.join(srcs))
282 for entry in srcs:
283 src = os.path.relpath(entry, kernel_dir)
284 entry_dst_name = entry_name_fn(entry)
285 cls.install_task.append((src, entry_dst_name))
286 else:
287 cls.install_task.append((src, dst))
288
289 @classmethod
290 def do_prepare_partition(cls, part, source_params, creator, cr_workdir,
291 oe_builddir, bootimg_dir, kernel_dir,
292 rootfs_dir, native_sysroot):
293 """
294 Called to do the actual content population for a partition i.e. it
295 'prepares' the partition to be incorporated into the image.
296 In this case, prepare content for an EFI (grub) boot partition.
297 """
298 if not kernel_dir:
299 kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
300 if not kernel_dir:
301 raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
302
303 staging_kernel_dir = kernel_dir
304
305 hdddir = "%s/hdd/boot" % cr_workdir
306
307 kernel = get_bitbake_var("KERNEL_IMAGETYPE")
308 if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
309 if get_bitbake_var("INITRAMFS_IMAGE"):
310 kernel = "%s-%s.bin" % \
311 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
312
313 if source_params.get('create-unified-kernel-image') == "true":
314 raise WicError("create-unified-kernel-image is no longer supported. Please use uki.bbclass.")
315
316 if source_params.get('install-kernel-into-boot-dir') != 'false':
317 install_cmd = "install -v -p -m 0644 %s/%s %s/%s" % \
318 (staging_kernel_dir, kernel, hdddir, kernel)
319 out = exec_cmd(install_cmd)
320 logger.debug("Installed kernel files:\n%s" % out)
321
322 if get_bitbake_var("IMAGE_EFI_BOOT_FILES"):
323 for src_path, dst_path in cls.install_task:
324 install_cmd = "install -v -p -m 0644 -D %s %s" \
325 % (os.path.join(kernel_dir, src_path),
326 os.path.join(hdddir, dst_path))
327 out = exec_cmd(install_cmd)
328 logger.debug("Installed IMAGE_EFI_BOOT_FILES:\n%s" % out)
329
330 try:
331 if source_params['loader'] == 'grub-efi':
332 shutil.copyfile("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir,
333 "%s/grub.cfg" % cr_workdir)
334 for mod in [x for x in os.listdir(kernel_dir) if x.startswith("grub-efi-")]:
335 cp_cmd = "cp -v -p %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[9:])
336 exec_cmd(cp_cmd, True)
337 shutil.move("%s/grub.cfg" % cr_workdir,
338 "%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir)
339 elif source_params['loader'] == 'systemd-boot':
340 for mod in [x for x in os.listdir(kernel_dir) if x.startswith("systemd-")]:
341 cp_cmd = "cp -v -p %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[8:])
342 out = exec_cmd(cp_cmd, True)
343 logger.debug("systemd-boot files:\n%s" % out)
344 elif source_params['loader'] == 'uefi-kernel':
345 kernel = get_bitbake_var("KERNEL_IMAGETYPE")
346 if not kernel:
347 raise WicError("Empty KERNEL_IMAGETYPE")
348 target = get_bitbake_var("TARGET_SYS")
349 if not target:
350 raise WicError("Empty TARGET_SYS")
351
352 if re.match("x86_64", target):
353 kernel_efi_image = "bootx64.efi"
354 elif re.match('i.86', target):
355 kernel_efi_image = "bootia32.efi"
356 elif re.match('aarch64', target):
357 kernel_efi_image = "bootaa64.efi"
358 elif re.match('arm', target):
359 kernel_efi_image = "bootarm.efi"
360 else:
361 raise WicError("UEFI stub kernel is incompatible with target %s" % target)
362
363 for mod in [x for x in os.listdir(kernel_dir) if x.startswith(kernel)]:
364 cp_cmd = "cp -v -p %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, kernel_efi_image)
365 out = exec_cmd(cp_cmd, True)
366 logger.debug("uefi-kernel files:\n%s" % out)
367 else:
368 raise WicError("unrecognized bootimg_efi loader: %s" %
369 source_params['loader'])
370
371 # must have installed at least one EFI bootloader
372 out = glob(os.path.join(hdddir, 'EFI', 'BOOT', 'boot*.efi'))
373 logger.debug("Installed EFI loader files:\n%s" % out)
374 if not out:
375 raise WicError("No EFI loaders installed to ESP partition. Check that grub-efi, systemd-boot or similar is installed.")
376
377 except KeyError:
378 raise WicError("bootimg_efi requires a loader, none specified")
379
380 startup = os.path.join(kernel_dir, "startup.nsh")
381 if os.path.exists(startup):
382 cp_cmd = "cp -v -p %s %s/" % (startup, hdddir)
383 out = exec_cmd(cp_cmd, True)
384 logger.debug("startup files:\n%s" % out)
385
386 for paths in part.include_path or []:
387 for path in paths:
388 cp_cmd = "cp -v -p -r %s %s/" % (path, hdddir)
389 exec_cmd(cp_cmd, True)
390 logger.debug("include_path files:\n%s" % out)
391
392 du_cmd = "du -bks %s" % hdddir
393 out = exec_cmd(du_cmd)
394 blocks = int(out.split()[0])
395
396 extra_blocks = part.get_extra_block_count(blocks)
397
398 if extra_blocks < BOOTDD_EXTRA_SPACE:
399 extra_blocks = BOOTDD_EXTRA_SPACE
400
401 blocks += extra_blocks
402
403 logger.debug("Added %d extra blocks to %s to get to %d total blocks",
404 extra_blocks, part.mountpoint, blocks)
405
406 # required for compatibility with certain devices expecting file system
407 # block count to be equal to partition block count
408 if blocks < part.fixed_size:
409 blocks = part.fixed_size
410 logger.debug("Overriding %s to %d total blocks for compatibility",
411 part.mountpoint, blocks)
412
413 # dosfs image, created by mkdosfs
414 bootimg = "%s/boot.img" % cr_workdir
415
416 label = part.label if part.label else "ESP"
417
418 dosfs_cmd = "mkdosfs -v -n %s -i %s -C %s %d" % \
419 (label, part.fsuuid, bootimg, blocks)
420 exec_native_cmd(dosfs_cmd, native_sysroot)
421 logger.debug("mkdosfs:\n%s" % (str(out)))
422
423 mcopy_cmd = "mcopy -v -p -i %s -s %s/* ::/" % (bootimg, hdddir)
424 out = exec_native_cmd(mcopy_cmd, native_sysroot)
425 logger.debug("mcopy:\n%s" % (str(out)))
426
427 chmod_cmd = "chmod 644 %s" % bootimg
428 exec_cmd(chmod_cmd)
429
430 du_cmd = "du -Lbks %s" % bootimg
431 out = exec_cmd(du_cmd)
432 bootimg_size = out.split()[0]
433
434 part.size = int(bootimg_size)
435 part.source_file = bootimg