SVGA_MODE      := -DSVGA_MODE=NORMAL_VGA
 
 targets                := vmlinux.bin setup.bin setup.elf bzImage
-targets                += fdimage fdimage144 fdimage288 image.iso mtools.conf
+targets                += fdimage fdimage144 fdimage288 image.iso hdimage
 subdir-                := compressed
 
 setup-y                += a20.o bioscall.o cmdline.o copy.o cpu.o cpuflags.o cpucheck.o
        $(Q)$(MAKE) $(build)=$(obj)/compressed $@
 
 # Set this if you want to pass append arguments to the
-# bzdisk/fdimage/isoimage kernel
+# bzdisk/fdimage/hdimage/isoimage kernel
 FDARGS =
-# Set this if you want an initrd included with the
-# bzdisk/fdimage/isoimage kernel
+# Set this if you want one or more initrds included in the image
 FDINITRD =
 
-image_cmdline = default linux $(FDARGS) $(if $(FDINITRD),initrd=initrd.img,)
+imgdeps = $(obj)/bzImage $(obj)/mtools.conf $(src)/genimage.sh
 
 $(obj)/mtools.conf: $(src)/mtools.conf.in
        sed -e 's|@OBJ@|$(obj)|g' < $< > $@
 
+targets += mtools.conf
+
+# genimage.sh requires bash, but it also has a bunch of other
+# external dependencies.
 quiet_cmd_genimage = GENIMAGE $3
-cmd_genimage = sh $(srctree)/$(src)/genimage.sh $2 $3 $(obj)/bzImage \
-                       $(obj)/mtools.conf '$(image_cmdline)' $(FDINITRD)
+cmd_genimage = $(BASH) $(srctree)/$(src)/genimage.sh $2 $3 $(obj)/bzImage \
+               $(obj)/mtools.conf '$(FDARGS)' $(FDINITRD)
 
-PHONY += bzdisk fdimage fdimage144 fdimage288 isoimage bzlilo install
+PHONY += bzdisk fdimage fdimage144 fdimage288 hdimage isoimage install
 
 # This requires write access to /dev/fd0
-bzdisk: $(obj)/bzImage $(obj)/mtools.conf
+# All images require syslinux to be installed; hdimage also requires
+# EDK2/OVMF if the kernel is compiled with the EFI stub.
+bzdisk: $(imgdeps)
        $(call cmd,genimage,bzdisk,/dev/fd0)
 
-# These require being root or having syslinux 2.02 or higher installed
-fdimage fdimage144: $(obj)/bzImage $(obj)/mtools.conf
+fdimage fdimage144: $(imgdeps)
        $(call cmd,genimage,fdimage144,$(obj)/fdimage)
        @$(kecho) 'Kernel: $(obj)/fdimage is ready'
 
-fdimage288: $(obj)/bzImage $(obj)/mtools.conf
+fdimage288: $(imgdeps)
        $(call cmd,genimage,fdimage288,$(obj)/fdimage)
        @$(kecho) 'Kernel: $(obj)/fdimage is ready'
 
-isoimage: $(obj)/bzImage
+hdimage: $(imgdeps)
+       $(call cmd,genimage,hdimage,$(obj)/hdimage)
+       @$(kecho) 'Kernel: $(obj)/hdimage is ready'
+
+isoimage: $(imgdeps)
        $(call cmd,genimage,isoimage,$(obj)/image.iso)
        @$(kecho) 'Kernel: $(obj)/image.iso is ready'
 
-bzlilo:
-       if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
-       if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi
-       cat $(obj)/bzImage > $(INSTALL_PATH)/vmlinuz
-       cp System.map $(INSTALL_PATH)/
-       if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
-
 install:
-       sh $(srctree)/$(src)/install.sh $(KERNELRELEASE) $(obj)/bzImage \
+       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh \
+               $(KERNELRELEASE) $(obj)/bzImage \
                System.map "$(INSTALL_PATH)"
 
-#!/bin/sh
+#!/bin/bash
 #
 # This file is subject to the terms and conditions of the GNU General Public
 # License.  See the file "COPYING" in the main directory of this archive
 #
 # Adapted from code in arch/x86/boot/Makefile by H. Peter Anvin and others
 #
-# "make fdimage/fdimage144/fdimage288/isoimage" script for x86 architecture
+# "make fdimage/fdimage144/fdimage288/hdimage/isoimage"
+# script for x86 architecture
 #
 # Arguments:
