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