Skip to content

Commit 0f8bb9b

Browse files
committed
ports/cheriot-rtos: Working Sonata port (uncompartmentalised).
Signed-off-by: Duncan Lowther <[email protected]>
1 parent 4c83449 commit 0f8bb9b

File tree

17 files changed

+515
-3
lines changed

17 files changed

+515
-3
lines changed

ports/cheriot-rtos/Makefile

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
include ../../py/mkenv.mk
2+
3+
CROSS = 0
4+
5+
# qstr definitions (must come before including py.mk)
6+
QSTR_DEFS = qstrdefsport.h
7+
8+
# MicroPython feature configurations
9+
MICROPY_ROM_TEXT_COMPRESSION ?= 0
10+
11+
# include py core make definitions
12+
include $(TOP)/py/py.mk
13+
14+
CLLVM_DIR ?= ~/cheriot-llvm/
15+
CRTOS_DIR ?= ../../../cheriot-rtos
16+
17+
CROSS_COMPILE = $(CLLVM_DIR)bin/
18+
AS = $(CROSS_COMPILE)clang
19+
CC = $(CROSS_COMPILE)clang
20+
CXX = $(CROSS_COMPILE)clang++
21+
LD = $(CROSS_COMPILE)ld.lld
22+
SYS_INC = -I$(CRTOS_DIR)/sdk/include/c++-config -I$(CRTOS_DIR)/sdk/include/libc++ -I$(CRTOS_DIR)/sdk/include -I$(CRTOS_DIR)/sdk/include/platform/sunburst -I$(CRTOS_DIR)/sdk/include/platform/generic-riscv
23+
# -I$(CRTOS_DIR)/sdk/include/platform/synopsis -I$(CRTOS_DIR)/sdk/include/platform/ibex
24+
25+
INC += -I.
26+
INC += -I$(TOP)
27+
INC += -I$(BUILD)
28+
29+
INC += $(SYS_INC)
30+
CFLAGS_CHERIOT_A7 = -target riscv32-unknown-unknown -mcpu=cheriot -mabi=cheriot -mxcheri-rvc -mrelax -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-builtin -fno-exceptions -fno-asynchronous-unwind-tables -fno-c++-static-destructors -fno-rtti
31+
CFLAGS += $(INC) -Wall -Werror -std=c99 -nostdlib $(CFLAGS_CHERIOT_A7) $(COPT) -DNO_ALLOCA_H -DCHERIOT_IBEX -DIBEX -DSUNBURST -DSUNBURST_SHADOW_BASE=0x30000000 -DSUNBURST_SHADOW_SIZE=0x4000 -DCPU_TIMER_HZ=33000000 -DTICK_RATE_HZ=100 -DCHERIOT_NO_AMBIENT_MALLOC -DDEVICE_EXISTS_shadow -DDEVICE_EXISTS_clint -DDEVICE_EXISTS_uart -DDEVICE_EXISTS_gpio -DDEVICE_EXISTS_gpio -DDEVICE_EXISTS_plic -DREVOKABLE_MEMORY_START=0x00100080 -cheri-compartment=mp_main
32+
LDFLAGS += --script=cheriot-a7-100t.ld --relax
33+
34+
CSUPEROPT = -Os # save some code space
35+
36+
# Tune for Debugging or Optimization
37+
CFLAGS += -g # always include debug info in the ELF
38+
ifeq ($(DEBUG), 1)
39+
CFLAGS += -O0
40+
else
41+
CFLAGS += -Os -DNDEBUG
42+
CFLAGS += -fdata-sections -ffunction-sections
43+
endif
44+
45+
# Flags for optional C++ source code
46+
CXXFLAGS += $(filter-out -std=c99,$(CFLAGS)) -std=c++20
47+
48+
LIBS =
49+
50+
SRC_C = \
51+
main.c \
52+
shared/libc/printf.c \
53+
shared/readline/readline.c \
54+
shared/runtime/pyexec.c \
55+
shared/runtime/stdout_helpers.c \
56+
$(BUILD)/_frozen_mpy.c \
57+
58+
SRC_CXX = uart_core.cpp
59+
60+
SRC_QSTR += shared/readline/readline.c shared/runtime/pyexec.c
61+
62+
OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
63+
OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o))
64+
OBJ += $(PY_CORE_O)
65+
66+
all: $(BUILD)/micropython.uf2
67+
68+
$(BUILD)/_frozen_mpy.c: $(TOP)/tests/frozen/frozentest.mpy $(BUILD)/genhdr/qstrdefs.generated.h
69+
$(ECHO) "MISC freezing bytecode"
70+
$(Q)$(TOP)/tools/mpy-tool.py -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h -mlongint-impl=none $< > $@
71+
72+
$(BUILD)/xmake.lua: xmake.lua.top xmake.lua.bottom
73+
cat xmake.lua.top > $@
74+
for OFILE in $(patsubst $(BUILD)/%,%,$(OBJ)) ; do echo " add_files(\"$$OFILE\", {rule = \"utils.merge.object\"})" >> $@ ; done
75+
cat xmake.lua.bottom >> $@
76+
77+
$(BUILD)/build/cheriot/cheriot/release/micropython: $(OBJ) $(BUILD)/xmake.lua
78+
cd $(BUILD) && xmake config --sdk=$(CLLVM_DIR) --board=sonata && xmake
79+
80+
#$(BUILD)/cpu0_iram.vhx: $(BUILD)/build/cheriot/cheriot/release/micropython
81+
# $(CLLVM_DIR)/bin/llvm-objcopy -O binary $< - | hexdump -v -e '"%08X" "\n"' > $@
82+
# echo >> $@
83+
84+
$(BUILD)/micropython.bin: $(BUILD)/build/cheriot/cheriot/release/micropython
85+
$(CLLVM_DIR)/bin/llvm-objcopy -O binary $< $@
86+
87+
$(BUILD)/micropython.uf2: $(BUILD)/micropython.bin
88+
~/.cargo/bin/uf2conv $< --base 0x00101000 --output $@
89+
90+
include $(TOP)/py/mkrules.mk

