blob: b98629cc0e82c38a970b78b9256b987b22efcc51 [file]
#!/bin/sh
#
# Generate a network bootable directory image
set -e
set -u
# Print usage message
#
help() {
echo "usage: ${0} [OPTIONS] foo.[k]pxe|foo.efi [bar.[k]pxe|bar.efi,...]"
echo
echo "where OPTIONS are:"
echo " -h show this help"
echo " -a ARCH select default CPU architecture [x86_64]"
echo " -d DIR install images to directory"
echo " -e SHIM specify an EFI shim helper"
echo " -o FILE save image archive to file"
}
# Get hex byte from binary file
#
get_byte() {
local FILENAME
local OFFSET
FILENAME="${1}"
OFFSET="${2}"
od -j "${OFFSET}" -N 1 -A n -t x1 -- "${FILENAME}" | tr -d " "
}
# Get hex word from binary file
#
get_word() {
local FILENAME
local OFFSET
local LSB
local MSB
FILENAME="${1}"
OFFSET="${2}"
LSB=$(get_byte "${FILENAME}" $(( ${OFFSET} + 0 )) )
MSB=$(get_byte "${FILENAME}" $(( ${OFFSET} + 1 )) )
echo "${MSB}${LSB}"
}
# Get hex dword from binary file
#
get_dword() {
local FILENAME
local OFFSET
local LSW
local MSW
FILENAME="${1}"
OFFSET="${2}"
LSW=$(get_word "${FILENAME}" $(( ${OFFSET} + 0 )) )
MSW=$(get_word "${FILENAME}" $(( ${OFFSET} + 2 )) )
echo "${MSW}${LSW}"
}
# Get appropriate subdirectory name for CPU architecture from EFI binary
#
efi_subdir_name() {
local FILENAME
local MZSIG
local PEOFF
local PESIG
local ARCH
local OPTSIG
local SECSIZE
local SBSUFFIX
FILENAME="${1}"
MZSIG=$(get_word "${FILENAME}" 0)
if [ "${MZSIG}" != "5a4d" ] ; then
echo "${FILENAME}: invalid MZ header" >&2
exit 1
fi
PEOFF=$(get_byte "${FILENAME}" 0x3c)
PESIG=$(get_word "${FILENAME}" 0x${PEOFF})
if [ "${PESIG}" != "4550" ] ; then
echo "${FILENAME}: invalid PE header" >&2
exit 1
fi
ARCH=$(get_word "${FILENAME}" $(( 0x${PEOFF} + 4 )) )
OPTSIG=$(get_word "${FILENAME}" $(( 0x${PEOFF} + 24 )) )
case "${OPTSIG}" in
"010b" )
SECSIZE=$(get_dword "${FILENAME}" $(( 0x${PEOFF} + 156 )) )
;;
"020b" )
SECSIZE=$(get_dword "${FILENAME}" $(( 0x${PEOFF} + 172 )) )
;;
* )
echo "${FILENAME}: unrecognised optional header ${OPTSIG}" >&2
exit 1
;;
esac
if [ "${SECSIZE}" != "00000000" ] ; then
SBSUFFIX="-sb"
else
SBSUFFIX=""
fi
case "${ARCH}" in
"014c" )
echo "i386${SBSUFFIX}"
;;
"8664" )
echo "x86_64${SBSUFFIX}"
;;
"01c2" )
echo "arm32${SBSUFFIX}"
;;
"6264" )
echo "loong64${SBSUFFIX}"
;;
"aa64" )
echo "arm64${SBSUFFIX}"
;;
"5064" )
echo "riscv64${SBSUFFIX}"
;;
"5032" )
echo "riscv32${SBSUFFIX}"
;;
* )
echo "${FILENAME}: unrecognised EFI architecture ${ARCH}" >&2
exit 1
esac
}
# Get appropriate subdirectory name for CPU architecture from iPXE NBP
#
nbp_subdir_name() {
local FILENAME
local LJMP
local SEGMENT
local MAGIC
local ARCH
FILENAME="${1}"
LJMP=$(get_byte "${FILENAME}" 0)
if [ "${LJMP}" != "ea" ] ; then
echo "${FILENAME}: invalid LJMP instruction" >&2
exit 1
fi
SEGMENT=$(get_word "${FILENAME}" 3)
if [ "${SEGMENT}" != "07c0" ] ; then
echo "${FILENAME}: invalid LJMP segment" >&2
exit 1
fi
MAGIC=$(get_word "${FILENAME}" 6)
if [ "${MAGIC}" != "18ae" ] ; then
echo "${FILENAME}: invalid iPXE magic" >&2
exit 1
fi
ARCH=$(get_byte "${FILENAME}" 5)
case "${ARCH}" in
"32" )
echo "i386"
;;
"64" )
echo "x86_64"
;;
* )
echo "${FILENAME}: unrecognised NBP architecture ${ARCH}" >&2
exit 1
esac
}
# Get appropriate subdirectory name for CPU architecture
#
subdir_name() {
local FILENAME
local BYTE
FILENAME="${1}"
BYTE=$(get_byte "${FILENAME}" 0)
case "${BYTE}" in
"4d" )
efi_subdir_name "${FILENAME}"
;;
"ea" )
nbp_subdir_name "${FILENAME}"
;;
* )
echo "${FILENAME}: unrecognised format" >&2
exit 1
esac
}
# Parse command-line options
#
DEFARCH=x86_64
OUTDIR=
OUTFILE=
SHIMAA64=
SHIMX64=
while getopts "ha:d:e:o:" OPTION ; do
case "${OPTION}" in
h)
help
exit 0
;;
a)
DEFARCH="${OPTARG}"
;;
d)
OUTDIR="${OPTARG}"
;;
e)
SHIM="${OPTARG}"
SHIMARCH=$(subdir_name "${SHIM}")
case "${SHIMARCH}" in
arm64* )
SHIMAA64="${SHIM}"
;;
x86_64* )
SHIMX64="${SHIM}"
;;
* )
echo "${SHIM}: unsupported shim architecture" >&2
exit 1
esac
;;
o)
OUTFILE="${OPTARG}"
;;
*)
help
exit 1
;;
esac
done
if [ -z "${OUTDIR}" -a -z "${OUTFILE}" ] ; then
echo "${0}: no output directory or file given" >&2
help
exit 1
fi
shift $(( OPTIND - 1 ))
if [ $# -eq 0 ] ; then
echo "${0}: no input files given" >&2
help
exit 1
fi
# Create temporary working directory, if applicable
#
WORKDIR=
if [ -z "${OUTDIR}" ] ; then
WORKDIR=$(mktemp -d "${OUTFILE}.XXXXXX")
OUTDIR="${WORKDIR}/ipxeboot"
fi
mkdir -p "${OUTDIR}"
# Copy files to output directory
#
for FILENAME ; do
SUBDIR=$(subdir_name "${FILENAME}")
ARCH="${SUBDIR%-sb}"
DESTDIR="${OUTDIR}/${SUBDIR}"
BASENAME=$(basename "${FILENAME}")
SHIMLINK="${BASENAME%.efi}-shim.efi"
mkdir -p "${DESTDIR}"
install -m 644 "${FILENAME}" "${DESTDIR}/${BASENAME}"
case "${SUBDIR}" in
arm64-sb )
if [ -n "${SHIMAA64}" ] ; then
install -m 644 "${SHIMAA64}" "${DESTDIR}/shimaa64.efi"
ln -sfn "shimaa64.efi" "${DESTDIR}/${SHIMLINK}"
fi
;;
x86_64-sb )
if [ -n "${SHIMX64}" ] ; then
install -m 644 "${SHIMX64}" "${DESTDIR}/shimx64.efi"
ln -sfn "shimx64.efi" "${DESTDIR}/${SHIMLINK}"
fi
;;
esac
if [ "${ARCH}" = "${DEFARCH}" ] ; then
if [ "${ARCH}" = "${SUBDIR}" ] ; then
ln -sfn "${SUBDIR}/${BASENAME}" "${OUTDIR}/${BASENAME}"
else
ln -sfn "${SUBDIR}" "${OUTDIR}/sb"
fi
fi
done
# Create output archive file, if applicable
#
if [ -n "${OUTFILE}" ] ; then
TOPDIR=$(dirname "${OUTDIR}")
BASENAME=$(basename "${OUTDIR}")
case "${OUTFILE}" in
*.tar )
tar cf "${OUTFILE}" -C "${TOPDIR}" "${BASENAME}"
;;
*.tar.gz | *.tgz )
tar czf "${OUTFILE}" -C "${TOPDIR}" "${BASENAME}"
;;
*.tar.bz2 )
tar cjf "${OUTFILE}" -C "${TOPDIR}" "${BASENAME}"
;;
*.tar.xz )
tar cJf "${OUTFILE}" -C "${TOPDIR}" "${BASENAME}"
;;
* )
echo "${OUTFILE}: unrecognised archive format" >&2
exit 1
;;
esac
fi
# Clean up temporary working directory
#
if [ -n "${WORKDIR}" ] ; then
rm -rf "${WORKDIR}"
fi