Installing s390x Ubuntu on hercules as a Linux Guest

Note: The current version of this README is at http://www.fargos.net/packages/README_UbuntuOnHercules.html.

A tar file of the most current version of the scripts is available for download as http://www.fargos.net/packages/ubuntuOnHercules.tar.gz. The contents of the scripts have been reproduced below for educational purposes and these reproductions are not guaranteed to be the most current versions. The tar archive referenced above holds the current versions.

Prerequisites

You will need the following:

Configuration Steps

  1. Create a directory into which you want to place the scripts and untar the contents of http://www.fargos.net/packages/ubuntuOnHercules.tar.gz.

    mkdir ubuntu-hercules
    cd ubuntu-hercules
    tar xvf ..../ubuntuOnHercules.tar.gz
    
  2. Create a virtual DASD image to which the operating system will be installed. This can be done with makeNewUbuntuDisk, which is a convenience cover for the dasdinit command. By default, the makeNewUbuntuDisk script generates a 12000 cylinder (8 gigabyte) disk named as dasd/ubuntu-v18.disk. The Ubuntu version and the total number of cylinders can be set on the command line:

    usage: ./makeNewUbuntuDisk [-c cylinders] [-v ubuntuVersion]
                    
  3. The hercules-ubuntu.cfg file currently assumes a single disk in the dasd subdirectory named ubuntu-v18.disk. If a different name was desired, it must be renamed in that configuration file.

    # DASD
    0120 3390 ./dasd/ubuntu-v18.disk
                    
  4. The ‑‑iso option of the boot_ubuntu.sh script will automatically mount an installation ISO image and boot it, filling out most of the configuration parameters that have to be entered via the system console. You will still need follow the instructions and login via ssh with the installation password to complete the installation process.

    First, invoke ./boot_ubuntu.sh ‑‑help to see what defaults are programmatically obtained on your system. You only need specify arguments for entries you wish to change to alternate values.

    usage: boot_ubuntu.sh [‑‑screen] [{‑‑iso isoFileName [‑‑dns DNSserver] [‑‑gw gatewayAddr] [‑‑host hostName] [‑‑domain domainName] [‑‑pw installPassword]}]
      --iso image = boot from CD image
      Image will be associated with a read-only loopback device and mounted,
      then IPLed using ubuntu.ins in the root directory
      Default gateway and DNS server: A.B.C.D
      Default hostname: ubuntu-z
      Default domain: your.domain.name
      Default install password: initialpw
                    

    Then invoke ./boot_ubuntu.sh ‑‑iso with any of the extra arguments needed to override the inferred default settings.

  5. Wait patiently for a nontrivial amount of time... If all goes well, you will eventually be prompted to login using secure shell from a different window.

    ssh installer@10.1.1.2
    

    Login with the installation password ("initialpw" by default). Work your way through all of the installation questions and, after many hours, the installation should complete.

  6. On subsequent startups of the guest, use sudo ./bootUnderScreen to run it under screen. This allows the invoking terminal to be detached and for different terminal sessions to attach and detach from the system console during the lifetime of the hercules process.

  7. If you want access to the console, sudo ./connectToScreen will connect to the screen-affiliated terminal session.

Configuration Files and Scripts

As noted in the introduction, these configuration files and scripts are presented for convenient reading and are not guaranteed to be the most current versions.

A tar file of the most current version of the files is available for download as http://www.fargos.net/packages/ubuntuOnHercules.tar.gz.

Sample Hercules Configuration File

The provided hercules-ubuntu.cfg configuration file defines a system with a maximum of 8 CPUs and about 6 gigabytes of memory. These can be changed to values more appropriate for the local environment.


# hercules-ubuntu.cfg - configuration file for Ubuntu with hercules
ARCHLVL  z/Arch
ARCHLVL  ENABLE BIT54 # Interlocked-access facility 2
ARCHLVL  ENABLE BIT44 # PFPO Facility
ARCHLVL  ENABLE BIT37 # unknown
ARCHLVL  ENABLE BIT45 # FAST BCR SERIAL (on by default)
ARCHLVL  ENABLE BIT49 # MISC INST EXTN 1 (on by default)
ARCHLVL  ENABLE asn_lx_reuse

