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