1#!/bin/bash
2
3# Copyright 2017 The Fuchsia Authors
4#
5# Use of this source code is governed by a MIT-style
6# license that can be found in the LICENSE file or at
7# https://opensource.org/licenses/MIT
8
9# Runs a single instance of the early boot entropy collection test.
10#
11# This script assumes that Zircon has already been built to support entropy quality tests (i.e with
12# the ENABLE_ENTROPY_COLLECTOR_TEST flag set at compile time). It handles starting Zircon either in
13# qemu or via netboot, then it extracts the entropy file once the boot-time test completes. Usually
14# you will call a "test driver" script that in turn calls this script, rather than calling this
15# script directly.
16#
17# Where sensible, the option flags agree with scripts/run-zircon.  The -a, -m, and -s command line
18# options are mandatory; the rest are optional. The output directory must also be passed on the
19# command line, as a positional argument (i.e. without a '-?' type flag).
20#
21# This script saves the captured entropy in a file named 'entropy.#########.bin' (to facilitate
22# running repeated tests with the same output directory). It also saves a metadata file, named
23# 'entropy.#########.meta', recording certain test parameters. See the source below for more
24# details.
25#
26# BUGS: netboot isn't supported yet, only qemu.
27
28set -e -u
29CDPATH=
30ZIRCONDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )"
31
32function HELP {
33    echo "$0 [options] output-dir/" >&2
34    echo "options:" >&2
35    echo "-a <arch>   : arm64 or x86" >&2
36    echo "-c <word>   : add word to kernel cmdline" >&2
37    echo "-l <len>    : entropy test length (bytes to collect)" >&2
38    echo "-m <method> : netboot or qemu" >&2
39    echo "-n <name>   : Zircon nodename to use" >&2
40    echo "-o <dir>    : build directory" >&2
41    echo "-s <source> : hwrng or jitterentropy" >&2
42    exit 1
43}
44
45ARCH=
46BUILDDIR=
47CMDLINE=""
48LEN=
49METHOD=
50NODENAME=
51OUTDIR=
52SRC=
53
54while getopts "a:c:hl:m:n:o:s:" FLAG; do
55    case "$FLAG" in
56        a) ARCH="$OPTARG";;
57        c) CMDLINE+="$OPTARG ";;
58        h) HELP;;
59        l) LEN="$OPTARG";;
60        m) METHOD="$OPTARG";;
61        n) NODENAME="$OPTARG";;
62        o) BUILDDIR="$OPTARG";;
63        s) SRC="$OPTARG";;
64        \?)
65            echo "unrecognized option" >&2
66            HELP
67            ;;
68    esac
69done
70shift $((OPTIND-1))
71
72if [[ "$#" -ne "1" ]]; then
73    echo "missing output dir" >&2
74    HELP
75fi
76OUTDIR="$1"
77shift
78
79# check required args
80if [[ -z $ARCH ]]; then
81    echo "must specify arch" >&2
82    HELP
83fi
84if [[ -z $METHOD ]]; then
85    echo "must specify method" >&2
86    HELP
87fi
88if [[ -z $SRC ]]; then
89    echo "must specify source" >&2
90    HELP
91fi
92if [[ -z $OUTDIR ]]; then
93    echo "must specify outdir" >&2
94    HELP
95fi
96
97# handle entropy-test specific cmdline args
98CMDLINE+="kernel.entropy-test.src=$SRC "
99if [[ -n $LEN ]]; then CMDLINE+="kernel.entropy-test.len=$LEN "; fi
100
101# find and read builddir
102PROJECT="$ARCH"
103
104if [[ -z "$BUILDDIR" ]]; then
105    if [[ -n $PROJECT ]]; then
106        BUILDDIR="$ZIRCONDIR/build-$PROJECT"
107    else
108        echo "could not autodetect builddir. use -o." >&2
109        HELP
110    fi
111fi
112
113if [[ ! -d $BUILDDIR || ! -x $BUILDDIR/tools/netcp ]]; then
114    echo "bad builddir: $BUILDDIR" >&2
115    HELP
116fi
117
118BUILDID="$(sed -n '/^#define\s\+BUILDID/{s/^[^"]*"//;s/".*$//;p;q}' "$BUILDDIR/config-buildid.h")"
119
120# set a few other variables
121if [[ -z $NODENAME ]]; then
122    if [[ $METHOD = "qemu" ]]; then
123        NODENAME="entropy-test-$(head -c16 /dev/urandom | xxd -p)";
124    elif [[ $METHOD = "netboot" ]]; then
125        echo "missing nodename - required for netboot" >&2
126        HELP
127    fi
128fi
129CMDLINE+="zircon.nodename=$NODENAME "
130NUM="$(cd "$OUTDIR";
131       find . \( -type f -name "entropy.*.bin" -print \) -o \( -type d ! -name . -prune \) |
132       sed 's/^.*entropy\.0*//;s/\.bin$//;s/^$/0/;' | grep '^[0-9]\+$' |
133       sort -nr | head -n 1)"
134if [[ -z $NUM ]]; then NUM=-1; fi
135NUM="$(printf "%09d" "$((NUM + 1))")"
136BINFILE="$OUTDIR/entropy.$NUM.bin"
137METAFILE="$OUTDIR/entropy.$NUM.meta"
138
139# launch zircon
140case "$METHOD" in
141    qemu)
142        "$ZIRCONDIR/scripts/run-zircon" -a "$ARCH" -c "$CMDLINE" -N -o "$BUILDDIR" </dev/null &
143        ;;
144    netboot)
145        "$BUILDDIR/tools/bootserver" --tftp -n "$NODENAME" \
146            "$BUILDDIR/zircon.bin" "$BUILDDIR/bootdata.bin" \
147            -- "$CMDLINE" >/dev/null 2>/dev/null &
148        BOOTSERVER_PID=$!
149        ;;
150    *)
151        echo "unrecognized method: $METHOD" >&2
152        HELP
153        ;;
154esac
155echo "launched zircon with nodename $NODENAME" >&2
156
157# wait for zircon to send us the entropy file
158while true; do
159    sleep 5;
160    if "$BUILDDIR"/tools/netcp --nowait --timeout=1000 \
161        "$NODENAME:/boot/kernel/debug/entropy.bin" "$BINFILE" &&
162        [[ -z $LEN ||  $(wc -c <"$BINFILE") -eq $LEN ]]
163    then
164        echo "Finished test on nodename $NODENAME" >&2
165        break
166    fi
167done
168
169# write the meta file
170{
171    echo "entropy-test $METHOD"
172    echo "date         $(date +"%Y-%m-%d %H:%M:%S %Z")"
173    echo "buildid      $BUILDID"
174    echo "arch         $ARCH"
175    echo "source       $SRC"
176    echo "len          $LEN"
177    echo "cmdline      $CMDLINE"
178} >$METAFILE
179
180# reset the test device
181case "$METHOD" in
182    qemu)
183        while "$BUILDDIR"/tools/netaddr --nowait --timeout=1000 \
184                "$NODENAME" >/dev/null 2>/dev/null; do
185            "$BUILDDIR"/tools/netruncmd --nowait --timeout=1000 "$NODENAME" dm shutdown
186            sleep 2
187        done
188        ;;
189    netboot)
190        kill "$BOOTSERVER_PID"
191        sleep 1 # make sure bootserver actually has time to shut down
192        while "$BUILDDIR"/tools/netaddr --nowait --timeout=1000 \
193                "$NODENAME" >/dev/null 2>/dev/null; do
194            "$BUILDDIR"/tools/netruncmd --nowait --timeout=1000 "$NODENAME" dm reboot
195            sleep 2
196        done
197        ;;
198esac
199