summaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/wic')
-rw-r--r--scripts/lib/wic/plugins/source/isoimage-isohybrid.py538
1 files changed, 538 insertions, 0 deletions
diff --git a/scripts/lib/wic/plugins/source/isoimage-isohybrid.py b/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
new file mode 100644
index 0000000000..1cee90637a
--- /dev/null
+++ b/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
@@ -0,0 +1,538 @@
1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License version 2 as
6# published by the Free Software Foundation.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along
14# with this program; if not, write to the Free Software Foundation, Inc.,
15# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16#
17# DESCRIPTION
18# This implements the 'isoimage-isohybrid' source plugin class for 'wic'
19#
20# AUTHORS
21# Mihaly Varga <mihaly.varga (at] ni.com>
22
23import os
24import re
25import shutil
26
27from wic import kickstart, msger
28from wic.pluginbase import SourcePlugin
29from wic.utils.oe.misc import exec_cmd, exec_native_cmd, get_bitbake_var
30
31class IsoImagePlugin(SourcePlugin):
32 """
33 Create a bootable ISO image
34
35 This plugin creates a hybrid, legacy and EFI bootable ISO image. The
36 generated image can be used on optical media as well as USB media.
37
38 Legacy boot uses syslinux and EFI boot uses grub or gummiboot (not
39 implemented yet) as bootloader. The plugin creates the directories required
40 by bootloaders and populates them by creating and configuring the
41 bootloader files.
42
43 Example kickstart file:
44 part /boot --source isoimage-isohybrid --sourceparams="loader=grub-efi, \\
45 image_name= IsoImage" --ondisk cd --label LIVECD --fstype=ext2
46 bootloader --timeout=10 --append=" "
47
48 In --sourceparams "loader" specifies the bootloader used for booting in EFI
49 mode, while "image_name" specifies the name of the generated image. In the
50 example above, wic creates an ISO image named IsoImage-cd.direct (default
51 extension added by direct imeger plugin) and a file named IsoImage-cd.iso
52 """
53
54 name = 'isoimage-isohybrid'
55
56 @classmethod
57 def do_configure_syslinux(cls, cr, cr_workdir):
58 """
59 Create loader-specific (syslinux) config
60 """
61 splash = os.path.join(cr_workdir, "/ISO/boot/splash.jpg")
62 if os.path.exists(splash):
63 splashline = "menu background splash.jpg"
64 else:
65 splashline = ""
66
67 options = cr.ks.handler.bootloader.appendLine
68
69 timeout = kickstart.get_timeout(cr.ks, 10)
70
71 syslinux_conf = ""
72 syslinux_conf += "PROMPT 0\n"
73 syslinux_conf += "TIMEOUT %s \n" % timeout
74 syslinux_conf += "\n"
75 syslinux_conf += "ALLOWOPTIONS 1\n"
76 syslinux_conf += "SERIAL 0 115200\n"
77 syslinux_conf += "\n"
78 if splashline:
79 syslinux_conf += "%s\n" % splashline
80 syslinux_conf += "DEFAULT boot\n"
81 syslinux_conf += "LABEL boot\n"
82
83 kernel = "/bzImage"
84 syslinux_conf += "KERNEL " + kernel + "\n"
85 syslinux_conf += "APPEND initrd=/initrd LABEL=boot %s\n" % options
86
87 msger.debug("Writing syslinux config %s/ISO/isolinux/isolinux.cfg" \
88 % cr_workdir)
89 with open("%s/ISO/isolinux/isolinux.cfg" % cr_workdir, "w") as cfg:
90 cfg.write(syslinux_conf)
91
92 @classmethod
93 def do_configure_grubefi(cls, part, cr, cr_workdir):
94 """
95 Create loader-specific (grub-efi) config
96 """
97 splash = os.path.join(cr_workdir, "/EFI/boot/splash.jpg")
98 if os.path.exists(splash):
99 splashline = "menu background splash.jpg"
100 else:
101 splashline = ""
102
103 options = cr.ks.handler.bootloader.appendLine
104
105 grubefi_conf = ""
106 grubefi_conf += "serial --unit=0 --speed=115200 --word=8 "
107 grubefi_conf += "--parity=no --stop=1\n"
108 grubefi_conf += "default=boot\n"
109 timeout = kickstart.get_timeout(cr.ks, 10)
110 grubefi_conf += "timeout=%s\n" % timeout
111 grubefi_conf += "\n"
112 grubefi_conf += "search --set=root --label %s " % part.label
113 grubefi_conf += "\n"
114 grubefi_conf += "menuentry 'boot'{\n"
115
116 kernel = "/bzImage"
117
118 grubefi_conf += "linux %s rootwait %s\n" \
119 % (kernel, options)
120 grubefi_conf += "initrd /initrd \n"
121 grubefi_conf += "}\n"
122
123 if splashline:
124 grubefi_conf += "%s\n" % splashline
125
126 msger.debug("Writing grubefi config %s/EFI/BOOT/grub.cfg" \
127 % cr_workdir)
128 with open("%s/EFI/BOOT/grub.cfg" % cr_workdir, "w") as cfg:
129 cfg.write(grubefi_conf)
130
131 @staticmethod
132 def _build_initramfs_path(rootfs_dir, cr_workdir):
133 """
134 Create path for initramfs image
135 """
136
137 initrd = get_bitbake_var("INITRD")
138 if not initrd:
139 initrd_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
140 if not initrd_dir:
141 msger.error("Couldn't find DEPLOY_DIR_IMAGE, exiting.\n")
142
143 image_name = get_bitbake_var("IMAGE_BASENAME")
144 if not image_name:
145 msger.error("Couldn't find IMAGE_BASENAME, exiting.\n")
146
147 image_type = get_bitbake_var("INITRAMFS_FSTYPES")
148 if not image_type:
149 msger.error("Couldn't find INITRAMFS_FSTYPES, exiting.\n")
150
151 machine_arch = get_bitbake_var("MACHINE_ARCH")
152 if not machine_arch:
153 msger.error("Couldn't find MACHINE_ARCH, exiting.\n")
154
155 initrd = "%s/%s-initramfs-%s.%s" \
156 % (initrd_dir, image_name, machine_arch, image_type)
157
158 if not os.path.exists(initrd):
159 # Create initrd from rootfs directory
160 initrd = "%s/initrd.cpio.gz" % cr_workdir
161 initrd_dir = "%s/INITRD" % cr_workdir
162 shutil.copytree("%s" % rootfs_dir, \
163 "%s" % initrd_dir, symlinks=True)
164
165 if os.path.isfile("%s/init" % rootfs_dir):
166 shutil.copy2("%s/init" % rootfs_dir, "%s/init" % initrd_dir)
167 elif os.path.lexists("%s/init" % rootfs_dir):
168 os.symlink(os.readlink("%s/init" % rootfs_dir), \
169 "%s/init" % initrd_dir)
170 elif os.path.isfile("%s/sbin/init" % rootfs_dir):
171 shutil.copy2("%s/sbin/init" % rootfs_dir, \
172 "%s" % initrd_dir)
173 elif os.path.lexists("%s/sbin/init" % rootfs_dir):
174 os.symlink(os.readlink("%s/sbin/init" % rootfs_dir), \
175 "%s/init" % initrd_dir)
176 else:
177 msger.error("Couldn't find or build initrd, exiting.\n")
178
179 exec_cmd("cd %s && find . | cpio -o -H newc >%s/initrd.cpio " \
180 % (initrd_dir, cr_workdir), as_shell=True)
181 exec_cmd("gzip -f -9 -c %s/initrd.cpio > %s" \
182 % (cr_workdir, initrd), as_shell=True)
183 shutil.rmtree(initrd_dir)
184
185 return initrd
186
187 @classmethod
188 def do_stage_partition(cls, part, source_params, cr, cr_workdir,
189 oe_builddir, bootimg_dir, kernel_dir,
190 native_sysroot):
191 """
192 Special content staging called before do_prepare_partition().
193 It cheks if all necessary tools are available, if not
194 tries to instal them.
195 """
196 # Make sure parted is available in native sysroot
197 if not os.path.isfile("%s/usr/sbin/parted" % native_sysroot):
198 msger.info("Building parted-native...\n")
199 exec_cmd("bitbake parted-native")
200
201 # Make sure mkfs.ext2/3/4 is available in native sysroot
202 if not os.path.isfile("%s/sbin/mkfs.ext2" % native_sysroot):
203 msger.info("Building e2fsprogs-native...\n")
204 exec_cmd("bitbake e2fsprogs-native")
205
206 # Make sure syslinux is available in sysroot and in native sysroot
207 syslinux_dir = get_bitbake_var("STAGING_DATADIR")
208 if not syslinux_dir:
209 msger.error("Couldn't find STAGING_DATADIR, exiting.\n")
210 if not os.path.exists("%s/syslinux" % syslinux_dir):
211 msger.info("Building syslinux...\n")
212 exec_cmd("bitbake syslinux")
213 msger.info("Building syslinux-native...\n")
214 exec_cmd("bitbake syslinux-native")
215 if not os.path.exists("%s/syslinux" % syslinux_dir):
216 msger.error("Please build syslinux first\n")
217
218 #Make sure mkisofs is available in native sysroot
219 if not os.path.isfile("%s/usr/bin/mkisofs" % native_sysroot):
220 msger.info("Building cdrtools-native...\n")
221 exec_cmd("bitbake cdrtools-native")
222
223 # Make sure mkfs.vfat is available in native sysroot
224 if not os.path.isfile("%s/sbin/mkfs.vfat" % native_sysroot):
225 msger.info("Building dosfstools-native...\n")
226 exec_cmd("bitbake dosfstools-native")
227
228 # Make sure mtools is available in native sysroot
229 if not os.path.isfile("%s/usr/bin/mcopy" % native_sysroot):
230 msger.info("Building mtools-native...\n")
231 exec_cmd("bitbake mtools-native")
232
233 @classmethod
234 def do_configure_partition(cls, part, source_params, cr, cr_workdir,
235 oe_builddir, bootimg_dir, kernel_dir,
236 native_sysroot):
237 """
238 Called before do_prepare_partition(), creates loader-specific config
239 """
240 isodir = "%s/ISO/" % cr_workdir
241
242 if os.path.exists(cr_workdir):
243 shutil.rmtree(cr_workdir)
244
245 install_cmd = "install -d %s " % isodir
246 exec_cmd(install_cmd)
247
248 # Overwrite the name of the created image
249 msger.debug("%s" % source_params)
250 if 'image_name' in source_params and \
251 source_params['image_name'].strip():
252 cr.name = source_params['image_name'].strip()
253 msger.debug("The name of the image is: %s" % cr.name)
254
255 @classmethod
256 def do_prepare_partition(cls, part, source_params, cr, cr_workdir,
257 oe_builddir, bootimg_dir, kernel_dir,
258 rootfs_dir, native_sysroot):
259 """
260 Called to do the actual content population for a partition i.e. it
261 'prepares' the partition to be incorporated into the image.
262 In this case, prepare content for a bootable ISO image.
263 """
264
265 isodir = "%s/ISO" % cr_workdir
266
267 if part.rootfs is None:
268 if not 'ROOTFS_DIR' in rootfs_dir:
269 msger.error("Couldn't find --rootfs-dir, exiting.\n")
270 rootfs_dir = rootfs_dir['ROOTFS_DIR']
271 else:
272 if part.rootfs in rootfs_dir:
273 rootfs_dir = rootfs_dir[part.rootfs]
274 elif part.rootfs:
275 rootfs_dir = part.rootfs
276 else:
277 msg = "Couldn't find --rootfs-dir=%s connection "
278 msg += "or it is not a valid path, exiting.\n"
279 msger.error(msg % part.rootfs)
280
281 if not os.path.isdir(rootfs_dir):
282 rootfs_dir = get_bitbake_var("IMAGE_ROOTFS")
283 if not os.path.isdir(rootfs_dir):
284 msger.error("Couldn't find IMAGE_ROOTFS, exiting.\n")
285
286 part.set_rootfs(rootfs_dir)
287
288 # Prepare rootfs.img
289 hdd_dir = get_bitbake_var("HDDDIR")
290 img_iso_dir = get_bitbake_var("ISODIR")
291
292 rootfs_img = "%s/rootfs.img" % hdd_dir
293 if not os.path.isfile(rootfs_img):
294 rootfs_img = "%s/rootfs.img" % img_iso_dir
295 if not os.path.isfile(rootfs_img):
296 # check if rootfs.img is in deploydir
297 deploy_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
298 image_name = get_bitbake_var("IMAGE_LINK_NAME")
299 rootfs_img = "%s/%s.%s" \
300 % (deploy_dir, image_name, part.fstype)
301
302 if not os.path.isfile(rootfs_img):
303 # create image file with type specified by --fstype
304 # which contains rootfs
305 du_cmd = "du -bks %s" % rootfs_dir
306 out = exec_cmd(du_cmd)
307 part.set_size(int(out.split()[0]))
308 part.extra_space = 0
309 part.overhead_factor = 1.2
310 part.prepare_rootfs(cr_workdir, oe_builddir, rootfs_dir, \
311 native_sysroot)
312 rootfs_img = part.source_file
313
314 install_cmd = "install -m 0644 %s %s/rootfs.img" \
315 % (rootfs_img, isodir)
316 exec_cmd(install_cmd)
317
318 # Remove the temporary file created by part.prepare_rootfs()
319 if os.path.isfile(part.source_file):
320 os.remove(part.source_file)
321
322 # Prepare initial ramdisk
323 initrd = "%s/initrd" % hdd_dir
324 if not os.path.isfile(initrd):
325 initrd = "%s/initrd" % img_iso_dir
326 if not os.path.isfile(initrd):
327 initrd = cls._build_initramfs_path(rootfs_dir, cr_workdir)
328
329 install_cmd = "install -m 0644 %s %s/initrd" \
330 % (initrd, isodir)
331 exec_cmd(install_cmd)
332
333 # Remove the temporary file created by _build_initramfs_path function
334 if os.path.isfile("%s/initrd.cpio.gz" % cr_workdir):
335 os.remove("%s/initrd.cpio.gz" % cr_workdir)
336
337 # Install bzImage
338 install_cmd = "install -m 0644 %s/bzImage %s/bzImage" % \
339 (kernel_dir, isodir)
340 exec_cmd(install_cmd)
341
342 #Create bootloader for efi boot
343 try:
344 if source_params['loader'] == 'grub-efi':
345 # Builds grub.cfg if ISODIR didn't exist or
346 # didn't contains grub.cfg
347 bootimg_dir = img_iso_dir
348 if not os.path.exists("%s/EFI/BOOT" % bootimg_dir):
349 bootimg_dir = "%s/bootimg" % cr_workdir
350 if os.path.exists(bootimg_dir):
351 shutil.rmtree(bootimg_dir)
352 install_cmd = "install -d %s/EFI/BOOT" % bootimg_dir
353 exec_cmd(install_cmd)
354
355 if not os.path.isfile("%s/EFI/BOOT/boot.cfg" % bootimg_dir):
356 cls.do_configure_grubefi(part, cr, bootimg_dir)
357
358 # Builds bootx64.efi/bootia32.efi if ISODIR didn't exist or
359 # didn't contains it
360 target_arch = get_bitbake_var("TARGET_SYS")
361 if not target_arch:
362 msger.error("Coludn't find target architecture\n")
363
364 if re.match("x86_64", target_arch):
365 grub_target = 'x86_64-efi'
366 grub_image = "bootx64.efi"
367 elif re.match('i.86', target_arch):
368 grub_target = 'i386-efi'
369 grub_image = "bootia32.efi"
370 else:
371 msger.error("grub-efi is incompatible with target %s\n" \
372 % target_arch)
373
374 if not os.path.isfile("%s/EFI/BOOT/%s" \
375 % (bootimg_dir, grub_image)):
376 grub_path = get_bitbake_var("STAGING_LIBDIR")
377 if not grub_path:
378 msger.error("Couldn't find STAGING_LIBDIR, exiting.\n")
379
380 grub_core = "%s/grub/%s" % (grub_path, grub_target)
381 if not os.path.exists(grub_core):
382 msger.info("Building grub-efi...\n")
383 exec_cmd("bitbake grub-efi")
384 if not os.path.exists(grub_core):
385 msger.error("Please build grub-efi first\n")
386
387 grub_cmd = "grub-mkimage -p '/EFI/BOOT' "
388 grub_cmd += "-d %s " % grub_core
389 grub_cmd += "-O %s -o %s/EFI/BOOT/%s " \
390 % (grub_target, bootimg_dir, grub_image)
391 grub_cmd += "part_gpt part_msdos ntfs ntfscomp fat ext2 "
392 grub_cmd += "normal chain boot configfile linux multiboot "
393 grub_cmd += "search efi_gop efi_uga font gfxterm gfxmenu "
394 grub_cmd += "terminal minicmd test iorw loadenv echo help "
395 grub_cmd += "reboot serial terminfo iso9660 loopback tar "
396 grub_cmd += "memdisk ls search_fs_uuid udf btrfs xfs lvm "
397 grub_cmd += "reiserfs ata "
398 exec_native_cmd(grub_cmd, native_sysroot)
399
400 else:
401 # TODO: insert gummiboot stuff
402 msger.error("unrecognized bootimg-efi loader: %s" \
403 % source_params['loader'])
404 except KeyError:
405 msger.error("bootimg-efi requires a loader, none specified")
406
407 if os.path.exists("%s/EFI/BOOT" % isodir):
408 shutil.rmtree("%s/EFI/BOOT" % isodir)
409
410 shutil.copytree(bootimg_dir+"/EFI/BOOT", isodir+"/EFI/BOOT")
411
412 # If exists, remove cr_workdir/bootimg temporary folder
413 if os.path.exists("%s/bootimg" % cr_workdir):
414 shutil.rmtree("%s/bootimg" % cr_workdir)
415
416 # Create efi.img that contains bootloader files for EFI booting
417 # if ISODIR didn't exist or didn't contains it
418 if os.path.isfile("%s/efi.img" % img_iso_dir):
419 install_cmd = "install -m 0644 %s/efi.img %s/efi.img" % \
420 (img_iso_dir, isodir)
421 exec_cmd(install_cmd)
422 else:
423 du_cmd = "du -bks %s/EFI" % isodir
424 out = exec_cmd(du_cmd)
425 blocks = int(out.split()[0])
426 # Add some extra space for file system overhead
427 blocks += 100
428 msg = "Added 100 extra blocks to %s to get to %d total blocks" \
429 % (part.mountpoint, blocks)
430 msger.debug(msg)
431
432 # Ensure total sectors is an integral number of sectors per
433 # track or mcopy will complain. Sectors are 512 bytes, and we
434 # generate images with 32 sectors per track. This calculation is
435 # done in blocks, thus the mod by 16 instead of 32.
436 blocks += (16 - (blocks % 16))
437
438 # dosfs image for EFI boot
439 bootimg = "%s/efi.img" % isodir
440
441 dosfs_cmd = 'mkfs.vfat -n "EFIimg" -S 512 -C %s %d' \
442 % (bootimg, blocks)
443 exec_native_cmd(dosfs_cmd, native_sysroot)
444
445 mmd_cmd = "mmd -i %s ::/EFI" % bootimg
446 exec_native_cmd(mmd_cmd, native_sysroot)
447
448 mcopy_cmd = "mcopy -i %s -s %s/EFI/* ::/EFI/" \
449 % (bootimg, isodir)
450 exec_native_cmd(mcopy_cmd, native_sysroot)
451
452 chmod_cmd = "chmod 644 %s" % bootimg
453 exec_cmd(chmod_cmd)
454
455 # Prepare files for legacy boot
456 syslinux_dir = get_bitbake_var("STAGING_DATADIR")
457 if not syslinux_dir:
458 msger.error("Couldn't find STAGING_DATADIR, exiting.\n")
459
460 if os.path.exists("%s/isolinux" % isodir):
461 shutil.rmtree("%s/isolinux" % isodir)
462
463 install_cmd = "install -d %s/isolinux" % isodir
464 exec_cmd(install_cmd)
465
466 cls.do_configure_syslinux(cr, cr_workdir)
467
468 install_cmd = "install -m 444 %s/syslinux/ldlinux.sys " % syslinux_dir
469 install_cmd += "%s/isolinux/ldlinux.sys" % isodir
470 exec_cmd(install_cmd)
471
472 install_cmd = "install -m 444 %s/syslinux/isohdpfx.bin " % syslinux_dir
473 install_cmd += "%s/isolinux/isohdpfx.bin" % isodir
474 exec_cmd(install_cmd)
475
476 install_cmd = "install -m 644 %s/syslinux/isolinux.bin " % syslinux_dir
477 install_cmd += "%s/isolinux/isolinux.bin" % isodir
478 exec_cmd(install_cmd)
479
480 install_cmd = "install -m 644 %s/syslinux/ldlinux.c32 " % syslinux_dir
481 install_cmd += "%s/isolinux/ldlinux.c32" % isodir
482 exec_cmd(install_cmd)
483
484 #create ISO image
485 iso_img = "%s/tempiso_img.iso" % cr_workdir
486 iso_bootimg = "isolinux/isolinux.bin"
487 iso_bootcat = "isolinux/boot.cat"
488 efi_img = "efi.img"
489
490 mkisofs_cmd = "mkisofs -V %s " % part.label
491 mkisofs_cmd += "-o %s -U " % iso_img
492 mkisofs_cmd += "-J -joliet-long -r -iso-level 2 -b %s " % iso_bootimg
493 mkisofs_cmd += "-c %s -no-emul-boot -boot-load-size 4 " % iso_bootcat
494 mkisofs_cmd += "-boot-info-table -eltorito-alt-boot "
495 mkisofs_cmd += "-eltorito-platform 0xEF -eltorito-boot %s " % efi_img
496 mkisofs_cmd += "-no-emul-boot %s " % isodir
497
498 msger.debug("running command: %s" % mkisofs_cmd)
499 exec_native_cmd(mkisofs_cmd, native_sysroot)
500
501 shutil.rmtree(isodir)
502
503 du_cmd = "du -Lbks %s" % iso_img
504 out = exec_cmd(du_cmd)
505 isoimg_size = int(out.split()[0])
506
507 part.set_size(isoimg_size)
508 part.set_source_file(iso_img)
509
510 @classmethod
511 def do_install_disk(cls, disk, disk_name, cr, workdir, oe_builddir,
512 bootimg_dir, kernel_dir, native_sysroot):
513 """
514 Called after all partitions have been prepared and assembled into a
515 disk image. In this case, we insert/modify the MBR using isohybrid
516 utility for booting via BIOS from disk storage devices.
517 """
518
519 full_path = cr._full_path(workdir, disk_name, "direct")
520 iso_img = "%s.p1" % full_path
521 full_path_iso = cr._full_path(workdir, disk_name, "iso")
522
523 isohybrid_cmd = "isohybrid -u %s" % iso_img
524 msger.debug("running command: %s" % \
525 isohybrid_cmd)
526 exec_native_cmd(isohybrid_cmd, native_sysroot)
527
528 # Replace the image created by direct plugin with the one created by
529 # mkisofs command. This is necessary because the iso image created by
530 # mkisofs has a very specific MBR is system area of the ISO image, and
531 # direct plugin adds and configures an another MBR.
532 msger.debug("Replaceing the image created by direct plugin\n")
533 os.remove(full_path)
534 shutil.copy2(iso_img, full_path_iso)
535 shutil.copy2(full_path_iso, full_path)
536
537 # Remove temporary ISO file
538 os.remove(iso_img)