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); }