-#   $1 - fdimage format
-#   $2 - target image file
-#   $3 - kernel bzImage file
-#   $4 - mtool configuration file
-#   $5 - kernel cmdline
-#   $6 - inird image file
+#   $1  - fdimage format
+#   $2  - target image file
+#   $3  - kernel bzImage file
+#   $4  - mtools configuration file
+#   $5  - kernel cmdline
+#   $6+ - initrd image file(s)
+#
+# This script requires:
+#   bash
+#   syslinux
+#   mtools (for fdimage* and hdimage)
+#   edk2/OVMF (for hdimage)
+#
+# Otherwise try to stick to POSIX shell commands...
 #
 
 # Use "make V=1" to debug this script
         ;;
 esac
 
-verify () {
-       if [ ! -f "$1" ]; then
-               echo ""                                                   1>&2
-               echo " *** Missing file: $1"                              1>&2
-               echo ""                                                   1>&2
-               exit 1
+# Exit the top-level shell with an error
+topshell=$$
+trap 'exit 1' USR1
+die() {
+       echo ""        1>&2
+       echo " *** $*" 1>&2
+       echo ""        1>&2
+       kill -USR1 $topshell
+}
+
+# Verify the existence and readability of a file
+verify() {
+       if [ ! -f "$1" -o ! -r "$1" ]; then
+               die "Missing file: $1"
        fi
 }
 
+diskfmt="$1"
+FIMAGE="$2"
+FBZIMAGE="$3"
+MTOOLSRC="$4"
+KCMDLINE="$5"
+shift 5                                # Remaining arguments = initrd files
+
+export MTOOLSRC
 
-export MTOOLSRC=$4
-FIMAGE=$2
-FBZIMAGE=$3
-KCMDLINE=$5
-FDINITRD=$6
+# common options for dd
+dd='dd iflag=fullblock'
 
 # Make sure the files actually exist
 verify "$FBZIMAGE"
 
-genbzdisk() {
-       verify "$MTOOLSRC"
-       mformat a:
-       syslinux $FIMAGE
-       echo "$KCMDLINE" | mcopy - a:syslinux.cfg
-       if [ -f "$FDINITRD" ] ; then
-               mcopy "$FDINITRD" a:initrd.img
+declare -a FDINITRDS
+irdpfx=' initrd='
+initrdopts_syslinux=''
+initrdopts_efi=''
+for f in "$@"; do
+       if [ -f "$f" -a -r "$f" ]; then
+           FDINITRDS=("${FDINITRDS[@]}" "$f")
+           fname="$(basename "$f")"
+           initrdopts_syslinux="${initrdopts_syslinux}${irdpfx}${fname}"
+           irdpfx=,
+           initrdopts_efi="${initrdopts_efi} initrd=${fname}"
        fi
-       mcopy $FBZIMAGE a:linux
+done
+
+# Read a $3-byte littleendian unsigned value at offset $2 from file $1
+le() {
+       local n=0
+       local m=1
+       for b in $(od -A n -v -j $2 -N $3 -t u1 "$1"); do
+               n=$((n + b*m))
+               m=$((m * 256))
+       done
+       echo $n
 }
 
-genfdimage144() {
-       verify "$MTOOLSRC"
-       dd if=/dev/zero of=$FIMAGE bs=1024 count=1440 2> /dev/null
-       mformat v:
-       syslinux $FIMAGE
-       echo "$KCMDLINE" | mcopy - v:syslinux.cfg
-       if [ -f "$FDINITRD" ] ; then
-               mcopy "$FDINITRD" v:initrd.img
-       fi
-       mcopy $FBZIMAGE v:linux
+# Get the EFI architecture name such that boot{name}.efi is the default
+# boot file name. Returns false with no output if the file is not an
+# EFI image or otherwise unknown.
+efiarch() {
+       [ -f "$1" ] || return
+       [ $(le "$1" 0 2) -eq 23117 ] || return          # MZ magic
+       peoffs=$(le "$1" 60 4)                          # PE header offset
+       [ $peoffs -ge 64 ] || return
+       [ $(le "$1" $peoffs 4) -eq 17744 ] || return    # PE magic
+       case $(le "$1" $((peoffs+4+20)) 2) in           # PE type
+               267)    ;;                              # PE32
+               523)    ;;                              # PE32+
+               *) return 1 ;;                          # Invalid
+       esac
+       [ $(le "$1" $((peoffs+4+20+68)) 2) -eq 10 ] || return # EFI app
+       case $(le "$1" $((peoffs+4)) 2) in              # Machine type
+                332)   echo i386       ;;
+                450)   echo arm        ;;
+                512)   echo ia64       ;;
+               20530)  echo riscv32    ;;
+               20580)  echo riscv64    ;;
+               20776)  echo riscv128   ;;
+               34404)  echo x64        ;;
+               43620)  echo aa64       ;;
+       esac
 }
 