# You can choose your own serial number
CPUSERIAL 001482
CPUMODEL  2964 # z13
#ALRF     ENABLE
DIAG8CMD ENABLE
ECPSVM YES

# chosen hostname
PANTITLE  "ubuntu-z"
# This 6 gigabyte size is usable
MAINSIZE  6000
# 8 CPUs, use them all
MAXCPU    8
NUMCPU    8

OSTAILOR  LINUX
PANRATE   SLOW


# Display Terminals

0700 3270
0701 3270


# DASD definitions
# Disk prepared by makeNewUbuntuDisk
0120 3390 ./dasd/ubuntu-v18.disk

# tape
0581    3420

# Tunnel will be configured on host by setupCTCnetwork script.
# network                               s390x    onHost
0A00,0A01  CTCI -n /dev/net/tun -t 1500 10.1.1.2 10.1.1.1

makeNewUbuntuDisk - Create DASD disk image

The makeNewUbuntuDisk script is a convenience cover script that invokes dasdinit to create a DASD image for use with hercules. It defaults to creating an 8 gigabyte drive image.


#!/bin/sh
# makeNewUbuntuDisk - create DASD image for hercules

version=18
cylinders=12000

while test $# -gt 0
do
        case "${1}" in
        -h | -help | --help)
                printf "usage: %s [-c cylinders] [-v ubuntuVersion]\n" "${0}" >&2
                exit 1
                ;;
        -c)
                cylinders=${2}
                shift
                ;;
        -v)
                version=${2}
                shift
                ;;
        -*)
                printf "%s: unrecognized option: \"%s\"\n" "${0}" "${1}" >&2
                exit 1
                ;;
        -*)
                printf "%s: unrecognized argument: \"%s\"\n" "${0}" "${1}" >&2
                exit 1
                ;;
        esac
        shift
done

diskName="dasd/ubuntu-v${version}.disk"
if test -e ${diskName}
then
        printf "%s: disk file %s already exists\n" "${0}" "${diskName}"
        exit 1
fi

echo dasdinit -lfs -linux ${diskName} 3390-3 UBU120 ${cylinders}
dasdinit -lfs -linux ${diskName} 3390-3 UBU120 ${cylinders}

setupCTCnetwork - Script to Configure Channel-To-Channel Tunnel to Hercules Guest

The setupCTCnetwork script configures the Linux kernel using iptables or nftablesto setup Network Address Translation for the hercules guest. Fedora 32 moved to nftables, so this script has been updated to use nft if it is available and fallback to iptables.

Note: This script requires root privileges. The boot_ubuntu.sh script will use the sudo command to obtain the required elevated privileges if it was not invoked by the root user.


#!/bin/bash
# setupCTCnetwork - setup network address translation for hercules

networkToUse="10.1.1.0/24"
userId=`id -u`
if test "${userId}" -ne 0
then
        printf "%s: must be root to set iptables\n" "${0}" >&2
        exit 1
fi

defaultIf=`netstat -rn | awk '/^0.0.0.0/ { print $NF; exit }'`
if test -z "${defaultIf}"
then
        printf "%S: could not determine default interface\n" "${0}" >&2
        exit 1
fi

iptables -F INPUT
iptables -F OUTPUT
iptables -F FORWARD
iptables -t nat -A POSTROUTING -o ${defaultIf} -s ${networkToUse} -j MASQUERADE
iptables -A FORWARD -s ${networkToUse} -j ACCEPT
iptables -A FORWARD -d ${networkToUse} -j ACCEPT
echo 1  > /proc/sys/net/ipv4/ip_forward
echo 1  > /proc/sys/net/ipv4/conf/all/proxy_arp
exit 0

setupSSHforwarding - Setup SSH port forwarding to hercules guest

