aboutsummaryrefslogtreecommitdiff
path: root/src/baremetal/run.sh
blob: 5f812fab5db43d66fa68d469b43e367bd0d3575d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/bin/bash
# Build a freestanding microcore and boot it on qemu-system-*.
#   src/baremetal/run.sh ready riscv|aarch64
#   src/baremetal/run.sh riscv|aarch64 run    examples/hello.zc
#   src/baremetal/run.sh riscv|aarch64 io     examples/echo.zc
#   src/baremetal/run.sh riscv|aarch64 compile examples/hello.zc
# run:    the program is compiled into the image. nothing is read.
# io:     the same image, but stdin is the program's input and 0x04 ends it.
# compile: the default compiler genome (build/core/genome.bin, from make genome). the source, a backtick, and
#          0x04 are written to the UART. the backtick ends the unit; the
#          0x04 is what eof() waits for after that.
# Prints the program's UART output and nothing else. Exits 0 on a clean
# power-off. A missing toolchain exits 0 from `ready` and 1 otherwise. the cross
# compilers need c23 #embed (gcc 15 or later) to take in the genome.
set -euo pipefail
case "${1:-}" in
    --help|-h) printf '%s\n' 'usage: src/baremetal/run.sh ready riscv|aarch64' '       src/baremetal/run.sh riscv|aarch64 run|io|compile FILE.zc' 'builds a freestanding microcore and boots it in qemu-system.' 'run compiles the program into the image; io also forwards program input.' 'compile sends source to the resident compiler over the uart.' 'ready checks for the cross compiler and emulator.'; exit 0 ;;
    -*) echo "baremetal run: unknown option $1; use --help for usage" >&2; exit 2 ;;
esac
cd "$(dirname "$0")/../.."

ready() {
	case "$1" in
	riscv) command -v riscv64-linux-gnu-gcc >/dev/null && command -v qemu-system-riscv64 >/dev/null ;;
	aarch64) command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-system-aarch64 >/dev/null ;;
	*) return 1 ;;
	esac
}

if [ "${1:-}" = ready ]; then
	ready "${2:-}"
	exit $?
fi
if [ $# -ne 3 ]; then
	echo "usage: src/baremetal/run.sh riscv|aarch64 run|io|compile FILE" >&2
	exit 2
fi
target=$1 mode=$2 src=$3
ready "$target" || { echo "baremetal: missing toolchain for $target" >&2; exit 1; }
[ -f "$src" ] || { echo "baremetal: no $src" >&2; exit 1; }

case "$target" in
riscv)
	cc=riscv64-linux-gnu-gcc
	qemu=qemu-system-riscv64
	cflags="-march=rv64gc -mabi=lp64d -mcmodel=medany -mno-relax"
	asm=src/baremetal/start_riscv.S
	ld=src/baremetal/link_riscv.ld
	def=-DBARE_RISCV
	qargs=(-machine virt -bios none -cpu rv64 -m 64)
	;;
aarch64)
	cc=aarch64-linux-gnu-gcc
	qemu=qemu-system-aarch64
	cflags="-march=armv8-a"
	asm=src/baremetal/start_aarch64.S
	ld=src/baremetal/link_aarch64.ld
	def=-DBARE_AARCH64
	qargs=(-machine virt -cpu cortex-a57 -m 64 -semihosting-config enable=on,target=native)
	;;
*) echo "baremetal: unknown target $target" >&2; exit 2 ;;
esac

D=$(mktemp -d)
trap 'rm -rf "$D"' EXIT
common="$cflags -ffreestanding -fno-pic -fno-pie -fno-builtin -fno-unwind-tables -fno-asynchronous-unwind-tables -O2 -Wall -Wextra"

if [ "$mode" = run ] || [ "$mode" = io ]; then
	# the map is sized to the program, so a bigger runtime or program just gets a bigger board
	${CC:-$(sh src/tools/findcc.sh)} -O2 -w src/tools/genmap.c -o "$D/genmap"
	GENMAP="$D/genmap" bash src/tools/automap.sh run "$D/img" src/runtime/run.zc "$src" >"$D/genmap.log" 2>&1 || { cat "$D/genmap.log" >&2; exit 1; }
	cp src/micro/micro.c src/micro/micro_port.h src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h src/core/genome.h "$D/img/"
	"$cc" $common $def -I"$D/img" -c "$D/img/micro.c" -o "$D/micro.o"
	if [ "$mode" = io ]; then
		"$cc" $common $def -DBARE_INPUT=1 -I"$D/img" -c src/baremetal/port.c -o "$D/port.o"
		timeout=60
	else
		"$cc" $common $def -DBARE_INPUT=0 -I"$D/img" -c src/baremetal/port.c -o "$D/port.o"
		timeout=120
	fi
elif [ "$mode" = compile ]; then
	[ -f build/core/genome.bin ] || { echo "baremetal: no build/core/genome.bin; run make genome first" >&2; exit 1; }
	cp src/micro/micro.c src/micro/micro_port.h build/core/map/memmap.h build/core/genome.bin src/core/genome.h src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h "$D/"
	"$cc" $common $def -I"$D" -c "$D/micro.c" -o "$D/micro.o"
	"$cc" $common $def -DBARE_INPUT=1 -I"$D" -c src/baremetal/port.c -o "$D/port.o"
	timeout=90
else
	echo "baremetal: mode is run, io or compile" >&2
	exit 2
fi
"$cc" $common $def -c "$asm" -o "$D/start.o"
"$cc" $common $def -nostdlib -static -Wl,--build-id=none -Wl,--no-warn-rwx-segments -T "$ld" -o "$D/bare.elf" "$D/start.o" "$D/port.o" "$D/micro.o"
if [ "${BARE_SIZE:-}" = 1 ]; then
	size=$(wc -c < "$D/bare.elf")
	text=$(size "$D/bare.elf" | awk 'NR==2{print $1+$2}')
	bss=$(size "$D/bare.elf" | awk 'NR==2{print $3}')
	echo "elf $size bytes, text+data $text, bss $bss" >&2
fi

# -serial stdio is the UART. The monitor stays off so it cannot eat bytes.
runq() {
	timeout "$timeout" "$qemu" "${qargs[@]}" -nographic -monitor none -serial stdio -kernel "$D/bare.elf"
}
if [ "$mode" = compile ]; then
	(cat "$src"; printf '`\004') | runq
elif [ "$mode" = io ]; then
	(cat; printf '\004') | runq
else
	runq </dev/null
fi