-genfdimage288() {
-       verify "$MTOOLSRC"
-       dd if=/dev/zero of=$FIMAGE bs=1024 count=2880 2> /dev/null
-       mformat w:
-       syslinux $FIMAGE
-       echo "$KCMDLINE" | mcopy - W:syslinux.cfg
-       if [ -f "$FDINITRD" ] ; then
-               mcopy "$FDINITRD" w:initrd.img
-       fi
-       mcopy $FBZIMAGE w:linux
+# Get the combined sizes in bytes of the files given, counting sparse
+# files as full length, and padding each file to a 4K block size
+filesizes() {
+       local t=0
+       local s
+       for s in $(ls -lnL "$@" 2>/dev/null | awk '/^-/{ print $5; }'); do
+               t=$((t + ((s+4095)/4096)*4096))
+       done
+       echo $t
 }
 
-geniso() {
-       tmp_dir=`dirname $FIMAGE`/isoimage
-       rm -rf $tmp_dir
-       mkdir $tmp_dir
-       for i in lib lib64 share ; do
-               for j in syslinux ISOLINUX ; do
-                       if [ -f /usr/$i/$j/isolinux.bin ] ; then
-                               isolinux=/usr/$i/$j/isolinux.bin
-                       fi
+# Expand directory names which should be in /usr/share into a list
+# of possible alternatives
+sharedirs() {
+       local dir file
+       for dir in /usr/share /usr/lib64 /usr/lib; do
+               for file; do
+                       echo "$dir/$file"
+                       echo "$dir/${file^^}"
                done
-               for j in syslinux syslinux/modules/bios ; do
-                       if [ -f /usr/$i/$j/ldlinux.c32 ]; then
-                               ldlinux=/usr/$i/$j/ldlinux.c32
-                       fi
+       done
+}
+efidirs() {
+       local dir file
+       for dir in /usr/share /boot /usr/lib64 /usr/lib; do
+               for file; do
+                       echo "$dir/$file"
+                       echo "$dir/${file^^}"
                done
-               if [ -n "$isolinux" -a -n "$ldlinux" ] ; then
-                       break
+       done
+}
+
+findsyslinux() {
+       local f="$(find -L $(sharedirs syslinux isolinux) \
+                   -name "$1" -readable -type f -print -quit 2>/dev/null)"
+       if [ ! -f "$f" ]; then
+               die "Need a $1 file, please install syslinux/isolinux."
+       fi
+       echo "$f"
+       return 0
+}
+
+findovmf() {
+       local arch="$1"
+       shift
+       local -a names=(-false)
+       local name f
+       for name; do
+               names=("${names[@]}" -or -iname "$name")
+       done
+       for f in $(find -L $(efidirs edk2 ovmf) \
+                       \( "${names[@]}" \) -readable -type f \
+                       -print 2>/dev/null); do
+               if [ "$(efiarch "$f")" = "$arch" ]; then
+                       echo "$f"
+                       return 0
                fi
        done
-       if [ -z "$isolinux" ] ; then
-               echo 'Need an isolinux.bin file, please install syslinux/isolinux.'
-               exit 1
+       die "Need a $1 file for $arch, please install EDK2/OVMF."
+}
+
+do_mcopy() {
+       if [ ${#FDINITRDS[@]} -gt 0 ]; then
+               mcopy "${FDINITRDS[@]}" "$1"
+       fi
+       if [ -n "$efishell" ]; then
+               mmd "$1"EFI "$1"EFI/Boot
+               mcopy "$efishell" "$1"EFI/Boot/boot${kefiarch}.efi
        fi
-       if [ -z "$ldlinux" ] ; then
-               echo 'Need an ldlinux.c32 file, please install syslinux/isolinux.'
-               exit 1
+       if [ -n "$kefiarch" ]; then
+               echo linux "$KCMDLINE$initrdopts_efi" | \
+                       mcopy - "$1"startup.nsh
        fi
-       cp $isolinux $tmp_dir
-       cp $ldlinux $tmp_dir
-       cp $FBZIMAGE $tmp_dir/linux
-       echo "$KCMDLINE" > $tmp_dir/isolinux.cfg
-       if [ -f "$FDINITRD" ] ; then
-               cp "$FDINITRD" $tmp_dir/initrd.img
+       echo default linux "$KCMDLINE$initrdopts_syslinux" | \
+               mcopy - "$1"syslinux.cfg
+       mcopy "$FBZIMAGE" "$1"linux
+}
+
+genbzdisk() {
+       verify "$MTOOLSRC"
+       mformat -v 'LINUX_BOOT' a:
+       syslinux "$FIMAGE"
+       do_mcopy a:
+}
+
+genfdimage144() {
+       verify "$MTOOLSRC"
+       $dd if=/dev/zero of="$FIMAGE" bs=1024 count=1440 2>/dev/null
+       mformat -v 'LINUX_BOOT' v:
+       syslinux "$FIMAGE"
+       do_mcopy v:
+}
+
+genfdimage288() {
+       verify "$MTOOLSRC"
+       $dd if=/dev/zero of="$FIMAGE" bs=1024 count=2880 2>/dev/null
+       mformat -v 'LINUX_BOOT' w:
+       syslinux "$FIMAGE"
+       do_mcopy w:
+}
+
+genhdimage() {
+       verify "$MTOOLSRC"
+       mbr="$(findsyslinux mbr.bin)"
+       kefiarch="$(efiarch "$FBZIMAGE")"
+       if [ -n "$kefiarch" ]; then
+               # The efishell provides command line handling
+               efishell="$(findovmf $kefiarch shell.efi shell${kefiarch}.efi)"
+               ptype='-T 0xef' # EFI system partition, no GPT
        fi
-       genisoimage -J -r -input-charset=utf-8 -quiet -o $FIMAGE \
-               -b isolinux.bin -c boot.cat -no-emul-boot -boot-load-size 4 \
-               -boot-info-table $tmp_dir
-       isohybrid $FIMAGE 2>/dev/null || true
-       rm -rf $tmp_dir
+       sizes=$(filesizes "$FBZIMAGE" "${FDINITRDS[@]}" "$efishell")
+       # Allow 1% + 1 MiB for filesystem and partition table overhead,
+       # syslinux, and config files
+       megs=$(((sizes + sizes/100 + 2*1024*1024 - 1)/(1024*1024)))
+       $dd if=/dev/zero of="$FIMAGE" bs=$((1024*1024)) count=$megs 2>/dev/null
+       mpartition -I -c -s 32 -h 64 -t $megs $ptype -b 512 -a h:
+       $dd if="$mbr" of="$FIMAGE" bs=440 count=1 conv=notrunc 2>/dev/null
+       mformat -v 'LINUX_BOOT' -s 32 -h 64 -t $megs h:
+       syslinux --offset $((512*512)) "$FIMAGE"
+       do_mcopy h:
+}
+
+geniso() {
+       tmp_dir="$(dirname "$FIMAGE")/isoimage"
+       rm -rf "$tmp_dir"
+       mkdir "$tmp_dir"
+       isolinux=$(findsyslinux isolinux.bin)
+       ldlinux=$(findsyslinux  ldlinux.c32)
+       cp "$isolinux" "$ldlinux" "$tmp_dir"
+       cp "$FBZIMAGE" "$tmp_dir"/linux
+       echo default linux "$KCMDLINE" > "$tmp_dir"/isolinux.cfg
+       cp "${FDINITRDS[@]}" "$tmp_dir"/
+       genisoimage -J -r -appid 'LINUX_BOOT' -input-charset=utf-8 \
+                   -quiet -o "$FIMAGE" -b isolinux.bin \
+                   -c boot.cat -no-emul-boot -boot-load-size 4 \
+                   -boot-info-table "$tmp_dir"
+       isohybrid "$FIMAGE" 2>/dev/null || true
+       rm -rf "$tmp_dir"
 }
 
-case $1 in
+rm -f "$FIMAGE"
+
+case "$diskfmt" in
        bzdisk)     genbzdisk;;
        fdimage144) genfdimage144;;
        fdimage288) genfdimage288;;
+       hdimage)    genhdimage;;
        isoimage)   geniso;;
-       *)          echo 'Unknown image format'; exit 1;
+       *)          die "Unknown image format: $diskfmt";;
 esac