1#!/bin/sh
2#
3# SPDX-License-Identifier: BSD-3-Clause
4#
5# Copyright © 2019 Keith Packard
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10#
11# 1. Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13#
14# 2. Redistributions in binary form must reproduce the above
15#    copyright notice, this list of conditions and the following
16#    disclaimer in the documentation and/or other materials provided
17#    with the distribution.
18#
19# 3. Neither the name of the copyright holder nor the names of its
20#    contributors may be used to endorse or promote products derived
21#    from this software without specific prior written permission.
22#
23# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34# OF THE POSSIBILITY OF SUCH DAMAGE.
35#
36
37qemu="qemu-system-riscv32"
38
39# select the program
40elf="$1"
41shift
42
43archstring=""
44# If readelf is installed, we can use the .riscv.attributes section
45# in the file to determine required extensions that we need in QEMU.
46# This will give us a string such as "rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
47if command -v readelf >/dev/null 2>/dev/null; then
48    # LLVM-based toolchains readelf produces different output from GNU, so
49    # check --version output first.
50    if readelf --version | grep LLVM >/dev/null; then
51        archstring=$(readelf --arch-specific "$elf" | grep "Value: rv" | cut -d: -f2)
52    else
53        archstring=$(readelf --arch-specific "$elf" | grep Tag_RISCV_arch | cut -d: -f2 | tr -d '"')
54    fi
55fi
56if [ -z "$archstring" ]; then
57    echo "Could not determine architecture for $elf, is readelf installed?" >&2
58    exit 1
59fi
60if [ "${archstring#*rv64}" != "$archstring" ]; then
61    qemu="qemu-system-riscv64"
62    cpu="rv64"
63elif [ "${archstring#*rv32}" != "$archstring" ]; then
64    qemu="qemu-system-riscv32"
65    cpu="rv32"
66else
67    echo "Unknown architecture for $elf: $archstring" >&2
68    exit 1
69fi
70# The I extension is always required, and since it's the first one there is
71# no preceding _ to match on. Just assume it's always there
72options="i"
73test "${archstring#*_m}" != "$archstring" && options="$options m"
74test "${archstring#*_a}" != "$archstring" && options="$options a"
75test "${archstring#*_f}" != "$archstring" && options="$options f"
76test "${archstring#*_d}" != "$archstring" && options="$options d"
77test "${archstring#*_c}" != "$archstring" && options="$options c"
78
79cpu="$cpu,mmu=false,pmp=false"
80
81if [ -z "$options" ]; then
82    options="$(echo "$elf" | sed 's/.*rv[36][24]\([a-z]*\)_.*$/\1/' | sed 's/\(.\)/\1 /g')"
83fi
84
85all_options="i e g m a f d c s u"
86
87if $qemu --version | grep -q 'version [789]'; then
88    all_options="h $all_options"
89fi
90
91if $qemu --version | grep -q 'version [89]'; then
92    if $qemu --version | grep -q 'version 8.1'; then
93        all_options="Zawrs $all_options"
94    else
95        all_options="zawrs $all_options"
96    fi
97fi
98
99if $qemu --version | grep -q 'version \(8.[1-9]\|9\)'; then
100    all_options="Zfa $all_options"
101fi
102
103for o in $all_options; do
104    if echo "$options" | grep -q "$o"; then
105	value=true
106    else
107	value=false
108    fi
109    cpu="$cpu,$o=$value"
110done
111
112# Set the target machine
113machine=virt,accel=tcg
114
115# Map stdio to a multiplexed character device so we can use it
116# for the monitor and semihosting output
117
118chardev=stdio,mux=on,id=stdio0
119
120# Point the semihosting driver at our new chardev
121
122cmdline="program-name"
123input=""
124done=0
125
126while [ "$done" != "1" ]; do
127    case "$1" in
128        --)
129            shift
130            done=1
131            ;;
132        -s|"")
133            done=1
134            ;;
135        *)
136            cmdline="$cmdline $1"
137            case "$input" in
138                "")
139                    input="$1"
140                    ;;
141                *)
142                    input="$input $1"
143                    ;;
144            esac
145            shift
146            ;;
147    esac
148done
149
150semi=enable=on,chardev=stdio0,arg="$cmdline"
151
152# Point the monitor at the new chardev too
153
154mon=none
155
156# Disable the serial port
157
158serial=none
159
160echo "$input" | $qemu -chardev $chardev -semihosting-config "$semi" -monitor "$mon" -serial "$serial" -machine "$machine" -cpu "$cpu" -kernel "$elf" -nographic "$@" -bios none
161