#!/bin/bash # # netboot - PXE-style boot for KVM on s390 # # Sample script to build a single s390 boot image consisting of # kernel, an initial ramdisk and kernel parameters from # individual components. Note that bash is required to run this script! # # Sample invocation: # # ./mk-s390image /boot/image -r /boot/initrd image # # The resulting image can be used to build a bootable # ISO or as firmware image for KVM. # # Copyright IBM Corp. 2017 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # Offsets OFFS_INITRD_START_BYTES=66568 OFFS_INITRD_SIZE_BYTES=66576 OFFS_COMMANDLINE_MAX_SIZE_BYTES=66608 OFFS_COMMANDLINE_BYTES=66688 LEGACY_MAX_PARMFILE_SIZE=896 # Variables cmd=$(basename -- "$0") kernel= ramdisk= parmfile= image= binval= success=no # Cleanup on exit # shellcheck disable=SC2317 cleanup() { if [ -n "$binval" ]; then rm -f -- "$binval" fi if [ -n "$image" ] && [ "$success" = no ]; then rm -f -- "$image" fi } trap cleanup EXIT # Usage usage() { cat <<- EOF Usage: $cmd KERNEL BOOT_IMAGE [-r RAMDISK] [-p PARMFILE] Build an s390 image BOOT_IMAGE suitable for CD/tape/network boot or as a KVM firmware image using a stripped Linux kernel file KERNEL. OPTIONS -p Use PARMFILE with kernel parameters in the image -r Include RAMDISK in the image -h Print this help, then exit -v Print version information, then exit EOF } printversion() { cat <<- EOD $cmd: version %S390_TOOLS_VERSION% Copyright IBM Corp. 2017 EOD } # Convert decimal number to big endian doubleword dec2be64() { local num=$1 local b local i for i in $(seq 1 8); do b="\\x$(printf '%x' "$((num % 256))")$b" num=$((num / 256)) || true done printf '%b' "$b" } max_kernel_cmdline_size() { local __kernel=$1 size v size_be="$(od -A n -j "${OFFS_COMMANDLINE_MAX_SIZE_BYTES}" -N 8 -t uL -- "$__kernel")" # If little endian then swap bytes if (("$(printf '\1' | od -dAn)" == 1)); then v="$(printf '%016x0' "$size_be")" size="0x${v:14:2}${v:12:2}${v:10:2}${v:8:2}${v:6:2}${v:4:2}${v:2:2}${v:0:2}" else size="$size_be" fi if ((size == 0)); then size="${LEGACY_MAX_PARMFILE_SIZE}" fi # Use arithmetic expression to remove leading and trailing whitespace. printf '%s' "$((size))" } # Do the image build dobuild() { local i local kernel_size local ramdisk_size local ramdisk_offset local parmfile_size # check whether all specified files exist for i in $kernel $ramdisk $parmfile; do if [ ! -f "$i" ]; then echo "$cmd: File $i not found" >&2 return 1 fi if [ ! -r "$i" ]; then echo "$cmd: File $i cannot be read, no read permission" >&2 return 1 fi done if ! file -b -- "$(readlink -f -- "$kernel")" | grep "Linux S390" > /dev/null; then echo "$cmd: Unrecognized file format for $kernel" >&2 return 1 fi # from now on we SHOULD only fail on disk shortage # or file permissions, let the shell handle that set -e # copy over kernel padded with zeroes to page boundary dd if="$kernel" of="$image" bs=4096 conv=sync status=none # append ramdisk if specified if [ "$ramdisk" != "" ]; then ramdisk_size=$(du -b -L -- "$ramdisk" | cut -f1) # shellcheck disable=SC2034 kernel_size=$(du -b -L -- "$kernel" | cut -f1) ramdisk_offset=$(du -b -L -- "$image" | cut -f1) cat -- "$ramdisk" >> "$image" binval=$(mktemp) dec2be64 "$ramdisk_offset" > "$binval" dd seek=$OFFS_INITRD_START_BYTES if="$binval" of="$image" bs=1 \ count=8 conv=notrunc status=none dec2be64 "$ramdisk_size" > "$binval" dd seek=$OFFS_INITRD_SIZE_BYTES if="$binval" of="$image" bs=1 \ count=8 conv=notrunc status=none fi # set cmdline if [ "$parmfile" != "" ]; then max_parmfile_size=$(max_kernel_cmdline_size "$kernel") parmfile_size=$(du -b -L -- "$parmfile" | cut -f1) if ((parmfile_size <= max_parmfile_size)); then # Clear any previous parameters dd seek=$OFFS_COMMANDLINE_BYTES bs=1 count="$max_parmfile_size" \ if=/dev/zero of="$image" conv=notrunc status=none dd seek=$OFFS_COMMANDLINE_BYTES bs=1 count="$parmfile_size" \ if="$parmfile" of="$image" conv=notrunc status=none else echo "$cmd: Size $parmfile_size of $parmfile exceeds command line limit of $max_parmfile_size bytes" >&2 return 1 fi fi # we've done it success=yes } # check args and build # shellcheck disable=SC2086,SC2048 if args=$(getopt "r:p:hv" $*); then # shellcheck disable=SC2086 set -- $args while [ "$1" != "" ]; do case $1 in -r) ramdisk=$2 shift 2 ;; -p) parmfile=$2 shift 2 ;; -h) usage exit 0 ;; -v) printversion exit 0 ;; --) shift break ;; *) echo "$cmd: Unexpected argument $1, exiting..." >&2 exit 1 ;; esac done fi if [ $# = 2 ]; then kernel=$1 image=$2 dobuild exit 0 fi # something wasn't right usage >&2 exit 1