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# How many TAP interfaces to create to provide the VM with real network access 92# via the host. This requires privileges (e.g., root access) on the host. 93# 94# This is not needed to run the tests, but can be used, for example, to allow 95# the VM to update system packages, or to write tests that need access to a 96# real network. The VM does not set up networking by default, but it contains a 97# DHCP client and has the ability to use IPv6 autoconfiguration. This script 98# does not perform any host-level setup beyond configuring tap interfaces; 99# configuring IPv4 NAT and/or IPv6 router advertisements or ND proxying must 100# be done separately. 101NUMTAPINTERFACES=0 102 103# The root filesystem disk image we'll use. 104ROOTFS=${ROOTFS:-net_test.rootfs.20221014} 105COMPRESSED_ROOTFS=$ROOTFS.xz 106URL=https://dl.google.com/dl/android/$COMPRESSED_ROOTFS 107 108# Parse arguments and figure out which test to run. 109ARCH=${ARCH:-um} 110J=${J:-$(nproc)} 111MAKE="make" 112OUT_DIR=$(readlink -f ${OUT_DIR:-.}) 113KERNEL_DIR=$(readlink -f ${KERNEL_DIR:-.}) 114if [ "$OUT_DIR" != "$KERNEL_DIR" ]; then 115 MAKE="$MAKE O=$OUT_DIR" 116fi 117SCRIPT_DIR=$(dirname $(readlink -f $0)) 118CONFIG_SCRIPT=${KERNEL_DIR}/scripts/config 119CONFIG_FILE=${OUT_DIR}/.config 120consolemode= 121netconfig= 122testmode= 123cmdline= 124nowrite=1 125nobuild=0 126norun=0 127 128if [[ ! -f "${KERNEL_DIR}/Makefile" ]]; then 129 echo "No kernel Makefile found. Are you running this from a kernel directory?" 130 exit 1 131fi 132 133KVER_MAJOR="$(sed -rn 's@^ *VERSION *= *([0-9]+)$@\1@p' < "${KERNEL_DIR}/Makefile")" 134KVER_MINOR="$(sed -rn 's@^ *PATCHLEVEL *= *([0-9]+)$@\1@p' < "${KERNEL_DIR}/Makefile")" 135KVER_LEVEL="$(sed -rn 's@^ *SUBLEVEL *= *([0-9]+)$@\1@p' < "${KERNEL_DIR}/Makefile")" 136KVER="${KVER_MAJOR}.${KVER_MINOR}.${KVER_LEVEL}" 137echo "Detected kernel version ${KVER}" 138 139if [[ -z "${DEFCONFIG:-}" ]]; then 140 case "${ARCH}" in 141 um) 142 export DEFCONFIG=defconfig 143 ;; 144 arm64) 145 if [[ -e arch/arm64/configs/gki_defconfig ]]; then 146 export DEFCONFIG=gki_defconfig 147 elif [[ -e arch/arm64/configs/cuttlefish_defconfig ]]; then 148 export DEFCONFIG=cuttlefish_defconfig 149 fi 150 ;; 151 x86_64) 152 if [[ -e arch/x86/configs/gki_defconfig ]]; then 153 export DEFCONFIG=gki_defconfig 154 elif [[ -e arch/x86/configs/x86_64_cuttlefish_defconfig ]]; then 155 export DEFCONFIG=x86_64_cuttlefish_defconfig 156 fi 157 esac 158fi 159 160if tty >/dev/null; then 161 verbose= 162else 163 verbose=1 164fi 165 166test=all_tests.sh 167while [[ -n "$1" ]]; do 168 if [[ "$1" == "--builder" || "$1" == "-b" ]]; then 169 consolemode="con=null,fd:1" 170 testmode=builder 171 shift 172 elif [[ "$1" == "--readwrite" || "$1" == "--rw" ]]; then 173 nowrite=0 174 shift 175 elif [[ "$1" == "--readonly" || "$1" == "--ro" ]]; then 176 nowrite=1 177 shift 178 elif [[ "$1" == "--nobuild" ]]; then 179 nobuild=1 180 shift 181 elif [[ "$1" == "--norun" ]]; then 182 norun=1 183 shift 184 elif [[ "$1" == "--verbose" ]]; then 185 verbose=1 186 shift 187 elif [[ "$1" == "--noverbose" ]]; then 188 verbose= 189 shift 190 else 191 test=$1 192 break # Arguments after the test file are passed to the test itself. 193 fi 194done 195 196# Check that test file exists and is readable 197test_file=$SCRIPT_DIR/$test 198if [[ ! -e $test_file ]]; then 199 echo "test file '${test_file}' does not exist" 200 exit 1 201fi 202 203if [[ ! -x $test_file ]]; then 204 echo "test file '${test_file}' is not executable" 205 exit 1 206fi 207 208# Collect trailing arguments to pass to $test 209test_args=${@:2} 210 211function isRunningTest() { 212 ! (( norun )) 213} 214 215function isBuildOnly() { 216 (( norun )) && ! (( nobuild )) 217} 218 219if ! isRunningTest && ! isBuildOnly; then 220 echo "Usage:" >&2 221 echo " $0 [--builder] [--readonly|--ro|--readwrite|--rw] [--nobuild] [--verbose] [<test>]" >&2 222 echo " - if [<test>] is not specified, run all_tests.sh" >&2 223 echo " $0 --norun" >&2 224 exit 1 225fi 226 227cd $OUT_DIR 228echo Running tests from: `pwd` 229 230set -e 231 232# Check if we need to uncompress the disk image. 233# We use xz because it compresses better: to 42M vs 72M (gzip) / 62M (bzip2). 234cd $SCRIPT_DIR 235if [ ! -f $ROOTFS ]; then 236 echo "Deleting $COMPRESSED_ROOTFS" >&2 237 rm -f $COMPRESSED_ROOTFS 238 echo "Downloading $URL" >&2 239 wget -nv $URL 240 echo "Uncompressing $COMPRESSED_ROOTFS" >&2 241 unxz $COMPRESSED_ROOTFS 242fi 243if ! [[ "${ROOTFS}" =~ ^/ ]]; then 244 ROOTFS="${SCRIPT_DIR}/${ROOTFS}" 245fi 246echo "Using $ROOTFS" 247cd - 248 249# If network access was requested, create NUMTAPINTERFACES tap interfaces on 250# the host, and prepare UML command line params to use them. The interfaces are 251# called <user>TAP0, <user>TAP1, on the host, and eth0, eth1, ..., in the VM. 252if (( $NUMTAPINTERFACES > 0 )); then 253 user=${USER:0:10} 254 tapinterfaces= 255 for id in $(seq 0 $(( NUMTAPINTERFACES - 1 )) ); do 256 tap=${user}TAP$id 257 tapinterfaces="$tapinterfaces $tap" 258 mac=$(printf fe:fd:00:00:00:%02x $id) 259 if [ "$ARCH" == "um" ]; then 260 netconfig="$netconfig eth$id=tuntap,$tap,$mac" 261 else 262 netconfig="$netconfig -netdev tap,id=hostnet$id,ifname=$tap,script=no,downscript=no" 263 netconfig="$netconfig -device virtio-net-pci,netdev=hostnet$id,id=net$id,mac=$mac" 264 fi 265 done 266 267 for tap in $tapinterfaces; do 268 if ! ip link list $tap > /dev/null; then 269 echo "Creating tap interface $tap" >&2 270 sudo tunctl -u $USER -t $tap 271 sudo ip link set $tap up 272 fi 273 done 274fi 275 276if [[ -n "${KERNEL_BINARY:-}" ]]; then 277 nobuild=1 278else 279 # Set default KERNEL_BINARY location if it was not provided. 280 if [ "$ARCH" == "um" ]; then 281 KERNEL_BINARY=./linux 282 elif [ "$ARCH" == "i386" -o "$ARCH" == "x86_64" -o "$ARCH" == "x86" ]; then 283 KERNEL_BINARY=./arch/x86/boot/bzImage 284 elif [ "$ARCH" == "arm64" ]; then 285 KERNEL_BINARY=./arch/arm64/boot/Image.gz 286 fi 287fi 288 289if ((nobuild == 0)); then 290 make_flags= 291 if [ "$ARCH" == "um" ]; then 292 # Exporting ARCH=um SUBARCH=x86_64 doesn't seem to work, as it 293 # "sometimes" (?) results in a 32-bit kernel. 294 make_flags="$make_flags ARCH=$ARCH SUBARCH=${SUBARCH:-x86_64} CROSS_COMPILE= " 295 fi 296 if [[ -n "${CC:-}" ]]; then 297 # The CC flag is *not* inherited from the environment, so it must be 298 # passed in on the command line. 299 make_flags="$make_flags CC=$CC" 300 fi 301 302 # If there's no kernel config at all, create one or UML won't work. 303 [ -f $CONFIG_FILE ] || (cd $KERNEL_DIR && $MAKE $make_flags $DEFCONFIG) 304 305 # Enable the kernel config options listed in $OPTIONS. 306 $CONFIG_SCRIPT --file $CONFIG_FILE ${OPTIONS// / -e } 307 308 # Increase acceptable frame size. 309 $CONFIG_SCRIPT --file $CONFIG_FILE --set-val FRAME_WARN 3172 310 311 # Disable the kernel config options listed in $DISABLE_OPTIONS. 312 $CONFIG_SCRIPT --file $CONFIG_FILE ${DISABLE_OPTIONS// / -d } 313 314 echo "Running: $MAKE $make_flags olddefconfig" 315 $MAKE $make_flags olddefconfig 316 317 # Compile the kernel. 318 if [ "$ARCH" == "um" ]; then 319 echo "Running: $MAKE -j$J $make_flags linux" 320 $MAKE -j$J $make_flags linux 321 else 322 echo "Running: $MAKE -j$J $make_flags" 323 $MAKE -j$J $make_flags 324 fi 325fi 326 327if (( norun == 1 )); then 328 exit 0 329fi 330 331if (( nowrite == 1 )); then 332 cmdline="ro" 333fi 334 335if (( verbose == 1 )); then 336 cmdline="$cmdline verbose=1" 337fi 338 339cmdline="$cmdline panic=1 init=/sbin/net_test.sh" 340cmdline="$cmdline net_test_args=\"$test_args\" net_test_mode=$testmode" 341 342# Experience shows that we need at least 128 bits of entropy for the 343# kernel's crng init to complete (before it fully initializes stuff behaves 344# *weirdly* and there's plenty of kernel warnings and some tests even fail), 345# hence net_test.sh needs at least 32 hex chars (which is the amount of hex 346# in a single random UUID) provided to it on the kernel cmdline. 347# 348# Just to be safe, we'll pass in 384 bits, and we'll do this as a random 349# 64 character base64 seed (because this is shorter than base16). 350# We do this by getting *three* random UUIDs and concatenating their hex 351# digits into an *even* length hex encoded string, which we then convert 352# into base64. 353entropy="$(cat /proc/sys/kernel/random{/,/,/}uuid | tr -d '\n-')" 354entropy="$(xxd -r -p <<< "${entropy}" | base64 -w 0)" 355cmdline="${cmdline} random.trust_cpu=on entropy=${entropy}" 356 357if [ "$ARCH" == "um" ]; then 358 # Get the absolute path to the test file that's being run. 359 cmdline="$cmdline net_test=/host$SCRIPT_DIR/$test" 360 361 # We'd use UML's /proc/exitcode feature to communicate errors on test failure, 362 # if not for UML having a tendency to crash during shutdown, 363 # so instead use an extra serial line we'll redirect to an open fd... 364 cmdline="$cmdline exitcode=/dev/ttyS3" 365 366 # Map the --readonly flag to UML block device names 367 if ((nowrite == 0)); then 368 blockdevice=ubda 369 else 370 blockdevice=ubdar 371 fi 372 373 # Create a temp file for 'serial line 3' for return code. 374 SSL3="$(mktemp)" 375 376 exitcode=0 377 $KERNEL_BINARY >&2 3>"${SSL3}" umid=net_test mem=512M \ 378 $blockdevice=$ROOTFS $netconfig $consolemode ssl3=null,fd:3 $cmdline \ 379 || exitcode=$? 380 381 # Return to beginning of line (via carriage return) after the above newline moved us down. 382 echo -en '\r' 383 # re-enable: 'postprocess output' and 'translate newline to carriage return-newline' 384 stty opost onlcr || : 385 386 if [[ "${exitcode}" == 134 && -s "${SSL3}" && "$(tr -d '\r' < "${SSL3}")" == 0 ]]; then 387 # Sometimes the tests all pass, but UML crashes during the shutdown process itself. 388 # As such we can't actually rely on the /proc/exitcode returned value. 389 echo "Warning: UML appears to have crashed after successfully executing the tests." 1>&2 390 elif [[ "${exitcode}" != 0 ]]; then 391 echo "Warning: UML exited with ${exitcode} instead of zero." 1>&2 392 fi 393 394 if [[ -s "${SSL3}" ]]; then 395 exitcode="$(tr -d '\r' < "${SSL3}")" 396 echo "Info: retrieved exit code ${exitcode}." 1>&2 397 fi 398 399 rm -f "${SSL3}" 400 unset SSL3 401 402 # UML is kind of crazy in how guest syscalls work. It requires host kernel 403 # to not be in vsyscall=none mode. 404 if [[ "${exitcode}" != '0' ]]; then 405 { 406 # Hopefully one of these exists 407 cat /proc/config || : 408 zcat /proc/config.gz || : 409 cat "/boot/config-$(uname -r)" || : 410 zcat "/boot/config-$(uname -r).gz" || : 411 } 2>/dev/null \ 412 | egrep -q '^CONFIG_LEGACY_VSYSCALL_NONE=y' \ 413 && ! egrep -q '(^| )vsyscall=(native|emulate|xonly)( |$)' /proc/cmdline \ 414 && { 415 echo -e "\r" 416 echo -e "-----=====-----\r" 417 echo -e "If above you saw a 'net_test.sh[1]: segfault at ...' followed by\r" 418 echo -e "'Kernel panic - not syncing: Attempted to kill init!' then please\r" 419 echo -e "set 'vsyscall=emulate' on *host* kernel command line.\r" 420 echo -e "On Linux 5.2+ you can instead use the slightly safer 'vsyscall=xonly'.\r" 421 echo -e "(for example via GRUB_CMDLINE_LINUX in /etc/default/grub)\r" 422 echo -e "-----=====-----\r" 423 } 424 fi 425else 426 # We boot into the filesystem image directly in all cases 427 cmdline="$cmdline root=/dev/vda" 428 429 # The path is stripped by the 9p export; we don't need SCRIPT_DIR 430 cmdline="$cmdline net_test=/host/$test" 431 432 # Map the --readonly flag to a QEMU block device flag 433 if ((nowrite > 0)); then 434 blockdevice=",readonly=on" 435 else 436 blockdevice= 437 fi 438 blockdevice="-drive file=$ROOTFS,format=raw,if=none,id=drive-virtio-disk0$blockdevice" 439 blockdevice="$blockdevice -device virtio-blk-pci,drive=drive-virtio-disk0" 440 441 # Pass through our current console/screen size to inner shell session 442 read rows cols < <(stty size 2>/dev/null) 443 [[ -z "${rows}" ]] || cmdline="${cmdline} console_rows=${rows}" 444 [[ -z "${cols}" ]] || cmdline="${cmdline} console_cols=${cols}" 445 unset rows cols 446 447 # QEMU has no way to modify its exitcode; simulate it with a serial port. 448 # 449 # Choose to do it this way over writing a file to /host, because QEMU will 450 # initialize the 'exitcode' file for us, it avoids unnecessary writes to the 451 # host filesystem (which is normally not written to) and it allows us to 452 # communicate an exit code back in cases we do not have /host mounted. 453 # 454 if [ "$ARCH" == "i386" -o "$ARCH" == "x86_64" -o "$ARCH" == "x86" ]; then 455 # Assume we have hardware-accelerated virtualization support for amd64 456 qemu="qemu-system-x86_64 -machine pc,accel=kvm -cpu host" 457 458 # We know 'ttyS0' will be our serial port on x86 from the hard-coded 459 # '-serial mon:stdio' flag below 460 cmdline="$cmdline console=ttyS0" 461 462 # The assignment of 'ttyS1' here is magical; we know ttyS0 was used up 463 # by '-serial mon:stdio', and so this second serial port will be 'ttyS1' 464 cmdline="$cmdline exitcode=/dev/ttyS1" 465 elif [ "$ARCH" == "arm64" ]; then 466 # This uses a software model CPU, based on cortex-a57 467 qemu="qemu-system-aarch64 -machine virt -cpu cortex-a57" 468 469 # We know 'ttyAMA0' will be our serial port on arm64 from the hard-coded 470 # '-serial mon:stdio' flag below 471 cmdline="$cmdline console=ttyAMA0" 472 473 # The kernel will print messages via a virtual ARM serial port (ttyAMA0), 474 # but for command line consistency with x86, we put the exitcode serial 475 # port on the PCI bus, and it will be the only one. 476 cmdline="$cmdline exitcode=/dev/ttyS0" 477 fi 478 479 $qemu >&2 -name net_test -m 512 \ 480 -kernel $KERNEL_BINARY \ 481 -no-user-config -nodefaults -no-reboot \ 482 -display none -nographic -serial mon:stdio -parallel none \ 483 -smp 4,sockets=4,cores=1,threads=1 \ 484 -device virtio-rng-pci \ 485 -chardev file,id=exitcode,path=exitcode \ 486 -device pci-serial,chardev=exitcode \ 487 -fsdev local,security_model=mapped-xattr,id=fsdev0,fmode=0644,dmode=0755,path=$SCRIPT_DIR \ 488 -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=host \ 489 $blockdevice $netconfig -append "$cmdline" 490 [[ -s exitcode ]] && exitcode=`cat exitcode | tr -d '\r'` || exitcode=1 491 rm -f exitcode 492fi 493 494# UML reliably screws up the ptys, QEMU probably can as well... 495fixup_ptys 496stty sane || : 497tput smam || : 498 499echo "Returning exit code ${exitcode}." 1>&2 500exit "${exitcode}" 501