| #!/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 |
| # for more details. |
| # |
| # Copyright (C) 2017 by Changbin Du <changbin.du@intel.com> |
| # |
| # Adapted from code in arch/x86/boot/Makefile by H. Peter Anvin and others |
| # |
| # "make fdimage/fdimage144/fdimage288/hdimage/isoimage" |
| # script for x86 architecture |
| # |
| # Arguments: |
| # $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 |
| case "${KBUILD_VERBOSE}" in |
| *1*) |
| set -x |
| ;; |
| esac |
| |
| # 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 |
| |
| # common options for dd |
| dd='dd iflag=fullblock' |
| |
| # Make sure the files actually exist |
| verify "$FBZIMAGE" |
| |
| 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 |
| 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 |
| } |
| |
| # 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 |
| } |
| |
| # 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 |
| } |
| |
| # 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 |
| 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 |
| 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 |
| 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 [ -n "$kefiarch" ]; then |
| echo linux "$KCMDLINE$initrdopts_efi" | \ |
| mcopy - "$1"startup.nsh |
| fi |
| 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 |
| 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" |
| } |
| |
| rm -f "$FIMAGE" |
| |
| case "$diskfmt" in |
| bzdisk) genbzdisk;; |
| fdimage144) genfdimage144;; |
| fdimage288) genfdimage288;; |
| hdimage) genhdimage;; |
| isoimage) geniso;; |
| *) die "Unknown image format: $diskfmt";; |
| esac |