The setupSSHforwarding script configures the Linux kernel using iptables or nftables to setup Network Address Translation to forward an alias for the SSH port to the hercules guest. Fedora 32 moved to nftables, so this script has been updated to use nft if it is available and fallback to iptables.

By default, port 3270 is used for the alias port. Assuming the hercules process was running on a host named host and the initial account for Ubuntu was named loginName,

ssh -p 3270 -l loginName host

will connect to the hercules-hosted operating system.

Note: This script requires root privileges. The boot_ubuntu.sh script will use the sudo command to obtain the required elevated privileges if it was not invoked by the root user.


#!/bin/sh
# setupSSHforwarding - forward to SSH port on local guest virtual machine image
# The aliasPort on the hosting machine will be forwarded to the guestPort
# listening at the end of the tunnel to guestAddr.
aliasPort=3270
guestPort=22
guestAddr="10.1.1.2"

userId=`id -u`
if test ${userId} -ne 0
then
        printf "%s: must be root to set iptables\n" "${0}" >&2
        exit 1
fi


defaultInterface=`netstat -rn | awk '/^0.0.0.0 / { print $NF; exit; }'`

while test $# -gt 0
do
        case "${1}" in
        -h | -help | --help)
                printf "usage: %s [--listen lPort] [--fwdport dPort] [--toaddr dAddr] [--if interface]\n" "${0}" >&2
                printf "  --listen = listen on port (default %d)\n" ${aliasPort} >&2
                printf "  --fwdport = forward to guest machine port (default %d)\n" ${guestPort} >&2
                printf "  --toaddr = forward to guest machine address (default %s)\n" "${guestAddr}" >&2
                printf "  --if = listen on interface (default %s)\n" "${defaultInterface}" >&2
                exit 1
                ;;
        --listen)
                aliasPort="${2}"
                shift
                ;;
        --fwdport)
                guestPort="${2}"
                shift
                ;;
        --toaddr)
                guestAddr="${2}"
                shift
                ;;
        --if)
                defaultInterface="${2}"
                shift
                ;;
        *)
                printf "%s: unrecognized argument \"%s\"\n" "${0}" "${1}" >&2
                exit 1
                ;;
        esac
        shift
done

set -x
# incoming connections to ${aliasPort} get redirected to guest machine address
iptables -I PREROUTING -p tcp -t nat -i "${defaultInterface}" --dport ${aliasPort} -j DNAT --to ${guestAddr}:${guestPort}

# Allow connection to guest port to be permitted
iptables -A FORWARD -p tcp -d ${guestAddr} --dport ${guestPort} -j ACCEPT

# required to allow established connection to flow
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
exit 0

boot_ubuntu.sh - Boot Ubuntu image under Hercules

The boot_ubuntu.sh script is normally used to invoke hercules with the hercules-ubuntu.cfg configuration file. It does have a second mode, enabled with the ‑‑iso option, that can mount a CD-ROM image of a Ubuntu install disk and perform the Initial Program Load from that virtual CD device so that the initial installation can be performed.

It makes use of setupCTCnetwork and setupSSHforwarding to configure Network Address Translation and the forwarding of an aliased port to the SSH port on the hercules guest.


#!/bin/sh
# boot_ubuntu.sh - boot Ubuntu s390x image using hercules

originDir=`dirname $0`
sudoCmd=""
mountISO=""
useScreen=0
rcBootFile="${originDir}/hercules-ipl.rc"

defaultGateway=`netstat -rn | awk '/^0.0.0.0/ { print $2; exit; }'`
defaultHostname="ubuntu-z"
DNSserver="${defaultGateway}"
initialPW="initialpw"
defaultDomain=`dnsdomainname`

