1#!/bin/bash
2
3# Copyright (c) 2020-2022 Arm Limited
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17function skip_instruction {
18
19    local SKIP_ADDRESS=$1
20    local SKIP_SIZE=$2
21
22    # Parse the ASM instruction from the address using gdb
23    INSTR=$($GDB $AXF_FILE --batch -ex "disassemble $SKIP_ADDRESS" | grep "^ *$SKIP_ADDRESS" | sed "s/.*:[ \t]*\(.*\)$/\1/g")
24    # Parse the C line from the address using gdb
25    LINE=$($GDB $AXF_FILE --batch -ex "info line *$SKIP_ADDRESS" | sed "s/Line \([0-9]*\).*\"\(.*\)\".*/\2:\1/g")
26
27    # Sometimes an address is in the middle of a 4 byte instruction. In that case
28    # don't run the test
29    if test "$INSTR" == ""; then
30        return
31    fi
32
33    # Print out the meta-info about the test, in YAML
34    echo "- skip_test:"
35    echo "    addr: $SKIP_ADDRESS"
36    echo "    asm:  \"$INSTR\""
37    echo "    line: \"$LINE\""
38    echo "    skip: $SKIP_SIZE"
39    # echo -ne "$SKIP_ADDRESS | $INSTR...\t"
40
41    cat >commands.gdb <<EOF
42target remote localhost: 1234
43file $IMAGE_DIR/bl2.axf
44b boot_go_for_image_id if image_id == 0
45continue
46delete breakpoints 1
47b *$SKIP_ADDRESS
48continue&
49eval "shell sleep 0.5"
50interrupt
51if \$pc == $SKIP_ADDRESS
52    echo "Stopped at breakpoint"
53else
54    echo "Failed to stop at breakpoint"
55end
56echo "PC before increase:"
57print \$pc
58set \$pc += $SKIP_SIZE
59echo "PC after increase:"
60print \$pc
61detach
62eval "shell sleep 0.5"
63EOF
64
65    echo -n '.' 1>&2
66
67    # start qemu, dump the serial output to $QEMU_LOG_FILE
68    QEMU_LOG_FILE=qemu.log
69    QEMU_PID_FILE=qemu_pid.txt
70    rm -f $QEMU_PID_FILE $QEMU_LOG_FILE
71    /usr/bin/qemu-system-arm \
72        -M mps2-an521 \
73        -s -S \
74        -kernel $IMAGE_DIR/bl2.axf \
75        -device loader,file=$IMAGE_DIR/tfm_s_ns_signed.bin,addr=0x10080000 \
76        -chardev file,id=char0,path=$QEMU_LOG_FILE \
77        -serial chardev:char0 \
78        -display none \
79        -pidfile $QEMU_PID_FILE \
80        -daemonize
81
82    # start qemu, skip the instruction, and continue execution
83    $GDB < ./commands.gdb &>gdb_out.txt
84
85    # kill qemu
86    kill -9 `cat $QEMU_PID_FILE`
87
88    # If "Secure image initializing" is seen the TFM booted, which means that a skip
89    # managed to defeat the signature check. Write out whether the image booted or
90    # not to the log in YAML
91    if cat $QEMU_LOG_FILE | grep -i "Starting bootloader" &>/dev/null; then
92        # bootloader started successfully
93        if cat gdb_out.txt | grep -i "Stopped at breakpoint" &>/dev/null; then
94            # The target was stopped at the desired address
95            if cat $QEMU_LOG_FILE | grep -i "Secure image initializing" &>/dev/null; then
96                echo "    test_exec_ok: True"
97                echo "    skipped: True"
98                echo "    boot: True"
99
100                #print the address that was skipped, and some context to the console
101                echo "" 1>&2
102                echo "Boot success: address: $SKIP_ADDRESS skipped: $SKIP_SIZE" 1>&2
103                arm-none-eabi-objdump -d $IMAGE_DIR/bl2.axf --start-address=$SKIP_ADDRESS -S | tail -n +7 | head -n 14 1>&2
104                echo "" 1>&2
105                echo "" 1>&2
106            else
107                LAST_LINE=`tail -n 1 $QEMU_LOG_FILE | tr -dc '[:print:]'`
108                echo "    test_exec_ok: True"
109                echo "    skipped: True"
110                echo "    boot: False"
111                echo "    last_line: \"$LAST_LINE\" "
112            fi
113        else
114            # The target was not stopped at the desired address.
115            # The most probable reason is that the instruction for that address is
116            # on a call path that is not taken in this run (e.g. error handling)
117            if cat $QEMU_LOG_FILE | grep -i "Secure image initializing" &>/dev/null; then
118                # The image booted, although it shouldn't happen as the test is to
119                # be run with a corrupt image.
120                echo "    test_exec_ok: False"
121                echo "    test_exec_fail_reason: \"No instructions were skipped (e.g. branch was not executed), but booted successfully\""
122            else
123                # the execution didn't stop at the address (e.g. the instruction
124                # is on a branch that is not taken)
125                echo "    test_exec_ok: True"
126                echo "    skipped: False"
127            fi
128        fi
129    else
130        # failed before the first printout
131        echo "    test_exec_ok: True"
132        echo "    skipped: True"
133        echo "    boot: False"
134        echo "    last_line: 'N/A' "
135    fi
136}
137
138# Inform how the script is used
139usage() {
140    echo "$0 <image_dir> <start_addr> [<end_addr>] [(-s | --skip) <skip_len>]"
141}
142
143#defaults
144SKIP=2
145BIN_DIR=$(pwd)/install/outputs
146AXF_FILE=$BIN_DIR/bl2.axf
147GDB=gdb-multiarch
148BOOTLOADER=true
149
150# Parse arguments
151while [[ $# -gt 0 ]]; do
152    case $1 in
153        -s|--skip)
154        SKIP="$2"
155        shift
156        shift
157        ;;
158        -h|--help)
159        usage
160        exit 0
161        ;;
162        *)
163        if test -z "$IMAGE_DIR"; then
164            IMAGE_DIR=$1
165        elif test -z "$START"; then
166            START=$1
167        elif test -z "$END"; then
168            END=$1
169        else
170            usage
171            exit 1
172        fi
173        shift
174        ;;
175    esac
176done
177
178# Check that image directory, start and end address have been supplied
179if test -z "$IMAGE_DIR"; then
180    usage
181    exit 2
182fi
183
184if test -z "$START"; then
185    usage
186    exit 2
187fi
188
189if test -z "$END"; then
190    END=$START
191fi
192
193if test -z "$SKIP"; then
194    SKIP='2'
195fi
196
197# Create the start-end address range (step 2)
198ADDRS=$(printf '0x%x\n' $(seq "$START" 2 "$END"))
199
200# For each address run the skip_instruction function on it
201for ADDR in $ADDRS; do
202    skip_instruction $ADDR $SKIP
203done
204