mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
The IPAPPEND option in the pxelinux config file can be used to pass information about the boot interface to the booted Linux kernel. The support for this option was missing and could lead to installation failures, e.g. using kickstart. Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
417 lines
9.2 KiB
Bash
Executable File
417 lines
9.2 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# netboot - PXE-style boot for KVM on s390
|
|
#
|
|
# Sample script to build an initramfs image suitable for performing a
|
|
# PXELINUX-style boot for KVM guests from a DHCP/BOOTP Server.
|
|
# Has to be executed on the KVM host where it will be deployed and needs
|
|
# to be re-run after kernel updates on the host, unless the virtio
|
|
# drivers are statically built into the host kernel.
|
|
#
|
|
# The script requires a busybox install tree, e.g. resulting from a build
|
|
# from source, after make defconfig && make install
|
|
#
|
|
# To keep things simple, we don't include udev but use devtmpfs
|
|
# which means the host kernel must have been built with CONFIG_DEVTMPFS=y
|
|
#
|
|
# Sample invocation:
|
|
#
|
|
# ./mk-pxelinux-ramfs -b /downloads/busyboxdir pxelinux.initramfs
|
|
#
|
|
# 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.
|
|
|
|
# Variables
|
|
cmd=$(basename $0)
|
|
busyboxdir=
|
|
builddir=
|
|
initramfs=
|
|
success=no
|
|
|
|
# Cleanup on exit
|
|
cleanup()
|
|
{
|
|
if [ -n $builddir ]
|
|
then
|
|
rm -rf $builddir
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# Usage
|
|
usage()
|
|
{
|
|
cat <<-EOF
|
|
Usage: $cmd -b BUSYBOX_DIR [-k KERNEL_VERSION] INITRAMFS_FILE
|
|
|
|
Build a PXELINUX style boot initramfs INITRAMFS_FILE using a busybox installed
|
|
in BUSYBOX_DIR and kernel modules from the currently running kernel or from
|
|
the kernel version specified with the '-k KERNEL_VERSION' option.
|
|
|
|
OPTIONS
|
|
-b Search installed busybox in directory BUSYBOX_DIR
|
|
-k Use KERNEL_VERSION instead of currently running kernel
|
|
-h Print this help, then exit
|
|
EOF
|
|
}
|
|
|
|
# Get shared objects for binary
|
|
sharedobjs()
|
|
{
|
|
ldd $1 | sed -e 's?[^/]*??' -e 's/(.*)//'
|
|
}
|
|
|
|
# Check args
|
|
args=$(getopt b:k:h $*)
|
|
if [ $? = 0 ]
|
|
then
|
|
set -- $args
|
|
while [ -n $1 ]
|
|
do
|
|
case $1 in
|
|
-b) busyboxdir=$2; shift 2;;
|
|
-k) kernelversion=$2; shift 2;;
|
|
-h) usage; exit 0;;
|
|
--) shift; break;;
|
|
*) echo "$cmd: Unexpected argument $1, exiting..." >&2; exit 1;;
|
|
esac
|
|
done
|
|
fi
|
|
|
|
if [ $# != 1 -o "$busyboxdir" = "" ]
|
|
then
|
|
usage >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Full output file path
|
|
initramfs=$(readlink -m $(dirname $1))/$(basename $1)
|
|
|
|
# Exit on error
|
|
set -e
|
|
|
|
# Module locations
|
|
if [ -n $kernelversion ]; then
|
|
moddir=/lib/modules/$kernelversion
|
|
else
|
|
moddir=/lib/modules/$(uname -r)
|
|
fi
|
|
netdir=$moddir/kernel/drivers/net
|
|
blkdir=$moddir/kernel/drivers/block
|
|
|
|
# Setup build directory
|
|
builddir=$(mktemp -d)
|
|
echo "$cmd: Building in $builddir"
|
|
|
|
ramfsdirs="/bin /dev /etc /lib64 /lib /mnt /proc /run /sbin /sys /tmp /usr /var"
|
|
for d in $ramfsdirs
|
|
do
|
|
mkdir -p $builddir/$d
|
|
done
|
|
|
|
# Kexec
|
|
echo "$cmd: Copying kexec"
|
|
|
|
# Install both binary and required shared libraries
|
|
OLDPATH=$PATH
|
|
PATH=$OLDPATH:/sbin:/usr/sbin
|
|
kexec_bin=$(which kexec)
|
|
kexec_sos=$(sharedobjs $kexec_bin)
|
|
PATH=$OLDPATH
|
|
|
|
cp $kexec_bin $builddir/sbin
|
|
for so in $kexec_sos
|
|
do
|
|
mkdir -p $builddir/$(dirname $so)
|
|
cp $so $builddir/$(dirname $so)
|
|
done
|
|
|
|
# virtio module(s), if present
|
|
echo "$cmd: Copying virtio modules"
|
|
|
|
mkdir -p $builddir/$netdir
|
|
mkdir -p $builddir/$blkdir
|
|
set +e
|
|
cp $netdir/virtio_net.ko* $builddir/$netdir 2> /dev/null
|
|
cp $blkdir/virtio_blk.ko* $builddir/$blkdir 2> /dev/null
|
|
set -e
|
|
|
|
# Busybox (+ dependencies)
|
|
echo "$cmd: Copying busybox files"
|
|
|
|
cp -a $busyboxdir/* $builddir
|
|
busybox_sos=$(sharedobjs $busyboxdir/bin/busybox)
|
|
for so in $busybox_sos
|
|
do
|
|
mkdir -p $builddir/$(dirname $so)
|
|
cp $so $builddir/$(dirname $so)
|
|
done
|
|
|
|
# ad_packet module(s), if present
|
|
echo "$cmd: Copying af_packet modules"
|
|
|
|
packetdir=$moddir/kernel/net/packet
|
|
mkdir -p $builddir/$packetdir
|
|
set +e
|
|
cp $packetdir/* $builddir/$packetdir 2> /dev/null
|
|
set -e
|
|
|
|
# Init script
|
|
echo "$cmd: Making init script"
|
|
|
|
# --- begin init script
|
|
cat <<'EOF' > $builddir/init
|
|
#!/bin/sh
|
|
/bin/mount -t devtmpfs none /dev
|
|
/bin/mount -t proc none /proc
|
|
/bin/mount -t sysfs none /sys
|
|
/bin/mount -t tmpfs none /run
|
|
|
|
/sbin/modprobe virtio_net
|
|
/sbin/udhcpc -O pxeconffile -O pxepathprefix -x 93:001F &
|
|
|
|
/sbin/pxeboot.script
|
|
EOF
|
|
# --- end init script
|
|
chmod +x $builddir/init
|
|
|
|
# udhcpc script
|
|
echo "$cmd: Making DHCP script"
|
|
|
|
mkdir -p $builddir/usr/share/udhcpc
|
|
# -- begin dhcp script
|
|
cat <<'EOF' > $builddir/usr/share/udhcpc/default.script
|
|
#!/bin/sh
|
|
# Setup name resolution and PXE boot configuration
|
|
# called by udhcpc
|
|
|
|
RESOLVCONF="/etc/resolv.conf"
|
|
PXECONF="/etc/pxe.conf"
|
|
|
|
ccidr()
|
|
{
|
|
# clumsy netmask to cidr transformation
|
|
# with minimal sanity checking
|
|
OLDIFS=$IFS
|
|
IFS=.
|
|
c=0
|
|
n=4
|
|
for i in $1
|
|
do
|
|
n=$(/usr/bin/expr $n - 1)
|
|
case $i in
|
|
255) c=$(/usr/bin/expr $c + 8);;
|
|
254) c=$(/usr/bin/expr $c + 7); break;;
|
|
252) c=$(/usr/bin/expr $c + 6); break;;
|
|
248) c=$(/usr/bin/expr $c + 5); break;;
|
|
240) c=$(/usr/bin/expr $c + 4); break;;
|
|
224) c=$(/usr/bin/expr $c + 3); break;;
|
|
192) c=$(/usr/bin/expr $c + 2); break;;
|
|
128) c=$(/usr/bin/expr $c + 1); break;;
|
|
0) break;;
|
|
*) c=0; break;;
|
|
esac
|
|
if [ $n = 0 ]
|
|
then
|
|
break
|
|
fi
|
|
done
|
|
IFS=$OLDIFS
|
|
echo $c
|
|
}
|
|
|
|
echo "DHCP response $1: "
|
|
case "$1" in
|
|
deconfig)
|
|
echo " interface: $interface"
|
|
/sbin/ip route flush table all
|
|
/sbin/ip addr flush $interface
|
|
/sbin/ip link set $interface up
|
|
/bin/rm -f $PXECONF $RESOLVCONF
|
|
;;
|
|
|
|
renew|bound)
|
|
echo " interface: $interface $ip $subnet"
|
|
echo " router: $router"
|
|
echo " domain: $domain $dns"
|
|
echo " tftp: $siaddr"
|
|
echo " pxepathprefix: $pxepathprefix"
|
|
# flush routes
|
|
/sbin/ip route flush table all
|
|
# setup if link
|
|
/sbin/ip addr flush $interface
|
|
/sbin/ip link set $interface up
|
|
# setup if addr
|
|
if [ -n "$subnet" ]
|
|
then
|
|
maskedip="$ip"/$(ccidr $subnet)
|
|
else
|
|
maskedip="ip"
|
|
fi
|
|
/sbin/ip addr add $maskedip broadcast $broadcast dev $interface
|
|
# setup default routes
|
|
if [ -n "$router" ]
|
|
then
|
|
/sbin/ip route add default via $router
|
|
fi
|
|
|
|
# setup resolv.conf
|
|
if [ -n "$domain" ]
|
|
then
|
|
echo "search $domain" > $RESOLVCONF
|
|
for i in $dns
|
|
do
|
|
echo " nameserver $i" >> $RESOLVCONF
|
|
done
|
|
fi
|
|
|
|
# pxe control
|
|
if [ -n "$siaddr" ]
|
|
then
|
|
echo "siaddr=$siaddr" > $PXECONF
|
|
echo "interface=$interface" >> $PXECONF
|
|
echo "ip=$ip" >> $PXECONF
|
|
echo "pxepathprefix=$pxepathprefix" >> $PXECONF
|
|
fi
|
|
;;
|
|
|
|
nak)
|
|
;;
|
|
*)
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
exit 0
|
|
|
|
EOF
|
|
# -- end dhcp script
|
|
chmod +x $builddir/usr/share/udhcpc/default.script
|
|
|
|
# pxeboot script
|
|
echo "$cmd: Making PXE boot script"
|
|
|
|
# -- begin pxeboot script
|
|
cat <<'EOF' > $builddir/sbin/pxeboot.script
|
|
#!/bin/sh
|
|
# Perform a PXE style boot using kexec
|
|
# Supports only super-simple config files
|
|
|
|
set -e
|
|
|
|
# Check if a valid pxe conf is available
|
|
# so far a non configurable 600 sec timeout
|
|
PXE_CONF=/etc/pxe.conf
|
|
TIMEOUT=600
|
|
WAITED=0
|
|
echo "waiting for pxe config from DHCP (max $TIMEOUT sec)"
|
|
while [ ! -f $PXE_CONF ]
|
|
do
|
|
sleep 1
|
|
WAITED=$(($WAITED + 1))
|
|
if [ $WAITED -gt $TIMEOUT ]
|
|
then
|
|
echo Error waiting for PXE configuration from DHCP
|
|
exit
|
|
fi
|
|
done
|
|
|
|
# Source the DHCP-generated TFTP info
|
|
# Currently we are just looking for siaddr
|
|
. $PXE_CONF
|
|
|
|
# Retrieve the config (default only for now)
|
|
CONFIGS=""
|
|
if [ -n "$siaddr" ];
|
|
then
|
|
# Enable UUID based config on s390
|
|
if [ $(/bin/uname -m) = "s390x" ]
|
|
then
|
|
set +e
|
|
uuid=$(/bin/grep UUID /proc/sysinfo | tail -1 | tr -d ' ' | cut -d ':' -f 2) 2> /dev/null
|
|
set -e
|
|
else
|
|
# not caring for other arches right now
|
|
uuid=""
|
|
fi
|
|
CONFIGS="$CONFIGS $uuid"
|
|
|
|
# Enable MAC based config
|
|
ifaddr=$(/bin/cat /sys/class/net/$interface/address | tr ':' '-')
|
|
CONFIGS="$CONFIGS 01-$ifaddr"
|
|
|
|
# Enable IP based config
|
|
iphex=$(printf %02X $(echo $ip | tr '.' ' '))
|
|
for i in 8 7 6 5 4 3 2 1
|
|
do
|
|
CONFIGS="$CONFIGS $(echo $iphex | cut -c 1-$i)"
|
|
done
|
|
|
|
# Finally enable default config
|
|
CONFIGS="$CONFIGS default"
|
|
|
|
set +e
|
|
for c in $CONFIGS
|
|
do
|
|
echo "fetching config pxelinux.cfg/$c from $siaddr"
|
|
if /usr/bin/tftp -g -l /tmp/config -r ${pxepathprefix}pxelinux.cfg/$c $siaddr
|
|
then
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ ! -f /tmp/config ]
|
|
then
|
|
echo no config found
|
|
exit
|
|
fi
|
|
|
|
# Simple config file parsing, only one entry allowed
|
|
kernel=$(/bin/grep -i "^[[:space:]]*kernel" /tmp/config | sed "s/^[[:space:]]*kernel[[:space:]]*//I")
|
|
initrd=$(/bin/grep -i "^[[:space:]]*initrd" /tmp/config | sed "s/^[[:space:]]*initrd[[:space:]]*//I")
|
|
append=$(/bin/grep -i "^[[:space:]]*append" /tmp/config | sed "s/^[[:space:]]*append[[:space:]]*//I")
|
|
ipappend=$(/bin/grep -i "^[[:space:]]*ipappend" /tmp/config | sed "s/^[[:space:]]*ipappend[[:space:]]*//I")
|
|
|
|
if [ -z "$kernel" ]
|
|
then
|
|
echo no kernel statement found in config
|
|
exit
|
|
else
|
|
echo fetch kernel $kernel from $siaddr
|
|
/usr/bin/tftp -g -l /tmp/kernel -r $pxepathprefix$kernel $siaddr
|
|
fi
|
|
|
|
if [ -n "$initrd" ]
|
|
then
|
|
echo fetch initrd $initrd from $siaddr
|
|
/usr/bin/tftp -g -l /tmp/initrd -r $pxepathprefix$initrd $siaddr
|
|
INITRD="--initrd=/tmp/initrd"
|
|
else
|
|
INITRD=""
|
|
fi
|
|
|
|
if [ -z "$append" ]; then
|
|
echo "Kexec load: kexec -l /tmp/kernel $INITRD"
|
|
kexec -l /tmp/kernel $INITRD
|
|
else
|
|
if [ "$ipappend" = "2" ]; then
|
|
$append="$append BOOTIF=01-$ifaddr"
|
|
fi
|
|
echo "Kexec load: kexec -l /tmp/kernel $INITRD --append=\"$append\""
|
|
kexec -l /tmp/kernel $INITRD --append="$append"
|
|
fi
|
|
kexec -e
|
|
EOF
|
|
# -- end pxeboot script
|
|
chmod +x $builddir/sbin/pxeboot.script
|
|
|
|
# The final initramfs
|
|
echo Building initramfs
|
|
|
|
cd $builddir
|
|
find . | cpio -o -Hnewc | gzip - > $initramfs
|
|
cd $OLDPWD
|