• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/bash
2
3# Builds mysteriously fail if stdout is non-blocking.
4fixup_ptys() {
5  python3 << 'EOF'
6import fcntl, os, sys
7fd = sys.stdout.fileno()
8flags = fcntl.fcntl(fd, fcntl.F_GETFL)
9flags &= ~(fcntl.FASYNC | os.O_NONBLOCK | os.O_APPEND)
10fcntl.fcntl(fd, fcntl.F_SETFL, flags)
11EOF
12}
13
14# Common kernel options
15OPTIONS=" ANDROID GKI_NET_XFRM_HACKS"
16OPTIONS="$OPTIONS DEBUG_SPINLOCK DEBUG_ATOMIC_SLEEP DEBUG_MUTEXES DEBUG_RT_MUTEXES"
17OPTIONS="$OPTIONS WARN_ALL_UNSEEDED_RANDOM IKCONFIG IKCONFIG_PROC"
18OPTIONS="$OPTIONS DEVTMPFS DEVTMPFS_MOUNT FHANDLE"
19OPTIONS="$OPTIONS IPV6 IPV6_ROUTER_PREF IPV6_MULTIPLE_TABLES IPV6_ROUTE_INFO"
20OPTIONS="$OPTIONS TUN SYN_COOKIES IP_ADVANCED_ROUTER IP_MULTIPLE_TABLES"
21OPTIONS="$OPTIONS NETFILTER NETFILTER_ADVANCED NETFILTER_XTABLES"
22OPTIONS="$OPTIONS NETFILTER_XT_MARK NETFILTER_XT_TARGET_MARK"
23OPTIONS="$OPTIONS IP_NF_IPTABLES IP_NF_MANGLE IP_NF_FILTER"
24OPTIONS="$OPTIONS IP6_NF_IPTABLES IP6_NF_MANGLE IP6_NF_FILTER INET6_IPCOMP"
25OPTIONS="$OPTIONS IPV6_OPTIMISTIC_DAD"
26OPTIONS="$OPTIONS IPV6_ROUTE_INFO IPV6_ROUTER_PREF"
27OPTIONS="$OPTIONS NETFILTER_XT_TARGET_IDLETIMER"
28OPTIONS="$OPTIONS NETFILTER_XT_TARGET_NFLOG"
29OPTIONS="$OPTIONS NETFILTER_XT_MATCH_POLICY"
30OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QUOTA"
31OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QUOTA2"
32OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QUOTA2_LOG"
33OPTIONS="$OPTIONS NETFILTER_XT_MATCH_SOCKET"
34OPTIONS="$OPTIONS NETFILTER_XT_MATCH_QTAGUID"
35OPTIONS="$OPTIONS INET_DIAG INET_UDP_DIAG INET_DIAG_DESTROY"
36OPTIONS="$OPTIONS IP_SCTP"
37OPTIONS="$OPTIONS IP_NF_TARGET_REJECT IP_NF_TARGET_REJECT_SKERR"
38OPTIONS="$OPTIONS IP6_NF_TARGET_REJECT IP6_NF_TARGET_REJECT_SKERR"
39OPTIONS="$OPTIONS NET_KEY XFRM_USER XFRM_STATISTICS CRYPTO_CBC"
40OPTIONS="$OPTIONS CRYPTO_CTR CRYPTO_HMAC CRYPTO_AES CRYPTO_SHA1"
41OPTIONS="$OPTIONS CRYPTO_XCBC CRYPTO_CHACHA20POLY1305"
42OPTIONS="$OPTIONS CRYPTO_USER INET_ESP INET_XFRM_MODE_TRANSPORT"
43OPTIONS="$OPTIONS INET_XFRM_MODE_TUNNEL INET6_ESP"
44OPTIONS="$OPTIONS INET6_XFRM_MODE_TRANSPORT INET6_XFRM_MODE_TUNNEL"
45OPTIONS="$OPTIONS CRYPTO_SHA256 CRYPTO_SHA512 CRYPTO_AES_X86_64 CRYPTO_NULL"
46OPTIONS="$OPTIONS CRYPTO_GCM CRYPTO_ECHAINIV NET_IPVTI"
47OPTIONS="$OPTIONS DUMMY"
48
49# Kernel version specific options
50OPTIONS="$OPTIONS XFRM_INTERFACE"                # Various device kernels
51OPTIONS="$OPTIONS XFRM_MIGRATE"                  # Added in 5.10
52OPTIONS="$OPTIONS CGROUP_BPF"                    # Added in android-4.9
53OPTIONS="$OPTIONS NF_SOCKET_IPV4 NF_SOCKET_IPV6" # Added in 4.9
54OPTIONS="$OPTIONS INET_SCTP_DIAG"                # Added in 4.7
55OPTIONS="$OPTIONS SOCK_CGROUP_DATA"              # Added in 4.5
56OPTIONS="$OPTIONS CRYPTO_ECHAINIV"               # Added in 4.1
57OPTIONS="$OPTIONS BPF_SYSCALL"                   # Added in 3.18
58OPTIONS="$OPTIONS IPV6_VTI"                      # Added in 3.13
59OPTIONS="$OPTIONS IPV6_PRIVACY"                  # Removed in 3.12
60OPTIONS="$OPTIONS NETFILTER_TPROXY"              # Removed in 3.11
61
62# UML specific options
63OPTIONS="$OPTIONS BLK_DEV_UBD HOSTFS"
64
65# QEMU specific options
66OPTIONS="$OPTIONS PCI VIRTIO VIRTIO_PCI VIRTIO_BLK NET_9P NET_9P_VIRTIO 9P_FS"
67OPTIONS="$OPTIONS CRYPTO_DEV_VIRTIO SERIAL_8250 SERIAL_8250_PCI"
68OPTIONS="$OPTIONS SERIAL_8250_CONSOLE PCI_HOST_GENERIC SERIAL_AMBA_PL011"
69OPTIONS="$OPTIONS SERIAL_AMBA_PL011_CONSOLE"
70
71# Obsolete options present at some time in Android kernels
72OPTIONS="$OPTIONS IP_NF_TARGET_REJECT_SKERR IP6_NF_TARGET_REJECT_SKERR"
73
74# b/262323440 - UML *sometimes* seems to have issues with:
75#   UPSTREAM: hardening: Clarify Kconfig text for auto-var-init
76# which is in 4.14.~299/4.19.~266 LTS and which does:
77#   prompt "Initialize kernel stack variables at function entry"
78#   default GCC_PLUGIN_STRUCTLEAK_BYREF_ALL if COMPILE_TEST && GCC_PLUGINS
79#   default INIT_STACK_ALL_PATTERN if COMPILE_TEST && CC_HAS_AUTO_VAR_INIT_PATTERN
80# + default INIT_STACK_ALL_ZERO if CC_HAS_AUTO_VAR_INIT_PATTERN
81#   default INIT_STACK_NONE
82# and thus presumably switches from INIT_STACK_NONE to INIT_STACK_ALL_ZERO
83#
84# My guess it that this is triggering some sort of UML and/or compiler bug...
85# Let's just turn it off... we don't care that much.
86OPTIONS="$OPTIONS INIT_STACK_NONE"
87
88# These two break the flo kernel due to differences in -Werror on recent GCC.
89DISABLE_OPTIONS=" REISERFS_FS ANDROID_PMEM"
90
91# Disable frame size warning on arm64. GCC 10 generates >1k stack frames.
92DISABLE_OPTIONS="$DISABLE_OPTIONS FRAME_WARN"
93
94# How many TAP interfaces to create to provide the VM with real network access
95# via the host. This requires privileges (e.g., root access) on the host.
96#
97# This is not needed to run the tests, but can be used, for example, to allow
98# the VM to update system packages, or to write tests that need access to a
99# real network. The VM does not set up networking by default, but it contains a
100# DHCP client and has the ability to use IPv6 autoconfiguration. This script
101# does not perform any host-level setup beyond configuring tap interfaces;
102# configuring IPv4 NAT and/or IPv6 router advertisements or ND proxying must
103# be done separately.
104NUMTAPINTERFACES=0
105
106# The root filesystem disk image we'll use.
107ROOTFS=${ROOTFS:-net_test.rootfs.20221014}
108COMPRESSED_ROOTFS=$ROOTFS.xz
109URL=https://dl.google.com/dl/android/$COMPRESSED_ROOTFS
110
111# Parse arguments and figure out which test to run.
112ARCH=${ARCH:-um}
113J=${J:-$(nproc)}
114MAKE="make"
115OUT_DIR=$(readlink -f ${OUT_DIR:-.})
116KERNEL_DIR=$(readlink -f ${KERNEL_DIR:-.})
117if [ "$OUT_DIR" != "$KERNEL_DIR" ]; then
118    MAKE="$MAKE O=$OUT_DIR"
119fi
120SCRIPT_DIR=$(dirname $(readlink -f $0))
121CONFIG_SCRIPT=${KERNEL_DIR}/scripts/config
122CONFIG_FILE=${OUT_DIR}/.config
123consolemode=
124netconfig=
125testmode=
126cmdline=
127nowrite=1
128nobuild=0
129norun=0
130
131KVER_MAJOR="$(sed -rn 's@^ *VERSION *= *([0-9]+)$@\1@p'    < "${KERNEL_DIR}/Makefile")"
132KVER_MINOR="$(sed -rn 's@^ *PATCHLEVEL *= *([0-9]+)$@\1@p' < "${KERNEL_DIR}/Makefile")"
133KVER_LEVEL="$(sed -rn 's@^ *SUBLEVEL *= *([0-9]+)$@\1@p'   < "${KERNEL_DIR}/Makefile")"
134KVER="${KVER_MAJOR}.${KVER_MINOR}.${KVER_LEVEL}"
135echo "Detected kernel version ${KVER}"
136
137if [[ -z "${DEFCONFIG:-}" ]]; then
138  case "${ARCH}" in
139    um)
140      export DEFCONFIG=defconfig
141      ;;
142    arm64)
143      if [[ -e arch/arm64/configs/gki_defconfig ]]; then
144        export DEFCONFIG=gki_defconfig
145      elif [[ -e arch/arm64/configs/cuttlefish_defconfig ]]; then
146        export DEFCONFIG=cuttlefish_defconfig
147      fi
148      ;;
149    x86_64)
150      if [[ -e arch/x86/configs/gki_defconfig ]]; then
151        export DEFCONFIG=gki_defconfig
152      elif [[ -e arch/x86/configs/x86_64_cuttlefish_defconfig ]]; then
153        export DEFCONFIG=x86_64_cuttlefish_defconfig
154      fi
155  esac
156fi
157
158if tty >/dev/null; then
159  verbose=
160else
161  verbose=1
162fi
163
164test=all_tests.sh
165while [[ -n "$1" ]]; do
166  if [[ "$1" == "--builder" || "$1" == "-b" ]]; then
167    consolemode="con=null,fd:1"
168    testmode=builder
169    shift
170  elif [[ "$1" == "--readwrite" || "$1" == "--rw" ]]; then
171    nowrite=0
172    shift
173  elif [[ "$1" == "--readonly" ||  "$1" == "--ro" ]]; then
174    nowrite=1
175    shift
176  elif [[ "$1" == "--nobuild" ]]; then
177    nobuild=1
178    shift
179  elif [[ "$1" == "--norun" ]]; then
180    norun=1
181    shift
182  elif [[ "$1" == "--verbose" ]]; then
183    verbose=1
184    shift
185  elif [[ "$1" == "--noverbose" ]]; then
186    verbose=
187    shift
188  else
189    test=$1
190    break  # Arguments after the test file are passed to the test itself.
191  fi
192done
193
194# Check that test file exists and is readable
195test_file=$SCRIPT_DIR/$test
196if [[ ! -e $test_file ]]; then
197  echo "test file '${test_file}' does not exist"
198  exit 1
199fi
200
201if [[ ! -x $test_file ]]; then
202  echo "test file '${test_file}' is not executable"
203  exit 1
204fi
205
206# Collect trailing arguments to pass to $test
207test_args=${@:2}
208
209function isRunningTest() {
210  ! (( norun ))
211}
212
213function isBuildOnly() {
214  (( norun )) && ! (( nobuild ))
215}
216
217if ! isRunningTest && ! isBuildOnly; then
218  echo "Usage:" >&2
219  echo "  $0 [--builder] [--readonly|--ro|--readwrite|--rw] [--nobuild] [--verbose] [<test>]" >&2
220  echo "      - if [<test>] is not specified, run all_tests.sh" >&2
221  echo "  $0 --norun" >&2
222  exit 1
223fi
224
225cd $OUT_DIR
226echo Running tests from: `pwd`
227
228set -e
229
230# Check if we need to uncompress the disk image.
231# We use xz because it compresses better: to 42M vs 72M (gzip) / 62M (bzip2).
232cd $SCRIPT_DIR
233if [ ! -f $ROOTFS ]; then
234  echo "Deleting $COMPRESSED_ROOTFS" >&2
235  rm -f $COMPRESSED_ROOTFS
236  echo "Downloading $URL" >&2
237  wget -nv $URL
238  echo "Uncompressing $COMPRESSED_ROOTFS" >&2
239  unxz $COMPRESSED_ROOTFS
240fi
241if ! [[ "${ROOTFS}" =~ ^/ ]]; then
242  ROOTFS="${SCRIPT_DIR}/${ROOTFS}"
243fi
244echo "Using $ROOTFS"
245cd -
246
247# If network access was requested, create NUMTAPINTERFACES tap interfaces on
248# the host, and prepare UML command line params to use them. The interfaces are
249# called <user>TAP0, <user>TAP1, on the host, and eth0, eth1, ..., in the VM.
250if (( $NUMTAPINTERFACES > 0 )); then
251  user=${USER:0:10}
252  tapinterfaces=
253  for id in $(seq 0 $(( NUMTAPINTERFACES - 1 )) ); do
254    tap=${user}TAP$id
255    tapinterfaces="$tapinterfaces $tap"
256    mac=$(printf fe:fd:00:00:00:%02x $id)
257    if [ "$ARCH" == "um" ]; then
258      netconfig="$netconfig eth$id=tuntap,$tap,$mac"
259    else
260      netconfig="$netconfig -netdev tap,id=hostnet$id,ifname=$tap,script=no,downscript=no"
261      netconfig="$netconfig -device virtio-net-pci,netdev=hostnet$id,id=net$id,mac=$mac"
262    fi
263  done
264
265  for tap in $tapinterfaces; do
266    if ! ip link list $tap > /dev/null; then
267      echo "Creating tap interface $tap" >&2
268      sudo tunctl -u $USER -t $tap
269      sudo ip link set $tap up
270    fi
271  done
272fi
273
274if [[ -n "${KERNEL_BINARY:-}" ]]; then
275  nobuild=1
276else
277  # Set default KERNEL_BINARY location if it was not provided.
278  if [ "$ARCH" == "um" ]; then
279    KERNEL_BINARY=./linux
280  elif [ "$ARCH" == "i386" -o "$ARCH" == "x86_64" -o "$ARCH" == "x86" ]; then
281    KERNEL_BINARY=./arch/x86/boot/bzImage
282  elif [ "$ARCH" == "arm64" ]; then
283    KERNEL_BINARY=./arch/arm64/boot/Image.gz
284  fi
285fi
286
287if ((nobuild == 0)); then
288  make_flags=
289  if [ "$ARCH" == "um" ]; then
290    # Exporting ARCH=um SUBARCH=x86_64 doesn't seem to work, as it
291    # "sometimes" (?) results in a 32-bit kernel.
292    make_flags="$make_flags ARCH=$ARCH SUBARCH=${SUBARCH:-x86_64} CROSS_COMPILE= "
293  fi
294  if [[ -n "${CC:-}" ]]; then
295    # The CC flag is *not* inherited from the environment, so it must be
296    # passed in on the command line.
297    make_flags="$make_flags CC=$CC"
298  fi
299
300  # If there's no kernel config at all, create one or UML won't work.
301  [ -f $CONFIG_FILE ] || (cd $KERNEL_DIR && $MAKE $make_flags $DEFCONFIG)
302
303  # Enable the kernel config options listed in $OPTIONS.
304  $CONFIG_SCRIPT --file $CONFIG_FILE ${OPTIONS// / -e }
305
306  # Disable the kernel config options listed in $DISABLE_OPTIONS.
307  $CONFIG_SCRIPT --file $CONFIG_FILE ${DISABLE_OPTIONS// / -d }
308
309  echo "Running: $MAKE $make_flags olddefconfig"
310  $MAKE $make_flags olddefconfig
311
312  # Compile the kernel.
313  if [ "$ARCH" == "um" ]; then
314    echo "Running: $MAKE -j$J $make_flags linux"
315    $MAKE -j$J $make_flags linux
316  else
317    echo "Running: $MAKE -j$J $make_flags"
318    $MAKE -j$J $make_flags
319  fi
320fi
321
322if (( norun == 1 )); then
323  exit 0
324fi
325
326if (( nowrite == 1 )); then
327  cmdline="ro"
328fi
329
330if (( verbose == 1 )); then
331  cmdline="$cmdline verbose=1"
332fi
333
334cmdline="$cmdline panic=1 init=/sbin/net_test.sh"
335cmdline="$cmdline net_test_args=\"$test_args\" net_test_mode=$testmode"
336
337# Experience shows that we need at least 128 bits of entropy for the
338# kernel's crng init to complete (before it fully initializes stuff behaves
339# *weirdly* and there's plenty of kernel warnings and some tests even fail),
340# hence net_test.sh needs at least 32 hex chars (which is the amount of hex
341# in a single random UUID) provided to it on the kernel cmdline.
342#
343# Just to be safe, we'll pass in 384 bits, and we'll do this as a random
344# 64 character base64 seed (because this is shorter than base16).
345# We do this by getting *three* random UUIDs and concatenating their hex
346# digits into an *even* length hex encoded string, which we then convert
347# into base64.
348entropy="$(cat /proc/sys/kernel/random{/,/,/}uuid | tr -d '\n-')"
349entropy="$(xxd -r -p <<< "${entropy}" | base64 -w 0)"
350cmdline="${cmdline} random.trust_cpu=on entropy=${entropy}"
351
352if [ "$ARCH" == "um" ]; then
353  # Get the absolute path to the test file that's being run.
354  cmdline="$cmdline net_test=/host$SCRIPT_DIR/$test"
355
356  # We'd use UML's /proc/exitcode feature to communicate errors on test failure,
357  # if not for UML having a tendency to crash during shutdown,
358  # so instead use an extra serial line we'll redirect to an open fd...
359  cmdline="$cmdline exitcode=/dev/ttyS3"
360
361  # Map the --readonly flag to UML block device names
362  if ((nowrite == 0)); then
363    blockdevice=ubda
364  else
365    blockdevice=ubdar
366  fi
367
368  # Create a temp file for 'serial line 3' for return code.
369  SSL3="$(mktemp)"
370
371  exitcode=0
372  $KERNEL_BINARY >&2 3>"${SSL3}" umid=net_test mem=512M \
373    $blockdevice=$ROOTFS $netconfig $consolemode ssl3=null,fd:3 $cmdline \
374  || exitcode=$?
375
376  # Return to beginning of line (via carriage return) after the above newline moved us down.
377  echo -en '\r'
378  # re-enable: 'postprocess output' and 'translate newline to carriage return-newline'
379  stty opost onlcr || :
380
381  if [[ "${exitcode}" == 134 && -s "${SSL3}" && "$(tr -d '\r' < "${SSL3}")" == 0 ]]; then
382    # Sometimes the tests all pass, but UML crashes during the shutdown process itself.
383    # As such we can't actually rely on the /proc/exitcode returned value.
384    echo "Warning: UML appears to have crashed after successfully executing the tests." 1>&2
385  elif [[ "${exitcode}" != 0 ]]; then
386    echo "Warning: UML exited with ${exitcode} instead of zero." 1>&2
387  fi
388
389  if [[ -s "${SSL3}" ]]; then
390    exitcode="$(tr -d '\r' < "${SSL3}")"
391    echo "Info: retrieved exit code ${exitcode}." 1>&2
392  fi
393
394  rm -f "${SSL3}"
395  unset SSL3
396
397  # UML is kind of crazy in how guest syscalls work.  It requires host kernel
398  # to not be in vsyscall=none mode.
399  if [[ "${exitcode}" != '0' ]]; then
400    {
401      # Hopefully one of these exists
402      cat /proc/config || :
403      zcat /proc/config.gz || :
404      cat "/boot/config-$(uname -r)" || :
405      zcat "/boot/config-$(uname -r).gz" || :
406    } 2>/dev/null \
407    | egrep -q '^CONFIG_LEGACY_VSYSCALL_NONE=y' \
408    && ! egrep -q '(^| )vsyscall=(native|emulate|xonly)( |$)' /proc/cmdline \
409    && {
410      echo -e "\r"
411      echo -e "-----=====-----\r"
412      echo -e "If above you saw a 'net_test.sh[1]: segfault at ...' followed by\r"
413      echo -e "'Kernel panic - not syncing: Attempted to kill init!' then please\r"
414      echo -e "set 'vsyscall=emulate' on *host* kernel command line.\r"
415      echo -e "On Linux 5.2+ you can instead use the slightly safer 'vsyscall=xonly'.\r"
416      echo -e "(for example via GRUB_CMDLINE_LINUX in /etc/default/grub)\r"
417      echo -e "-----=====-----\r"
418    }
419  fi
420else
421  # We boot into the filesystem image directly in all cases
422  cmdline="$cmdline root=/dev/vda"
423
424  # The path is stripped by the 9p export; we don't need SCRIPT_DIR
425  cmdline="$cmdline net_test=/host/$test"
426
427  # Map the --readonly flag to a QEMU block device flag
428  if ((nowrite > 0)); then
429    blockdevice=",readonly=on"
430  else
431    blockdevice=
432  fi
433  blockdevice="-drive file=$ROOTFS,format=raw,if=none,id=drive-virtio-disk0$blockdevice"
434  blockdevice="$blockdevice -device virtio-blk-pci,drive=drive-virtio-disk0"
435
436  # Pass through our current console/screen size to inner shell session
437  read rows cols < <(stty size 2>/dev/null)
438  [[ -z "${rows}" ]] || cmdline="${cmdline} console_rows=${rows}"
439  [[ -z "${cols}" ]] || cmdline="${cmdline} console_cols=${cols}"
440  unset rows cols
441
442  # QEMU has no way to modify its exitcode; simulate it with a serial port.
443  #
444  # Choose to do it this way over writing a file to /host, because QEMU will
445  # initialize the 'exitcode' file for us, it avoids unnecessary writes to the
446  # host filesystem (which is normally not written to) and it allows us to
447  # communicate an exit code back in cases we do not have /host mounted.
448  #
449  if [ "$ARCH" == "i386" -o "$ARCH" == "x86_64" -o "$ARCH" == "x86" ]; then
450    # Assume we have hardware-accelerated virtualization support for amd64
451    qemu="qemu-system-x86_64 -machine pc,accel=kvm -cpu host"
452
453    # We know 'ttyS0' will be our serial port on x86 from the hard-coded
454    # '-serial mon:stdio' flag below
455    cmdline="$cmdline console=ttyS0"
456
457    # The assignment of 'ttyS1' here is magical; we know ttyS0 was used up
458    # by '-serial mon:stdio', and so this second serial port will be 'ttyS1'
459    cmdline="$cmdline exitcode=/dev/ttyS1"
460  elif [ "$ARCH" == "arm64" ]; then
461    # This uses a software model CPU, based on cortex-a57
462    qemu="qemu-system-aarch64 -machine virt -cpu cortex-a57"
463
464    # We know 'ttyAMA0' will be our serial port on arm64 from the hard-coded
465    # '-serial mon:stdio' flag below
466    cmdline="$cmdline console=ttyAMA0"
467
468    # The kernel will print messages via a virtual ARM serial port (ttyAMA0),
469    # but for command line consistency with x86, we put the exitcode serial
470    # port on the PCI bus, and it will be the only one.
471    cmdline="$cmdline exitcode=/dev/ttyS0"
472  fi
473
474  $qemu >&2 -name net_test -m 512 \
475    -kernel $KERNEL_BINARY \
476    -no-user-config -nodefaults -no-reboot \
477    -display none -nographic -serial mon:stdio -parallel none \
478    -smp 4,sockets=4,cores=1,threads=1 \
479    -device virtio-rng-pci \
480    -chardev file,id=exitcode,path=exitcode \
481    -device pci-serial,chardev=exitcode \
482    -fsdev local,security_model=mapped-xattr,id=fsdev0,fmode=0644,dmode=0755,path=$SCRIPT_DIR \
483    -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=host \
484    $blockdevice $netconfig -append "$cmdline"
485  [[ -s exitcode ]] && exitcode=`cat exitcode | tr -d '\r'` || exitcode=1
486  rm -f exitcode
487fi
488
489# UML reliably screws up the ptys, QEMU probably can as well...
490fixup_ptys
491stty sane || :
492tput smam || :
493
494echo "Returning exit code ${exitcode}." 1>&2
495exit "${exitcode}"
496