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