1#!/bin/sh
2##########################################################################
3# Copyright (c) 2009-2015 ETH Zurich.
4# All rights reserved.
5#
6# This file is distributed under the terms in the attached LICENSE file.
7# If you do not find this file, copies can be found by writing to:
8# ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich,
9# Attn: Systems Group.
10#
11# Shell script for running Qemu with a Barrelfish image
12#
13##########################################################################
14
15HDFILE=hd.img
16EFI_FLASH0=flash0.img
17EFI_FLASH1=flash1.img
18HAGFISH_LOCATION="/home/netos/tftpboot/Hagfish.efi"
19MENUFILE=""
20ARCH=""
21DEBUG_SCRIPT=""
22# Grab SMP from env, if unset default to 1
23SMP=${SMP:-1}
24# Grab NIC_MODEL from env, if unset default to e1000
25NIC_MODEL="${NIC_MODEL:-e1000e}"
26
27
28usage () {
29    echo "Usage: $0 --menu <file> --arch <arch>  [options]"
30    echo "  where:"
31    echo "    'arch' is one of: x86_64, a15ve, armv8, zynq7"
32    echo "    'file' is a menu.lst format file to read module list from"
33    echo "  and options can be:"
34    echo "    --debug <script>   (run under the specified GDB script)"
35    echo "    --hdfile <file>    (hard disk image to be build for AHCI, defaults to $HDFILE"
36    echo "    --kernel <file>    (kernel binary, if no menu.lst given)"
37    echo "    --initrd <file>    (initial RAM disk, if no menu.lst given)"
38    echo "    --image  <file>    (prebaked boot image, instead of kernel/initrd)"
39    echo "    --args <args>      (kernel command-line args, if no menu.lst given)"
40    echo "    --smp <cores>      (number of cores to use, defaults to $SMP)"
41    echo "    --nic-model <name> (nic model to use, defaults to $NIC_MODEL)"
42    echo "    --hagfish <file>   (Hagfish boot loader, defaults to $HAGFISH_LOCATION)"
43    echo "  "
44    echo "  The following environment variables are considered:"
45    echo "    QEMU_PATH         (Path for qemu-system-* binary)"
46    echo "    NIC_MODEL         (Same as --nic-model)"
47    echo "    SMP               (Same as --smp)"
48    exit 1
49}
50
51# Result in $?
52qemu_supports_device() {
53    ${QEMU_PATH}qemu-system-x86_64 -device help 2>&1 | grep \"$1\" > /dev/null
54}
55
56
57if test $# = 0; then usage ; fi
58while test $# != 0; do
59    case $1 in
60    "--help"|"-h")
61        usage
62        exit 0
63        ;;
64    "--menu")
65        shift; MENUFILE="$1"
66        ;;
67    "--arch")
68        shift; ARCH="$1"
69        ;;
70    "--hdfile")
71        shift; HDFILE="$1"
72        ;;
73    "--debug")
74        shift; DEBUG_SCRIPT="$1"
75        ;;
76    "--initrd")
77        shift; INITRD="$1"
78        ;;
79    "--kernel")
80        shift; KERNEL="$1"
81        ;;
82    "--image")
83        shift; IMAGE="$1"
84        ;;
85    "--args")
86        shift; KERNEL_CMDS="$1"
87        ;;
88    "--smp")
89        shift; SMP="$1"
90        ;;
91    "--hagfish")
92        shift; HAGFISH_LOCATION="$1"
93        ;;
94    "--nic-model")
95        shift; NIC_MODEL="$1"
96        ;;
97    *)
98        echo "Unknown option $1 (try: --help)" >&2
99        exit 1
100        ;;
101    esac
102    shift
103done
104
105if test -z "$IMAGE"; then
106    if test -z "$MENUFILE"; then
107        echo "No menu.lst file specified."
108        if test -z "$KERNEL"; then
109        echo "ERROR: No initial kernel given and no menu.lst file." >&2; exit 1
110        fi
111        if test -z "$INITRD"; then
112        echo "ERROR: No initial RAM disk given and no menu.lst file." >&2; exit 1
113        fi
114    else
115        echo "Using menu file $MENUFILE"
116        ROOT=`sed -rne 's,^root[ \t]*([^ ]*).*,\1,p' "$MENUFILE"`
117        if test "$ROOT" != "(nd)"; then
118            echo "Root: $ROOT"
119        fi
120        KERNEL=`sed -rne 's,^kernel[ \t]*/([^ ]*).*,\1,p' "$MENUFILE"`
121        if test "$ROOT" != "(nd)"; then
122            KERNEL="$ROOT/$KERNEL"
123        fi
124        if test -z "$KERNEL"; then
125        echo "ERROR: No initial kernel specified in menu.lst file." >&2; exit 1
126        fi
127        KERNEL_CMDS=`sed -rne 's,^kernel[ \t]*[^ ]*[ \t]*(.*),\1,p' "$MENUFILE"`
128        if test "$ROOT" != "(nd)"; then
129            AWKSCRIPT='{ if (NR == 1) printf(root "/" $$0); else printf("," root "/" $$0) }'
130            AWKARGS="-v root=$ROOT"
131        else
132            AWKSCRIPT='{ if (NR == 1) printf($$0); else printf("," $$0) }'
133        fi
134        INITRD=`sed -rne 's,^module(nounzip)?[ \t]*/(.*),\2,p' "$MENUFILE" | awk $AWKARGS "$AWKSCRIPT"`
135        if test -z "$INITRD"; then
136        echo "ERROR: No initial ram disk modules specified in menu.lst file." >&2; exit 1
137        fi
138    fi
139    echo "Initial kernel file: $KERNEL"
140    echo "Initial RAM disk contents: $INITRD"
141else
142    echo "Booting image: $IMAGE"
143fi
144
145echo "Kernel command line arguments: $KERNEL_CMDS"
146echo "Requested architecture is $ARCH."
147
148case "$ARCH" in
149    "x86_64")
150    qemu_supports_device $NIC_MODEL
151    if [ $? = 1 ] ; then
152        echo "$NIC_MODEL not supported. Fall back to e1000"
153        NIC_MODEL=e1000 ;
154    fi
155    
156    # Two NIC qemu conf
157    #QEMU_CMD="${QEMU_PATH}qemu-system-x86_64 \
158    #    -machine type=q35 \
159    #    -smp $SMP \
160    #    -enable-kvm \
161    #    -m 1024 \
162    #    -netdev user,id=network0 \
163    #    -netdev user,id=network1 \
164    #    -device $NIC_MODEL,netdev=network0
165    #    -device $NIC_MODEL,netdev=network1
166    #    -device ahci,id=ahci \
167    #    -device ide-drive,drive=disk,bus=ahci.0 \
168    #    -drive id=disk,file="$HDFILE",if=none"
169
170    QEMU_CMD="${QEMU_PATH}qemu-system-x86_64 \
171        -machine type=q35 \
172        -smp $SMP \
173        -m 1024 \
174        -netdev user,id=network0 \
175        -device $NIC_MODEL,netdev=network0 \
176        -device ahci,id=ahci \
177        -device ide-drive,drive=disk,bus=ahci.0 \
178        -drive id=disk,file="$HDFILE",if=none"
179    QEMU_NONDEBUG=-nographic
180    GDB=gdb-multiarch
181    echo "Creating hard disk image $HDFILE"
182    qemu-img create "$HDFILE" 10M
183    ;;
184    "a15ve")
185        QEMU_CMD="${QEMU_PATH}qemu-system-arm \
186        -m 1024 \
187        -smp $SMP \
188        -machine vexpress-a15"
189    GDB=gdb
190    QEMU_NONDEBUG=-nographic
191    ;;
192    "armv8")
193       QEMU_CMD="${QEMU_PATH}qemu-system-aarch64 \
194                -m 1024 \
195                -cpu cortex-a57 \
196                -M virt -d guest_errors \
197                -M gic_version=3 \
198                -smp $SMP \
199                -bios /usr/share/qemu-efi/QEMU_EFI.fd \
200                -device virtio-blk-device,drive=image \
201                -drive if=none,id=image,file=$IMAGE,format=raw"
202       GDB=gdb-multiarch
203       QEMU_NONDEBUG=-nographic
204       EFI=1
205       ;;
206    "zynq7")
207        QEMU_CMD="${QEMU_PATH}qemu-system-arm \
208        -machine xilinx-zynq-a9 \
209        -m 1024 \
210        -serial /dev/null \
211        -serial mon:stdio"
212    GDB=gdb
213    QEMU_NONDEBUG=-nographic
214    ;;
215    *)
216    echo "No QEmu environment defined for architecture=$ARCH." >&2
217    exit 1
218    ;;
219esac
220
221export QEMU_AUDIO_DRV=none
222
223if test "$DEBUG_SCRIPT" = ""; then
224    echo "OK: about to run the follow qemu command:"
225    if test -z "$EFI"; then
226        if test -z "$IMAGE"; then
227            echo "$QEMU_CMD $QEMU_NONDEBUG -kernel $KERNEL -append '$KERNEL_CMDS' -initrd $INITRD"
228            exec $QEMU_CMD $QEMU_NONDEBUG -kernel $KERNEL -append '$KERNEL_CMDS'  -initrd "$INITRD"
229        else
230            echo "$QEMU_CMD $QEMU_NONDEBUG -kernel $IMAGE"
231            exec $QEMU_CMD $QEMU_NONDEBUG -kernel "$IMAGE"
232        fi
233    else
234        echo $QEMU_CMD $QEMU_NONDEBUG
235        exec $QEMU_CMD $QEMU_NONDEBUG
236    fi
237fi
238
239
240# Now we run the debugger instead
241GDB_ARGS="-x $DEBUG_SCRIPT"
242SERIAL_OUTPUT=file:/dev/stdout
243PORT=$((10000 + UID))
244
245if test "${SERIAL_OUTPUT}" = ""; then
246    # Assuming session is interactive. Use terminal for serial output because
247    # stdout does not work for daemonized qemu and output is lost. This likely
248    # only matters on ARM where there is no video driver at time of writing.
249    SERIAL_OUTPUT=`tty`
250fi
251
252PIDFILE=/tmp/qemu_debugsim_${USER}_${PORT}.pid
253if test -f $PIDFILE; then
254    if ps `cat $PIDFILE` >/dev/null; then
255    echo "Another QEMU already running (PID: `cat $PIDFILE` PIDFILE: $PIDFILE)"
256    exit 1
257    else
258    echo "Deleting stale lockfile $PIDFILE"
259    rm -f $PIDFILE
260    fi
261fi
262
263echo args = $GDB_ARGS
264
265cat > barrelfish_debug.gdb <<EOF
266# Connect to QEMU instance
267target remote localhost:$PORT
268EOF
269
270if test -z "$EFI"; then
271    if test -z "$IMAGE"; then
272        QEMU_INVOCATION="${QEMU_CMD} \
273            -kernel \"$KERNEL\" \
274            -append \"$KERNEL_CMDS\" \
275            -initrd \"$INITRD\" \
276            -serial $SERIAL_OUTPUT \
277            -gdb tcp::$PORT \
278            -S \
279            -display none \
280            -daemonize \
281            -pidfile $PIDFILE"
282    else
283        QEMU_INVOCATION="${QEMU_CMD} \
284            -kernel \"$IMAGE\" \
285            -append \"$KERNEL_CMDS\" \
286            -serial $SERIAL_OUTPUT \
287            -gdb tcp::$PORT \
288            -S \
289            -display none \
290            -daemonize \
291            -pidfile $PIDFILE"
292    fi
293else
294    QEMU_INVOCATION="${QEMU_CMD} \
295        -serial $SERIAL_OUTPUT \
296        -gdb tcp::$PORT \
297        -S \
298        -display none \
299        -daemonize \
300        -pidfile $PIDFILE"
301fi
302
303echo $QEMU_INVOCATION
304set -x
305
306eval $QEMU_INVOCATION
307
308if test $? -eq 0; then
309    stty sane
310    trap '' INT
311    ${GDB} -x barrelfish_debug.gdb ${GDB_ARGS}
312    PID=`cat ${PIDFILE}`
313    kill ${PID} > /dev/null || true
314    rm -f $PIDFILE
315else
316    echo Failed to launch qemu with:
317    echo "   ${QEMU_INVOCATION}"
318fi
319