diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1cd1376ae..0afffc092 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -360,7 +360,7 @@ jobs: shimx64.efi combine: - name: BIOS + UEFI + name: Combine runs-on: ubuntu-latest needs: - bios @@ -370,7 +370,10 @@ jobs: container: image: ghcr.io/ipxe/ipxe-builder-utils env: - binaries: >- + efishims: >- + -e shim/shimaa64.efi + -e shim/shimx64.efi + fsbinaries: >- bin-x86_64-pcbios/${DRIVERS}.lkrn bin-arm32-efi/${DRIVERS}.efi bin-arm64-efi/${DRIVERS}.efi @@ -379,6 +382,38 @@ jobs: bin-riscv32-efi/${DRIVERS}.efi bin-riscv64-efi/${DRIVERS}.efi bin-x86_64-efi/${DRIVERS}.efi + srvbinaries: >- + bin/ipxe.pxe + bin/ipxe-legacy.pxe + bin/undionly.kpxe + bin-arm32-efi/ipxe.efi + bin-arm32-efi/ipxe-legacy.efi + bin-arm32-efi/snponly.efi + bin-arm64-efi/ipxe.efi + bin-arm64-efi/ipxe-legacy.efi + bin-arm64-efi/snponly.efi + bin-arm64-efi-sb/ipxe.efi + bin-arm64-efi-sb/snponly.efi + bin-i386-efi/ipxe.efi + bin-i386-efi/ipxe-legacy.efi + bin-i386-efi/snponly.efi + bin-loong64-efi/ipxe.efi + bin-loong64-efi/ipxe-legacy.efi + bin-loong64-efi/snponly.efi + bin-riscv32-efi/ipxe.efi + bin-riscv32-efi/ipxe-legacy.efi + bin-riscv32-efi/snponly.efi + bin-riscv64-efi/ipxe.efi + bin-riscv64-efi/ipxe-legacy.efi + bin-riscv64-efi/snponly.efi + bin-x86_64-efi/ipxe.efi + bin-x86_64-efi/ipxe-legacy.efi + bin-x86_64-efi/snponly.efi + bin-x86_64-efi-sb/ipxe.efi + bin-x86_64-efi-sb/snponly.efi + bin-x86_64-pcbios/ipxe.pxe + bin-x86_64-pcbios/ipxe-legacy.pxe + bin-x86_64-pcbios/undionly.kpxe steps: - name: Check out code @@ -387,9 +422,9 @@ jobs: - name: Download uses: actions/download-artifact@v7 with: - pattern: "{bin-x86_64-pcbios,bin-*-efi,bin-*-efi-sb,shim}" + pattern: "{bin,bin-x86_64-pcbios,bin-*-efi,bin-*-efi-sb,shim}" - - name: Combine + - name: ISO + USB run: | # Provide an editable placeholder autoexec.ipxe for the USB image cat > autoexec.ipxe <<'EOF' @@ -400,11 +435,16 @@ jobs: && shell || autoboot EOF for DRIVERS in ipxe ipxe-legacy ; do - ./src/util/genfsimg -o ${DRIVERS}.iso ${{ env.binaries }} + ./src/util/genfsimg -o ${DRIVERS}.iso ${{ env.fsbinaries }} ./src/util/genfsimg -o ${DRIVERS}.usb -s autoexec.ipxe \ - ${{ env.binaries }} + ${{ env.fsbinaries }} done + - name: Server + run: | + ./src/util/gensrvimg -o ipxeboot.tar.gz ${{ env.efishims }} \ + ${{ env.srvbinaries }} + - name: Upload uses: actions/upload-artifact@v6 with: @@ -415,6 +455,7 @@ jobs: ipxe.usb ipxe-legacy.iso ipxe-legacy.usb + ipxeboot.tar.gz netboot: name: Netboot diff --git a/src/util/genfsimg b/src/util/genfsimg index 7195ea2fa..e25e58bc4 100755 --- a/src/util/genfsimg +++ b/src/util/genfsimg @@ -49,6 +49,8 @@ get_word() { # efi_boot_name() { local FILENAME + local MZSIG + local PEOFF local PESIG local ARCH diff --git a/src/util/gensrvimg b/src/util/gensrvimg new file mode 100755 index 000000000..b98629cc0 --- /dev/null +++ b/src/util/gensrvimg @@ -0,0 +1,325 @@ +#!/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