diff --git a/nixos/lib/make-iso9660-image.nix b/nixos/lib/make-iso9660-image.nix index 8a9d89e5dacc6..b1039bc57212d 100644 --- a/nixos/lib/make-iso9660-image.nix +++ b/nixos/lib/make-iso9660-image.nix @@ -41,6 +41,9 @@ # Whether this should be an efi-bootable El-Torito CD. efiBootable ? false, + # Whether this should be a CHRP-compatible CD. + chrpBootable ? false, + # Whether this should be an hybrid CD (bootable from USB as well as CD). usbBootable ? false, @@ -50,6 +53,12 @@ # The path (in the ISO file system) of the efi boot image. efiBootImage ? "", + # The path (in the ISO file system) to bless for booting on New World ROM PowerMacs (path that "\\" in Open Firmware will resolve to) + tbxiBlessDir ? "", + + # The file in tbxiBlessDir to bless as type tbxi + tbxiBlessFile ? "", + # The path (outside the ISO file system) of the isohybrid-mbr image. isohybridMbrImage ? "", @@ -63,6 +72,9 @@ assert bootable -> bootImage != ""; assert efiBootable -> efiBootImage != ""; +assert + (tbxiBlessDir != "" || tbxiBlessFile != "") + -> (chrpBootable && tbxiBlessDir != "" && tbxiBlessFile != ""); assert usbBootable -> isohybridMbrImage != ""; let @@ -97,6 +109,9 @@ stdenv.mkDerivation { volumeID efiBootImage efiBootable + chrpBootable + tbxiBlessDir + tbxiBlessFile isohybridMbrImage usbBootable ; diff --git a/nixos/lib/make-iso9660-image.sh b/nixos/lib/make-iso9660-image.sh index 5881195e461f8..4368e7d167c64 100644 --- a/nixos/lib/make-iso9660-image.sh +++ b/nixos/lib/make-iso9660-image.sh @@ -53,6 +53,18 @@ if test -n "$efiBootable"; then -isohybrid-gpt-basdat" fi +if test -n "$chrpBootable"; then + chrpBootFlags="-sysid PPC -chrp-boot-part" + + if [ -n "$tbxiBlessDir" -a -n "$tbxiBlessFile" ]; then + chrpBootFlags="-hfsplus + -apm-block-size 2048 + -hfs-bless-by p $tbxiBlessDir + -hfsplus-file-creator-type chrp tbxi $tbxiBlessDir/$tbxiBlessFile + $chrpBootFlags" + fi +fi + touch pathlist @@ -113,6 +125,7 @@ xorriso="xorriso ${isoBootFlags} ${usbBootFlags} ${efiBootFlags} + ${chrpBootFlags} -r -path-list pathlist --sort-weight 0 / diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix index e73fa287c45bb..f38ccbe28fec8 100644 --- a/nixos/modules/installer/cd-dvd/iso-image.nix +++ b/nixos/modules/installer/cd-dvd/iso-image.nix @@ -216,6 +216,13 @@ let [ baseIsolinuxCfg ] ++ lib.optional config.boot.loader.grub.memtest86.enable isolinuxMemtest86Entry ); + chrpRoot = "/boot/grub"; + # Firmware expects \ + chrpRootBackslashed = lib.strings.replaceStrings [ "/" ] [ "\\" ] chrpRoot; + chrpBootExecutable = "grub.elf"; + + efiRoot = "/EFI/BOOT"; + refindBinary = if targetArch == "x64" || targetArch == "aa64" then "refind_${targetArch}.efi" else null; @@ -224,13 +231,32 @@ let if refindBinary != null then '' # Adds rEFInd to the ISO. - cp -v ${pkgs.refind}/share/refind/${refindBinary} $out/EFI/BOOT/ + cp -v ${pkgs.refind}/share/refind/${refindBinary} $out/${efiRoot}/ '' else "# No refind for ${targetArch}"; + tbxiBlessDir = "/System/Library/CoreServices"; + tbxiBlessFile = "BootX"; + grubPkgs = if config.boot.loader.grub.forcei686 then pkgs.pkgsi686Linux else pkgs; + grubRoot = if config.isoImage.makeChrpBootable then chrpRoot else efiRoot; + + grubMarkerFile = "/nixos-installer-marker"; + + # "#" may not be handled in these, producing an error on the console + grubEmbeddedCfg = pkgs.writers.writeText "grub-embedded.cfg" ( + # Search using a "marker file" + '' + search --set=root --file ${grubMarkerFile} + '' + # Switch to real config file + + '' + configfile ($root)${grubRoot}/grub.cfg + '' + ); + grubMenuCfg = '' set textmode=${lib.boolToString (config.isoImage.forceTextMode)} @@ -239,7 +265,7 @@ let # # Search using a "marker file" - search --set=root --file /EFI/nixos-installer-image + search --set=root --file ${grubMarkerFile} insmod gfxterm insmod png @@ -280,13 +306,13 @@ let if config.isoImage.grubTheme != null then '' # Sets theme. - set theme=(\$root)/EFI/BOOT/grub-theme/theme.txt + set theme=(\$root)${grubRoot}/grub-theme/theme.txt # Load theme fonts - $(find ${config.isoImage.grubTheme} -iname '*.pf2' -printf "loadfont (\$root)/EFI/BOOT/grub-theme/%P\n") + $(find ${config.isoImage.grubTheme} -iname '*.pf2' -printf "loadfont (\$root)${grubRoot}/grub-theme/%P\n") '' else '' - if background_image (\$root)/EFI/BOOT/efi-background.png; then + if background_image (\$root)${efiRoot}/efi-background.png; then # Black background means transparent background when there # is a background image set... This seems undocumented :( set color_normal=black/black @@ -300,43 +326,64 @@ let } hiddenentry 'Text mode' --hotkey 't' { - loadfont (\$root)/EFI/BOOT/unicode.pf2 + loadfont (\$root)${grubRoot}/unicode.pf2 set textmode=true terminal_output console } ${lib.optionalString (config.isoImage.grubTheme != null) '' hiddenentry 'GUI mode' --hotkey 'g' { - $(find ${config.isoImage.grubTheme} -iname '*.pf2' -printf "loadfont (\$root)/EFI/BOOT/grub-theme/%P\n") + $(find ${config.isoImage.grubTheme} -iname '*.pf2' -printf "loadfont (\$root)${grubRoot}/grub-theme/%P\n") set textmode=false terminal_output gfxterm } ''} ''; - # The EFI boot image. + # The EFI/CHRP boot image. # Notes about grub: # * Yes, the grubMenuCfg has to be repeated in all submenus. Otherwise you # will get white-on-black console-like text on sub-menus. *sigh* - efiDir = - pkgs.runCommand "efi-directory" + grubDir = + let + grubBuildPkg = + if config.isoImage.makeChrpBootable then + pkgs.buildPackages.grub2_ieee1275 + else + pkgs.buildPackages.grub2_efi; + grubHostPkg = + if config.isoImage.makeChrpBootable then grubPkgs.grub2_ieee1275 else grubPkgs.grub2_efi; + grubBootName = + if config.isoImage.makeChrpBootable then + chrpBootExecutable + else + "BOOT${lib.toUpper targetArch}.EFI"; + in + pkgs.runCommand "grub-directory" { - nativeBuildInputs = [ pkgs.buildPackages.grub2_efi ]; + nativeBuildInputs = [ grubBuildPkg ]; strictDeps = true; } - '' - mkdir -p $out/EFI/BOOT - - # Add a marker so GRUB can find the filesystem. - touch $out/EFI/nixos-installer-image - - # ALWAYS required modules. - MODULES=( - # Basic modules for filesystems and partition schemes - "fat" - "iso9660" - "part_gpt" - "part_msdos" + ( + '' + grub-script-check ${grubEmbeddedCfg} + + # Add a marker so GRUB can find the filesystem. + mkdir $out + touch $out/${grubMarkerFile} + + # ALWAYS required modules. + MODULES=( + # Basic modules for filesystems and partition schemes + "fat" + "iso9660" + "part_gpt" + "part_msdos" + '' + + lib.optionalString config.isoImage.makeNewWorldMacBootable '' + "part_apple" + '' + + '' # Basic stuff "normal" @@ -344,141 +391,154 @@ let "linux" "configfile" "loopback" + '' + + lib.optionalString (!config.isoImage.makeChrpBootable) '' "chain" + '' + + '' "halt" + '' + + lib.optionalString config.isoImage.makeEfiBootable '' # Allows rebooting into firmware setup interface "efifwsetup" # EFI Graphics Output Protocol "efi_gop" + '' + + '' + + # User commands + "ls" + + # System commands + "search" + "search_label" + "search_fs_uuid" + "search_fs_file" + "echo" + + # We're not using it anymore, but we'll leave it in so it can be used + # by user, with the console using "C" + "serial" + + # Graphical mode stuff + "gfxmenu" + "gfxterm" + "gfxterm_background" + "test" + "loadenv" + "all_video" + "videoinfo" + + # File types for graphical mode + "png" + ) + + echo "Building GRUB with modules:" + for mod in ''${MODULES[@]}; do + echo " - $mod" + done + + # Modules that may or may not be available per-platform. + echo "Adding additional modules:" + for mod in efi_uga; do + if [ -f ${grubHostPkg}/lib/grub/${grubHostPkg.grubTarget}/$mod.mod ]; then + echo " - $mod" + MODULES+=("$mod") + fi + done + + mkdir -p $out/${grubRoot} + + # Make our own efi program, we can't rely on "grub-install" since it seems to + # probe for devices, even with --skip-fs-probe. + grub-mkimage \ + --directory=${grubHostPkg}/lib/grub/${grubHostPkg.grubTarget} \ + -o $out/${grubRoot}/${grubBootName} \ + -c ${grubEmbeddedCfg} \ + -p ${grubRoot} \ + -O ${grubHostPkg.grubTarget} \ + ''${MODULES[@]} + cp ${grubHostPkg}/share/grub/unicode.pf2 $out/${grubRoot} + + cat < $out/${grubRoot}/grub.cfg + + set timeout=${toString grubEfiTimeout} - # User commands - "ls" - - # System commands - "search" - "search_label" - "search_fs_uuid" - "search_fs_file" - "echo" - - # We're not using it anymore, but we'll leave it in so it can be used - # by user, with the console using "C" - "serial" - - # Graphical mode stuff - "gfxmenu" - "gfxterm" - "gfxterm_background" - "test" - "loadenv" - "all_video" - "videoinfo" - - # File types for graphical mode - "png" - ) - - echo "Building GRUB with modules:" - for mod in ''${MODULES[@]}; do - echo " - $mod" - done + clear + # This message will only be viewable on the default (i.e. UEFI) console. + echo "" + echo "Loading graphical boot menu..." + echo "" + echo "Press 't' to use the text boot menu on this console..." + echo "" - # Modules that may or may not be available per-platform. - echo "Adding additional modules:" - for mod in efi_uga; do - if [ -f ${grubPkgs.grub2_efi}/lib/grub/${grubPkgs.grub2_efi.grubTarget}/$mod.mod ]; then - echo " - $mod" - MODULES+=("$mod") + ${grubMenuCfg} + + # If the parameter iso_path is set, append the findiso parameter to the kernel + # line. We need this to allow the nixos iso to be booted from grub directly. + if [ \''${iso_path} ] ; then + set isoboot="findiso=\''${iso_path}" fi - done - # Make our own efi program, we can't rely on "grub-install" since it seems to - # probe for devices, even with --skip-fs-probe. - grub-mkimage \ - --directory=${grubPkgs.grub2_efi}/lib/grub/${grubPkgs.grub2_efi.grubTarget} \ - -o $out/EFI/BOOT/BOOT${lib.toUpper targetArch}.EFI \ - -p /EFI/BOOT \ - -O ${grubPkgs.grub2_efi.grubTarget} \ - ''${MODULES[@]} - cp ${grubPkgs.grub2_efi}/share/grub/unicode.pf2 $out/EFI/BOOT/ - - cat < $out/EFI/BOOT/grub.cfg - - set timeout=${toString grubEfiTimeout} - - clear - # This message will only be viewable on the default (UEFI) console. - echo "" - echo "Loading graphical boot menu..." - echo "" - echo "Press 't' to use the text boot menu on this console..." - echo "" - - ${grubMenuCfg} - - # If the parameter iso_path is set, append the findiso parameter to the kernel - # line. We need this to allow the nixos iso to be booted from grub directly. - if [ \''${iso_path} ] ; then - set isoboot="findiso=\''${iso_path}" - fi - - # - # Menu entries - # - - ${buildMenuGrub2 { }} - submenu "Options" --class submenu --class hidpi { - ${grubMenuCfg} + # + # Menu entries + # + + ${buildMenuGrub2 { }} + submenu "Options" --class submenu --class hidpi { + ${grubMenuCfg} + + ${lib.concatMapStringsSep "\n" ( + { + title, + class, + params, + }: + '' + submenu "${title}" --class ${class} { + ${grubMenuCfg} + ${buildMenuGrub2 { inherit params; }} + } + '' + ) optionsSubMenus} + } - ${lib.concatMapStringsSep "\n" ( - { - title, - class, - params, - }: - '' - submenu "${title}" --class ${class} { - ${grubMenuCfg} - ${buildMenuGrub2 { inherit params; }} + ${lib.optionalString (refindBinary != null) '' + # GRUB apparently cannot do "chainloader" operations on "CD". + if [ "\$root" != "cd0" ]; then + menuentry 'rEFInd' --class refind { + # Force root to be the FAT partition + # Otherwise it breaks rEFInd's boot + search --set=root --no-floppy --fs-uuid 1234-5678 + chainloader (\$root)${efiRoot}/${refindBinary} } - '' - ) optionsSubMenus} - } - - ${lib.optionalString (refindBinary != null) '' - # GRUB apparently cannot do "chainloader" operations on "CD". - if [ "\$root" != "cd0" ]; then - menuentry 'rEFInd' --class refind { - # Force root to be the FAT partition - # Otherwise it breaks rEFInd's boot - search --set=root --no-floppy --fs-uuid 1234-5678 - chainloader (\$root)/EFI/BOOT/${refindBinary} + fi + ''} + ${lib.optionalString config.boot.loader.grub.memtest86.enable '' + menuentry 'Memtest86+' --class debug { + linux (\$root)/boot/memtest.bin ${toString config.boot.loader.grub.memtest86.params} } - fi - ''} - ${lib.optionalString config.boot.loader.grub.memtest86.enable '' - menuentry 'Memtest86+' --class debug { - linux (\$root)/boot/memtest.bin ${toString config.boot.loader.grub.memtest86.params} + ''} + menuentry 'Firmware Setup' --class settings { + fwsetup + clear + echo "" + echo "If you see this message, then your system doesn't support this feature." + echo "" } - ''} - menuentry 'Firmware Setup' --class settings { - fwsetup - clear - echo "" - echo "If you see this message, your EFI system doesn't support this feature." - echo "" - } - menuentry 'Shutdown' --class shutdown { - halt - } - EOF - - grub-script-check $out/EFI/BOOT/grub.cfg + menuentry 'Shutdown' --class shutdown { + halt + } + EOF - ${refind} - ''; + grub-script-check $out/${grubRoot}/grub.cfg + '' + + lib.optionalString config.isoImage.makeEfiBootable '' + ${refind} + '' + ); efiImg = pkgs.runCommand "efi-image_eltorito" @@ -494,8 +554,8 @@ let # dates (cp -p, touch, mcopy -m, faketime for label), IDs (mkfs.vfat -i) '' mkdir ./contents && cd ./contents - mkdir -p ./EFI/BOOT - cp -rp "${efiDir}"/EFI/BOOT/{grub.cfg,*.EFI,*.efi} ./EFI/BOOT + mkdir -p ./${efiRoot} + cp -rp "${grubDir}"/${efiRoot}/{grub.cfg,*.EFI,*.efi} ./${efiRoot} # Rewrite dates for everything in the FS find . -exec touch --date=2000-01-01 {} + @@ -656,6 +716,22 @@ in ''; }; + isoImage.makeChrpBootable = lib.mkOption { + default = false; + type = lib.types.bool; + description = '' + Whether the ISO image should be bootable on CHRP machines. + ''; + }; + + isoImage.makeNewWorldMacBootable = lib.mkOption { + default = false; + type = lib.types.bool; + description = '' + Whether the ISO image should be bootable on PowerMacs with New World ROMs. + ''; + }; + isoImage.makeUsbBootable = lib.mkOption { default = false; type = lib.types.bool; @@ -847,6 +923,10 @@ in in "isoImage.volumeID ${config.isoImage.volumeID} is ${howmany} characters. That is ${toomany} characters longer than the limit of 32."; } + { + assertion = config.isoImage.makeNewWorldMacBootable -> config.isoImage.makeChrpBootable; + message = "isoImage.makeNewWorldMacBootable requires isoImage.makeChrpBootable. New World ROM Macs use CHRP boot scripts."; + } ( let badSpecs = lib.filterAttrs ( @@ -872,11 +952,19 @@ in # here and it causes a cyclic dependency. boot.loader.grub.enable = lib.mkImageMediaOverride false; - environment.systemPackages = [ - grubPkgs.grub2 - ] - ++ lib.optional (config.isoImage.makeBiosBootable) pkgs.syslinux; - system.extraDependencies = [ grubPkgs.grub2_efi ]; + environment.systemPackages = + if config.isoImage.makeChrpBootable then + [ + grubPkgs.grub2_ieee1275 + ] + else + ( + [ + grubPkgs.grub2 + ] + ++ lib.optional (config.isoImage.makeBiosBootable) pkgs.syslinux + ); + system.extraDependencies = lib.optional (lib.meta.availableOn grubPkgs.hostPlatform grubPkgs.grub2_efi) grubPkgs.grub2_efi; # In stage 1 of the boot, mount the CD as the root FS by label so # that we don't need to know its device. We pass the label of the @@ -991,25 +1079,96 @@ in target = "/isolinux"; } ] + ++ lib.optionals config.isoImage.makeChrpBootable [ + { + source = "${grubDir}/${chrpRoot}"; + target = chrpRoot; + } + { + # TODO: Customise (name, logo). Icon is in different format than OS-BADGE-ICONS. + source = pkgs.runCommand "grub-bootinfo" { } '' + cp ${grubPkgs.grub2_ieee1275}/lib/grub/powerpc-ieee1275/bootinfo.txt $out + substituteInPlace $out --replace-fail '\boot\grub\powerpc.elf' '${chrpRootBackslashed}\${chrpBootExecutable}' + ''; + target = "/ppc/bootinfo.txt"; + } + ] + ++ lib.optionals config.isoImage.makeNewWorldMacBootable [ + { + source = + let + bootxStart = pkgs.writers.writeText "BootX-start" '' + + + MacRISC MacRISC3 MacRISC4 + + + GRand Unified Bootloader + + + NixOS + + + ''; + bootxEnd = pkgs.writers.writeText "BootX-end" '' + + + boot &device;:&partition;,${chrpRootBackslashed}\${chrpBootExecutable} + + + ''; + # Reading SVGs with ImageMagick on big-endian produces switched ARGB channels + # https://github.com/ImageMagick/ImageMagick/issues/8596 + # rsvg-convert gets us most of the way, but can't do netpbm formats + nix-snowflake-ppm = + pkgs.runCommand "nix-snowflake.ppm" + { + nativeBuildInputs = [ + pkgs.imagemagick + pkgs.librsvg + ]; + } + '' + rsvg-convert \ + --background-color white \ + --width 52 --height 52 \ + ${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg \ + | magick - $out + ''; + in + pkgs.runCommand "grub-BootX" { nativeBuildInputs = [ pkgs.netpbm2osbadgeicons ]; } '' + cat ${bootxStart} > $out + netpbm2osbadgeicons ${nix-snowflake-ppm} >> $out + cat ${bootxEnd} >> $out + ''; + target = "${tbxiBlessDir}/${tbxiBlessFile}"; + } + ] ++ lib.optionals config.isoImage.makeEfiBootable [ { source = efiImg; target = "/boot/efi.img"; } { - source = "${efiDir}/EFI"; + source = "${grubDir}/EFI"; target = "/EFI"; } { source = config.isoImage.efiSplashImage; - target = "/EFI/BOOT/efi-background.png"; + target = "${efiRoot}/efi-background.png"; + } + ] + ++ lib.optionals (config.isoImage.makeChrpBootable || config.isoImage.makeEfiBootable) [ + { + source = "${grubDir}/${grubMarkerFile}"; + target = grubMarkerFile; } ] ++ lib.optionals (config.isoImage.makeEfiBootable && !config.boot.initrd.systemd.enable) [ # http://www.supergrubdisk.org/wiki/Loopback.cfg # This feature will be removed, and thus is not supported by systemd initrd { - source = (pkgs.writeTextDir "grub/loopback.cfg" "source /EFI/BOOT/grub.cfg") + "/grub"; + source = (pkgs.writeTextDir "grub/loopback.cfg" "source ${efiRoot}/grub.cfg") + "/grub"; target = "/boot/grub"; } ] @@ -1022,7 +1181,7 @@ in ++ lib.optionals (config.isoImage.grubTheme != null) [ { source = config.isoImage.grubTheme; - target = "/EFI/BOOT/grub-theme"; + target = "${grubRoot}/grub-theme"; } ]; @@ -1053,6 +1212,12 @@ in efiBootable = true; efiBootImage = "boot/efi.img"; } + // lib.optionalAttrs config.isoImage.makeChrpBootable { + chrpBootable = true; + } + // lib.optionalAttrs config.isoImage.makeNewWorldMacBootable { + inherit tbxiBlessDir tbxiBlessFile; + } ); systemd.services.register-nix-paths = { diff --git a/pkgs/by-name/ne/netpbm2osbadgeicons/package.nix b/pkgs/by-name/ne/netpbm2osbadgeicons/package.nix new file mode 100644 index 0000000000000..ccea91b4c97e2 --- /dev/null +++ b/pkgs/by-name/ne/netpbm2osbadgeicons/package.nix @@ -0,0 +1,53 @@ +{ + lib, + stdenv, + fetchFromGitHub, + cmake, + ctestCheckHook, + netpbm, + valgrind, +}: + +stdenv.mkDerivation (finalAttrs: { + pname = "netpbm2osbadgeicons"; + version = "1.0.0"; + + src = fetchFromGitHub { + owner = "OPNA2608"; + repo = "netpbm2osbadgeicons"; + tag = "v${finalAttrs.version}"; + hash = "sha256-qbkw4jF5/sB9VB59trrNqvojYhUitfN34a0QXYA2v10="; + }; + + __structuredAttrs = true; + strictDeps = true; + + nativeBuildInputs = [ + cmake + ]; + + buildInputs = [ + netpbm + ]; + + nativeCheckInputs = [ + ctestCheckHook + valgrind + ]; + + cmakeFlags = [ + (lib.strings.cmakeBool "NETPBM2OSBADGEICONS_WERROR" true) + (lib.strings.cmakeBool "NETPBM2OSBADGEICONS_TEST_VALGRIND" true) + ]; + + doCheck = stdenv.buildPlatform.canExecute stdenv.hostPlatform; + + meta = { + description = "Convert Netpbm images to OS-BADGE-ICONS information"; + homepage = "https://github.com/OPNA2608/netpbm2osbadgeicons"; + license = lib.licenses.gpl3Plus; + mainProgram = "netpbm2osbadgeicons"; + maintainers = [ lib.maintainers.OPNA2608 ]; + platforms = lib.platforms.all; + }; +})