ports/cheriot-rtos/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# The minimal port
2+
3+
This port is intended to be a minimal MicroPython port that actually runs.
4+
It can run under Linux (or similar) and on any STM32F4xx MCU (eg the pyboard).
5+
6+
## Building and running Linux version
7+
8+
By default the port will be built for the host machine:
9+
10+
$ make
11+
12+
To run the executable and get a basic working REPL do:
13+
14+
$ make run
15+
16+
## Building for an STM32 MCU
17+
18+
The Makefile has the ability to build for a Cortex-M CPU, and by default
19+
includes some start-up code for an STM32F4xx MCU and also enables a UART
20+
for communication. To build:
21+
22+
$ make CROSS=1
23+
24+
If you previously built the Linux version, you will need to first run
25+
`make clean` to get rid of incompatible object files.
26+
27+
Building will produce the build/firmware.dfu file which can be programmed
28+
to an MCU using:
29+
30+
$ make CROSS=1 deploy
31+
32+
This version of the build will work out-of-the-box on a pyboard (and
33+
anything similar), and will give you a MicroPython REPL on UART1 at 9600
34+
baud. Pin PA13 will also be driven high, and this turns on the red LED on
35+
the pyboard.
36+
37+
## Building without the built-in MicroPython compiler
38+
39+
This minimal port can be built with the built-in MicroPython compiler
40+
disabled. This will reduce the firmware by about 20k on a Thumb2 machine,
41+
and by about 40k on 32-bit x86. Without the compiler the REPL will be
42+
disabled, but pre-compiled scripts can still be executed.
43+
44+
To test out this feature, change the `MICROPY_ENABLE_COMPILER` config
45+
option to "0" in the mpconfigport.h file in this directory. Then
46+
recompile and run the firmware and it will execute the frozentest.py
47+
file.

ports/cheriot-rtos/cheriintrin.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <cheri-builtins.h>
2+
3+
#define cheri_length_get(foo) cgetlen(foo)
4+
#define cheri_perms_get(foo) cgetperms(foo)
5+
#define cheri_type_get(foo) cgettype(foo)
6+
#define cheri_tag_get(foo) cgettag(foo)
7+
#define cheri_offset_get(foo) cgetoffset(foo)
8+
#define cheri_offset_set(a, b) csetoffset((a), (b))
9+
#define cheri_address_get(foo) cgetaddr(foo)
10+
#define cheri_address_set(a, b) csetaddr((a), (b))
11+
#define cheri_base_get(foo) cgetbase(foo)
12+
#define cheri_perms_and(a, b) candperms((a), (b))
13+
#define cheri_seal(a, b) cseal((a), (b))
14+
#define cheri_unseal(a, b) cunseal((a), (b))
15+
#define cheri_bounds_set(a, b) csetbounds((a), (b))
16+
#define cheri_bounds_set_exact(a, b) csetboundsext((a), (b))
17+

