diff --git a/.gitignore b/.gitignore
index ef0fa359c..7ec0ccb3d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,8 +3,12 @@
*.d
*.clang-tidy
*.deb
+*.a
+*.lib
+*.wasm
build
+pkg
third-party/downloads
third-party/mongoose-*
src/remote-cartesi-machine
@@ -22,8 +26,11 @@ doc/xml/
.vscode
.ycm_extra_conf.py
+*.tar.gz
*.raw
*.so
+*.dll
+*.exe
*.dylib
*.dtb
.DS_Store
diff --git a/Dockerfile b/Dockerfile
index 6b1bb3b77..c18ce436e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,7 +8,7 @@ RUN apt-get update && \
DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y \
build-essential vim wget git clang-tidy-15 clang-format-15 lcov \
libboost1.81-dev libssl-dev \
- ca-certificates automake libtool patchelf pkg-config lua5.4 liblua5.4-dev \
+ ca-certificates pkg-config lua5.4 liblua5.4-dev \
libgrpc++-dev libprotobuf-dev protobuf-compiler-grpc \
luarocks && \
update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 120 && \
@@ -48,7 +48,7 @@ ARG MACHINE_EMULATOR_VERSION=0.0.0
ARG TARGETARCH
RUN make install-tests
-RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt install -y \
+RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y \
./cartesi-machine-v${MACHINE_EMULATOR_VERSION}_${TARGETARCH}.deb \
&& rm -rf /var/lib/apt/lists/*
@@ -62,7 +62,7 @@ ARG TARGETARCH
COPY --from=installer \
/usr/src/emulator/cartesi-machine-v${MACHINE_EMULATOR_VERSION}_${TARGETARCH}.deb \
cartesi-machine.deb
-RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt install -y \
+RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y \
./cartesi-machine.deb \
&& rm -rf /var/lib/apt/lists/* \
&& rm cartesi-machine.deb
diff --git a/Makefile b/Makefile
index 02a7af23c..38ac126ac 100644
--- a/Makefile
+++ b/Makefile
@@ -14,14 +14,15 @@
# with this program (see COPYING). If not, see .
#
-UNAME:=$(shell uname)
+TARGET_OS?=$(shell uname)
+export TARGET_OS
# Install settings
-ARCH:= $(shell dpkg --print-architecture 2>/dev/null || echo amd64)
+DEB_ARCH= $(shell dpkg --print-architecture 2>/dev/null || echo amd64)
PREFIX= /usr
-MACHINE_EMULATOR_VERSION:= $(shell make -sC src version)
-MACHINE_EMULATOR_SO_VERSION:= $(shell make -sC src so-version)
-DEB_FILENAME= cartesi-machine-v$(MACHINE_EMULATOR_VERSION)_$(ARCH).deb
+MACHINE_EMULATOR_VERSION= $(shell make -sC src version)
+MACHINE_EMULATOR_SO_VERSION= $(shell make -sC src so-version)
+DEB_FILENAME= cartesi-machine-v$(MACHINE_EMULATOR_VERSION)_$(DEB_ARCH).deb
BIN_RUNTIME_PATH= $(PREFIX)/bin
LIB_RUNTIME_PATH= $(PREFIX)/lib
DOC_RUNTIME_PATH= $(PREFIX)/doc/cartesi-machine
@@ -29,40 +30,52 @@ SHARE_RUNTIME_PATH= $(PREFIX)/share/cartesi-machine
IMAGES_RUNTIME_PATH= $(SHARE_RUNTIME_PATH)/images
LUA_RUNTIME_CPATH= $(PREFIX)/lib/lua/5.4
LUA_RUNTIME_PATH= $(PREFIX)/share/lua/5.4
-INSTALL_PLAT = install-$(UNAME)
-
-LIBCARTESI_Darwin=libcartesi.dylib
-LIBCARTESI_Linux=libcartesi.so
-LIBCARTESI_GRPC_Darwin=libcartesi_grpc.dylib
-LIBCARTESI_GRPC_Linux=libcartesi_grpc.so
-
-LIBCARTESI_SO_Darwin:=libcartesi-$(MACHINE_EMULATOR_SO_VERSION).dylib
-LIBCARTESI_SO_Linux:=libcartesi-$(MACHINE_EMULATOR_SO_VERSION).so
-LIBCARTESI_SO_GRPC_Darwin:=libcartesi_grpc-$(MACHINE_EMULATOR_SO_VERSION).dylib
-LIBCARTESI_SO_GRPC_Linux:=libcartesi_grpc-$(MACHINE_EMULATOR_SO_VERSION).so
-
-BIN_INSTALL_PATH:= $(DESTDIR)$(BIN_RUNTIME_PATH)
-LIB_INSTALL_PATH:= $(DESTDIR)$(LIB_RUNTIME_PATH)
-DOC_INSTALL_PATH:= $(DESTDIR)$(DOC_RUNTIME_PATH)
-SHARE_INSTALL_PATH:= $(DESTDIR)$(SHARE_RUNTIME_PATH)
-IMAGES_INSTALL_PATH:= $(DESTDIR)$(IMAGES_RUNTIME_PATH)
-UARCH_INSTALL_PATH:= $(SHARE_INSTALL_PATH)/uarch
-LUA_INSTALL_CPATH:= $(DESTDIR)$(LUA_RUNTIME_CPATH)
-LUA_INSTALL_PATH:= $(DESTDIR)$(LUA_RUNTIME_PATH)
-INC_INSTALL_PATH:= $(DESTDIR)$(PREFIX)/include/cartesi-machine
-
-INSTALL= cp -RP
+
+ifeq ($(TARGET_OS),Darwin)
+LIBCARTESI=libcartesi.dylib
+LIBCARTESI_GRPC=libcartesi_grpc.dylib
+LIBCARTESI_JSONRPC=libcartesi_jsonrpc.dylib
+LIBCARTESI_SO=libcartesi-$(MACHINE_EMULATOR_SO_VERSION).dylib
+LIBCARTESI_SO_GRPC=libcartesi_grpc-$(MACHINE_EMULATOR_SO_VERSION).dylib
+LIBCARTESI_SO_JSONRPC=libcartesi_jsonrpc-$(MACHINE_EMULATOR_SO_VERSION).dylib
+else
+LIBCARTESI=libcartesi.so
+LIBCARTESI_GRPC=libcartesi_grpc.so
+LIBCARTESI_JSONRPC=libcartesi_jsonrpc.so
+LIBCARTESI_SO=libcartesi-$(MACHINE_EMULATOR_SO_VERSION).so
+LIBCARTESI_SO_GRPC=libcartesi_grpc-$(MACHINE_EMULATOR_SO_VERSION).so
+LIBCARTESI_SO_JSONRPC=libcartesi_jsonrpc-$(MACHINE_EMULATOR_SO_VERSION).so
+endif
+
+BIN_INSTALL_PATH= $(abspath $(DESTDIR)$(BIN_RUNTIME_PATH))
+LIB_INSTALL_PATH= $(abspath $(DESTDIR)$(LIB_RUNTIME_PATH))
+DOC_INSTALL_PATH= $(abspath $(DESTDIR)$(DOC_RUNTIME_PATH))
+SHARE_INSTALL_PATH= $(abspath $(DESTDIR)$(SHARE_RUNTIME_PATH))
+IMAGES_INSTALL_PATH= $(abspath $(DESTDIR)$(IMAGES_RUNTIME_PATH))
+UARCH_INSTALL_PATH= $(abspath $(SHARE_INSTALL_PATH)/uarch)
+LUA_INSTALL_CPATH= $(abspath $(DESTDIR)$(LUA_RUNTIME_CPATH))
+LUA_INSTALL_PATH= $(abspath $(DESTDIR)$(LUA_RUNTIME_PATH))
+INC_INSTALL_PATH= $(abspath $(DESTDIR)$(PREFIX)/include/cartesi-machine)
+
+INSTALL_FILE= install -m0644
+INSTALL_EXEC= install -m0755
+INSTALL_DIR= cp -RP
+SYMLINK= ln -sf
CHMOD_EXEC= chmod 0755
-CHMOD_DATA= chmod 0644
-STRIP_EXEC= strip -x
-
-EMU_TO_BIN= jsonrpc-remote-cartesi-machine remote-cartesi-machine merkle-tree-hash
-EMU_TO_LIB= $(LIBCARTESI_SO_$(UNAME)) $(LIBCARTESI_SO_GRPC_$(UNAME))
-EMU_LUA_TO_BIN= cartesi-machine.lua cartesi-machine-stored-hash.lua rollup-memory-range.lua
-EMU_LUA_TEST_TO_BIN= cartesi-machine-tests.lua uarch-riscv-tests.lua
-EMU_TO_LUA_PATH= cartesi/util.lua cartesi/proof.lua cartesi/gdbstub.lua
-EMU_TO_LUA_CPATH= cartesi.so
-EMU_TO_LUA_CARTESI_CPATH= cartesi/grpc.so cartesi/jsonrpc.so
+STRIP= strip
+STRIP_BINARY= $(STRIP)
+STRIP_SHARED= $(STRIP) -S -x
+STRIP_STATIC= $(STRIP) -S
+
+EMU_TO_BIN= src/jsonrpc-remote-cartesi-machine src/remote-cartesi-machine src/merkle-tree-hash
+EMU_TEST_TO_BIN= src/tests/test-merkle-tree-hash src/tests/test-machine-c-api
+EMU_TO_LIB= src/$(LIBCARTESI_SO) src/$(LIBCARTESI_SO_GRPC) src/$(LIBCARTESI_SO_JSONRPC)
+EMU_TO_LIB_A= src/libcartesi.a src/libcartesi_grpc.a src/libcartesi_jsonrpc.a
+EMU_LUA_TO_BIN= src/cartesi-machine.lua src/cartesi-machine-stored-hash.lua src/rollup-memory-range.lua
+EMU_LUA_TEST_TO_BIN= src/cartesi-machine-tests.lua src/uarch-riscv-tests.lua
+EMU_TO_LUA_PATH= src/cartesi/util.lua src/cartesi/proof.lua src/cartesi/gdbstub.lua
+EMU_TO_LUA_CPATH= src/cartesi.so
+EMU_TO_LUA_CARTESI_CPATH= src/cartesi/grpc.so src/cartesi/jsonrpc.so
EMU_TO_INC= $(addprefix src/,jsonrpc-machine-c-api.h grpc-machine-c-api.h machine-c-api.h \
machine-c-defines.h machine-c-version.h pma-defines.h rtc-defines.h htif-defines.h uarch-defines.h)
UARCH_TO_SHARE= uarch-ram.bin
@@ -70,12 +83,12 @@ UARCH_TO_SHARE= uarch-ram.bin
MONGOOSE_VERSION=7.12
# Build settings
-DEPDIR := third-party
-SRCDIR := $(abspath src)
-DOWNLOADDIR := $(DEPDIR)/downloads
-DEPDIRS := third-party/mongoose-$(MONGOOSE_VERSION)
-SUBCLEAN := $(addsuffix .clean,$(SRCDIR) uarch third-party/riscv-arch-tests)
-COREPROTO := lib/grpc-interfaces/core.proto
+DEPDIR = third-party
+SRCDIR = $(abspath src)
+DOWNLOADDIR = $(DEPDIR)/downloads
+DEPDIRS = third-party/mongoose-$(MONGOOSE_VERSION)
+SUBCLEAN = $(addsuffix .clean,$(SRCDIR) uarch third-party/riscv-arch-tests)
+COREPROTO = lib/grpc-interfaces/core.proto
# Docker image tag
TAG ?= devel
@@ -97,22 +110,25 @@ export release
export coverage
# Mac OS X specific settings
-ifeq ($(UNAME),Darwin)
-LUA_PLAT ?= macosx
+ifeq ($(TARGET_OS),Darwin)
export CC = clang
export CXX = clang++
-LUACC = "CC=$(CXX)"
-LIBRARY_PATH := "export DYLD_LIBRARY_PATH="
+LIBRARY_PATH = "export DYLD_LIBRARY_PATH="
+SHA1SUM=shasum
# Linux specific settings
-else ifeq ($(UNAME),Linux)
-LUA_PLAT ?= linux
-LIBRARY_PATH := "export LD_LIBRARY_PATH=$(SRCDIR)"
-LUACC = "CC=g++"
-# Unknown platform
+else ifeq ($(TARGET_OS),Linux)
+export CC=gcc
+export CXX=g++
+LIBRARY_PATH = "export LD_LIBRARY_PATH=$(SRCDIR)"
+SHA1SUM=sha1sum
+
+# Other system
else
-LUA_PLAT ?= none
-INSTALL_PLAT=
+export CC=gcc
+export CXX=g++
+SHA1SUM=sha1sum
+
endif
all: source-default
@@ -157,7 +173,7 @@ help:
@echo ' uarch-tests-with-linux-env - build and run microarchitecture rv64i instruction tests using the linux-env docker image'
checksum:
- @cd $(DEPDIR) && shasum -c shasumfile
+ @cd $(DEPDIR) && $(SHA1SUM) -c shasumfile
$(DOWNLOADDIR):
@mkdir -p $(DOWNLOADDIR)
@@ -170,6 +186,13 @@ third-party/downloads/$(MONGOOSE_VERSION).tar.gz: | downloads
third-party/mongoose-$(MONGOOSE_VERSION): third-party/downloads/$(MONGOOSE_VERSION).tar.gz
tar -C third-party -xzf $< mongoose-$(MONGOOSE_VERSION)/mongoose.c mongoose-$(MONGOOSE_VERSION)/mongoose.h
+bundle-boost: third-party/downloads/boost
+third-party/downloads/boost: | downloads
+ wget -O third-party/downloads/boost_1_81_0.tar.gz https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.tar.gz
+ tar -C third-party/downloads -xzf third-party/downloads/boost_1_81_0.tar.gz boost_1_81_0/boost
+ mv third-party/downloads/boost_1_81_0/boost third-party/downloads/boost
+ rm -rf third-party/downloads/boost_1_81_0.tar.gz third-party/downloads/boost_1_81_0
+
dep: $(DEPDIRS)
submodules:
@@ -184,6 +207,9 @@ grpc: | $(COREPROTO)
hash luacartesi grpc test lint coverage-report check-format format check-format-lua check-lua format-lua:
@eval $$($(MAKE) -s --no-print-directory env); $(MAKE) -C $(SRCDIR) $@
+libcartesi libcartesi_grpc libcartesi_jsonrpc libcartesi.a libcartesi_grpc.a libcartesi_jsonrpc.a libcartesi.so libcartesi_grpc.so libcartesi_jsonrpc.so:
+ @eval $$($(MAKE) -s --no-print-directory env); $(MAKE) -C $(SRCDIR) $@
+
version:
@eval $$($(MAKE) -s --no-print-directory env); $(MAKE) -sC $(SRCDIR) $@
@@ -257,61 +283,48 @@ uarch-with-linux-env:
uarch-tests-with-linux-env:
@$(MAKE) linux-env-exec CONTAINER_COMMAND="make uarch-tests"
-install-Darwin:
- install_name_tool -delete_rpath $(SRCDIR) -add_rpath $(LIB_RUNTIME_PATH) $(LUA_INSTALL_CPATH)/cartesi.so
- install_name_tool -delete_rpath $(SRCDIR) -add_rpath $(LIB_RUNTIME_PATH) $(LUA_INSTALL_CPATH)/cartesi/grpc.so
- install_name_tool -delete_rpath $(SRCDIR) -add_rpath $(LIB_RUNTIME_PATH) $(LUA_INSTALL_CPATH)/cartesi/jsonrpc.so
- cd $(BIN_INSTALL_PATH) && \
- for x in $(EMU_TO_BIN); do \
- install_name_tool -delete_rpath $(SRCDIR) -add_rpath $(LIB_RUNTIME_PATH) $$x ;\
- done
+install-headers: $(INC_INSTALL_PATH)
+ $(INSTALL_FILE) $(EMU_TO_INC) $(INC_INSTALL_PATH)
+
+install-emulator: $(BIN_INSTALL_PATH) $(LIB_INSTALL_PATH) $(LUA_INSTALL_CPATH)/cartesi $(LUA_INSTALL_PATH)/cartesi $(IMAGES_INSTALL_PATH)
+ $(INSTALL_EXEC) $(EMU_TO_BIN) $(BIN_INSTALL_PATH)
+ $(INSTALL_EXEC) $(EMU_TO_LIB) $(LIB_INSTALL_PATH)
+ $(INSTALL_FILE) $(EMU_TO_LIB_A) $(LIB_INSTALL_PATH)
+ $(INSTALL_FILE) $(EMU_LUA_TO_BIN) $(LUA_INSTALL_PATH)
+ $(INSTALL_EXEC) $(EMU_TO_LUA_CPATH) $(LUA_INSTALL_CPATH)
+ $(INSTALL_EXEC) $(EMU_TO_LUA_CARTESI_CPATH) $(LUA_INSTALL_CPATH)/cartesi
+ $(INSTALL_FILE) $(EMU_TO_LUA_PATH) $(LUA_INSTALL_PATH)/cartesi
+ cat tools/template/cartesi-machine.template | sed 's|ARG_LUA_PATH|$(LUA_RUNTIME_PATH)/?.lua|g;s|ARG_LUA_CPATH|$(LUA_RUNTIME_CPATH)/?.so|g;s|ARG_INSTALL_PATH|$(IMAGES_RUNTIME_PATH)|g;s|ARG_LUA_RUNTIME_PATH|$(LUA_RUNTIME_PATH)|g' > $(BIN_INSTALL_PATH)/cartesi-machine
+ cat tools/template/cartesi-machine-stored-hash.template | sed 's|ARG_LUA_PATH|$(LUA_RUNTIME_PATH)/?.lua|g;s|ARG_LUA_CPATH|$(LUA_RUNTIME_CPATH)/?.so|g;s|ARG_LUA_RUNTIME_PATH|$(LUA_RUNTIME_PATH)|g' > $(BIN_INSTALL_PATH)/cartesi-machine-stored-hash
+ cat tools/template/rollup-memory-range.template | sed 's|ARG_LUA_PATH|$(LUA_RUNTIME_PATH)/?.lua|g;s|ARG_LUA_CPATH|$(LUA_RUNTIME_CPATH)/?.so|g;s|ARG_LUA_RUNTIME_PATH|$(LUA_RUNTIME_PATH)|g' > $(BIN_INSTALL_PATH)/rollup-memory-range
+ $(CHMOD_EXEC) $(BIN_INSTALL_PATH)/cartesi-machine $(BIN_INSTALL_PATH)/cartesi-machine-stored-hash $(BIN_INSTALL_PATH)/rollup-memory-range
+ $(SYMLINK) $(LIBCARTESI_SO) $(LIB_INSTALL_PATH)/$(LIBCARTESI)
+ $(SYMLINK) $(LIBCARTESI_SO_GRPC) $(LIB_INSTALL_PATH)/$(LIBCARTESI_GRPC)
+ $(SYMLINK) $(LIBCARTESI_SO_JSONRPC) $(LIB_INSTALL_PATH)/$(LIBCARTESI_JSONRPC)
+ $(INSTALL_DIR) tools/gdb $(SHARE_INSTALL_PATH)/gdb
-install-Linux:
- cd $(BIN_INSTALL_PATH) && for x in $(EMU_TO_BIN); do patchelf --set-rpath $(LIB_RUNTIME_PATH) $$x ; done
- cd $(LIB_INSTALL_PATH) && for x in $(EMU_TO_LIB); do patchelf --set-rpath $(LIB_RUNTIME_PATH) $$x; done
- cd $(LUA_INSTALL_CPATH) && for x in $(EMU_TO_LUA_CPATH) $(EMU_TO_LUA_CARTESI_CPATH); do patchelf --set-rpath $(LIB_RUNTIME_PATH) $$x ; done
+install-strip: install-emulator
+ $(STRIP_BINARY) $(subst src/,$(BIN_INSTALL_PATH)/,$(EMU_TO_BIN))
+ $(STRIP_SHARED) $(subst src/,$(LUA_INSTALL_CPATH)/,$(EMU_TO_LUA_CPATH))
+ $(STRIP_SHARED) $(subst src/,$(LIB_INSTALL_PATH)/,$(EMU_TO_LIB))
+ $(STRIP_STATIC) $(subst src/,$(LIB_INSTALL_PATH)/,$(EMU_TO_LIB_A))
+
+install: install-strip install-headers
install-tests: install
- cd src && $(INSTALL) $(EMU_LUA_TEST_TO_BIN) $(LUA_INSTALL_PATH)
- cd src && $(INSTALL) tests/test-merkle-tree-hash tests/test-machine-c-api $(BIN_INSTALL_PATH)
+ $(INSTALL_FILE) $(EMU_LUA_TEST_TO_BIN) $(LUA_INSTALL_PATH)
+ $(INSTALL_EXEC) $(EMU_TEST_TO_BIN) $(BIN_INSTALL_PATH)
cat tools/template/cartesi-machine-tests.template | sed 's|ARG_LUA_PATH|$(LUA_RUNTIME_PATH)/?.lua|g;s|ARG_LUA_CPATH|$(LUA_RUNTIME_CPATH)/?.so|g;s|ARG_LUA_RUNTIME_PATH|$(LUA_RUNTIME_PATH)|g' > $(BIN_INSTALL_PATH)/cartesi-machine-tests
cat tools/template/uarch-riscv-tests.template | sed 's|ARG_LUA_PATH|$(LUA_RUNTIME_PATH)/?.lua|g;s|ARG_LUA_CPATH|$(LUA_RUNTIME_CPATH)/?.so|g;s|ARG_LUA_RUNTIME_PATH|$(LUA_RUNTIME_PATH)|g' > $(BIN_INSTALL_PATH)/uarch-riscv-tests
- cd $(LUA_INSTALL_PATH) && $(CHMOD_DATA) $(EMU_LUA_TEST_TO_BIN)
- cd $(BIN_INSTALL_PATH) && $(CHMOD_EXEC) cartesi-machine-tests uarch-riscv-tests
- patchelf --set-rpath $(LIB_RUNTIME_PATH) src/tests/test-merkle-tree-hash
- patchelf --set-rpath $(LIB_RUNTIME_PATH) src/tests/test-machine-c-api
-
-install-emulator: $(BIN_INSTALL_PATH) $(LIB_INSTALL_PATH) $(LUA_INSTALL_CPATH)/cartesi $(LUA_INSTALL_PATH)/cartesi $(INC_INSTALL_PATH) $(IMAGES_INSTALL_PATH)
- cd src && $(INSTALL) $(EMU_TO_BIN) $(BIN_INSTALL_PATH)
- cd src && $(INSTALL) $(EMU_TO_LIB) $(LIB_INSTALL_PATH)
- cd src && $(INSTALL) $(EMU_LUA_TO_BIN) $(LUA_INSTALL_PATH)
- cd src && $(INSTALL) $(EMU_TO_LUA_CPATH) $(LUA_INSTALL_CPATH)
- cd src && $(INSTALL) $(EMU_TO_LUA_CARTESI_CPATH) $(LUA_INSTALL_CPATH)/cartesi
- cd src && $(INSTALL) $(EMU_TO_LUA_PATH) $(LUA_INSTALL_PATH)/cartesi
- cat tools/template/cartesi-machine.template | sed 's|ARG_LUA_PATH|$(LUA_RUNTIME_PATH)/?.lua|g;s|ARG_LUA_CPATH|$(LUA_RUNTIME_CPATH)/?.so|g;s|ARG_INSTALL_PATH|$(IMAGES_RUNTIME_PATH)|g;s|ARG_LUA_RUNTIME_PATH|$(LUA_RUNTIME_PATH)|g' > $(BIN_INSTALL_PATH)/cartesi-machine
- cat tools/template/cartesi-machine-stored-hash.template | sed 's|ARG_LUA_PATH|$(LUA_RUNTIME_PATH)/?.lua|g;s|ARG_LUA_CPATH|$(LUA_RUNTIME_CPATH)/?.so|g;s|ARG_LUA_RUNTIME_PATH|$(LUA_RUNTIME_PATH)|g' > $(BIN_INSTALL_PATH)/cartesi-machine-stored-hash
- cat tools/template/rollup-memory-range.template | sed 's|ARG_LUA_PATH|$(LUA_RUNTIME_PATH)/?.lua|g;s|ARG_LUA_CPATH|$(LUA_RUNTIME_CPATH)/?.so|g;s|ARG_LUA_RUNTIME_PATH|$(LUA_RUNTIME_PATH)|g' > $(BIN_INSTALL_PATH)/rollup-memory-range
- cd $(BIN_INSTALL_PATH) && $(CHMOD_EXEC) $(EMU_TO_BIN) cartesi-machine cartesi-machine-stored-hash rollup-memory-range
- cd $(LIB_INSTALL_PATH) && ln -sf $(LIBCARTESI_SO_$(UNAME)) $(LIBCARTESI_$(UNAME))
- cd $(LIB_INSTALL_PATH) && ln -sf $(LIBCARTESI_SO_GRPC_$(UNAME)) $(LIBCARTESI_GRPC_$(UNAME))
- cd $(LUA_INSTALL_PATH) && $(CHMOD_DATA) $(EMU_LUA_TO_BIN)
- $(INSTALL) $(EMU_TO_INC) $(INC_INSTALL_PATH)
- $(INSTALL) tools/gdb $(SHARE_INSTALL_PATH)/gdb
- cd $(LUA_INSTALL_CPATH) && $(CHMOD_EXEC) $(EMU_TO_LUA_CPATH)
+ $(CHMOD_EXEC) $(BIN_INSTALL_PATH)/cartesi-machine-tests $(BIN_INSTALL_PATH)/uarch-riscv-tests
install-uarch: install $(UARCH_INSTALL_PATH)
- $(INSTALL) uarch/$(UARCH_TO_SHARE) $(UARCH_INSTALL_PATH)
-
-install-strip: install-emulator
- cd $(BIN_INSTALL_PATH) && $(STRIP_EXEC) $(EMU_TO_BIN)
- cd $(LIB_INSTALL_PATH) && $(STRIP_EXEC) $(EMU_TO_LIB)
- cd $(LUA_INSTALL_CPATH) && $(STRIP_EXEC) $(EMU_TO_LUA_CPATH)
+ $(INSTALL_FILE) uarch/$(UARCH_TO_SHARE) $(UARCH_INSTALL_PATH)
-install: install-emulator install-strip $(INSTALL_PLAT)
debian-package: install
mkdir -p $(DESTDIR)/DEBIAN $(DOC_INSTALL_PATH)
- $(INSTALL) COPYING $(DOC_INSTALL_PATH)/copyright
- cat tools/template/control.template | sed 's|ARG_VERSION|$(MACHINE_EMULATOR_VERSION)|g;s|ARG_ARCH|$(ARCH)|g' > $(DESTDIR)/DEBIAN/control
+ $(INSTALL_FILE) COPYING $(DOC_INSTALL_PATH)/copyright
+ cat tools/template/control.template | sed 's|ARG_VERSION|$(MACHINE_EMULATOR_VERSION)|g;s|ARG_ARCH|$(DEB_ARCH)|g' > $(DESTDIR)/DEBIAN/control
dpkg-deb -Zxz --root-owner-group --build $(DESTDIR) $(DEB_FILENAME)
.SECONDARY: $(DOWNLOADDIR) $(DEPDIRS) $(COREPROTO)
diff --git a/README.md b/README.md
index d51abe540..dc4ed3578 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,6 @@ Docker targets:
- C++ Compiler with support for C++17 (tested with GCC >= 8+ and Clang >= 8.x).
- GNU Make >= 3.81
-- Cryptopp >= 7.0.0
- GRPC >= 1.45.0
- Lua >= 5.4.4
- Boost >= 1.81
@@ -30,10 +29,10 @@ Obs: Please note that Apple Clang Version number does not follow upstream LLVM/C
#### Debian Bookworm
-```
-apt-get install build-essential wget git clang-tidy-15 clang-format-15 \
+```bash
+sudo apt-get install build-essential wget git clang-tidy-15 clang-format-15 \
libboost1.81-dev libssl-dev \
- ca-certificates automake libtool patchelf pkg-config lua5.4 liblua5.4-dev \
+ ca-certificates pkg-config lua5.4 liblua5.4-dev \
libgrpc++-dev libprotobuf-dev protobuf-compiler-grpc \
luarocks
@@ -46,8 +45,8 @@ sudo luarocks install --lua-version=5.4 luaposix
#### MacOS
##### MacPorts
-```
-sudo port install clang-15 automake boost libtool wget pkgconfig grpc openssl lua lua-luarocks
+```bash
+sudo port install clang-15 boost libtool wget pkgconfig grpc openssl lua lua-luarocks
sudo luarocks install --lua-version=5.4 lpeg
sudo luarocks install --lua-version=5.4 dkjson
@@ -57,13 +56,13 @@ sudo luarocks install --lua-version=5.4 luaposix
```
##### Homebrew
-```
-brew install llvm@15 automake boost wget pkg-config grpc openssl lua@5.4 luarocks
-luarocks --lua-dir=$(brew --prefix)/opt/lua@5.4 install lpeg
-luarocks --lua-dir=$(brew --prefix)/opt/lua@5.4 install dkjson
-luarocks --lua-dir=$(brew --prefix)/opt/lua@5.4 install luasocket
-luarocks --lua-dir=$(brew --prefix)/opt/lua@5.4 install luasec
-luarocks --lua-dir=$(brew --prefix)/opt/lua@5.4 install luaposix
+```bash
+brew install llvm@15 boost wget pkg-config grpc openssl lua luarocks
+luarocks --lua-dir=$(brew --prefix)/opt/lua install lpeg
+luarocks --lua-dir=$(brew --prefix)/opt/lua install dkjson
+luarocks --lua-dir=$(brew --prefix)/opt/lua install luasocket
+luarocks --lua-dir=$(brew --prefix)/opt/lua install luasec
+luarocks --lua-dir=$(brew --prefix)/opt/lua install luaposix
```
For emulator scripts to work it is expected that `lua5.4` binary is available in the system PATH. If operating system/package manager that you are using provides only `lua` or lua binary named in a different way (e.g. on `Homebrew`), please create symbolic link or alias `lua5.4`.
@@ -71,23 +70,58 @@ For emulator scripts to work it is expected that `lua5.4` binary is available in
### Build
```bash
-$ make submodules
-$ make downloads
-$ make dep
-$ make
+make submodules
+make downloads
+make dep
+make
```
Cleaning:
```bash
-$ make depclean
-$ make clean
+make depclean
+make clean
```
### Install
```bash
-$ make install
+sudo make install PREFIX=/usr/local
+```
+
+### Build C libraries in standalone
+
+Both `libcartesi` and `libcartes_jsonrpc` C libraries can be compiled in standalone, either as static or shared library:
+
+```bash
+make submodules
+make downloads
+make dep
+make bundle-boost
+make -C src release=yes libcartesi.a libcartesi_jsonrpc.a libcartesi.so libcartesi_jsonrpc.so
+```
+
+The `.a` and `.so` files will be available in `src` directory, you can use any of them to link your application.
+
+You can even use other toolchains to cross compile targeting other platforms:
+
+```bash
+# Target WASM with Emscripten toolchain
+make -C src release=yes \
+ CC=emcc CXX=em++ AR="emar rcs" \
+ libcartesi.a
+
+# Target WASM with WASI SDK toolchain
+make -C src release=yes \
+ CC=/opt/wasi-sdk/bin/clang CXX=/opt/wasi-sdk/bin/clang++ AR="/opt/wasi-sdk/bin/llvm-ar rcs" \
+ libcartesi.a
+
+# Target Windows with mingw-w64 toolchain
+make -C src release=yes \
+ CC=x86_64-w64-mingw32-gcc \
+ CXX=x86_64-w64-mingw32-g++ \
+ AR="x86_64-w64-mingw32-ar rcs" \
+ libcartesi.a
```
## Running Tests
@@ -95,13 +129,13 @@ $ make install
Copy the tests binaries to a directory called `tests` and run: (Eg.: )
```bash
-$ make test
+make test
```
The default search path for binaries is `machine-emulator/tests`. Alternatively you can specify the binaries path using the `CARTESI_TESTS_PATH` variable as in:
```bash
-$ make test CARTESI_TESTS_PATH=/full/path/to/test/binaries
+make test CARTESI_TESTS_PATH=/full/path/to/test/binaries
```
## Linter
@@ -115,14 +149,14 @@ We use clang-tidy 14 as the linter.
You need to install the package clang-tidy-15 and set it as the default executable with update-alternatives.
```bash
-$ apt install clang-tidy-15
-$ update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-15 120
+apt install clang-tidy-15
+update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-15 120
```
### Running Lint
```bash
-$ make lint -j$(nproc)
+make lint -j$(nproc)
```
## Code format
@@ -136,20 +170,20 @@ We use clang-format to format the code base.
You need to install the package clang-format-15 and set is as the default executable with update-alternatives.
```bash
-$ apt install clang-format-15
-$ update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 120
+apt install clang-format-15
+update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 120
```
### Formatting code
```bash
-$ make format
+make format
```
### Checking whether the code is formatted
```bash
-$ make check-format
+make check-format
```
## Coverage
@@ -161,13 +195,13 @@ $ make check-format
If you want to run the GCC-based coverage, you should install the lcov package with the following command.
```bash
-$ sudo apt install lcov
+sudo apt install lcov
```
If you want to run the clang-based coverage, you should install the clang package with the following command.
```bash
-$ sudo apt install clang llvm
+sudo apt install clang llvm
```
### Compilation
@@ -177,13 +211,13 @@ Make sure you run `make clean` to clean up any previous compilation.
For GCC-based coverage run the following command.
```bash
-$ make coverage-toolchain=gcc -j$(nproc)
+make coverage-toolchain=gcc -j$(nproc)
```
For clang-based coverage run the following command.
```bash
-$ make coverage-toolchain=clang -j$(nproc)
+make coverage-toolchain=clang -j$(nproc)
```
### Running coverage
@@ -194,7 +228,7 @@ You also need to specify the directory containing the kernel and rootfs with the
For instance:
```bash
-$ make coverage=yes test-all coverage-report \
+make coverage=yes test-all coverage-report \
CARTESI_TESTS_PATH=$(realpath ../tests/build) \
CARTESI_IMAGES_PATH=$(realpath ./src)
```
diff --git a/src/Makefile b/src/Makefile
index d4c412334..d786b0ab8 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -22,21 +22,22 @@ EMULATOR_VERSION_MINOR=15
EMULATOR_VERSION_PATCH=2
EMULATOR_VERSION_LABEL=
-UNAME:=$(shell uname)
+TARGET_OS?=$(shell uname)
-LUA_INC:=$(shell pkg-config --cflags-only-I lua5.4)
-LUA_LIB:=-llua5.4
-LUA_BIN?=lua5.4
-GRPC_DIR:=../lib/grpc-interfaces
+LUA_INC=$(shell pkg-config --cflags-only-I lua5.4)
+LUA_LIB=$(shell pkg-config --libs lua5.4)
+LUA_BIN=lua5.4
+GRPC_DIR=../lib/grpc-interfaces
-PROTOC:=$(shell which protoc)
-PROTOC_FLAGS:=--experimental_allow_proto3_optional
-GRPC_CPP_PLUGIN:=$(shell which grpc_cpp_plugin)
+PROTOC=protoc
+PROTOC_FLAGS=--experimental_allow_proto3_optional
+GRPC_CPP_PLUGIN=$(shell which grpc_cpp_plugin)
# Code instrumentation
release?=no
sanitize?=no
coverage?=no
+nothreads?=no
COVERAGE_TOOLCHAIN?=gcc
COVERAGE_OUTPUT_DIR?=coverage
@@ -49,116 +50,99 @@ GCLDFLAGS=-Wl,--gc-sections,--print-gc-sections
endif
# Mac OS X specific setup
-SOLDFLAGS_Darwin:=-bundle -undefined dynamic_lookup
-CC_Darwin=clang
-CXX_Darwin=clang++
-INCS_Darwin=
+ifeq ($(TARGET_OS),Darwin)
+PICCFLAGS=-fPIC
+SOLDFLAGS=-bundle -undefined dynamic_lookup
+LIBLDFLAGS=-dynamiclib
+EXELDFLAGS=
+PTHREAD_CFLAGS=
+PTHREAD_LDFLAGS=-lpthread
+CC=clang
+CXX=clang++
+AR=libtool -static -o
+INCS=
-ifeq ($(UNAME),Darwin)
ifeq ($(MACOSX_DEPLOYMENT_TARGET),)
-export MACOSX_DEPLOYMENT_TARGET := $(shell sw_vers -productVersion | sed -r "s/([[:digit:]]+)\.([[:digit:]]+)\..+/\1.\2.0/")
+export MACOSX_DEPLOYMENT_TARGET := $(shell sw_vers -productVersion | sed -E "s/([[:digit:]]+)\.([[:digit:]]+)\..+/\1.\2.0/")
endif
-SOLDFLAGS_Darwin+=-Wl,-rpath,$(CURDIR)
+
# Homebrew installation
ifneq (,$(shell which brew))
-BREW_PREFIX := $(shell brew --prefix)
-BOOST_LIB_DIR_Darwin=-L$(BREW_PREFIX)/lib
-BOOST_INC_Darwin=-I$(BREW_PREFIX)/include
-GRPC_INC_Darwin:=$(shell pkg-config --cflags-only-I grpc++)
-GRPC_LIB_Darwin:=$(shell pkg-config --libs grpc++)
-PROTOBUF_INC_Darwin:=$(shell pkg-config --cflags-only-I protobuf)
-PROTOBUF_LIB_Darwin:=$(shell pkg-config --libs protobuf)
-else ifneq (,$(shell which port)) # Macports installation
-PORT_PREFIX := /opt/local
-BOOST_LIB_DIR_Darwin=-L$(PORT_PREFIX)/libexec/boost/1.81/lib
-BOOST_INC_Darwin=-I$(PORT_PREFIX)/libexec/boost/1.81/include
-GRPC_INC_Darwin:=-I$(PORT_PREFIX)/include
-GRPC_LIB_Darwin=-L$(PORT_PREFIX)/lib -lgrpc++ -lgrpc -lgpr -lprotobuf -lpthread -labsl_synchronization
-PROTOBUF_INC_Darwin:=-I$(PORT_PREFIX)/include
-PROTOBUF_LIB_Darwin:=-L$(PORT_PREFIX)/lib -lprotobuf -lpthread
+BREW_PREFIX = $(shell brew --prefix)
+BOOST_LIB_DIR=-L$(BREW_PREFIX)/lib
+BOOST_INC=-I$(BREW_PREFIX)/include
+GRPC_PROTOBUF_INC=$(shell pkg-config --cflags-only-I grpc++ protobuf)
+GRPC_PROTOBUF_LIB=$(shell pkg-config --libs grpc++ protobuf)
+
+# Macports installation
+else ifneq (,$(shell which port))
+PORT_PREFIX = /opt/local
+BOOST_LIB_DIR=-L$(PORT_PREFIX)/libexec/boost/1.81/lib
+BOOST_INC=-I$(PORT_PREFIX)/libexec/boost/1.81/include
+GRPC_PROTOBUF_INC=-I$(PORT_PREFIX)/include
+GRPC_PROTOBUF_LIB=-L$(PORT_PREFIX)/lib -lgrpc++ -lgrpc -lgpr -lprotobuf -lpthread -labsl_synchronization
else
$(error Neither Homebrew nor MacPorts is installed)
endif
-endif
-PTHREAD_LIB_Darwin:=-lpthread
-LIBCARTESI_Darwin=libcartesi-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).dylib
-LIBCARTESI_LDFLAGS_Darwin=-dynamiclib -undefined dynamic_lookup -install_name '@rpath/$(LIBCARTESI_Darwin)'
-LIBCARTESI_TESTS_LDFLAGS_Darwin=-Wl,-rpath,$(CURDIR)
-LIBCARTESI_GRPC_Darwin=libcartesi_grpc-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).dylib
-LIBCARTESI_GRPC_LDFLAGS_Darwin=-dynamiclib -undefined dynamic_lookup -install_name '@rpath/$(LIBCARTESI_GRPC_Darwin)'
-LIBCARTESI_GRPC_TESTS_LDFLAGS_Darwin=-Wl,-rpath,$(CURDIR)
-LIBCARTESI_JSONRPC_Darwin=libcartesi_jsonrpc-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).dylib
-LIBCARTESI_JSONRPC_LDFLAGS_Darwin=-dynamiclib -undefined dynamic_lookup -install_name '@rpath/$(LIBCARTESI_JSONRPC_Darwin)'
-LIBCARTESI_JSONRPC_TESTS_LDFLAGS_Darwin=-Wl,-rpath,$(CURDIR)
-CARTESI_EXECUTABLE_LDFLAGS_Darwin=-Wl,-rpath,$(CURDIR)
-PROFILE_DATA_Darwin=default.profdata
+LIBCARTESI=libcartesi-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).dylib
+LIBCARTESI_LDFLAGS=-install_name '@rpath/$(LIBCARTESI)'
+LIBCARTESI_GRPC=libcartesi_grpc-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).dylib
+LIBCARTESI_GRPC_LDFLAGS=-install_name '@rpath/$(LIBCARTESI_GRPC)'
+LIBCARTESI_JSONRPC=libcartesi_jsonrpc-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).dylib
+LIBCARTESI_JSONRPC_LDFLAGS=-install_name '@rpath/$(LIBCARTESI_JSONRPC)'
+PROFILE_DATA=default.profdata
+
+# Linux or some other POSIX platform
+else
# Linux specific setup
-SOLDFLAGS_Linux:=-shared -fPIC -pthread
-CC_Linux=gcc
-CXX_Linux=g++
-INCS_Linux=
-FS_LIB_Linux=-lstdc++fs
-PTHREAD_LIB_Linux:=-lpthread
-BOOST_INC_Linux:=
-GRPC_INC_Linux:=$(shell pkg-config --cflags-only-I grpc++)
-GRPC_LIB_Linux:=$(shell pkg-config --libs grpc++)
-PROTOBUF_INC_Linux:=$(shell pkg-config --cflags-only-I protobuf)
-PROTOBUF_LIB_Linux:=$(shell pkg-config --libs protobuf)
-LIBCARTESI_Linux=libcartesi-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).so
-LIBCARTESI_LDFLAGS_Linux:=$(SOLDFLAGS_Linux)
-LIBCARTESI_TESTS_LDFLAGS_Linux=-Wl,-rpath,'$$ORIGIN/..'
-LIBCARTESI_GRPC_Linux=libcartesi_grpc-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).so
-LIBCARTESI_GRPC_LDFLAGS_Linux:=$(SOLDFLAGS_Linux)
-LIBCARTESI_GRPC_TESTS_LDFLAGS_Linux=-Wl,-rpath,'$$ORIGIN/..'
-LIBCARTESI_JSONRPC_Linux=libcartesi_jsonrpc-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).so
-LIBCARTESI_JSONRPC_LDFLAGS_Linux:=$(SOLDFLAGS_Linux)
-LIBCARTESI_JSONRPC_TESTS_LDFLAGS_Linux=-Wl,-rpath,'$$ORIGIN/..'
-CARTESI_EXECUTABLE_LDFLAGS_Linux=-Wl,-rpath,'$$ORIGIN/'
-PROFILE_DATA_Linux=
-
-CC=$(CC_$(UNAME))
-CXX=$(CXX_$(UNAME))
-SOLDFLAGS:=$(SOLDFLAGS_$(UNAME)) $(GCLDFLAGS)
-PTHREAD_LIB:=$(PTHREAD_LIB_$(UNAME))
-BOOST_INC:=$(BOOST_INC_$(UNAME))
-GRPC_INC:=$(GRPC_INC_$(UNAME))
-GRPC_LIB:=$(GRPC_LIB_$(UNAME))
-PROTOBUF_INC:=$(PROTOBUF_INC_$(UNAME))
-PROTOBUF_LIB:=$(PROTOBUF_LIB_$(UNAME))
-LIBCARTESI=$(LIBCARTESI_$(UNAME))
-LIBCARTESI_LDFLAGS=$(LIBCARTESI_LDFLAGS_$(UNAME))
-LIBCARTESI_TESTS_LDFLAGS=$(LIBCARTESI_TESTS_LDFLAGS_$(UNAME))
-LIBCARTESI_LIB=-L. -lcartesi-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR)
-CARTESI_EXECUTABLE_LDFLAGS=$(CARTESI_EXECUTABLE_LDFLAGS_$(UNAME))
-
-LIBCARTESI_GRPC=$(LIBCARTESI_GRPC_$(UNAME))
-LIBCARTESI_GRPC_LDFLAGS=$(LIBCARTESI_GRPC_LDFLAGS_$(UNAME))
-LIBCARTESI_GRPC_TESTS_LDFLAGS=$(LIBCARTESI_GRPC_TESTS_LDFLAGS_$(UNAME))
-LIBCARTESI_GRPC_LIB=-L. -lcartesi_grpc-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR)
-
-LIBCARTESI_LIBS:=
-LIBCARTESI_GRPC_LIBS:=$(GRPC_LIB) $(PROTOBUF_LIB)
-LUACARTESI_LIBS:=$(LIBCARTESI_LIB)
-LUACARTESI_GRPC_LIBS:=$(LIBCARTESI_LIB) $(LIBCARTESI_GRPC_LIB)
-LUACARTESI_JSONRPC_LIBS:=$(LIBCARTESI_LIB)
-REMOTE_CARTESI_MACHINE_LIBS:=$(GRPC_LIB) $(PROTOBUF_LIB)
-JSONRPC_REMOTE_CARTESI_MACHINE_LIBS:=
-TEST_MACHINE_C_API_LIBS:=$(LIBCARTESI_LIB) $(LIBCARTESI_GRPC_LIB) $(PTHREAD_LIB)
-HASH_LIBS:=
+PICCFLAGS=-fPIC
+SOLDFLAGS=-shared $(PICCFLAGS) $(GCLDFLAGS)
+LIBLDFLAGS=$(SOLDFLAGS)
+EXELDFLAGS=$(GCLDFLAGS)
+PTHREAD_CFLAGS=-pthread
+PTHREAD_LDFLAGS=-pthread -lpthread
+CC=gcc
+CXX=g++
+AR=ar rcs
+INCS=
+
+BOOST_INC=
+GRPC_PROTOBUF_INC=$(shell pkg-config --cflags-only-I grpc++ protobuf)
+GRPC_PROTOBUF_LIB=$(shell pkg-config --libs grpc++ protobuf)
+LIBCARTESI=libcartesi-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).so
+LIBCARTESI_LDFLAGS=
+LIBCARTESI_GRPC=libcartesi_grpc-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).so
+LIBCARTESI_GRPC_LDFLAGS=
+LIBCARTESI_JSONRPC=libcartesi_jsonrpc-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).so
+LIBCARTESI_JSONRPC_LDFLAGS=
+PROFILE_DATA=
+
+endif
+
+LIBCARTESI_LIBS=
+LIBCARTESI_GRPC_LIBS=$(GRPC_PROTOBUF_LIB)
+LIBCARTESI_JSONRPC_LIBS=
+LUACARTESI_LIBS=
+LUACARTESI_GRPC_LIBS=$(GRPC_PROTOBUF_LIB)
+LUACARTESI_JSONRPC_LIBS=
+REMOTE_CARTESI_MACHINE_LIBS=$(GRPC_PROTOBUF_LIB)
+JSONRPC_REMOTE_CARTESI_MACHINE_LIBS=
+TEST_MACHINE_C_API_LIBS=$(GRPC_PROTOBUF_LIB)
+HASH_LIBS=
#DEFS+= -DMT_ALL_DIRTY
-WARNS=-W -Wall -pedantic
+WARNS=-Wall -Wpedantic
# Place our include directories before the system's
-INCS= \
+INCS+= \
-I../third-party/llvm-flang-uint128 \
-I../third-party/tiny_sha3 \
-I../third-party/downloads \
-I../third-party/mongoose-7.12 \
- $(LUA_INC) $(BOOST_INC) $(PROTOBUF_INC) $(GRPC_INC) $(INCS_$(UNAME))
+ $(BOOST_INC)
ifeq ($(dump),yes)
#DEFS+=-DDUMP_ILLEGAL_INSN_EXCEPTIONS
@@ -199,9 +183,6 @@ DEFS+=-DNDEBUG
## Mongoose log is disabled because it generates code using __FILE__ macro,
## which is reported as an error when packaging in some Linux distributions.
MONGOOSE_DEFS+=-DMG_ENABLE_LOG=0
-# Disable jump tables, because it degrades the instruction decoding performance in the interpret loop,
-# since it generates a memory indirection that has a high cost in opcode switches.
-OPTFLAGS+=-fno-jump-tables
ifneq (,$(filter gcc,$(CC)))
# The following flag helps GCC to eliminate more redundant computations in the interpret loop,
# saving some host instructions and improving performance.
@@ -209,6 +190,9 @@ ifneq (,$(filter gcc,$(CC)))
# but we don't use -O3 because it enables some other flags that are not worth for the interpreter.
OPTFLAGS+=-fgcse-after-reload -fpredictive-commoning -fsplit-paths -ftree-partial-pre
endif
+# Disable jump tables, because it degrades the instruction decoding performance in the interpret loop,
+# since it generates a memory indirection that has a high cost in opcode switches.
+OPTFLAGS+=-fno-jump-tables
endif
# Link time optimizations
@@ -268,9 +252,17 @@ EMPTY:=
SPACE:=$(EMPTY) $(EMPTY)
CLANG_TIDY_HEADER_FILTER=$(PWD)/($(subst $(SPACE),|,$(LINTER_HEADERS)))
-CXXFLAGS+=$(OPTFLAGS) -std=c++17 -fvisibility=hidden -fPIC -MMD $(CC_MARCH) $(INCS) $(GCFLAGS) $(UBFLAGS) $(DEFS) $(WARNS) $(MYCFLAGS)
-CFLAGS+=$(OPTFLAGS) -std=gnu99 -fvisibility=hidden -fPIC -MMD $(CC_MARCH) $(INCS) $(GCFLAGS) $(UBFLAGS) $(DEFS) $(WARNS) $(MYCFLAGS)
-LDFLAGS+=$(UBFLAGS) $(MYLDFLAGS)
+ifeq ($(nothreads),no)
+CFLAGS+=$(PTHREAD_CFLAGS)
+CXXFLAGS+=$(PTHREAD_CFLAGS)
+LDFLAGS+=$(PTHREAD_LDFLAGS)
+else
+DEFS+=-DNO_THREADS
+endif
+
+CXXFLAGS+=$(OPTFLAGS) -std=gnu++17 -fvisibility=hidden -MMD $(PICCFLAGS) $(CC_MARCH) $(INCS) $(GCFLAGS) $(UBFLAGS) $(DEFS) $(WARNS)
+CFLAGS+=$(OPTFLAGS) -std=gnu99 -fvisibility=hidden -MMD $(PICCFLAGS) $(CC_MARCH) $(INCS) $(GCFLAGS) $(UBFLAGS) $(DEFS) $(WARNS)
+LDFLAGS+=$(UBFLAGS)
COVERAGE_WORKLOAD=\
dhrystone 100; \
@@ -295,7 +287,14 @@ $(error invalid value for COVERAGE_TOOLCHAIN: $(COVERAGE_TOOLCHAIN))
endif
endif
-all: luacartesi grpc hash c-api jsonrpc-remote-cartesi-machine
+CXXFLAGS+=$(MYCXXFLAGS) $(MYDEFS)
+CFLAGS+=$(MYCFLAGS) $(MYDEFS)
+LDFLAGS+=$(MYLDFLAGS)
+SOLDFLAGS+=$(MYSOLDFLAGS)
+LIBLDFLAGS+=$(MYLIBLDFLAGS)
+EXELDFLAGS+=$(MYEXELDFLAGS)
+
+all: libcartesi.a libcartesi_jsonrpc.a c-api luacartesi jsonrpc-remote-cartesi-machine grpc hash
.PHONY: all generate use clean test lint format format-lua check-format check-format-lua luacartesi grpc hash c-api compile_flags.txt
@@ -304,7 +303,7 @@ LIBCARTESI_OBJS:= \
clint.o \
clint-factory.o \
dtb.o \
- tty.o \
+ os.o \
htif.o \
htif-factory.o \
shadow-state.o \
@@ -323,19 +322,21 @@ LIBCARTESI_OBJS:= \
base64.o \
interpret.o \
virtual-machine.o \
- machine-c-api.o \
uarch-machine.o \
uarch-step.o \
- uarch-interpret.o
+ uarch-interpret.o \
+ machine-c-api.o
+
+CARTESI_CLUA_OBJS:= \
+ clua.o \
+ clua-i-virtual-machine.o \
+ clua-htif.o \
+ clua-machine-util.o
LUACARTESI_OBJS:= \
- sha3.o \
clua-cartesi.o \
- clua-i-virtual-machine.o \
clua-machine.o \
- clua-htif.o \
- clua-machine-util.o \
- clua.o
+ $(CARTESI_CLUA_OBJS)
PROTOBUF_GEN_OBJS:= \
versioning.pb.o \
@@ -347,41 +348,35 @@ GRPC_GEN_OBJS:= \
cartesi-machine-checkin.grpc.pb.o
LIBCARTESI_GRPC_OBJS:= \
- sha3.o \
- machine-merkle-tree.o \
- pristine-merkle-tree.o \
- $(GRPC_GEN_OBJS) \
- $(PROTOBUF_GEN_OBJS) \
grpc-virtual-machine.o \
+ grpc-machine-c-api.o \
protobuf-util.o \
- grpc-machine-c-api.o
+ $(GRPC_GEN_OBJS) \
+ $(PROTOBUF_GEN_OBJS)
+
+LIBCARTESI_JSONRPC_OBJS:= \
+ jsonrpc-virtual-machine.o \
+ jsonrpc-machine-c-api.o \
+ mongoose.o
LUACARTESI_GRPC_OBJS:= \
- clua-i-virtual-machine.o \
- clua-htif.o \
- clua-machine-util.o \
clua-cartesi-grpc.o \
clua-grpc-machine.o \
- clua.o
+ $(CARTESI_CLUA_OBJS)
LUACARTESI_JSONRPC_OBJS:= \
- clua-i-virtual-machine.o \
- clua-htif.o \
- clua-machine-util.o \
clua-cartesi-jsonrpc.o \
clua-jsonrpc-machine.o \
- json-util.o \
- base64.o \
- mongoose.o \
- jsonrpc-machine-c-api.o \
- jsonrpc-virtual-machine.o \
- clua.o
+ $(CARTESI_CLUA_OBJS)
ifeq ($(gperf),yes)
DEFS+=-DGPERF
LIBCARTESI_LIBS+=-lprofiler
LIBCARTESI_GRPC_LIBS+=-lprofiler
+LIBCARTESI_JSONRPC_LIBS+=-lprofiler
LUACARTESI_LIBS+=-lprofiler
+LUACARTESI_GRPC_LIBS+=-lprofiler
+LUACARTESI_JSONRPC_LIBS+=-lprofiler
endif
version:
@@ -390,20 +385,39 @@ version:
so-version:
@echo $(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR)
+libcartesi: libcartesi.a $(LIBCARTESI)
+libcartesi.so: $(LIBCARTESI)
+libcartesi_grpc: libcartesi_grpc.a $(LIBCARTESI_GRPC)
+libcartesi_grpc.so: $(LIBCARTESI_GRPC)
+libcartesi_jsonrpc: libcartesi_jsonrpc.a $(LIBCARTESI_JSONRPC)
+libcartesi_jsonrpc.so: $(LIBCARTESI_JSONRPC)
+
+libcartesi.a: $(LIBCARTESI_OBJS)
+ $(AR) $@ $^
+
+libcartesi_grpc.a: $(LIBCARTESI_GRPC_OBJS)
+ $(AR) $@ $^
+
+libcartesi_jsonrpc.a: $(LIBCARTESI_JSONRPC_OBJS)
+ $(AR) $@ $^
+
$(LIBCARTESI): $(LIBCARTESI_OBJS)
- $(CXX) $(LDFLAGS) $(LIBCARTESI_LDFLAGS) -o $@ $(LIBCARTESI_OBJS) $(LIBCARTESI_LIBS)
+ $(CXX) -o $@ $^ $(LIBCARTESI_LIBS) $(LDFLAGS) $(LIBCARTESI_LDFLAGS) $(LIBLDFLAGS)
+
+$(LIBCARTESI_GRPC): $(LIBCARTESI_GRPC_OBJS) libcartesi.a
+ $(CXX) -o $@ $^ $(LIBCARTESI_GRPC_LIBS) $(LDFLAGS) $(LIBCARTESI_GRPC_LDFLAGS) $(LIBLDFLAGS)
-$(LIBCARTESI_GRPC): $(LIBCARTESI_GRPC_OBJS) $(LIBCARTESI)
- $(CXX) $(LDFLAGS) $(LIBCARTESI_GRPC_LDFLAGS) -o $@ $(LIBCARTESI_GRPC_OBJS) $(LIBCARTESI_GRPC_LIBS) $(LIBCARTESI_LIB)
+$(LIBCARTESI_JSONRPC): $(LIBCARTESI_JSONRPC_OBJS) libcartesi.a
+ $(CXX) -o $@ $^ $(LIBCARTESI_JSONRPC_LIBS) $(LDFLAGS) $(LIBCARTESI_JSONRPC_LDFLAGS) $(LIBLDFLAGS)
-cartesi.so: $(LUACARTESI_OBJS) $(LIBCARTESI)
- $(CXX) $(LDFLAGS) $(SOLDFLAGS) -o $@ $(LUACARTESI_OBJS) $(LUACARTESI_LIBS)
+cartesi.so: $(LUACARTESI_OBJS) libcartesi.a
+ $(CXX) -o $@ $^ $(LUACARTESI_LIBS) $(LDFLAGS) $(SOLDFLAGS)
-cartesi/grpc.so: $(LUACARTESI_GRPC_OBJS) $(LIBCARTESI_GRPC) $(LIBCARTESI)
- $(CXX) $(LDFLAGS) $(SOLDFLAGS) -o $@ $(LUACARTESI_GRPC_OBJS) $(LUACARTESI_GRPC_LIBS)
+cartesi/grpc.so: $(LUACARTESI_GRPC_OBJS) libcartesi_grpc.a libcartesi.a
+ $(CXX) -o $@ $^ $(LUACARTESI_GRPC_LIBS) $(LDFLAGS) $(SOLDFLAGS)
-cartesi/jsonrpc.so: $(LUACARTESI_JSONRPC_OBJS) $(LIBCARTESI_JSONRPC) $(LIBCARTESI)
- $(CXX) $(LDFLAGS) $(SOLDFLAGS) -o $@ $(LUACARTESI_JSONRPC_OBJS) $(LUACARTESI_JSONRPC_LIBS)
+cartesi/jsonrpc.so: $(LUACARTESI_JSONRPC_OBJS) libcartesi_jsonrpc.a libcartesi.a
+ $(CXX) -o $@ $^ $(LUACARTESI_JSONRPC_LIBS) $(LDFLAGS) $(SOLDFLAGS)
test: luacartesi
$(LUA) cartesi-machine-tests.lua --test-path="$(CARTESI_TESTS_PATH)" --test=".*" run
@@ -483,8 +497,8 @@ coverage-report:
./cartesi.so \
-object ./cartesi/grpc.so \
-object ./cartesi/jsonrpc.so \
- -object ./libcartesi.so \
- -object ./libcartesi_grpc.so \
+ -object ./$(LIBCARTESI) \
+ -object ./$(LIBCARTESI_GRPC) \
-object ./tests/test-merkle-tree-hash \
-object ./tests/test-machine-c-api \
-object ./remote-cartesi-machine \
@@ -496,12 +510,14 @@ valgrind: luacartesi
valgrind --leak-check=full --tool=memcheck --track-origins=yes $(LUA_BIN) cartesi-machine-tests.lua --test-path="$(CARTESI_TESTS_PATH)" --test=".*" run
valgrind --leak-check=full --tool=memcheck --track-origins=yes $(LUA_BIN) cartesi-machine.lua --initial-hash --final-hash -- /bin/true
-$(PROFILE_DATA_Darwin):
+ifeq ($(TARGET_OS),Darwin)
+$(PROFILE_DATA):
llvm-profdata merge -output=default.profdata default*.profraw
+endif
use: CXXFLAGS += -fprofile-use -Wno-missing-profile
use: LDFLAGS += -fprofile-use
-use: $(PROFILE_DATA_$(UNAME)) luacartesi
+use: $(PROFILE_DATA) luacartesi
compile_flags.txt:
@echo "$(CXXFLAGS)" "-xc++" | sed -e $$'s/ \{1,\}/\\\n/g' | grep -v "MMD" > $@
@@ -521,7 +537,7 @@ jsonrpc: cartesi/jsonrpc.so jsonrpc-remote-cartesi-machine
hash: merkle-tree-hash tests/test-merkle-tree-hash
-c-api: $(LIBCARTESI) $(LIBCARTESI_GRPC) tests/test-machine-c-api
+c-api: $(LIBCARTESI) $(LIBCARTESI_GRPC) $(LIBCARTESI_JSONRPC) tests/test-machine-c-api
MERKLE_TREE_HASH_OBJS:= \
sha3.o \
@@ -539,9 +555,7 @@ TEST_MERKLE_TREE_HASH_OBJS:= \
TEST_MACHINE_C_API_OBJS:= \
test-machine-c-api.o \
- sha3.o \
- back-merkle-tree.o \
- pristine-merkle-tree.o
+ back-merkle-tree.o
PROTO_OBJS:= \
$(PROTOBUF_GEN_OBJS) \
@@ -552,67 +566,14 @@ $(PROTO_OBJS): CXXFLAGS += -Wno-zero-length-array -Wno-unused-parameter -Wno-de
PROTO_SOURCES:=$(PROTO_OBJS:.o=.cc)
REMOTE_CARTESI_MACHINE_OBJS:= \
- $(GRPC_GEN_OBJS) \
- $(PROTOBUF_GEN_OBJS) \
- slog.o \
remote-machine.o \
- protobuf-util.o \
- pma-driver.o \
- clint.o \
- clint-factory.o \
- dtb.o \
- tty.o \
- htif.o \
- htif-factory.o \
- shadow-state.o \
- shadow-state-factory.o \
- shadow-pmas.o \
- shadow-pmas-factory.o \
- shadow-tlb.o \
- shadow-tlb-factory.o \
- sha3.o \
- machine-merkle-tree.o \
- pristine-merkle-tree.o \
- pma.o \
- machine.o \
- machine-config.o \
- json-util.o \
- base64.o \
- interpret.o \
- uarch-machine.o \
- uarch-step.o \
- uarch-interpret.o
+ slog.o
JSONRPC_REMOTE_CARTESI_MACHINE_OBJS:= \
- slog.o \
jsonrpc-remote-machine.o \
jsonrpc-discover.o \
mongoose.o \
- pma-driver.o \
- clint.o \
- clint-factory.o \
- dtb.o \
- tty.o \
- htif.o \
- htif-factory.o \
- json-util.o \
- base64.o \
- shadow-state.o \
- shadow-state-factory.o \
- shadow-pmas.o \
- shadow-pmas-factory.o \
- shadow-tlb.o \
- shadow-tlb-factory.o \
- sha3.o \
- machine-merkle-tree.o \
- pristine-merkle-tree.o \
- pma.o \
- machine.o \
- machine-config.o \
- interpret.o \
- uarch-machine.o \
- uarch-step.o \
- uarch-interpret.o
+ slog.o
CYCLE_PERIOD ?= 13
HASH_DIR = hashes/$(CYCLE_PERIOD)
@@ -676,23 +637,25 @@ $(GROUND_TRUTH_DIR)/$(LOG_DIR)/%.json.br: $(CARTESI_TESTS_PATH)/%.bin $(GROUND_T
brotli -j -f $(basename $@)
merkle-tree-hash: $(MERKLE_TREE_HASH_OBJS)
- $(CXX) $(LDFLAGS) $(CARTESI_EXECUTABLE_LDFLAGS) -o $@ $^ $(HASH_LIBS)
+ $(CXX) -o $@ $^ $(HASH_LIBS) $(LDFLAGS) $(EXELDFLAGS)
tests/test-merkle-tree-hash: $(TEST_MERKLE_TREE_HASH_OBJS)
- $(CXX) $(LDFLAGS) $(CARTESI_EXECUTABLE_LDFLAGS) -o $@ $^ $(HASH_LIBS)
+ $(CXX) -o $@ $^ $(HASH_LIBS) $(LDFLAGS) $(EXELDFLAGS)
grpc-interfaces: $(PROTO_SOURCES)
-remote-cartesi-machine: $(REMOTE_CARTESI_MACHINE_OBJS)
- $(CXX) $(LDFLAGS) $(CARTESI_EXECUTABLE_LDFLAGS) -o $@ $(REMOTE_CARTESI_MACHINE_OBJS) $(REMOTE_CARTESI_MACHINE_LIBS)
+remote-cartesi-machine: $(REMOTE_CARTESI_MACHINE_OBJS) libcartesi_grpc.a libcartesi.a
+ $(CXX) -o $@ $^ $(REMOTE_CARTESI_MACHINE_LIBS) $(LDFLAGS) $(EXELDFLAGS)
+
+jsonrpc-remote-cartesi-machine: $(JSONRPC_REMOTE_CARTESI_MACHINE_OBJS) libcartesi_jsonrpc.a libcartesi.a
+ $(CXX) -o $@ $^ $(JSONRPC_REMOTE_CARTESI_MACHINE_LIBS) $(LDFLAGS) $(EXELDFLAGS)
-jsonrpc-remote-cartesi-machine: $(JSONRPC_REMOTE_CARTESI_MACHINE_OBJS)
- $(CXX) $(LDFLAGS) $(CARTESI_EXECUTABLE_LDFLAGS) -o $@ $(JSONRPC_REMOTE_CARTESI_MACHINE_OBJS) $(JSONRPC_REMOTE_CARTESI_MACHINE_LIBS)
+$(PROTOBUF_GEN_OBJS) $(GRPC_GEN_OBJS) protobuf-util.o grpc-virtual-machine.o grpc-machine-c-api.o remote-machine.o remote-cartesi-machine: CXXFLAGS += $(GRPC_PROTOBUF_INC)
-remote-cartesi-machine: CXXFLAGS := $(PROTOBUF_INC) $(GRPC_INC) $(CXXFLAGS)
+clua-%.o clua.o: CXXFLAGS += $(LUA_INC)
-tests/test-machine-c-api: $(TEST_MACHINE_C_API_OBJS) $(LIBCARTESI) $(LIBCARTESI_GRPC)
- $(CXX) $(LDFLAGS) $(CARTESI_EXECUTABLE_LDFLAGS) -o $@ $(TEST_MACHINE_C_API_OBJS) $(TEST_MACHINE_C_API_LIBS)
+tests/test-machine-c-api: $(TEST_MACHINE_C_API_OBJS) libcartesi_grpc.a libcartesi.a
+ $(CXX) -o $@ $^ $(TEST_MACHINE_C_API_LIBS) $(LDFLAGS) $(EXELDFLAGS)
.PRECIOUS: %.grpc.pb.cc %.grpc.pb.h %.pb.cc %.pb.h
@@ -718,8 +681,8 @@ jsonrpc-discover.cpp: jsonrpc-discover.json
echo '} // namespace cartesi' >> jsonrpc-discover.cpp
%.clang-tidy: %.cpp $(PROTO_SOURCES) machine-c-version.h
- @$(CLANG_TIDY) --header-filter='$(CLANG_TIDY_HEADER_FILTER)' $< -- $(CXXFLAGS) 2>/dev/null
- @$(CXX) $(CXXFLAGS) $< -MM -MT $@ -MF $@.d > /dev/null 2>&1
+ @$(CLANG_TIDY) --header-filter='$(CLANG_TIDY_HEADER_FILTER)' $< -- $(CXXFLAGS) $(LUA_INC) $(GRPC_PROTOBUF_INC) 2>/dev/null
+ @$(CXX) $(CXXFLAGS) $(LUA_INC) $(GRPC_PROTOBUF_INC) $< -MM -MT $@ -MF $@.d > /dev/null 2>&1
@touch $@
%.clang-tidy: %.c $(PROTO_SOURCES)
@@ -757,7 +720,7 @@ clean-objs:
@rm -f *.o *.d
clean-libcartesi: clean-objs
- @rm -f $(LIBCARTESI) $(LIBCARTESI_GRPC) cartesi.so cartesi/grpc.so cartesi/jsonrpc.so
+ @rm -f *.so *.a cartesi/*.so
clean-executables:
@rm -f jsonrpc-remote-cartesi-machine remote-cartesi-machine merkle-tree-hash
diff --git a/src/htif.cpp b/src/htif.cpp
index c6d3fa5de..d124b1feb 100644
--- a/src/htif.cpp
+++ b/src/htif.cpp
@@ -17,9 +17,9 @@
#include "htif.h"
#include "i-device-state-access.h"
#include "machine-runtime-config.h"
+#include "os.h"
#include "pma-constants.h"
#include "strict-aliasing.h"
-#include "tty.h"
namespace cartesi {
@@ -101,7 +101,7 @@ static execute_status htif_console(htif_runtime_config *runtime_config, i_device
// In microarchitecture runtime_config will always be nullptr,
// therefore the HTIF runtime config is actually ignored.
if (!runtime_config || !runtime_config->no_console_putchar) {
- tty_putchar(ch);
+ os_putchar(ch);
}
a->write_htif_fromhost(HTIF_BUILD(HTIF_DEVICE_CONSOLE, cmd, 0));
} else if (cmd == HTIF_CONSOLE_GETCHAR) {
@@ -109,7 +109,7 @@ static execute_status htif_console(htif_runtime_config *runtime_config, i_device
// to every participant in a dispute: where would c come from? So if the code reached here in the
// blockchain, there must be some serious bug
// In interactive mode, we just get the next character from the console and send it back in the ack
- const int c = tty_getchar();
+ const int c = os_getchar();
a->write_htif_fromhost(HTIF_BUILD(HTIF_DEVICE_CONSOLE, cmd, c));
}
}
diff --git a/src/interpret.cpp b/src/interpret.cpp
index 048347e2a..4e03b6849 100644
--- a/src/interpret.cpp
+++ b/src/interpret.cpp
@@ -5324,7 +5324,7 @@ static FORCE_INLINE execute_status execute_insn(STATE_ACCESS &a, uint64_t &pc, u
return execute_ADDW_MULW_SUBW(a, pc, insn);
case insn_funct3_00000_opcode::SRLW_DIVUW_SRAW:
return execute_SRLW_DIVUW_SRAW(a, pc, insn);
- case insn_funct3_00000_opcode::privileged:
+ case insn_funct3_00000_opcode::PRIVILEGED:
return execute_privileged(a, pc, mcycle, insn);
default: {
// Here we are sure that the next instruction, at best, can only be a floating point instruction,
@@ -5504,6 +5504,7 @@ static FORCE_INLINE fetch_status fetch_insn(STATE_ACCESS &a, uint64_t &pc, uint3
/// \brief Checks that false brk is consistent with rest of state
template
static void assert_no_brk(STATE_ACCESS &a) {
+ (void) a;
assert(get_pending_irq_mask(a) == 0); // LCOV_EXCL_LINE
assert(a.read_iflags_X() == 0); // LCOV_EXCL_LINE
assert(a.read_iflags_Y() == 0); // LCOV_EXCL_LINE
diff --git a/src/json-util.h b/src/json-util.h
index cfcd3f919..54f9ce670 100644
--- a/src/json-util.h
+++ b/src/json-util.h
@@ -21,6 +21,8 @@
#include
#include
+// Disable JSON filesystem support because it is not supported in some targets
+#define JSON_HAS_FILESYSTEM 0
#include
#include "base64.h"
@@ -39,9 +41,7 @@ std::string to_string(const char *s);
// Generate a new optional-like type
template
-struct new_optional : public std::optional {
- using std::optional::optional;
-};
+struct new_optional : public std::optional {};
// Optional-like type used by parse_args function to identify an optional parameter
template
diff --git a/src/jsonrpc-remote-machine.cpp b/src/jsonrpc-remote-machine.cpp
index d086f618a..c6738784f 100644
--- a/src/jsonrpc-remote-machine.cpp
+++ b/src/jsonrpc-remote-machine.cpp
@@ -37,7 +37,6 @@
#include
#include
-#include
#include
#include "base64.h"
diff --git a/src/jsonrpc-virtual-machine.cpp b/src/jsonrpc-virtual-machine.cpp
index 939630c11..335f1289f 100644
--- a/src/jsonrpc-virtual-machine.cpp
+++ b/src/jsonrpc-virtual-machine.cpp
@@ -19,7 +19,6 @@
#include
#include
-#include
#include
#include "jsonrpc-mg-mgr.h"
diff --git a/src/machine-c-api-internal.h b/src/machine-c-api-internal.h
index 8f5a09369..d7caf2cd3 100644
--- a/src/machine-c-api-internal.h
+++ b/src/machine-c-api-internal.h
@@ -25,42 +25,42 @@
#include "semantic-version.h"
/// \brief Helper function that returns error result from C api function
-CM_API int cm_result_failure(char **err_msg);
+int cm_result_failure(char **err_msg);
/// \brief Helper function that returns success result from C api function
-CM_API int cm_result_success(char **err_msg);
+int cm_result_success(char **err_msg);
/// \brief Helper function that create empty string in case
/// that C string is NULL
-CM_API std::string null_to_empty(const char *s);
+std::string null_to_empty(const char *s);
/// \brief Helper function that parses machine configuration cartesi::machine_config
/// from C api structure cm_machine_config
-CM_API cartesi::machine_config convert_from_c(const cm_machine_config *c_config);
+cartesi::machine_config convert_from_c(const cm_machine_config *c_config);
/// \brief Helper function that parses machine runtime configuration
/// from C api structure cm_machine_runtime_config
-CM_API cartesi::machine_runtime_config convert_from_c(const cm_machine_runtime_config *c_config);
+cartesi::machine_runtime_config convert_from_c(const cm_machine_runtime_config *c_config);
/// \brief Helper function converts machine configuration to C api structure
-CM_API cm_machine_config *convert_to_c(const cartesi::machine_config &cpp_config);
+cm_machine_config *convert_to_c(const cartesi::machine_config &cpp_config);
/// \brief Helper function converts a semantic version to C api structure
-CM_API cm_semantic_version *convert_to_c(const cartesi::semantic_version &cpp_version);
+cm_semantic_version *convert_to_c(const cartesi::semantic_version &cpp_version);
/// \brief Helper function that parses hash from C api structure
-CM_API cartesi::machine_merkle_tree::hash_type convert_from_c(const cm_hash *c_hash);
+cartesi::machine_merkle_tree::hash_type convert_from_c(const cm_hash *c_hash);
/// \brief Helper function that parses access log tyoe from C
-CM_API cartesi::access_log::type convert_from_c(const cm_access_log_type *type);
+cartesi::access_log::type convert_from_c(const cm_access_log_type *type);
/// \brief Helper function that parses access log from C api structure
-CM_API cm_access_log *convert_to_c(const cartesi::access_log &cpp_access_log);
+cm_access_log *convert_to_c(const cartesi::access_log &cpp_access_log);
/// \brief Helper function converts access log to C api structure
-CM_API cartesi::access_log convert_from_c(const cm_access_log *c_acc_log);
+cartesi::access_log convert_from_c(const cm_access_log *c_acc_log);
/// \brief Helper function converts C++ string to allocated C string
-CM_API char *convert_to_c(const std::string &cpp_str);
+char *convert_to_c(const std::string &cpp_str);
#endif // CM_C_API_INTERNAL_H
diff --git a/src/machine-c-api.cpp b/src/machine-c-api.cpp
index 60db702ac..fc80358d2 100644
--- a/src/machine-c-api.cpp
+++ b/src/machine-c-api.cpp
@@ -14,12 +14,10 @@
// with this program (see COPYING). If not, see .
//
-#include
#include
#include
#include
-#include
-#include
+#include
#include
#include
#include
@@ -68,8 +66,6 @@ int cm_result_failure(char **err_msg) try { throw; } catch (std::exception &e) {
return CM_ERROR_LENGTH_ERROR;
} catch (std::out_of_range &ex) {
return CM_ERROR_OUT_OF_RANGE;
- } catch (std::future_error &ex) {
- return CM_ERROR_FUTURE_ERROR;
} catch (std::logic_error &ex) {
return CM_ERROR_LOGIC_ERROR;
} catch (std::bad_optional_access &ex) {
@@ -84,8 +80,6 @@ int cm_result_failure(char **err_msg) try { throw; } catch (std::exception &e) {
return CM_ERROR_REGEX_ERROR;
} catch (std::ios_base::failure &ex) {
return CM_ERROR_SYSTEM_IOS_BASE_FAILURE;
- } catch (std::filesystem::filesystem_error &ex) {
- return CM_ERROR_FILESYSTEM_ERROR;
} catch (std::runtime_error &ex) {
return CM_ERROR_RUNTIME_ERROR;
} catch (std::bad_typeid &ex) {
diff --git a/src/machine-c-api.h b/src/machine-c-api.h
index c290b60d9..4cae7c3cf 100644
--- a/src/machine-c-api.h
+++ b/src/machine-c-api.h
@@ -55,7 +55,6 @@ typedef enum { // NOLINT(modernize-use-using)
CM_ERROR_DOMAIN_ERROR,
CM_ERROR_LENGTH_ERROR,
CM_ERROR_OUT_OF_RANGE,
- CM_ERROR_FUTURE_ERROR,
CM_ERROR_LOGIC_ERROR,
CM_LOGIC_ERROR_END,
// Bad optional access error
diff --git a/src/machine.cpp b/src/machine.cpp
index a1d811446..6de5ee927 100644
--- a/src/machine.cpp
+++ b/src/machine.cpp
@@ -15,17 +15,11 @@
//
#include
-#include
#include
#include
#include
-#include
-#include
#include
#include
-#include
-#include
-#include
#include "clint-factory.h"
#include "dtb.h"
@@ -367,13 +361,19 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) :
const std::string flash_description = "flash drive "s + std::to_string(i++);
// Auto detect flash drive image length
if (f.length == UINT64_C(-1)) {
- std::error_code ec;
- f.length = std::filesystem::file_size(f.image_filename, ec);
- if (ec) {
- throw std::system_error{ec.value(), ec.category(),
+ auto fp = unique_fopen(f.image_filename.c_str(), "rb");
+ if (fseek(fp.get(), 0, SEEK_END) != 0) {
+ throw std::system_error{errno, std::generic_category(),
"unable to obtain length of image file '"s + f.image_filename + "' when initializing "s +
flash_description};
}
+ const auto length = ftell(fp.get());
+ if (length < 0) {
+ throw std::system_error{errno, std::generic_category(),
+ "unable to obtain length of image file '"s + f.image_filename + "' when initializing "s +
+ flash_description};
+ }
+ f.length = length;
}
register_pma_entry(make_flash_drive_pma_entry(flash_description, f));
}
@@ -469,7 +469,7 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) :
// Initialize TTY if console input is enabled
if (m_c.htif.console_getchar) {
- tty_initialize();
+ os_open_tty();
}
// Initialize memory range descriptions returned by get_memory_ranges method
@@ -697,7 +697,7 @@ static void store_hash(const machine::hash_type &h, const std::string &dir) {
}
void machine::store(const std::string &dir) const {
- if (mkdir(dir.c_str(), 0700)) {
+ if (os_mkdir(dir.c_str(), 0700)) {
throw std::runtime_error{"error creating directory '" + dir + "'"};
}
if (!update_merkle_tree()) {
@@ -715,7 +715,7 @@ void machine::store(const std::string &dir) const {
machine::~machine() {
// Cleanup TTY if console input was enabled
if (m_c.htif.console_getchar) {
- tty_finalize();
+ os_close_tty();
}
#ifdef DUMP_HIST
(void) fprintf(stderr, "\nInstruction Histogram:\n");
@@ -1384,14 +1384,6 @@ void machine::set_iflags_H(void) {
m_s.iflags.H = true;
}
-#if 0 // Unused
-static double now(void) {
- using namespace std::chrono;
- return static_cast(duration_cast(high_resolution_clock::now().time_since_epoch()).count()) *
- 1.e-6;
-}
-#endif
-
void machine::mark_write_tlb_dirty_pages(void) const {
for (uint64_t i = 0; i < PMA_TLB_SIZE; ++i) {
const tlb_hot_entry &tlbhe = m_s.tlb.hot[TLB_WRITE][i];
@@ -1404,7 +1396,6 @@ void machine::mark_write_tlb_dirty_pages(void) const {
}
bool machine::verify_dirty_page_maps(void) const {
- // double begin = now();
static_assert(PMA_PAGE_SIZE == machine_merkle_tree::get_page_size(),
"PMA and machine_merkle_tree page sizes must match");
machine_merkle_tree::hasher_type h;
@@ -1453,13 +1444,12 @@ bool machine::verify_dirty_page_maps(void) const {
}
static uint64_t get_task_concurrency(uint64_t value) {
- const uint64_t concurrency = value > 0 ? value : std::max(std::thread::hardware_concurrency(), 1U);
+ const uint64_t concurrency = value > 0 ? value : std::max(os_get_concurrency(), UINT64_C(1));
return std::min(concurrency, static_cast(THREADS_MAX));
}
bool machine::update_merkle_tree(void) const {
machine_merkle_tree::hasher_type gh;
- // double begin = now();
static_assert(PMA_PAGE_SIZE == machine_merkle_tree::get_page_size(),
"PMA and machine_merkle_tree page sizes must match");
// Go over the write TLB and mark as dirty all pages currently there
@@ -1473,68 +1463,55 @@ bool machine::update_merkle_tree(void) const {
// For each PMA, we launch as many threads (n) as defined on concurrency
// runtime config or as the hardware supports.
const uint64_t n = get_task_concurrency(m_r.concurrency.update_merkle_tree);
- // The update_page_node_hash function in the machine_merkle_tree is not thread
- // safe, so we protect it with a mutex
- std::mutex updatex;
- // Each thread is launched as a future, whose value tells if the
- // computation succeeded
- std::vector> futures;
- futures.reserve(n);
- for (uint64_t j = 0; j < n; ++j) {
- futures.emplace_back(std::async((n == 1) ? std::launch::deferred : std::launch::async,
- [&](int j) -> bool {
- auto scratch = unique_calloc(PMA_PAGE_SIZE, std::nothrow_t{});
- if (!scratch) {
- return false;
- }
- machine_merkle_tree::hasher_type h;
- // Thread j is responsible for page i if i % n == j.
- for (uint64_t i = j; i < pages_in_range; i += n) {
- const uint64_t page_start_in_range = i * PMA_PAGE_SIZE;
- const uint64_t page_address = pma->get_start() + page_start_in_range;
- const unsigned char *page_data = nullptr;
- // Skip any clean pages
- if (!pma->is_page_marked_dirty(page_start_in_range)) {
- continue;
- }
- // If the peek failed, or if it returned a page for update but
- // we failed updating it, the entire process failed
- if (!peek(*pma, *this, page_start_in_range, &page_data, scratch.get())) {
+ const bool succeeded = os_parallel_for(n, [&](int j, const parallel_for_mutex &mutex) -> bool {
+ auto scratch = unique_calloc(PMA_PAGE_SIZE, std::nothrow_t{});
+ if (!scratch) {
+ return false;
+ }
+ machine_merkle_tree::hasher_type h;
+ // Thread j is responsible for page i if i % n == j.
+ for (uint64_t i = j; i < pages_in_range; i += n) {
+ const uint64_t page_start_in_range = i * PMA_PAGE_SIZE;
+ const uint64_t page_address = pma->get_start() + page_start_in_range;
+ const unsigned char *page_data = nullptr;
+ // Skip any clean pages
+ if (!pma->is_page_marked_dirty(page_start_in_range)) {
+ continue;
+ }
+ // If the peek failed, or if it returned a page for update but
+ // we failed updating it, the entire process failed
+ if (!peek(*pma, *this, page_start_in_range, &page_data, scratch.get())) {
+ return false;
+ }
+ if (page_data) {
+ const bool is_pristine = std::all_of(page_data, page_data + PMA_PAGE_SIZE,
+ [](unsigned char pp) -> bool { return pp == '\0'; });
+
+ if (is_pristine) {
+ // The update_page_node_hash function in the machine_merkle_tree is not thread
+ // safe, so we protect it with a mutex
+ const parallel_for_mutex_guard lock(mutex);
+ if (!m_t.update_page_node_hash(page_address,
+ machine_merkle_tree::get_pristine_hash(machine_merkle_tree::get_log2_page_size()))) {
return false;
}
- if (page_data) {
- const bool is_pristine = std::all_of(page_data, page_data + PMA_PAGE_SIZE,
- [](unsigned char pp) -> bool { return pp == '\0'; });
-
- if (is_pristine) {
- const std::lock_guard lock(updatex);
- if (!m_t.update_page_node_hash(page_address,
- machine_merkle_tree::get_pristine_hash(
- machine_merkle_tree::get_log2_page_size()))) {
- return false;
- }
- } else {
- hash_type hash;
- m_t.get_page_node_hash(h, page_data, hash);
- {
- const std::lock_guard lock(updatex);
- if (!m_t.update_page_node_hash(page_address, hash)) {
- return false;
- }
- }
+ } else {
+ hash_type hash;
+ m_t.get_page_node_hash(h, page_data, hash);
+ {
+ // The update_page_node_hash function in the machine_merkle_tree is not thread
+ // safe, so we protect it with a mutex
+ const parallel_for_mutex_guard lock(mutex);
+ if (!m_t.update_page_node_hash(page_address, hash)) {
+ return false;
}
}
}
- return true;
- },
- j));
- }
- // Check if any thread failed
- bool succeeded = true;
- for (auto &f : futures) {
- succeeded = succeeded && f.get();
- }
- // If so, we also failed
+ }
+ }
+ return true;
+ });
+ // If any thread failed, we also failed
if (!succeeded) {
m_t.end_update(gh);
return false;
@@ -1542,10 +1519,7 @@ bool machine::update_merkle_tree(void) const {
// Otherwise, mark all pages in PMA as clean and move on to next
pma->mark_pages_clean();
}
- // std::cerr << "page updates done in " << now()-begin << "s\n";
- // begin = now();
const bool ret = m_t.end_update(gh);
- // std::cerr << "inner tree updates done in " << now()-begin << "s\n";
return ret;
}
diff --git a/src/os.cpp b/src/os.cpp
new file mode 100644
index 000000000..0236f6684
--- /dev/null
+++ b/src/os.cpp
@@ -0,0 +1,568 @@
+// Copyright Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or (at your option) any
+// later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License along
+// with this program (see COPYING). If not, see .
+//
+
+#if !defined(NO_TTY)
+#define HAVE_TTY
+#endif
+
+#if !defined(NO_THREADS)
+#define HAVE_THREADS
+#endif
+
+#if !defined(_WIN32) && !defined(__wasi__) && !defined(NO_TERMIOS)
+#define HAVE_TERMIOS
+#endif
+
+#if !defined(_WIN32) && !defined(__wasi__) && !defined(NO_MMAP)
+#define HAVE_MMAP
+#endif
+
+#if !defined(_WIN32) && !defined(NO_MKDIR)
+#define HAVE_MKDIR
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "os.h"
+#include "unique-c-ptr.h"
+
+#ifdef HAVE_THREADS
+#include
+#include
+#include
+#endif
+
+#if defined(HAVE_TTY) || defined(HAVE_MMAP) || defined(HAVE_TERMIOS) || defined(_WIN32)
+#include // open
+#endif
+
+#ifdef HAVE_TERMIOS
+#include // tcgetattr/tcsetattr
+#endif
+
+#ifdef HAVE_MMAP
+#include // mmap/munmap
+#endif
+
+#if defined(HAVE_MMAP) || defined(HAVE_MKDIR) || defined(_WIN32)
+#include // fstat/mkdir
+#endif
+
+#ifdef _WIN32
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include // mkdir
+#include // _write/_close
+#include
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 0
+#endif
+
+#else // not _WIN32
+
+#if defined(HAVE_TTY) || defined(HAVE_MMAP) || defined(HAVE_TERMIOS)
+#include // write/read/close
+#endif
+
+#if defined(HAVE_TTY)
+#include // select
+#endif
+
+#endif // _WIN32
+
+namespace cartesi {
+
+using namespace std::string_literals;
+
+#ifdef HAVE_TTY
+/// \brief TTY global state
+struct tty_state {
+ bool initialized{false};
+ std::array buf{}; // Characters in console input buffer
+ intptr_t buf_pos{};
+ intptr_t buf_len{};
+#ifdef HAVE_TERMIOS
+ int ttyfd{-1};
+ termios oldtty{};
+#elif defined(_WIN32)
+ HANDLE hStdin{};
+ DWORD dwOldStdinMode{};
+#endif
+};
+
+/// Returns pointer to the global TTY state
+static tty_state *get_state() {
+ static tty_state data;
+ return &data;
+}
+#endif // HAVE_TTY
+
+#ifdef HAVE_TERMIOS
+static int new_ttyfd(const char *path) {
+ int fd{};
+ do { // NOLINT(cppcoreguidelines-avoid-do-while)
+ fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK);
+ } while (fd == -1 && errno == EINTR);
+ return fd;
+}
+
+static int get_ttyfd(void) {
+ char *path{};
+ // NOLINTBEGIN(bugprone-assignment-in-if-condition)
+ if ((path = ttyname(STDERR_FILENO)) != nullptr) {
+ return new_ttyfd(path);
+ } else if ((path = ttyname(STDOUT_FILENO)) != nullptr) {
+ return new_ttyfd(path);
+ } else if ((path = ttyname(STDIN_FILENO)) != nullptr) {
+ return new_ttyfd(path);
+ } else if ((path = ctermid(nullptr)) != nullptr) {
+ return new_ttyfd(path);
+ } else {
+ errno = ENOTTY; /* No terminal */
+ }
+ // NOLINTEND(bugprone-assignment-in-if-condition)
+ return -1;
+}
+#endif // HAVE_TERMIOS
+
+void os_open_tty(void) {
+#ifdef HAVE_TTY
+ auto *s = get_state();
+ s->initialized = true;
+
+#ifdef HAVE_TERMIOS
+ if (s->ttyfd >= 0) { // Already open
+ return;
+ }
+ const int ttyfd = get_ttyfd();
+ if (ttyfd < 0) { // Failed to open tty fd
+ return;
+ }
+ struct termios tty {};
+ if (tcgetattr(ttyfd, &tty) < 0) { // Failed to retrieve old mode
+ close(ttyfd);
+ return;
+ }
+ s->oldtty = tty;
+ // Set terminal to "raw" mode
+ tty.c_lflag &= ~(ECHO | // Echo off
+ ICANON | // Canonical mode off
+ ECHONL | // Do not echo NL (redundant with ECHO and ICANON)
+ ISIG | // Signal chars off
+ IEXTEN // Extended input processing off
+ );
+ tty.c_iflag &= ~(IGNBRK | // Generate \377 \0 \0 on BREAK
+ BRKINT | //
+ PARMRK | //
+ ICRNL | // No CR-to-NL
+ ISTRIP | // Do not strip off 8th bit
+ INLCR | // No NL-to-CR
+ IGNCR | // Do not ignore CR
+ IXON // Disable XON/XOFF flow control on output
+ );
+ tty.c_oflag |= OPOST; // Enable output processing
+ // Enable parity generation on output and checking for input
+ tty.c_cflag &= ~(CSIZE | PARENB);
+ tty.c_cflag |= CS8;
+ // Read returns with 1 char and no delay
+ tty.c_cc[VMIN] = 1;
+ tty.c_cc[VTIME] = 0;
+ if (tcsetattr(ttyfd, TCSANOW, &tty) < 0) { // Failed to set raw mode
+ close(ttyfd);
+ return;
+ }
+ s->ttyfd = ttyfd;
+#elif defined(_WIN32)
+ // Get stdin handle
+ s->hStdin = GetStdHandle(STD_INPUT_HANDLE);
+ if (!s->hStdin) {
+ return;
+ }
+ // Set console in raw mode
+ if (GetConsoleMode(s->hStdin, &s->dwOldStdinMode)) {
+ DWORD dwMode = s->dwOldStdinMode;
+ dwMode &= ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
+ dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
+ SetConsoleMode(s->hStdin, dwMode);
+ }
+#endif // HAVE_TERMIOS
+
+#else
+ throw std::runtime_error("unable to open console input, stdin is unsupported in this platform");
+#endif // HAVE_TTY
+}
+
+void os_close_tty(void) {
+#ifdef HAVE_TTY
+#ifdef HAVE_TERMIOS
+ auto *s = get_state();
+ if (s->ttyfd >= 0) { // Restore old mode
+ tcsetattr(s->ttyfd, TCSANOW, &s->oldtty);
+ close(s->ttyfd);
+ s->ttyfd = 1;
+ }
+
+#elif defined(_WIN32)
+ auto *s = get_state();
+ if (s->hStdin) {
+ SetConsoleMode(s->hStdin, s->dwOldStdinMode);
+ s->hStdin = NULL;
+ }
+
+#endif // HAVE_TERMIOS
+#endif // HAVE_TTY
+}
+
+void os_poll_tty(uint64_t wait) {
+#ifdef HAVE_TTY
+ auto *s = get_state();
+ if (!s->initialized) {
+ throw std::runtime_error("can't poll console input, it is not initialized");
+ }
+ if (s->buf_pos < s->buf_len) {
+ // Input buffer still has pending characters to be read
+ return;
+ }
+
+#ifdef _WIN32
+ s->buf_len = -1;
+ if (s->hStdin) {
+ // Wait for an input event
+ const uint64_t wait_millis = (wait + 999) / 1000;
+ if (WaitForSingleObject(s->hStdin, wait_millis) != WAIT_OBJECT_0) {
+ // No input events
+ return;
+ }
+ // Consume input events until buffer is full or the event list is empty
+ INPUT_RECORD inputRecord{};
+ DWORD numberOfEventsRead = 0;
+ while (PeekConsoleInput(s->hStdin, &inputRecord, 1, &numberOfEventsRead)) {
+ if (numberOfEventsRead == 0) {
+ // Nothing to read
+ return;
+ } else if (inputRecord.EventType == KEY_EVENT && inputRecord.Event.KeyEvent.bKeyDown) {
+ // Key was pressed
+ DWORD numberOfCharsRead = 0;
+ // We must read input buffer through ReadConsole() to read raw terminal input
+ if (ReadConsole(s->hStdin, s->buf.data(), s->buf.size(), &numberOfCharsRead, NULL)) {
+ s->buf_len = static_cast(numberOfCharsRead);
+ }
+ break;
+ } else {
+ // Consume input event
+ ReadConsoleInput(s->hStdin, &inputRecord, 1, &numberOfEventsRead);
+ }
+ }
+ }
+
+#else
+ const int fd_max{0};
+ fd_set rfds{};
+ timeval tv{};
+ tv.tv_usec = static_cast(wait);
+ FD_ZERO(&rfds); // NOLINT: suppress cause on MacOSX it resolves to __builtin_bzero
+ FD_SET(STDIN_FILENO, &rfds);
+ if (select(fd_max + 1, &rfds, nullptr, nullptr, &tv) <= 0 || !FD_ISSET(0, &rfds)) {
+ // Nothing to read
+ return;
+ }
+ s->buf_len = static_cast(read(STDIN_FILENO, s->buf.data(), s->buf.size()));
+
+#endif // _WIN32
+
+ // If stdin is closed, pass EOF to client
+ if (s->buf_len <= 0) {
+ s->buf_len = 1;
+ s->buf[0] = 4; // CTRL+D
+ }
+ s->buf_pos = 0;
+
+#else
+ (void) wait;
+ throw std::runtime_error("can't poll console input, it is unsupported in this platform");
+#endif // HAVE_TTY
+}
+
+int os_getchar(void) {
+#ifdef HAVE_TTY
+ auto *s = get_state();
+ if (!s->initialized) {
+ throw std::runtime_error("can't get char, console input is not initialized");
+ }
+ os_poll_tty(0);
+ if (s->buf_pos < s->buf_len) {
+ return s->buf[s->buf_pos++] + 1;
+ }
+#else
+ throw std::runtime_error("can't get char, console input is unsupported in this platform");
+#endif // HAVE_TTY
+ return 0;
+}
+
+static void fputc_with_line_buffering(uint8_t ch) {
+ // Write through fputc(), so we can take advantage of buffering.
+ (void) fputc(ch, stdout);
+ // On Linux, stdout in fully buffered by default when it's not a TTY,
+ // here we flush every new line to perform line buffering.
+ if (ch == '\n') {
+ (void) fflush(stdout);
+ }
+}
+
+void os_putchar(uint8_t ch) {
+#ifdef HAVE_TTY
+ auto *s = get_state();
+ if (!s->initialized) {
+ // Write through fputc(), so we can take advantage of buffering.
+ fputc_with_line_buffering(ch);
+ } else {
+ // In interactive sessions we want to immediately write the character to stdout,
+ // without any buffering.
+#ifdef _WIN32
+ if (_write(STDOUT_FILENO, &ch, 1) < 1) {
+ ;
+ }
+#else
+ if (write(STDOUT_FILENO, &ch, 1) < 1) {
+ ;
+ }
+#endif
+ }
+#else
+ fputc_with_line_buffering(ch);
+#endif // HAVE_TTY
+}
+
+int os_mkdir(const char *path, int mode) {
+#ifdef HAVE_MKDIR
+ return mkdir(path, mode);
+#elif defined(_WIN32)
+ (void) mode;
+ return _mkdir(path);
+#else
+ return -1;
+#endif // HAVE_MKDIR
+}
+
+unsigned char *os_map_file(const char *path, uint64_t length, bool shared) {
+ if (!path || *path == '\0') {
+ throw std::runtime_error{"image file path must be specified"s};
+ }
+
+#ifdef HAVE_MMAP
+ const int oflag = shared ? O_RDWR : O_RDONLY;
+
+ // Try to open image file
+ const int backing_file = open(path, oflag);
+ if (backing_file < 0) {
+ throw std::system_error{errno, std::generic_category(), "could not open image file '"s + path + "'"s};
+ }
+
+ // Try to get file size
+ struct stat statbuf {};
+ if (fstat(backing_file, &statbuf) < 0) {
+ close(backing_file);
+ throw std::system_error{errno, std::generic_category(),
+ "unable to obtain length of image file '"s + path + "'"s};
+ }
+
+ // Check that it matches range length
+ if (static_cast(statbuf.st_size) != length) {
+ close(backing_file);
+ throw std::invalid_argument{"image file '"s + path + "' size ("s +
+ std::to_string(static_cast(statbuf.st_size)) + ") does not match range length ("s +
+ std::to_string(length) + ")"s};
+ }
+
+ // Try to map image file to host memory
+ const int mflag = shared ? MAP_SHARED : MAP_PRIVATE;
+ auto *host_memory =
+ static_cast(mmap(nullptr, length, PROT_READ | PROT_WRITE, mflag, backing_file, 0));
+ if (host_memory == MAP_FAILED) { // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr)
+ close(backing_file);
+ throw std::system_error{errno, std::generic_category(), "could not map image file '"s + path + "' to memory"s};
+ }
+
+ // We can close the file after mapping it, because the OS will retain a reference of the file on its own
+ close(backing_file);
+ return host_memory;
+
+#elif defined(_WIN32)
+ const int oflag = (shared ? _O_RDWR : _O_RDONLY) | _O_BINARY;
+
+ // Try to open image file
+ const int backing_file = _open(path, oflag);
+ if (backing_file < 0) {
+ throw std::system_error{errno, std::generic_category(), "could not open image file '"s + path + "'"s};
+ }
+
+ // Try to get file size
+ struct __stat64 statbuf {};
+ if (_fstat64(backing_file, &statbuf) < 0) {
+ _close(backing_file);
+ throw std::system_error{errno, std::generic_category(),
+ "unable to obtain length of image file '"s + path + "'"s};
+ }
+
+ // Check that it matches range length
+ if (static_cast(statbuf.st_size) != length) {
+ _close(backing_file);
+ throw std::invalid_argument{"image file '"s + path + "' size ("s +
+ std::to_string(static_cast(statbuf.st_size)) + ") does not match range length ("s +
+ std::to_string(length) + ")"s};
+ }
+
+ // Try to map image file to host memory
+ DWORD flProtect = shared ? PAGE_READWRITE : PAGE_READONLY;
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ HANDLE hFile = reinterpret_cast(_get_osfhandle(backing_file));
+ HANDLE hFileMappingObject = CreateFileMapping(hFile, NULL, flProtect, length >> 32, length & 0xffffffff, NULL);
+ if (!hFileMappingObject) {
+ _close(backing_file);
+ throw std::system_error{errno, std::generic_category(), "could not map image file '"s + path + "' to memory"s};
+ }
+
+ DWORD dwDesiredAccess = shared ? FILE_MAP_WRITE : FILE_MAP_COPY;
+ auto *host_memory = static_cast(MapViewOfFile(hFileMappingObject, dwDesiredAccess, 0, 0, length));
+ if (!host_memory) {
+ _close(backing_file);
+ throw std::system_error{errno, std::generic_category(), "could not map image file '"s + path + "' to memory"s};
+ }
+
+ // We can close the file after mapping it, because the OS will retain a reference of the file on its own
+ _close(backing_file);
+ return host_memory;
+
+#else
+ if (shared) {
+ throw std::runtime_error{"shared image file mapping is unsupported"s};
+ }
+
+ auto fp = unique_fopen(path, "rb", std::nothrow_t{});
+ if (!fp) {
+ throw std::system_error{errno, std::generic_category(), "error opening image file '"s + path + "'"s};
+ }
+ // Get file size
+ if (fseek(fp.get(), 0, SEEK_END)) {
+ throw std::system_error{errno, std::generic_category(),
+ "error obtaining length of image file '"s + path + "'"s};
+ }
+ auto file_length = ftell(fp.get());
+ if (fseek(fp.get(), 0, SEEK_SET)) {
+ throw std::system_error{errno, std::generic_category(),
+ "error obtaining length of image file '"s + path + "'"s};
+ }
+ // Check against PMA range size
+ if (static_cast(file_length) > length) {
+ throw std::runtime_error{"image file '"s + path + "' of "s + " is too large for range"s};
+ }
+
+ // use calloc to improve performance
+ // NOLINTNEXTLINE(cppcoreguidelines-no-malloc, cppcoreguidelines-prefer-member-initializer)
+ auto host_memory = static_cast(std::calloc(1, length));
+ if (!host_memory) {
+ throw std::runtime_error{"error allocating memory"s};
+ }
+
+ // Read to host memory
+ auto read = fread(host_memory, 1, length, fp.get());
+ (void) read;
+ if (ferror(fp.get())) {
+ throw std::system_error{errno, std::generic_category(), "error reading from image file '"s + path + "'"s};
+ }
+ return host_memory;
+
+#endif // HAVE_MMAP
+}
+
+void os_unmap_file(unsigned char *host_memory, uint64_t length) {
+#ifdef HAVE_MMAP
+ munmap(host_memory, length);
+
+#elif defined(_WIN32)
+ (void) length;
+ UnmapViewOfFile(host_memory);
+
+#else
+ (void) length;
+ std::free(host_memory);
+
+#endif // HAVE_MMAP
+}
+
+int64_t os_now_us() {
+ std::chrono::time_point start{};
+ static bool started = false;
+ if (!started) {
+ started = true;
+ start = std::chrono::high_resolution_clock::now();
+ }
+ auto end = std::chrono::high_resolution_clock::now();
+ return static_cast(std::chrono::duration_cast(end - start).count());
+}
+
+uint64_t os_get_concurrency() {
+#ifdef HAVE_THREADS
+ return std::thread::hardware_concurrency();
+#else
+ return 1;
+#endif
+}
+
+bool os_parallel_for(uint64_t n, const std::function &task) {
+#ifdef HAVE_THREADS
+ if (n > 1) {
+ std::mutex mutex;
+ const parallel_for_mutex for_mutex = {[&] { mutex.lock(); }, [&] { mutex.unlock(); }};
+ std::vector> futures;
+ futures.reserve(n);
+ for (uint64_t j = 0; j < n; ++j) {
+ futures.emplace_back(std::async(std::launch::async, task, j, for_mutex));
+ }
+ // Check if any thread failed
+ bool succeeded = true;
+ for (auto &f : futures) {
+ succeeded = succeeded && f.get();
+ }
+ // Return overall status
+ return succeeded;
+ }
+#endif
+ // Run without extra threads when concurrency is 1 or as fallback
+ const parallel_for_mutex for_mutex{[] {}, [] {}};
+ bool succeeded = true;
+ for (uint64_t j = 0; j < n; ++j) {
+ succeeded = succeeded && task(j, for_mutex);
+ }
+ return succeeded;
+}
+
+} // namespace cartesi
diff --git a/src/os.h b/src/os.h
new file mode 100644
index 000000000..7440ef85e
--- /dev/null
+++ b/src/os.h
@@ -0,0 +1,93 @@
+// Copyright Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or (at your option) any
+// later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License along
+// with this program (see COPYING). If not, see .
+//
+
+#ifndef OS_H
+#define OS_H
+
+#include
+#include
+#include
+
+/// \file
+/// \brief System-specific OS handling operations
+
+namespace cartesi {
+
+/// \brief Initialize console
+void os_open_tty(void);
+
+/// \brief Cleanup console initialization
+void os_close_tty(void);
+
+/// \brief Polls console for input characters
+/// \param wait Timeout to wait for characters in microseconds
+void os_poll_tty(uint64_t wait);
+
+/// \brief Reads an input character from the console
+/// \return Charater read from console
+int os_getchar(void);
+
+/// \brief Writes an output character to the console
+/// \param ch Character to write
+void os_putchar(uint8_t ch);
+
+/// \brief Creates a new directory
+int os_mkdir(const char *path, int mode);
+
+/// \brief Maps a file to memory
+unsigned char *os_map_file(const char *path, uint64_t length, bool shared);
+
+/// \brief Unmaps a file from memory
+void os_unmap_file(unsigned char *host_memory, uint64_t length);
+
+/// \brief Get time elapsed since its first call with microsecond precision
+int64_t os_now_us();
+
+/// \brief Get the number of concurrent threads supported by the OS
+uint64_t os_get_concurrency();
+
+/// \brief Mutex for os_parallel_for()
+struct parallel_for_mutex {
+ std::function lock;
+ std::function unlock;
+};
+
+/// \brief Mutex guard for os_parallel_for()
+struct parallel_for_mutex_guard {
+ parallel_for_mutex_guard(const parallel_for_mutex &mutex) : mutex(mutex) {
+ mutex.lock();
+ }
+ ~parallel_for_mutex_guard() {
+ mutex.unlock();
+ }
+
+ parallel_for_mutex_guard() = delete;
+ parallel_for_mutex_guard(const parallel_for_mutex_guard &) = default;
+ parallel_for_mutex_guard(parallel_for_mutex_guard &&) = default;
+ parallel_for_mutex_guard &operator=(const parallel_for_mutex_guard &) = delete;
+ parallel_for_mutex_guard &operator=(parallel_for_mutex_guard &&) = delete;
+
+private:
+ parallel_for_mutex mutex;
+};
+
+/// \brief Runs a for loop in parallel using up to n threads
+/// \return True if all thread tasks succeeded
+bool os_parallel_for(uint64_t n, const std::function &task);
+
+} // namespace cartesi
+
+#endif
diff --git a/src/pma.cpp b/src/pma.cpp
index 5fc23b650..bece75ced 100644
--- a/src/pma.cpp
+++ b/src/pma.cpp
@@ -14,16 +14,12 @@
// with this program (see COPYING). If not, see .
//
-#include // open
-#include // mmap, munmap
-#include // fstat
-#include // close
-
#include
#include
#include
#include
+#include "os.h"
#include "pma.h"
#include "unique-c-ptr.h"
@@ -32,10 +28,9 @@ namespace cartesi {
using namespace std::string_literals;
void pma_memory::release(void) {
- if (m_backing_file >= 0) {
- munmap(m_host_memory, m_length);
- close(m_backing_file);
- m_backing_file = -1;
+ if (m_mmapped) {
+ os_unmap_file(m_host_memory, m_length);
+ m_mmapped = false;
} else {
std::free(m_host_memory); // NOLINT(cppcoreguidelines-no-malloc)
}
@@ -50,17 +45,17 @@ pma_memory::~pma_memory() {
pma_memory::pma_memory(pma_memory &&other) noexcept :
m_length{std::move(other.m_length)},
m_host_memory{std::move(other.m_host_memory)},
- m_backing_file{std::move(other.m_backing_file)} {
+ m_mmapped{std::move(other.m_mmapped)} {
// set other to safe state
other.m_host_memory = nullptr;
- other.m_backing_file = -1;
+ other.m_mmapped = false;
other.m_length = 0;
}
pma_memory::pma_memory(const std::string &description, uint64_t length, const callocd &c) :
m_length{length},
m_host_memory{nullptr},
- m_backing_file{-1} {
+ m_mmapped{false} {
(void) c;
// use calloc to improve performance
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc, cppcoreguidelines-prefer-member-initializer)
@@ -73,7 +68,7 @@ pma_memory::pma_memory(const std::string &description, uint64_t length, const ca
pma_memory::pma_memory(const std::string &description, uint64_t length, const mockd &m) :
m_length{length},
m_host_memory{nullptr},
- m_backing_file{-1} {
+ m_mmapped{false} {
(void) m;
(void) description;
}
@@ -114,60 +109,24 @@ pma_memory::pma_memory(const std::string &description, uint64_t length, const st
pma_memory::pma_memory(const std::string &description, uint64_t length, const std::string &path, const mmapd &m) :
m_length{length},
m_host_memory{nullptr},
- m_backing_file{-1} {
- if (path.empty()) {
- throw std::runtime_error{"image file must be specified for "s + description};
- }
-
- const int oflag = m.shared ? O_RDWR : O_RDONLY;
- const int mflag = m.shared ? MAP_SHARED : MAP_PRIVATE;
-
- // Try to open image file
- const int backing_file = open(path.c_str(), oflag);
- if (backing_file < 0) {
- throw std::system_error{errno, std::generic_category(),
- "could not open image file '"s + path + "' when initializing "s + description};
- }
-
- // Try to get file size
- struct stat statbuf {};
- if (fstat(backing_file, &statbuf) < 0) {
- close(backing_file);
- throw std::system_error{errno, std::generic_category(),
- "unable to obtain length of image file '"s + path + "' when initializing "s + description};
+ m_mmapped{false} {
+ try {
+ m_host_memory = os_map_file(path.c_str(), length, m.shared);
+ m_mmapped = true;
+ } catch (std::exception &e) {
+ throw std::runtime_error{e.what() + " when initializing "s + description};
}
-
- // Check that it matches range length
- if (static_cast(statbuf.st_size) != length) {
- close(backing_file);
- throw std::invalid_argument{"image file '"s + path + "' size ("s +
- std::to_string(static_cast(statbuf.st_size)) + ") does not match range length ("s +
- std::to_string(length) + ") of "s + description};
- }
-
- // Try to map image file to host memory
- auto *host_memory =
- static_cast(mmap(nullptr, length, PROT_READ | PROT_WRITE, mflag, backing_file, 0));
- if (host_memory == MAP_FAILED) { // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr)
- close(backing_file);
- throw std::system_error{errno, std::generic_category(),
- "could not map image file '"s + path + "' to memory when initializing "s + description};
- }
-
- // Finally store everything in object
- m_host_memory = host_memory;
- m_backing_file = backing_file;
}
pma_memory &pma_memory::operator=(pma_memory &&other) noexcept {
release();
// copy from other
m_host_memory = std::move(other.m_host_memory);
- m_backing_file = std::move(other.m_backing_file);
+ m_mmapped = std::move(other.m_mmapped);
m_length = std::move(other.m_length);
// set other to safe state
other.m_host_memory = nullptr;
- other.m_backing_file = -1;
+ other.m_mmapped = false;
other.m_length = 0;
return *this;
}
diff --git a/src/pma.h b/src/pma.h
index 1c952f114..ae8763746 100644
--- a/src/pma.h
+++ b/src/pma.h
@@ -94,7 +94,7 @@ class pma_memory final {
uint64_t m_length; ///< Length of memory range (copy of PMA length field).
unsigned char *m_host_memory; ///< Start of associated memory region in host.
- int m_backing_file; ///< File descryptor for mmaped memory.
+ bool m_mmapped; ///< True if memory was mapped from a file.
/// \brief Close file and/or release memory.
void release(void);
@@ -162,11 +162,6 @@ class pma_memory final {
return m_host_memory;
}
- /// \brief Returns file descryptor for mmaped memory.
- int get_backing_file(void) const {
- return m_backing_file;
- }
-
/// \brief Returns copy of PMA length field (needed for munmap).
uint64_t get_length(void) const {
return m_length;
diff --git a/src/riscv-constants.h b/src/riscv-constants.h
index 067814b97..42c1a6270 100644
--- a/src/riscv-constants.h
+++ b/src/riscv-constants.h
@@ -793,7 +793,7 @@ enum class insn_funct3_00000_opcode : uint32_t {
AND_REMU = 0b111000000110011,
ADDW_MULW_SUBW = 0b000000000111011,
SRLW_DIVUW_SRAW = 0b101000000111011,
- privileged = 0b000000001110011,
+ PRIVILEGED = 0b000000001110011,
};
/// \brief The result of insn >> 26 (6 most significant bits of funct7) can be
diff --git a/src/state-access.h b/src/state-access.h
index 11a97585c..fecb938ca 100644
--- a/src/state-access.h
+++ b/src/state-access.h
@@ -21,15 +21,14 @@
/// \brief Fast state access implementation
#include
-#include
#include "device-state-access.h"
#include "i-state-access.h"
#include "machine.h"
+#include "os.h"
#include "pma.h"
#include "rtc.h"
#include "strict-aliasing.h"
-#include "tty.h"
namespace cartesi {
@@ -406,12 +405,10 @@ class state_access : public i_state_access {
if (warp_cycle > mcycle) {
constexpr uint64_t cycles_per_us = RTC_CLOCK_FREQ / 1000000; // CLOCK_FREQ / 10^6
const uint64_t wait = (warp_cycle - mcycle) / cycles_per_us;
- timeval start{};
- timeval end{};
- gettimeofday(&start, nullptr);
- tty_poll_console(wait);
- gettimeofday(&end, nullptr);
- const uint64_t elapsed_us = (end.tv_sec - start.tv_sec) * 1000000 + end.tv_usec - start.tv_usec;
+ const int64_t start = os_now_us();
+ os_poll_tty(wait);
+ const int64_t end = os_now_us();
+ const uint64_t elapsed_us = static_cast(std::max(end - start, INT64_C(0)));
const uint64_t tty_cycle = mcycle + (elapsed_us * cycles_per_us);
mcycle = std::min(std::max(tty_cycle, mcycle), warp_cycle);
}
diff --git a/src/test-machine-c-api.cpp b/src/test-machine-c-api.cpp
index 85e17f92b..4d2ab8c35 100644
--- a/src/test-machine-c-api.cpp
+++ b/src/test-machine-c-api.cpp
@@ -20,9 +20,8 @@
#include
#include
-#include
-
#include "grpc-machine-c-api.h"
+#include "json-util.h"
#include "machine-c-api.h"
#include "riscv-constants.h"
#include "test-utils.h"
diff --git a/src/test-utils.h b/src/test-utils.h
index ee871ed1c..aac1bb5ff 100644
--- a/src/test-utils.h
+++ b/src/test-utils.h
@@ -31,8 +31,8 @@ using hash_type = cartesi::keccak_256_hasher::hash_type;
namespace detail {
constexpr int WORD_LOG2_SIZE = 3;
-constexpr int PAGE_LOG2_SIZE = 12;
-constexpr int PAGE_SIZE = (UINT64_C(1) << PAGE_LOG2_SIZE);
+constexpr int MERKLE_PAGE_LOG2_SIZE = 12;
+constexpr int MERKLE_PAGE_SIZE = (UINT64_C(1) << MERKLE_PAGE_LOG2_SIZE);
static hash_type merkle_hash(cartesi::keccak_256_hasher &h, const std::string_view &data, int log2_size) {
hash_type result;
@@ -89,7 +89,7 @@ static hash_type calculate_proof_root_hash(const cm_merkle_tree_proof *proof) {
static hash_type calculate_emulator_hash(cm_machine *machine) {
cartesi::back_merkle_tree tree(64, 12, 3);
std::string page;
- page.resize(detail::PAGE_SIZE);
+ page.resize(detail::MERKLE_PAGE_SIZE);
cm_memory_range_descr_array *mrds = nullptr;
auto mrds_deleter = [](cm_memory_range_descr_array **mrds) { cm_delete_memory_range_descr_array(*mrds); };
std::unique_ptr auto_mrds(&mrds, mrds_deleter);
@@ -102,14 +102,14 @@ static hash_type calculate_emulator_hash(cm_machine *machine) {
uint64_t last = 0;
for (size_t i = 0; i < mrds->count; ++i) {
const auto &m = mrds->entry[i];
- tree.pad_back((m.start - last) >> detail::PAGE_LOG2_SIZE);
+ tree.pad_back((m.start - last) >> detail::MERKLE_PAGE_LOG2_SIZE);
auto end = m.start + m.length;
- for (uint64_t s = m.start; s < end; s += detail::PAGE_SIZE) {
+ for (uint64_t s = m.start; s < end; s += detail::MERKLE_PAGE_SIZE) {
if (cm_read_memory(machine, s, reinterpret_cast(page.data()), page.size(), &err_msg) !=
0) {
throw std::runtime_error{err_msg};
}
- auto page_hash = merkle_hash(page, detail::PAGE_LOG2_SIZE);
+ auto page_hash = merkle_hash(page, detail::MERKLE_PAGE_LOG2_SIZE);
tree.push_back(page_hash);
}
last = end;
diff --git a/src/tty.cpp b/src/tty.cpp
deleted file mode 100644
index 61a225237..000000000
--- a/src/tty.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: LGPL-3.0-or-later
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option) any
-// later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License along
-// with this program (see COPYING). If not, see .
-//
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "tty.h"
-
-namespace cartesi {
-
-static const int CONSOLE_BUF_SIZE = 1024; ///< Number of characters in console input buffer
-
-/// \brief TTY global state
-struct tty_state {
- bool initialized{false};
- int ttyfd{-1};
- termios oldtty{};
- std::array buf{};
- ssize_t buf_pos{};
- ssize_t buf_len{};
-};
-
-static int new_ttyfd(const char *path) {
- int fd{};
- do {
- fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK);
- } while (fd == -1 && errno == EINTR);
- return fd;
-}
-
-static int get_ttyfd(void) {
- char *path{};
- // NOLINTBEGIN(bugprone-assignment-in-if-condition)
- if ((path = ttyname(STDERR_FILENO)) != nullptr) {
- return new_ttyfd(path);
- } else if ((path = ttyname(STDOUT_FILENO)) != nullptr) {
- return new_ttyfd(path);
- } else if ((path = ttyname(STDIN_FILENO)) != nullptr) {
- return new_ttyfd(path);
- } else if ((path = ctermid(nullptr)) != nullptr) {
- return new_ttyfd(path);
- } else {
- errno = ENOTTY; /* No terminal */
- }
- // NOLINTEND(bugprone-assignment-in-if-condition)
- return -1;
-}
-
-static bool try_read_chars_from_stdin(uint64_t wait, char *data, size_t max_len, long *actual_len) {
- const int fd_max{0};
- fd_set rfds{};
- timeval tv{};
- tv.tv_usec = static_cast(wait);
- FD_ZERO(&rfds); // NOLINT: suppress cause on MacOSX it resolves to __builtin_bzero
- FD_SET(STDIN_FILENO, &rfds);
- if (select(fd_max + 1, &rfds, nullptr, nullptr, &tv) > 0 && FD_ISSET(0, &rfds)) {
- *actual_len = read(STDIN_FILENO, data, max_len);
- // If stdin is closed, pass EOF to client
- if (*actual_len <= 0) {
- *actual_len = 1;
- data[0] = 4; // CTRL+D
- }
- return true;
- }
- return false;
-}
-
-/// Returns pointer to the global TTY state
-static tty_state *get_state() {
- static tty_state data;
- return &data;
-}
-
-void tty_initialize(void) {
- auto *s = get_state();
- if (s->initialized) {
- throw std::runtime_error("TTY already initialized.");
- }
- s->initialized = true;
- // NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
- if ((s->ttyfd = get_ttyfd()) >= 0) {
- struct termios tty {};
- tcgetattr(s->ttyfd, &tty);
- s->oldtty = tty;
- // Set terminal to "raw" mode
- tty.c_lflag &= ~(ECHO | // Echo off
- ICANON | // Canonical mode off
- ECHONL | // Do not echo NL (redundant with ECHO and ICANON)
- ISIG | // Signal chars off
- IEXTEN // Extended input processing off
- );
- tty.c_iflag &= ~(IGNBRK | // Generate \377 \0 \0 on BREAK
- BRKINT | //
- PARMRK | //
- ICRNL | // No CR-to-NL
- ISTRIP | // Do not strip off 8th bit
- INLCR | // No NL-to-CR
- IGNCR | // Do not ignore CR
- IXON // Disable XON/XOFF flow control on output
- );
- tty.c_oflag |= OPOST; // Enable output processing
- // Enable parity generation on output and checking for input
- tty.c_cflag &= ~(CSIZE | PARENB);
- tty.c_cflag |= CS8;
- // Read returns with 1 char and no delay
- tty.c_cc[VMIN] = 1;
- tty.c_cc[VTIME] = 0;
- tcsetattr(s->ttyfd, TCSANOW, &tty);
- //??D Should we check to see if changes stuck?
- }
-}
-
-void tty_finalize(void) {
- auto *s = get_state();
- if (!s->initialized) {
- throw std::runtime_error("TTY not initialized");
- }
- s->initialized = false;
- if (s->ttyfd >= 0) {
- tcsetattr(s->ttyfd, TCSANOW, &s->oldtty);
- close(s->ttyfd);
- s->ttyfd = -1;
- }
-}
-
-void tty_poll_console(uint64_t wait) {
- auto *s = get_state();
- if (!s->initialized) {
- throw std::runtime_error("can't poll TTY, it is not initialized");
- }
- // Check for input from console, if requested by HTIF
- // Obviously, somethind different must be done in blockchain
- // If we don't have any characters left in buffer, try to obtain more
- if (s->buf_pos >= s->buf_len) {
- if (try_read_chars_from_stdin(wait, s->buf.data(), s->buf.size(), &s->buf_len)) {
- s->buf_pos = 0;
- }
- }
-}
-
-int tty_getchar(void) {
- auto *s = get_state();
- if (!s->initialized) {
- throw std::runtime_error("can't get char, TTY is not initialized");
- }
- tty_poll_console(0);
- if (s->buf_pos < s->buf_len) {
- return s->buf[s->buf_pos++] + 1;
- }
- return 0;
-}
-
-void tty_putchar(uint8_t ch) {
- auto *s = get_state();
- if (!s->initialized) {
- // Write through fputc(), so we can take advantage of buffering.
- (void) fputc(ch, stdout);
- // On Linux, stdout in fully buffered by default when it's not a TTY,
- // here we flush every new line to perform line buffering.
- if (ch == '\n') {
- (void) fflush(stdout);
- }
- } else {
- // In interactive sessions we want to immediately write the character to stdout,
- // without any buffering.
- if (write(STDOUT_FILENO, &ch, 1) < 1) {
- ;
- }
- }
-}
-
-} // namespace cartesi
diff --git a/src/tty.h b/src/tty.h
deleted file mode 100644
index d8a5f6d26..000000000
--- a/src/tty.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: LGPL-3.0-or-later
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option) any
-// later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License along
-// with this program (see COPYING). If not, see .
-//
-
-#ifndef TTY_H
-#define TTY_H
-
-#include
-#include
-
-/// \file
-/// \brief System-specific TTY handling operations
-
-namespace cartesi {
-
-/// \brief Initialize TTY for console input
-void tty_initialize(void);
-
-/// \brief Cleanup TTY console input initialization
-void tty_finalize(void);
-
-/// \brief Polls TTY for input characters
-/// \param wait Timeout to wait for characters in microseconds
-void tty_poll_console(uint64_t wait);
-
-/// \brief Reads a character from the console
-/// \return Charater read from console
-int tty_getchar(void);
-
-/// \brief Writes a character to TTY
-/// \param ch Character to write
-void tty_putchar(uint8_t ch);
-
-} // namespace cartesi
-
-#endif
diff --git a/uarch/uarch-runtime.cpp b/uarch/uarch-runtime.cpp
index d57c8853c..f48201c6b 100644
--- a/uarch/uarch-runtime.cpp
+++ b/uarch/uarch-runtime.cpp
@@ -15,7 +15,7 @@
//
#include "uarch-runtime.h"
-#include "tty.h"
+#include "os.h"
#include "uarch-constants.h"
#include
@@ -79,19 +79,19 @@ extern "C" NO_RETURN void abort(void) {
namespace cartesi {
-void tty_initialize(void) {}
+void os_open_tty(void) {}
-void tty_finalize(void) {}
+void os_close_tty(void) {}
-void tty_poll_console(uint64_t wait) {
+void os_poll_tty(uint64_t wait) {
(void) wait;
}
-int tty_getchar(void) {
+int os_getchar(void) {
return 0;
}
-void tty_putchar(uint8_t ch) {
+void os_putchar(uint8_t ch) {
_putchar(ch);
}