#!/bin/bash # Copyright (c) 2020-2022 Arm Limited # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. source $(dirname "$0")/paths.sh function skip_instruction { local SKIP_ADDRESS=$1 local SKIP_SIZE=$2 # Parse the ASM instruction from the address using gdb INSTR=$($GDB $AXF_FILE --batch -ex "disassemble $SKIP_ADDRESS" | grep "^ *$SKIP_ADDRESS" | sed "s/.*:[ \t]*\(.*\)$/\1/g") # Parse the C line from the address using gdb LINE=$($GDB $AXF_FILE --batch -ex "info line *$SKIP_ADDRESS" | sed "s/Line \([0-9]*\).*\"\(.*\)\".*/\2:\1/g") # Sometimes an address is in the middle of a 4 byte instruction. In that case # don't run the test if test "$INSTR" == ""; then return fi # Print out the meta-info about the test, in YAML echo "- skip_test:" echo " addr: $SKIP_ADDRESS" echo " asm: \"$INSTR\"" echo " line: \"$LINE\"" echo " skip: $SKIP_SIZE" # echo -ne "$SKIP_ADDRESS | $INSTR...\t" cat >commands.gdb <<EOF target remote localhost: 1234 file $AXF_FILE b boot_go_for_image_id if image_id == 0 continue delete breakpoints 1 b *$SKIP_ADDRESS continue& eval "shell sleep 0.5" interrupt if \$pc == $SKIP_ADDRESS echo "Stopped at breakpoint" else echo "Failed to stop at breakpoint" end echo "PC before increase:" print \$pc set \$pc += $SKIP_SIZE echo "PC after increase:" print \$pc detach eval "shell sleep 0.5" EOF echo -n '.' 1>&2 # start qemu, dump the serial output to $QEMU_LOG_FILE QEMU_LOG_FILE=qemu.log QEMU_PID_FILE=qemu_pid.txt rm -f $QEMU_PID_FILE $QEMU_LOG_FILE /usr/bin/qemu-system-arm \ -M mps2-an521 \ -s -S \ -kernel $AXF_FILE \ -device loader,file=$TFM_IMAGE_PATH,addr=0x10080000 \ -chardev file,id=char0,path=$QEMU_LOG_FILE \ -serial chardev:char0 \ -display none \ -pidfile $QEMU_PID_FILE \ -daemonize # start qemu, skip the instruction, and continue execution $GDB < ./commands.gdb &>gdb_out.txt # kill qemu kill -9 `cat $QEMU_PID_FILE` # If "Secure image initializing" is seen the TFM booted, which means that a skip # managed to defeat the signature check. Write out whether the image booted or # not to the log in YAML if cat $QEMU_LOG_FILE | grep -i "Starting bootloader" &>/dev/null; then # bootloader started successfully if cat gdb_out.txt | grep -i "Stopped at breakpoint" &>/dev/null; then # The target was stopped at the desired address if cat $QEMU_LOG_FILE | grep -i "Secure image initializing" &>/dev/null; then echo " test_exec_ok: True" echo " skipped: True" echo " boot: True" #print the address that was skipped, and some context to the console echo "" 1>&2 echo "Boot success: address: $SKIP_ADDRESS skipped: $SKIP_SIZE" 1>&2 arm-none-eabi-objdump -d $AXF_FILE --start-address=$SKIP_ADDRESS -S | tail -n +7 | head -n 14 1>&2 echo "" 1>&2 echo "" 1>&2 else LAST_LINE=`tail -n 1 $QEMU_LOG_FILE | tr -dc '[:print:]'` echo " test_exec_ok: True" echo " skipped: True" echo " boot: False" echo " last_line: \"$LAST_LINE\" " fi else # The target was not stopped at the desired address. # The most probable reason is that the instruction for that address is # on a call path that is not taken in this run (e.g. error handling) if cat $QEMU_LOG_FILE | grep -i "Secure image initializing" &>/dev/null; then # The image booted, although it shouldn't happen as the test is to # be run with a corrupt image. echo " test_exec_ok: False" echo " test_exec_fail_reason: \"No instructions were skipped (e.g. branch was not executed), but booted successfully\"" else # the execution didn't stop at the address (e.g. the instruction # is on a branch that is not taken) echo " test_exec_ok: True" echo " skipped: False" fi fi else # failed before the first printout echo " test_exec_ok: True" echo " skipped: True" echo " boot: False" echo " last_line: 'N/A' " fi } # Inform how the script is used usage() { echo "$0 <image_dir> <start_addr> [<end_addr>] [(-s | --skip) <skip_len>]" } #defaults SKIP=2 AXF_FILE=${BOOTLOADER_AXF_PATH} GDB=gdb-multiarch BOOTLOADER=true # Parse arguments while [[ $# -gt 0 ]]; do case $1 in -s|--skip) SKIP="$2" shift shift ;; -h|--help) usage exit 0 ;; *) if test -z "$IMAGE_DIR"; then IMAGE_DIR=$1 elif test -z "$START"; then START=$1 elif test -z "$END"; then END=$1 else usage exit 1 fi shift ;; esac done # Check that image directory, start and end address have been supplied if test -z "$IMAGE_DIR"; then usage exit 2 fi if test -z "$START"; then usage exit 2 fi if test -z "$END"; then END=$START fi if test -z "$SKIP"; then SKIP='2' fi # Create the start-end address range (step 2) ADDRS=$(printf '0x%x\n' $(seq "$START" 2 "$END")) # For each address run the skip_instruction function on it for ADDR in $ADDRS; do skip_instruction $ADDR $SKIP done