ports/cheriot-rtos/main.c

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#include <stdint.h>
2+
#include <stdio.h>
3+
#include <string.h>
4+
#include <compartment.h>
5+
6+
#include "py/builtin.h"
7+
#include "py/compile.h"
8+
#include "py/runtime.h"
9+
#include "py/repl.h"
10+
#include "py/gc.h"
11+
#include "py/mperrno.h"
12+
#include "shared/runtime/pyexec.h"
13+
14+
#if MICROPY_ENABLE_COMPILER
15+
void do_str(const char *src, mp_parse_input_kind_t input_kind) {
16+
nlr_buf_t nlr;
17+
if (nlr_push(&nlr) == 0) {
18+
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
19+
qstr source_name = lex->source_name;
20+
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
21+
mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true);
22+
mp_call_function_0(module_fun);
23+
nlr_pop();
24+
} else {
25+
// uncaught exception
26+
mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
27+
}
28+
}
29+
#endif
30+
31+
static char *stack_top;
32+
#if MICROPY_ENABLE_GC
33+
static char heap[16384]; //[MICROPY_HEAP_SIZE];
34+
#endif
35+
36+
#include <stdio.h>
37+
38+
void __cheri_compartment("mp_main") mp_main(void) {
39+
MP_STATE_THREAD_HACK_INIT
40+
*MMIO_CAPABILITY(uint32_t, gpio) = 0xaa;
41+
printf("Test\n");
42+
int stack_dummy;
43+
stack_top = (char *)&stack_dummy;
44+
#if MICROPY_ENABLE_GC
45+
gc_init(heap, heap + sizeof(heap));
46+
printf("GC initialised\n");
47+
#endif
48+
mp_init();
49+
printf("Micropython initialised\n");
50+
#if MICROPY_ENABLE_COMPILER
51+
#if MICROPY_REPL_EVENT_DRIVEN
52+
pyexec_event_repl_init();
53+
for (;;) {
54+
int c = mp_hal_stdin_rx_chr();
55+
if (pyexec_event_repl_process_char(c)) {
56+
break;
57+
}
58+
}
59+
#else
60+
pyexec_friendly_repl();
61+
#endif
62+
// do_str("print('hello world!', list(x+1 for x in range(10)), end='eol\\n')", MP_PARSE_SINGLE_INPUT);
63+
// do_str("for i in range(10):\r\n print(i)", MP_PARSE_FILE_INPUT);
64+
#else
65+
#error "We don't want to be using frozen modules"
66+
pyexec_frozen_module("frozentest.py", false);
67+
#endif
68+
mp_deinit();
69+
}
70+
71+
enum ErrorRecoveryBehaviour compartment_error_handler(struct ErrorState *frame, size_t mcause, size_t mtval) {
72+
static const char * const cheri_exc_code[32] = { "NONE", "BOUNDS", "TAG", "SEAL" , "04" , "05" , "06", "07",
73+
"08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
74+
"10", "EXEC", "LOAD", "STORE", "14", "STCAP", "STLOC", "17",
75+
"SYSREG", "19", "1a", "1b", "1c", "1d", "1e", "1f" };
76+
static const char * const reg_names[16] = { "zr", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
77+
"s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5" };
78+
79+
if(mcause == 0x1c) {
80+
printf("CHERI fault (type %s):\n", cheri_exc_code[mtval & 0x1f]);
81+
printf("\tat %p\n", frame->pcc);
82+
if(mtval & 0x400) { // S=1, special register
83+
switch(mtval >> 5) {
84+
case 0x20: printf("\ton PCC\n"); break;
85+
case 0x3c: printf("\ton MTCC\n"); break;
86+
case 0x3d: printf("\ton MTDC\n"); break;
87+
case 0x3e: printf("\ton MScratchC\n"); break;
88+
case 0x3f: printf("\ton MEPCC\n"); break;
89+
default: printf("on unknown special register %ld\n", (mtval >> 5) & 0x1f);
90+
}
91+
} else {
92+
int errreg = mtval >> 5;
93+
void * errcap = frame->registers[errreg - 1];
94+
printf("\ton capability %p [base %lx, len %lx, perms %lx] in register c%s\n", errcap, __builtin_cheri_base_get(errcap), __builtin_cheri_length_get(errcap), __builtin_cheri_perms_get(errcap), reg_names[errreg]);
95+
}
96+
}
97+
else {
98+
printf("Error (mcause 0x%lx; mtval 0x%lx):\n", mcause, mtval);
99+
printf("\tat %p\n", frame->pcc);
100+
}
101+
printf("\tRegisters:\n");
102+
for(int i = 0; i < 15; i++) {
103+
printf("\t\tc%s : %p [base %lx, len %lx, perms %lx, tag %d]\n", reg_names[i+1], frame->registers[i], __builtin_cheri_base_get(frame->registers[i]), __builtin_cheri_length_get(frame->registers[i]), __builtin_cheri_perms_get(frame->registers[i]), __builtin_cheri_tag_get(frame->registers[i]));
104+
}
105+
printf("\tStack view:\n", frame->registers[1]);
106+
void ** stack = (void**) frame->registers[1];
107+
if(__builtin_cheri_tag_get(stack)) for(int i = 0; i < 32; i++) {
108+
printf("\t\t+%02x : %p [base %lx, len %lx, perms %lx, tag %d]\n", sizeof(void*)*i, stack[i], __builtin_cheri_base_get(stack[i]), __builtin_cheri_length_get(stack[i]), __builtin_cheri_perms_get(stack[i]), __builtin_cheri_tag_get(stack[i]));
109+
}
110+
while(1);//mp_raise_OSError(mcause);
111+
}
112+
113+
114+
#if MICROPY_ENABLE_GC
115+
void gc_collect(void) {
116+
// WARNING: This gc_collect implementation doesn't try to get root
117+
// pointers from CPU registers, and thus may function incorrectly.
118+
void *dummy;
119+
gc_collect_start();
120+
gc_collect_root(&dummy, ((mp_uint_t)stack_top - (mp_uint_t)&dummy) / sizeof(mp_uint_t));
121+
gc_collect_end();
122+
gc_dump_info(&mp_plat_print);
123+
}
124+
#endif
125+
126+
mp_lexer_t *mp_lexer_new_from_file(qstr filename) {
127+
mp_raise_OSError(MP_ENOENT);
128+
}
129+
130+
mp_import_stat_t mp_import_stat(const char *path) {
131+
return MP_IMPORT_STAT_NO_EXIST;
132+
}
133+
134+
void nlr_jump_fail(void *val) {
135+
while (1) {
136+
;
137+
}
138+
}
139+
140+
void NORETURN __fatal_error(const char *msg) {
141+
while (1) {
142+
;
143+
}
144+
}
145+
146+
#ifndef NDEBUG
147+
void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) {
148+
printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line);
149+
__fatal_error("Assertion failed");
150+
}
151+
#endif
152+