while test $# -gt 0
do
        case "${1}" in
        -h | -help | --help)
                printf "usage: %s [--screen] [{--iso isoFileName [--dns DNSserver] [--gw gatewayAddr] [--host hostName] [--domain domainName] [--pw installPassword]}]\n" "${0}" >&2
                printf "  --iso image = boot from CD image\n" >&2
                printf "  Image will be associated with a read-only loopback device and mounted,\n" >&2
                printf "  then IPLed using ubuntu.ins in the root directory\n" >&2
                printf "  Default gateway and DNS server: %s\n" "${defaultGateway}" >&2
                printf "  Default hostname: %s\n" "${defaultHostname}" >&2
                printf "  Default domain: %s\n" "${defaultDomain}" >&2
                printf "  Default install password: %s\n" "${initialPW}" >&2
                exit 1
                ;;
        --screen)
                useScreen=1
                ;;
        --iso)
                mountISO="${2}"
                shift
                ;;
        --dns)
                DNSserver="${2}"
                shift
                ;;
        --domain)
                defaultDomain="${2}"
                shift
                ;;
        --gw)
                defaultGateway="${2}"
                shift
                ;;
        --host)
                defaultHostname="${2}"
                shift
                ;;
        --pw)
                initialPW="${2}"
                shift
                ;;
        -*)
                printf "%s: unrecognized option: \"%s\"\n" "${0}" "${1}" >&2
                exit 1
                ;;
        *)
                printf "%s: unrecognized argument: \"%s\"\n" "${0}" "${1}" >&2
                exit 1
                ;;
        esac
        shift
done

userId=`id -u`

if test ${userId} -ne 0
then # not root
        printf "%s: using sudo to gain root permission to configure network\n" "${0}"
        sudoCmd="sudo"
fi

# clear IP tables, setup NAT
${sudoCmd} ${originDir}/setupCTCnetwork
rc=$?
if test ${rc} -ne 0
then
        printf "%s: could not configure networking\n" "${0}" >&2
        exit 1
fi

${sudoCmd} ${originDir}/setupSSHforwarding

# start hercules

if test -n "${mountISO}"
then # boot from mounted ISO
        set -x
        mountPoint="/tmp/s390x.iso"
        useLoopDevice=`${sudoCmd} losetup -r --find --show ${mountISO}`
        ${sudoCmd} mkdir -p ${mountPoint}
        ${sudoCmd} mount -r ${useLoopDevice} ${mountPoint}
        iplInstructions=`ls ${mountPoint}/ubuntu.ins`
        if test -z "${iplInstructions}"
        then
                printf "%s: could not find IPL instructions, expected ubuntu.ins under %s\n" "${0}" "${mountPoint}" >&2
                exit 1
        fi
        rcBootFile="/tmp/iplFromISO.rc"
# ipl instructions
# 1 = use CTC
# 1 = set read device
# 2 = set write device
# 2 = set protocol to Linux
# 2 = set network configuration manually
# 10.1.1.2 = local IP address
# 255.255.255.0 = network mask
# ${defaultGateway} = gateway
# ${defaultDNS} = nameserver
# ${defaultHostname} = hostname
# ${defaultDomain} = domain name
# initialpw = install password
# initialpw = install password
        cat > ${rcBootFile} <<endIPL
ipl ${iplInstructions}
.1
.1
.2
.2
.2
.10.1.1.2
.255.255.255.0
.${defaultGateway}
.${DNSserver}
.${defaultHostname}
.${defaultDomain}
.${initialPW}
.${initialPW}
endIPL
        trap "rm -f ${rcBootFile}" EXIT
fi


if test ${useScreen} -eq 1
then
        screen -D -m -S 'hercules zSeries Ubuntu' -t 'zeries Ubuntu' \
        hercules -f ${originDir}/hercules-ubuntu.cfg --rc ${rcBootFile}
        sleep 2
else
        hercules -f ${originDir}/hercules-ubuntu.cfg --rc ${rcBootFile}
        if test -n "${mountISO}"
        then
                echo umount ${mountPoint}
                umount ${mountPoint}
                echo losetup -d ${useLoopDevice}
                losetup -d ${useLoopDevice}
        fi
fi

bootUnderScreen - Convenience cover to use screen with boot_ubuntu.sh

