diff --git a/conf/liveusb.ks b/conf/liveusb.ks index 8f81c33..af00d24 100644 --- a/conf/liveusb.ks +++ b/conf/liveusb.ks @@ -33,8 +33,7 @@ anaconda qubes-live -# FIXME: xen.efi not supported by livecd-tools, disable it for now --shim +shim %end diff --git a/live/qubes-live.spec b/live/qubes-live.spec index 5375efc..9fbc853 100644 --- a/live/qubes-live.spec +++ b/live/qubes-live.spec @@ -11,6 +11,10 @@ Group: System License: GPL URL: https://qubes-os.org +# ensure that the whole %%post is run when xen and kernel are already installed +Requires(post): xen-hypervisor +Requires(post): kernel + %description Various fixes for Qubes Live edition @@ -49,6 +53,22 @@ for kernel in /boot/vmlinuz-*; do ln -s $(basename ${xen}) /boot/xen.gz-${short_version} done +# EFI: prepare small version of initrd to fit in 32MB ISO9660 limit +xen=$(ls -1 /boot/efi/EFI/qubes/xen-*.efi | tail -n 1) +if [ -n "$xen" ]; then + kernel=$(ls -1 /boot/vmlinuz-*|sort -n|tail -n 1) + cp "${kernel}" /boot/efi/EFI/qubes/vmlinuz + version=$(echo ${kernel} | cut -f 2- -d -) + # copy from lorax-templates-qubes/templates/efi.tmpl: + scsi_modules="3w-9xxx 3w-sas 3w-xxxx BusLogic a100u2w aacraid advansys aic79xx aic7xxx am53c974 arcmsr atp870u bfa bnx2fc csiostor dc395x dmx3191d esas2r esp_scsi fcoe fnic gdth hpsa hptiop hv_storvsc initio ipr ips isci iscsi_boot_sysfs libfc libfcoe libiscsi libosd libsas lpfc megaraid megaraid_mbox megaraid_mm megaraid_sas mpt2sas mpt3sas mvsas mvumi osd pm80xx pmcraid qla1280 qla2xxx qla4xxx raid_class scsi_debug scsi_dh_emc scsi_dh_rdac scsi_transport_fc scsi_transport_iscsi scsi_transport_sas scsi_transport_spi scsi_transport_srp stex sym53c8xx ufshcd virtio_scsi vmw_pvscsi wd719x" + extra_modules="affs befs coda cuse dlm gfs2 mptfc ncpfs nilfs2 ocfs2 ocfs2_dlm ocfs2_dlmfs ocfs2_nodemanager ocfs2_stack_o2cb ocfs2_stack_user ocfs2_stackglue sctp sysv ubifs ufs" + dracut --nomdadmconf --nolvmconf --xz \ + --omit "network multipath modsign systemd crypt shutdown plymouth" \ + --omit "fcoe fcoe-uefi nfs iscsi ifcfg" \ + --omit-drivers="${scsi_modules}" \ + --omit-drivers="${extra_modules}" \ + /boot/efi/EFI/qubes/initrd-small.img ${version} +fi %files /etc/rc.d/init.d/livesys diff --git a/livecd-creator-qubes b/livecd-creator-qubes index ee858ef..b374c2a 100755 --- a/livecd-creator-qubes +++ b/livecd-creator-qubes @@ -19,18 +19,227 @@ import os import os.path +import glob +import shutil import stat +import subprocess import sys import time import optparse import logging import imgcreate +from imgcreate.fs import makedirs class Usage(Exception): def __init__(self, msg = None, no_error = False): Exception.__init__(self, msg, no_error) +class LiveEFIImageCreator(imgcreate.LiveImageCreator): + + def _get_mkisofs_options(self, isodir): + options = [ "-b", "isolinux/isolinux.bin", + "-c", "isolinux/boot.cat", + "-no-emul-boot", "-boot-info-table", + "-boot-load-size", "4" ] + if os.path.exists(isodir + "/isolinux/efiboot.img"): + options.extend([ "-eltorito-alt-boot", + "-e", "isolinux/efiboot.img", + "-no-emul-boot"]) + if os.path.exists(isodir + "/isolinux/macboot.img"): + options.extend([ "-eltorito-alt-boot", + "-e", "isolinux/macboot.img", + "-no-emul-boot"]) + return options + + def __copy_efi_files(self, isodir): + """ Copy the efi files into /EFI/BOOT/ + If any of them are missing, return False. + requires: + xen.efi + gcdx64.efi + vmlinuz + initrd + """ + fail = False + missing = [] + # XXX: when adding multiple kernel support, vmlinuz and initrd needs to + # be suffixed with index + files = [("/boot/efi/EFI/*/shim.efi", "/EFI/BOOT/BOOT%s.efi" % (self.efiarch,)), + ("/boot/efi/EFI/*/gcd*.efi", "/EFI/BOOT/grubx64.efi"), + ("/boot/efi/EFI/*/xen-*.efi", "/EFI/BOOT/xen.efi"), + ("/boot/efi/EFI/*/vmlinuz", "/EFI/BOOT/vmlinuz"), + ("/boot/efi/EFI/*/initrd-small.img", "/EFI/BOOT/initrd"), + ("/boot/efi/EFI/*/fonts/unicode.pf2", "/EFI/BOOT/fonts/"), + ] + makedirs(isodir+"/EFI/BOOT/fonts/") + for src, dest in files: + src_glob = glob.glob(self._instroot+src) + if not src_glob: + missing.append("Missing EFI file (%s)" % (src,)) + fail = True + else: + shutil.copy(src_glob[0], isodir+dest) + map(logging.error, missing) + return fail + + + def __get_xen_efi_image_stanza(self, **args): + if self._isDracut: + args["rootlabel"] = "live:LABEL=%(fslabel)s" % args + else: + args["rootlabel"] = "CDLABEL=%(fslabel)s" % args + return """[%(name)s%(index)s] +kernel=vmlinuz%(index)s root=%(rootlabel)s %(liveargs)s %(extra)s +ramdisk=initrd%(index)s + +""" %args + + + def __get_efi_image_stanza(self, **args): + return """menuentry '%(long)s' --class qubes --class gnu-linux --class gnu --class os { + chainloader /efi/boot/xen.efi placeholder %(name)s%(index)s +} +""" %args + + def __get_efi_image_stanzas(self, isodir, name): + # FIXME: this only supports one kernel right now... + + kernel_options = self._get_kernel_options() + checkisomd5 = self._has_checkisomd5() + + cfg = "" + + for index in range(0, 9): + # only one supported anyway, so simply drop the suffix + index = "" + cfg += self.__get_efi_image_stanza(long = "Start " + self.product, + index = index, name = "normal") + if checkisomd5: + cfg += self.__get_efi_image_stanza( + long = "Test this media & start " + self.product, + index = index, name = "check") + cfg += """ +submenu 'Troubleshooting -->' { +""" + cfg += self.__get_efi_image_stanza(long = "Start " + self.product + " in basic graphics mode", + index = index, name = "basicvideo") + + cfg+= """} +""" + break + + return cfg + + def __get_xen_efi_image_stanzas(self, isodir, name): + # FIXME: this only supports one kernel right now... + + kernel_options = self._get_kernel_options() + checkisomd5 = self._has_checkisomd5() + + cfg = "" + + for index in range(0, 9): + # only one supported anyway, so simply drop the suffix + index = "" + cfg += self.__get_xen_efi_image_stanza(fslabel = self.fslabel, + liveargs = kernel_options, + long = "Start " + self.product, + extra = "", index = index, + name = "normal") + if checkisomd5: + cfg += self.__get_xen_efi_image_stanza(fslabel = self.fslabel, + liveargs = kernel_options, + long = "Test this media & start " + self.product, + extra = "rd.live.check", + index = index, name = "check") + cfg += self.__get_xen_efi_image_stanza(fslabel = self.fslabel, + liveargs = kernel_options, + long = "Start " + self.product + " in basic graphics mode", + extra = "nomodeset", index = index, + name = "basicvideo") + + break + + return cfg + + def __get_basic_xen_efi_config(self): + return """ +[global] +default=normal +""" + + def __get_basic_efi_config(self, **args): + return """ +set default="0" + +function load_video { + insmod efi_gop + insmod efi_uga + insmod video_bochs + insmod video_cirrus + insmod all_video +} + +load_video +set gfxpayload=keep +insmod gzio +insmod part_gpt +insmod ext2 + +set timeout=%(timeout)d +### END /etc/grub.d/00_header ### + +# do not use 'search' - root should be already set based on grub.efi location + +### BEGIN /etc/grub.d/10_linux ### +""" %args + + def _configure_efi_bootloader(self, isodir): + """Set up the configuration for an EFI bootloader""" + if self.__copy_efi_files(isodir): + shutil.rmtree(isodir + "/EFI") + logging.warn("Failed to copy EFI files, no EFI Support will be included.") + return + + cfg = self.__get_basic_efi_config(isolabel = self.fslabel, + timeout = self._timeout) + cfg += self.__get_efi_image_stanzas(isodir, self.name) + + xen_cfg = self.__get_basic_xen_efi_config() + xen_cfg += self.__get_xen_efi_image_stanzas(isodir, self.name) + + cfgf = open(isodir + "/EFI/BOOT/grub.cfg", "w") + cfgf.write(cfg) + cfgf.close() + + xen_cfgf = open(isodir + "/EFI/BOOT/xen.cfg", "w") + xen_cfgf.write(xen_cfg) + xen_cfgf.close() + + def _generate_efiboot(self, isodir): + """Generate EFI boot images.""" + if not glob.glob(self._instroot+"/boot/efi/EFI/*/xen-*.efi"): + logging.error("Missing xen-*.efi, skipping efiboot.img creation.") + return + + subprocess.call(["mkefiboot", "--label", "QUBESEFI", isodir + "/EFI/BOOT", + isodir + "/isolinux/efiboot.img"]) + # FIXME: replace icon + # FIXME: this is broken for many reasons: + # - mkefiboot generates unnecessary big image (about 4 times bigger + # than required) - the bug is in mkmacboot function: + # size = estimate_size(bootdir, graft=graft) * 2 + # ^^^^^^^^^^^^^^^^^^^^ already counted twice + # - mkefiboot -a assumes that the loader is grub.efi + # - it isn't clear whether xen.efi would even work on Apple + subprocess.call(["mkefiboot", "-a", isodir + "/EFI/BOOT", + isodir + "/isolinux/macboot.img", "-l", self.product, + "-n", "/usr/share/pixmaps/bootloader/fedora-media.vol", + "-i", "/usr/share/pixmaps/bootloader/fedora.icns", + "-p", self.product]) + + def parse_options(args): parser = optparse.OptionParser() @@ -181,7 +390,7 @@ def main(): try: if options.image_type == 'livecd': - creator = imgcreate.LiveImageCreator(ks, name, + creator = LiveEFIImageCreator(ks, name, fslabel=fslabel, releasever=options.releasever, tmpdir=os.path.abspath(options.tmpdir),