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    all_options="Zawrs $all_options"
93fi
94
95for o in $all_options; do
96    if echo "$options" | grep -q "$o"; then
97	value=true
98    else
99	value=false
100    fi
101    cpu="$cpu,$o=$value"
102done
103
104# Set the target machine
105machine=virt,accel=tcg
106
107# Map stdio to a multiplexed character device so we can use it
108# for the monitor and semihosting output
109
110chardev=stdio,mux=on,id=stdio0
111
112# Point the semihosting driver at our new chardev
113
114semi=enable=on,chardev=stdio0
115
116input=""
117
118case "$1" in
119    --)
120	semi="$semi",arg="$2"
121	shift
122	shift
123	;;
124    -*|"")
125	;;
126    *)
127	semi="$semi",arg="$1"
128	input="$1"
129	shift
130	;;
131esac
132
133# Point the monitor at the new chardev too
134
135mon=none
136
137# Disable the serial port
138
139serial=none
140
141echo "$input" | $qemu -chardev $chardev -semihosting-config "$semi" -monitor "$mon" -serial "$serial" -machine "$machine" -cpu "$cpu" -kernel "$elf" -nographic "$@" -bios none
142