The bootUnderScreen script uses the screen command to launch an instance of boot_ubuntu.sh. The associated script connectToHerculesScreen is used to connect to the screen when desired.


#/bin/sh
# bootUnderScreen - run boot_ubuntu.sh under screen
originDir=`dirname $0`

PATH=${PATH}:${originDir}
export PATH

screen -D -m -S 'hercules zSeries Ubuntu' -t 'zeries Ubuntu' boot_ubuntu.sh "$@"

connectToHerculesScreen - Convenience cover to attach to screen created by bootUnderScreen

The connectToHerculesScreen is a convenience cover script that uses the screen command to locate and attach to the virtual screen created by a previous invocation of bootUnderScreen.


#!/bin/sh
# connectToHerculesScreen - connect to screen created by bootUnderScreen

sudoCmd=""
userId=`id -u`

if test ${userId} -ne 0
then # not root
        printf "%s: using sudo to gain root permission to access screen\n" "${0}"
        sudoCmd="sudo"
fi

screenSession=`${sudoCmd} screen -list | fgrep zSeries | awk '{ print $1; }'`
sessionTitle=`${sudoCmd} screen -list | fgrep zSeries | awk '{ s = $2; for(i=3;i<NF;i+=1) { s = s " " $i; } print s; }'`
echo screen ${screenSession} title ${sessionTitle}
set -x
${sudoCmd} screen -r "${screenSession}" -s "${sessionTitle}"

Post-Install Network Setup

If you are lucky, the Ubuntu installer will correctly configure the network for you. If not, you will have to effect repairs manually. The current release of Ubuntu moved to using netplan and is biased towards using systemd-networkd for servers and NetworkManager for desktops.

It is tedious to use the system console terminal, so it is simplest to effect a temporary repair to enable networking and then login using a regular terminal session. Login on the console using the login name you created for yourself, remembering to prefix each line with a period ("."), and use ifconfig to setup the point-to-point link. Your session will look something like the following:

.loginName
.password
.sudo ifconfig dstaddr 10.1.1.1 slca00
.password

Once the point-to-point link is up, you can login via an SSH session. From the host running the hercules guest, you can use 10.1.1.2 as the destination address. From elsewhere, you can use the name of the host running hercules and port 3270. Thus:

ssh -l loginName 10.1.1.2
or
ssh -l loginName -p 3270 hostName

The /etc/netplan/01-netcfg.yaml file should appear similar to:

# This file describes the network interfaces available on your system
# For more information, see netplan(5).
network:
  version: 2
  renderer: networkd
  ethernets:
    slca00:
      dhcp4: no
      dhcp6: no
      addresses: [ 10.1.1.2/24 ]
      routes:
      - to: 0.0.0.0/0
        via: 10.1.1.1
        on-link: true
      gateway4: 10.1.1.1
      nameservers:
          search: [ your.domain.name ]
          addresses:
              - d.n.s.ip

After updating the /etc/netplan/01-netcfg.yaml file, generate and apply the configuration:

netplan --debug apply

Ensure the systemd-networkd service is enabled:

systemctl enable --now systemd-networkd

Reboot to test that the configuration is applied correctly.

Systemd hercules.service

In some environments, the Linux-for-zSeries image will only be booted as needed by manually invoking bootUnderScreen. In others, it will be desireable to have the image automatically booted whenever the host system completes its startup. For those scenarios, a hercules.service systemd service description is provided that can be installed into a directory such as /etc/systemd/system. This service description is dependent upon a /etc/sysconfig/hercules.env configuration file that must be created locally. The file is intended to define the HERCULES-LINUX-DIR environment variable, which is expected to point at the directory into which this package was unpacked. It will appear similar to:

HERCULES-LINUX-DIR=/root/hercules-linux
To enable the service:
# systemctl enable hercules.service
and start it:
# systemctl start hercules.service
Verify startup:
# systemctl status hercules.service
Shutdown:
# systemctl stop hercules.service

Written by
Geoff Carpenter
http://www.fargos.net/gcc.html