ports/cheriot-rtos/mpconfigport.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <stdint.h>
2+
3+
// options to control how MicroPython is built
4+
5+
// Use the minimal starting configuration (disables all optional features).
6+
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_MINIMUM)
7+
8+
// You can disable the built-in MicroPython compiler by setting the following
9+
// config option to 0. If you do this then you won't get a REPL prompt, but you
10+
// will still be able to execute pre-compiled scripts, compiled with mpy-cross.
11+
#define MICROPY_ENABLE_COMPILER (1)
12+
13+
#define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool
14+
#define MICROPY_ENABLE_GC (1)
15+
#define MICROPY_HELPER_REPL (1)
16+
#define MICROPY_MODULE_FROZEN_MPY (1)
17+
#define MICROPY_ENABLE_EXTERNAL_IMPORT (1)
18+
19+
#define MICROPY_ALLOC_PATH_MAX (256)
20+
21+
// Use the minimum headroom in the chunk allocator for parse nodes.
22+
#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16)
23+
24+
// avoid STORE_LOCAL violations with thread state
25+
#define MICROPY_PY_STATE_THREAD_HACK (1)
26+
27+
// type definitions for the specific machine
28+
29+
#include <stddef.h>
30+
typedef ssize_t mp_int_t; // must be pointer size -- or does it?
31+
typedef size_t mp_uint_t; // must be pointer size -- or does it?
32+
33+
typedef long mp_off_t;
34+
35+
#define MICROPY_HEAP_SIZE (65536)
36+
#define MICROPY_MIN_USE_CHERIOT_A7 (1)
37+
#define alloca(size) __builtin_alloca (size)
38+
static __attribute__((unused)) __attribute__((always_inline)) size_t strnlen(const char * s, size_t maxlen) {
39+
int i = 0;
40+
while(*s++ && ++i < maxlen);
41+
return i;
42+
}
43+
44+
#define MICROPY_HW_BOARD_NAME "cheriot"
45+
#define MICROPY_HW_MCU_NAME "unknown-cpu"
46+
47+
#define MP_STATE_PORT MP_STATE_VM
48+

ports/cheriot-rtos/mphalport.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
static inline mp_uint_t mp_hal_ticks_ms(void) {
2+
return 0;
3+
}
4+
static inline void mp_hal_set_interrupt_char(char c) {
5+
}

ports/cheriot-rtos/qstrdefsport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// qstrs specific to this port
2+
// *FORMAT-OFF*

ports/cheriot-rtos/uart_core.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <compartment.h>
2+
#include <platform-uart.hh>
3+
4+
#include "mpconfigport.h"
5+
6+
/*
7+
* Core UART functions to implement for a port
8+
*/
9+
10+
// Receive single character
11+
extern "C" int mp_hal_stdin_rx_chr(void) {
12+
auto uart = MMIO_CAPABILITY(Uart, uart);
13+
return uart->blocking_read();
14+
}
15+
16+
// Send string of given length
17+
extern "C" mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
18+
auto uart = MMIO_CAPABILITY(Uart, uart);
19+
mp_uint_t ret = len;
20+
while(len--) {
21+
uart->blocking_write(*str++);
22+
}
23+
return ret;
24+
}

ports/cheriot-rtos/unistd.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#define SEEK_SET 0
2+
#define SEEK_CUR 1

0 commit comments

Comments
 (0)