From b7532232bb74ce95c8c501043be5165385e8b511 Mon Sep 17 00:00:00 2001 From: Marcos Pernambuco Motta <1091485+mpernambuco@users.noreply.github.commits> Date: Fri, 6 Oct 2023 12:51:43 -0300 Subject: [PATCH] feat: Uarch reset refactoring --- .github/workflows/build.yml | 3 +- .gitignore | 1 + Makefile | 4 +- lib/grpc-interfaces | 2 +- lib/machine-emulator-defines | 2 +- src/Makefile | 63 ++++- src/access-log.h | 64 ++++- src/cartesi-machine-tests.lua | 25 +- src/cartesi-machine.lua | 67 ++--- src/cartesi/util.lua | 18 +- src/clua-cartesi.cpp | 12 +- src/clua-grpc-machine.cpp | 67 ++++- src/clua-i-virtual-machine.cpp | 30 ++- src/clua-jsonrpc-machine.cpp | 67 ++++- src/clua-machine-util.cpp | 45 +++- src/clua-machine.cpp | 50 +++- src/clua.cpp | 6 + src/clua.h | 10 +- src/compute-uarch-pristine-hash.cpp | 127 +++++++++ src/grpc-config.h | 27 ++ src/grpc-machine-c-api.cpp | 34 ++- src/grpc-machine-c-api.h | 31 ++- src/grpc-virtual-machine.cpp | 80 ++++-- src/grpc-virtual-machine.h | 17 +- src/i-state-access.h | 4 - src/i-uarch-reset-state-access.h | 61 +++++ ...e-access.h => i-uarch-step-state-access.h} | 3 +- src/i-virtual-machine.h | 26 +- src/interpret.cpp | 2 +- src/json-util.cpp | 110 ++++++-- src/jsonrpc-discover.json | 131 +++++++++- src/jsonrpc-machine-c-api.cpp | 34 ++- src/jsonrpc-machine-c-api.h | 31 ++- src/jsonrpc-remote-machine.cpp | 154 +++++++++-- src/jsonrpc-virtual-machine.cpp | 53 +++- src/jsonrpc-virtual-machine.h | 17 +- src/machine-c-api.cpp | 108 ++++++-- src/machine-c-api.h | 61 +++-- src/machine-config.cpp | 4 - src/machine.cpp | 121 ++++++--- src/machine.h | 38 ++- src/pma.cpp | 25 ++ src/pma.h | 34 +++ src/protobuf-util.cpp | 72 ++++-- src/remote-machine-proxy.cpp | 131 ++++++---- src/remote-machine.cpp | 141 +++++++--- src/riscv-constants.h | 3 + src/shadow-uarch-state-factory.cpp | 1 - src/shadow-uarch-state.cpp | 5 - src/shadow-uarch-state.h | 6 - src/state-access.h | 4 - src/test-machine-c-api.cpp | 182 +++++++------ src/test-utils.h | 40 +-- src/tests/htif-yield.lua | 20 +- src/tests/log-with-mtime-transition.lua | 7 +- src/tests/machine-bind.lua | 244 +++++++++++++++--- src/tests/machine-test.lua | 10 +- src/tests/mcycle-overflow.lua | 2 +- src/tests/mtime-interrupt.lua | 4 +- src/tests/util.lua | 70 +++-- src/uarch-bridge.h | 5 - src/uarch-config.h | 1 - src/uarch-constants.h | 39 +++ src/uarch-interpret.cpp | 2 +- src/uarch-interpret.h | 4 +- src/uarch-machine.cpp | 51 ++-- src/uarch-machine.h | 6 - src/uarch-pristine-state-hash.cpp | 8 + src/uarch-pristine-state-hash.h | 30 +++ src/uarch-record-reset-state-access.h | 196 ++++++++++++++ ...ess.h => uarch-record-step-state-access.h} | 75 ++++-- src/uarch-replay-reset-state-access.h | 237 +++++++++++++++++ ...ess.h => uarch-replay-step-state-access.h} | 44 ++-- src/uarch-reset-state-access.h | 84 ++++++ src/uarch-reset-state.cpp | 45 ++++ src/uarch-reset-state.h | 46 ++++ src/uarch-riscv-tests.lua | 3 +- src/uarch-state.h | 13 +- ...ate-access.h => uarch-step-state-access.h} | 18 +- src/uarch-step.cpp | 20 +- src/uarch-step.h | 12 +- src/virtual-machine.cpp | 16 +- src/virtual-machine.h | 6 +- .../src/run-rv64i-arch-test.lua | 8 +- uarch/uarch-ram-entry.S | 5 +- 85 files changed, 2939 insertions(+), 846 deletions(-) create mode 100644 src/compute-uarch-pristine-hash.cpp create mode 100644 src/grpc-config.h create mode 100644 src/i-uarch-reset-state-access.h rename src/{i-uarch-state-access.h => i-uarch-step-state-access.h} (98%) create mode 100644 src/uarch-pristine-state-hash.cpp create mode 100644 src/uarch-pristine-state-hash.h create mode 100644 src/uarch-record-reset-state-access.h rename src/{uarch-record-state-access.h => uarch-record-step-state-access.h} (84%) create mode 100644 src/uarch-replay-reset-state-access.h rename src/{uarch-replay-state-access.h => uarch-replay-step-state-access.h} (91%) create mode 100644 src/uarch-reset-state-access.h create mode 100644 src/uarch-reset-state.cpp create mode 100644 src/uarch-reset-state.h rename src/{uarch-state-access.h => uarch-step-state-access.h} (90%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6aa3f8cbb..4238907c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,10 +5,11 @@ env: KERNEL_VERSION: v0.17.0 LINUX_VERSION: 5.15.63-ctsi-2-v0.17.0 ROOTFS_VERSION: v0.18.0 - TEST_VERSION: vshadow-uarch-test + TEST_VERSION: vreset-uarch-test CARTESI_TESTS_PATH: /usr/share/cartesi-machine/tests CARTESI_IMAGES_PATH: /usr/share/cartesi-machine/images CARTESI_UARCH_PATH: /usr/share/cartesi-machine/uarch/uarch-ram.bin + BUILD_UARCH_IN_DOCKER_LINUX_ENV: no jobs: build: name: Build diff --git a/.gitignore b/.gitignore index e1608cbce..684c73bb5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ third-party/downloads third-party/grpc third-party/mongoose-7.9 src/remote-cartesi-machine-proxy +src/compute-uarch-pristine-hash src/remote-cartesi-machine src/jsonrpc-remote-cartesi-machine src/merkle-tree-hash diff --git a/Makefile b/Makefile index d396b0cc6..71409f0ea 100644 --- a/Makefile +++ b/Makefile @@ -250,7 +250,7 @@ linux-env: check-linux-env -e GROUP=$$(id -g -n) \ -e UID=$$(id -u) \ -e GID=$$(id -g) \ - -e UARCH_TOOLCHAIN_AVAILABLE=yes \ + -e BUILD_UARCH_IN_DOCKER_LINUX_ENV=no \ -v `pwd`:/opt/cartesi/machine-emulator \ -w /opt/cartesi/machine-emulator \ cartesi/linux-env:$(TAG) /bin/bash @@ -261,7 +261,7 @@ linux-env-exec: check-linux-env -e GROUP=$$(id -g -n) \ -e UID=$$(id -u) \ -e GID=$$(id -g) \ - -e UARCH_TOOLCHAIN_AVAILABLE=yes \ + -e BUILD_UARCH_IN_DOCKER_LINUX_ENV=no \ -v `pwd`:/opt/cartesi/machine-emulator \ -w /opt/cartesi/machine-emulator \ cartesi/linux-env:$(TAG) /bin/bash -c "$(CONTAINER_COMMAND)" diff --git a/lib/grpc-interfaces b/lib/grpc-interfaces index cba7c3f1c..35d147f02 160000 --- a/lib/grpc-interfaces +++ b/lib/grpc-interfaces @@ -1 +1 @@ -Subproject commit cba7c3f1c53b83bee83d8a1e5a1074591279220f +Subproject commit 35d147f02e532eeddea66d7d34cdfc67bfa884d7 diff --git a/lib/machine-emulator-defines b/lib/machine-emulator-defines index 9bc521efc..25b22776b 160000 --- a/lib/machine-emulator-defines +++ b/lib/machine-emulator-defines @@ -1 +1 @@ -Subproject commit 9bc521efc79e63a41425b76ebae1a697f9579bad +Subproject commit 25b22776bad2c467f63751fd56aaa2ea1c4b6a82 diff --git a/src/Makefile b/src/Makefile index 7123e4359..55f90a15c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -31,11 +31,19 @@ LUA_LIB:=-llua5.4 LUA_BIN?=lua5.4 GRPC_DIR:=../lib/grpc-interfaces -# Path to the file containing the uarch ram image to be embedded in the emulator. Override to use a pre-built one. +# This flag indicates whether your system has the toolchain required to compile the microarchitecture ram image. +# If "no", the build process will use the toolchain within the linux-env Docker environment. +# If "yes", the toolchain will be run locally. +# This makefile needs to now how to build the microarchitecture ram image because it is a dependency of the emulator. +BUILD_UARCH_IN_DOCKER_LINUX_ENV ?= yes + +# If instead of building the microarchitecture you wamt to use a pre-built image file, modify this variable to +# indicate the path of the desired file. UARCH_RAM_IMAGE ?= ../uarch/uarch-ram.bin -# Indicates whether the toolchain required to compile the uarch is present. If not, the one within the linux-env Docker environment will be used. -UARCH_TOOLCHAIN_AVAILABLE ?= no +# Allow mongoose to receive large messages +# The biggest message is reset uarch with large_data: 2x 4MB + small data + overhead +MG_MAX_RECV_SIZE := 0xc00000 PROTOC:=$(shell which protoc) GRPC_CPP_PLUGIN:=$(shell which grpc_cpp_plugin) @@ -351,8 +359,16 @@ CARTESI_OBJS:= \ machine-c-api.o \ uarch-machine.o \ uarch-step.o \ + uarch-reset-state.o \ uarch-interpret.o \ - uarch-ram.bin.o + uarch-pristine-ram.o \ + uarch-pristine-state-hash.o + +COMPUTE_UARCH_PRISTINE_HASH_OBJS := \ + compute-uarch-pristine-hash.o \ + machine-merkle-tree.o \ + pristine-merkle-tree.o \ + uarch-pristine-ram.o LUACARTESI_OBJS:= \ clua-cartesi.o \ @@ -360,7 +376,8 @@ LUACARTESI_OBJS:= \ clua-machine.o \ clua-htif.o \ clua-machine-util.o \ - clua.o + clua.o \ + uarch-pristine-state-hash.o CARTESI_PROTOBUF_GEN_OBJS:= \ versioning.pb.o \ @@ -537,6 +554,10 @@ compile_flags.txt: luacartesi: cartesi.so cartesi/grpc.so cartesi/jsonrpc.so +compute-uarch-pristine-hash: $(COMPUTE_UARCH_PRISTINE_HASH_OBJS) + $(CXX) $(LDFLAGS) $(CARTESI_EXECUTABLE_LDFLAGS) -o $@ $^ $(HASH_LIBS) + + luacartesi-pgo: $(MAKE) --no-print-directory generate ./cartesi-machine.lua -- "$(PGO_WORKLOAD)" @@ -607,8 +628,10 @@ REMOTE_CARTESI_MACHINE_OBJS:= \ interpret.o \ uarch-machine.o \ uarch-step.o \ + uarch-reset-state.o \ uarch-interpret.o \ - uarch-ram.bin.o + uarch-pristine-ram.o \ + uarch-pristine-state-hash.o JSONRPC_REMOTE_CARTESI_MACHINE_OBJS:= \ slog.o \ @@ -641,8 +664,10 @@ JSONRPC_REMOTE_CARTESI_MACHINE_OBJS:= \ interpret.o \ uarch-machine.o \ uarch-step.o \ + uarch-reset-state.o \ uarch-interpret.o \ - uarch-ram.bin.o + uarch-pristine-ram.o \ + uarch-pristine-state-hash.o CYCLE_PERIOD ?= 13 HASH_DIR = hashes/$(CYCLE_PERIOD) @@ -716,7 +741,7 @@ grpc-interfaces: $(PROTO_SOURCES) remote-cartesi-machine: $(REMOTE_CARTESI_MACHINE_OBJS) $(LIBCARTESI_PROTOBUF) $(CXX) $(LDFLAGS) $(CARTESI_EXECUTABLE_LDFLAGS) -o $@ $(REMOTE_CARTESI_MACHINE_OBJS) $(REMOTE_CARTESI_MACHINE_LIBS) -$(BUILDDIR)/lib/mongoose.o: CFLAGS := $(patsubst %-std=c99,%,$(CFLAGS)) +$(BUILDDIR)/lib/mongoose.o: CFLAGS := -D MG_MAX_RECV_SIZE=$(MG_MAX_RECV_SIZE) $(patsubst %-std=c99,%,$(CFLAGS)) jsonrpc-remote-cartesi-machine: $(JSONRPC_REMOTE_CARTESI_MACHINE_OBJS) $(LIBCARTESI_PROTOBUF) $(CXX) $(LDFLAGS) $(CARTESI_EXECUTABLE_LDFLAGS) -o $@ $(JSONRPC_REMOTE_CARTESI_MACHINE_OBJS) $(JSONRPC_REMOTE_CARTESI_MACHINE_LIBS) @@ -752,6 +777,16 @@ jsonrpc-discover.cpp: jsonrpc-discover.json echo ')json";' >> jsonrpc-discover.cpp echo '} // namespace cartesi' >> jsonrpc-discover.cpp +uarch-pristine-state-hash.cpp: compute-uarch-pristine-hash + @echo '// This file is auto-generated and should not be modified' > $@ + @echo '// clang-format off' >> $@ + @echo '#include "uarch-pristine-state-hash.h"' >> $@ + @echo 'namespace cartesi {' >> $@ + @echo ' const machine_merkle_tree::hash_type uarch_pristine_state_hash{' >> $@ + @compute-uarch-pristine-hash >> $@ + @echo ' };' >> $@ + @echo '} // namespace cartesi' >> $@ + %.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 @@ -771,21 +806,21 @@ jsonrpc-discover.cpp: jsonrpc-discover.json %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< -uarch-ram.bin.o: $(UARCH_RAM_IMAGE) - xxd -i -n embedded_uarch_ram $< | $(CXX) $(CFLAGS) -c -x c -o $@ - +uarch-pristine-ram.o: $(UARCH_RAM_IMAGE) + @xxd -i -n uarch_pristine_ram $< | $(CXX) $(CFLAGS) -c -x c -o $@ - ../uarch/uarch-ram.bin: - if [ "$(UARCH_TOOLCHAIN_AVAILABLE)" = "yes" ]; then \ - $(MAKE) -C .. uarch; \ - else \ + @if [ "$(BUILD_UARCH_IN_DOCKER_LINUX_ENV)" = "yes" ]; then \ $(MAKE) -C .. uarch-with-linux-env; \ + else \ + $(MAKE) -C .. uarch; \ fi clean: clean-auto-generated clean-coverage clean-profile clean-proto-sources clean-tidy clean-libcartesi clean-executables clean-tests clean-auto-generated: - @rm -f jsonrpc-discover.cpp machine-c-version.h + @rm -f jsonrpc-discover.cpp machine-c-version.h uarch-pristine-state-hash.cpp clean-proto-sources: @rm -f *.pb.cc *.pb.h diff --git a/src/access-log.h b/src/access-log.h index e91c0d973..e92f1f99b 100644 --- a/src/access-log.h +++ b/src/access-log.h @@ -58,6 +58,7 @@ static inline uint64_t get_word_access_data(const access_data &ad) { class access { using proof_type = machine_merkle_tree::proof_type; + using hash_type = machine_merkle_tree::hash_type; public: void set_type(access_type type) { @@ -102,10 +103,10 @@ class access { /// \brief Gets data that can be read at address before access. /// \returns Data at address. - const access_data &get_read(void) const { + const std::optional &get_read(void) const { return m_read; } - access_data &get_read(void) { + std::optional &get_read(void) { return m_read; } @@ -120,13 +121,43 @@ class access { /// \brief Gets data that was written at address after access. /// \returns Data at address. - const access_data &get_written(void) const { + const std::optional &get_written(void) const { return m_written; } - access_data &get_written(void) { + std::optional &get_written(void) { return m_written; } + /// \brief Sets hash of data that was written at address after access. + /// \param hash Hash of new data at address. + void set_written_hash(const hash_type &hash) { + m_written_hash = hash; + } + + /// \brief Gets hash of data that was written at address after access. + /// \returns Hash of written data at address. + const std::optional &get_written_hash(void) const { + return m_written_hash; + } + std::optional &get_written_hash(void) { + return m_written_hash; + } + + /// \brief Sets hash of data that can be read at address before access. + /// \param hash Hash of data at address. + void set_read_hash(const hash_type &hash) { + m_read_hash = hash; + } + + /// \brief Gets hash of data that can be read at address before access. + /// \returns Hash of data at address. + const hash_type &get_read_hash(void) const { + return m_read_hash; + } + hash_type &get_read_hash(void) { + return m_read_hash; + } + /// \brief Sets proof that data read at address was in /// Merkle tree before access. /// \param proof Corresponding Merkle tree proof. @@ -151,12 +182,14 @@ class access { } private: - access_type m_type{0}; ///< Type of access - uint64_t m_address{0}; ///< Address of access - int m_log2_size{0}; ///< Log2 of size of access - access_data m_read; ///< Data before access - access_data m_written; ///< Data after access (if writing) - std::optional m_proof{}; ///< Proof of data before access + access_type m_type{0}; ///< Type of access + uint64_t m_address{0}; ///< Address of access + int m_log2_size{0}; ///< Log2 of size of access + std::optional m_read; ///< Data before access + hash_type m_read_hash; ///< Hash of data before access + std::optional m_written{}; ///< Written data + std::optional m_written_hash; ///< Hash of written data + std::optional m_proof{}; ///< Proof of data before access }; /// \brief Log of state accesses @@ -166,11 +199,15 @@ class access_log { class type { bool m_proofs; ///< Includes proofs bool m_annotations; ///< Includes annotations + bool m_large_data; ///< Includes data bigger than 8 bytes public: /// \brief Default constructur /// \param proofs Include proofs /// \param annotations Include annotations (default false) - explicit type(bool proofs, bool annotations = false) : m_proofs(proofs), m_annotations(annotations) { + explicit type(bool proofs, bool annotations = false, bool large_data = false) : + m_proofs(proofs), + m_annotations(annotations), + m_large_data(large_data) { ; } @@ -183,6 +220,11 @@ class access_log { bool has_annotations(void) const { return m_annotations; } + + /// \brief Returns whether log includes data bigger than 8 bytes + bool has_large_data(void) const { + return m_large_data; + } }; private: diff --git a/src/cartesi-machine-tests.lua b/src/cartesi-machine-tests.lua index 45bdd2c04..a7caf2a96 100755 --- a/src/cartesi-machine-tests.lua +++ b/src/cartesi-machine-tests.lua @@ -355,9 +355,6 @@ where options are: --uarch-ram-image= name of file containing microarchitecture RAM image. - --uarch-ram-length= - set microarchitecture RAM length. - and command can be: run @@ -559,16 +556,6 @@ local options = { return true end, }, - { - "^%-%-uarch%-ram%-length%=(.+)$", - function(n) - if not n then return false end - uarch = uarch or {} - uarch.ram = uarch.ram or {} - uarch.ram.length = assert(util.parse_number(n), "invalid microarchitecture RAM length " .. n) - return true - end, - }, { ".*", function(all) error("unrecognized option " .. all) end }, } @@ -608,7 +595,7 @@ local function run_machine(machine, ctx, max_mcycle, advance_machine_fn) end local function advance_machine_with_uarch(machine) - if machine:run_uarch() == cartesi.UARCH_BREAK_REASON_UARCH_HALTED then machine:reset_uarch_state() end + if machine:run_uarch() == cartesi.UARCH_BREAK_REASON_UARCH_HALTED then machine:reset_uarch() end end local function run_machine_with_uarch(machine, ctx, max_mcycle) @@ -686,12 +673,10 @@ local function print_machine(test_name, expected_cycles) --rom-image='%s'\ --ram-image='%s'\ --no-rom-bootargs\ - --uarch-ram-length=%d\ --uarch-ram-image=%s\ --max-mcycle=%d ", test_path .. "/bootstrap.bin", test_path .. "/" .. test_name, - uarch.ram.length, uarch.ram.image_filename, 2 * expected_cycles ) @@ -852,7 +837,7 @@ local function hash(tests) total_cycles = total_cycles + 1 end if status == cartesi.UARCH_BREAK_REASON_UARCH_HALTED then - machine:reset_uarch_state() + machine:reset_uarch() if machine:read_iflags_H() then break end end end @@ -919,13 +904,13 @@ local function step(tests) local final_uarch_cycle = machine:read_uarch_cycle() total_uarch_cycles = total_uarch_cycles + (final_uarch_cycle - init_uarch_cycle) if machine:read_uarch_halt_flag() then - machine:reset_uarch_state() + machine:reset_uarch() if machine:read_iflags_H() then break end end if not periodic_action or total_uarch_cycles == next_action_uarch_cycle then local init_mcycle = machine:read_mcycle() init_uarch_cycle = machine:read_uarch_cycle() - local log = machine:step_uarch(log_type) + local log = machine:log_uarch_step(log_type) local final_mcycle = machine:read_mcycle() final_uarch_cycle = machine:read_uarch_cycle() if total_logged_steps > 0 then out:write(",\n") end @@ -933,7 +918,7 @@ local function step(tests) total_uarch_cycles = total_uarch_cycles + 1 total_logged_steps = total_logged_steps + 1 if machine:read_uarch_halt_flag() then - machine:reset_uarch_state() + machine:reset_uarch() if machine:read_iflags_H() then break end end end diff --git a/src/cartesi-machine.lua b/src/cartesi-machine.lua index 7fdb94153..295d53162 100755 --- a/src/cartesi-machine.lua +++ b/src/cartesi-machine.lua @@ -278,10 +278,7 @@ where options are: --max-uarch-cycle= stop at a given micro cycle. - - --auto-reset-uarch-state - automatically reset state after miroarchitecture halt - + -i or --htif-console-getchar run in interactive mode. @@ -310,8 +307,14 @@ where options are: --initial-hash and --final-hash. (default: none) - --step-uarch - print step log for 1 additional micro cycle when done. + --log-uarch-step + advance one micro step and print access log . + + --log-uarch-reset + reset the microarchitecture state and print the access log. + + --auto-uarch-reset + reset uarch automatically after halt --json-steps= output json with step logs for all micro cycles to . @@ -328,9 +331,6 @@ where options are: --uarch-ram-image= name of file containing microarchitecture RAM image. - --uarch-ram-length= - set microarchitecture RAM length. - --dump-pmas dump all PMA ranges to disk when done. @@ -426,8 +426,9 @@ local dump_pmas = false local max_mcycle = math.maxinteger local json_steps local max_uarch_cycle = 0 -local auto_reset_uarch_state = false -local step_uarch = false +local log_uarch_step = false +local auto_uarch_reset = false +local log_uarch_reset = false local store_dir local load_dir local cmdline_opts_finished = false @@ -573,16 +574,6 @@ local options = { return true end, }, - { - "^%-%-uarch%-ram%-length%=(.+)$", - function(n) - if not n then return false end - uarch = uarch or {} - uarch.ram = uarch.ram or {} - uarch.ram.length = assert(util.parse_number(n), "invalid microarchitecture RAM length " .. n) - return true - end, - }, { "^%-%-uarch%-ram%-image%=(.*)$", function(o) @@ -857,10 +848,18 @@ local options = { end, }, { - "^%-%-step%-uarch$", + "^%-%-log%-uarch%-step$", + function(all) + if not all then return false end + log_uarch_step = true + return true + end, + }, + { + "^%-%-log%-uarch%-reset$", function(all) if not all then return false end - step_uarch = true + log_uarch_reset = true return true end, }, @@ -881,10 +880,10 @@ local options = { end, }, { - "^%-%-auto%-reset%-uarch%-state$", + "^%-%-auto%-uarch%-reset$", function(all) if not all then return false end - auto_reset_uarch_state = true + auto_uarch_reset = true return true end, }, @@ -1217,8 +1216,6 @@ local function store_machine_config(config, output) end output(" uarch = {\n") output(" ram = {\n") - output(" length = 0x%x,", config.uarch.ram.length or def.uarch.ram.length) - comment_default(config.uarch.ram.length, def.uarch.ram.length) output(" image_filename = %q,", config.uarch.ram.image_filename or def.uarch.ram.image_filename) comment_default(config.uarch.ram.image_filename, def.uarch.ram.image_filename) output(" },\n") @@ -1617,7 +1614,7 @@ if json_steps then if init_mcycle > max_mcycle then break end if init_mcycle == max_mcycle and init_uarch_cycle == max_uarch_cycle then break end -- Advance one micro step - local log = machine:step_uarch(log_type) + local log = machine:log_uarch_step(log_type) steps_count = steps_count + 1 local final_mcycle = machine:read_mcycle() local final_uarch_cycle = machine:read_uarch_cycle() @@ -1628,7 +1625,7 @@ if json_steps then -- microarchitecture halted because it finished interpreting a whole mcycle machine:reset_iflags_Y() -- move past any potential yield -- Reset uarch_halt_flag in order to allow interpreting the next mcycle - machine:reset_uarch_state() + machine:reset_uarch() if machine:read_iflags_H() then stderr("Halted at %u.%u\n", final_mcycle, final_uarch_cycle) break @@ -1792,18 +1789,22 @@ else -- The mcycle counter was incremented, unless the machine was already halted if machine:read_iflags_H() and not previously_halted then stderr("Halted\n") end stderr("Cycles: %u\n", machine:read_mcycle()) - if auto_reset_uarch_state then - machine:reset_uarch_state() + if auto_uarch_reset then + machine:reset_uarch() else stderr("uCycles: %u\n", machine:read_uarch_cycle()) end end end if gdb_stub then gdb_stub:close() end - if step_uarch then + if log_uarch_step then assert(not config.htif.console_getchar, "micro step proof is meaningless in interactive mode") stderr("Gathering micro step log: please wait\n") - util.dump_log(machine:step_uarch({ proofs = true, annotations = true }), io.stderr) + util.dump_log(machine:log_uarch_step({ proofs = true, annotations = true }), io.stderr) + end + if log_uarch_reset then + stderr("Resetting microarchitecture state: please wait\n") + util.dump_log(machine:log_uarch_reset({ proofs = true, annotations = true }), io.stderr) end if dump_pmas then machine:dump_pmas() end if final_hash then diff --git a/src/cartesi/util.lua b/src/cartesi/util.lua index 27e5bece3..03a14148a 100644 --- a/src/cartesi/util.lua +++ b/src/cartesi/util.lua @@ -192,17 +192,17 @@ end local function hexhash8(hash) return string.sub(hexhash(hash), 1, 8) end -local function accessdatastring(data, log2_size) +local function accessdatastring(data, data_hash, log2_size) if log2_size == 3 then data = string.unpack("(uarch_pristine_state_hash.data()), + uarch_pristine_state_hash.size(), "UARCH_PRISTINE_STATE_HASH", -1); clua_setintegerfield(L, MVENDORID_INIT, "MVENDORID", -1); clua_setintegerfield(L, MARCHID_INIT, "MARCHID", -1); clua_setintegerfield(L, MIMPID_INIT, "MIMPID", -1); diff --git a/src/clua-grpc-machine.cpp b/src/clua-grpc-machine.cpp index de737b4fe..a1e2c3782 100644 --- a/src/clua-grpc-machine.cpp +++ b/src/clua-grpc-machine.cpp @@ -42,9 +42,9 @@ static int grpc_machine_class_get_default_config(lua_State *L) { return 1; } -/// \brief This is the machine.verify_access_log() +/// \brief This is the machine.verify_uarch_step_log() /// static method implementation. -static int grpc_machine_class_verify_access_log(lua_State *L) { +static int grpc_machine_class_verify_uarch_step_log(lua_State *L) { const int stubidx = lua_upvalueindex(1); const int ctxidx = lua_upvalueindex(2); lua_settop(L, 3); @@ -53,8 +53,8 @@ static int grpc_machine_class_verify_access_log(lua_State *L) { clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_access_log(L, 1, ctxidx)), ctxidx); auto &managed_runtime_config = clua_push_to(L, clua_managed_cm_ptr(clua_opt_cm_machine_runtime_config(L, 2, {}, ctxidx)), ctxidx); - TRY_EXECUTE(cm_grpc_verify_access_log(managed_grpc_stub.get(), managed_log.get(), managed_runtime_config.get(), - lua_toboolean(L, 3), err_msg)); + TRY_EXECUTE(cm_grpc_verify_uarch_step_log(managed_grpc_stub.get(), managed_log.get(), managed_runtime_config.get(), + true, err_msg)); managed_log.reset(); managed_runtime_config.reset(); lua_pop(L, 2); @@ -62,9 +62,53 @@ static int grpc_machine_class_verify_access_log(lua_State *L) { return 1; } -/// \brief This is the machine.verify_state_transition() +/// \brief This is the machine.verify_uarch_reset_log() /// static method implementation. -static int grpc_machine_class_verify_state_transition(lua_State *L) { +static int grpc_machine_class_verify_uarch_reset_log(lua_State *L) { + const int stubidx = lua_upvalueindex(1); + const int ctxidx = lua_upvalueindex(2); + lua_settop(L, 3); + auto &managed_grpc_stub = clua_check>(L, stubidx, ctxidx); + auto &managed_log = + clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_access_log(L, 1, ctxidx)), ctxidx); + auto &managed_runtime_config = clua_push_to(L, + clua_managed_cm_ptr(clua_opt_cm_machine_runtime_config(L, 2, {}, ctxidx)), ctxidx); + TRY_EXECUTE(cm_grpc_verify_uarch_reset_log(managed_grpc_stub.get(), managed_log.get(), managed_runtime_config.get(), + true, err_msg)); + managed_log.reset(); + managed_runtime_config.reset(); + lua_pop(L, 2); + lua_pushnumber(L, 1); + return 1; +} + +/// \brief This is the machine.verify_uarch_reset_state_transition() +/// static method implementation. +static int grpc_machine_class_verify_uarch_reset_state_transition(lua_State *L) { + const int stubidx = lua_upvalueindex(1); + const int ctxidx = lua_upvalueindex(2); + lua_settop(L, 5); + auto &managed_grpc_stub = clua_check>(L, stubidx, ctxidx); + cm_hash root_hash{}; + clua_check_cm_hash(L, 1, &root_hash); + auto &managed_log = + clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_access_log(L, 2, ctxidx)), ctxidx); + cm_hash target_hash{}; + clua_check_cm_hash(L, 3, &target_hash); + auto &managed_runtime_config = clua_push_to(L, + clua_managed_cm_ptr(clua_opt_cm_machine_runtime_config(L, 4, {}, ctxidx)), ctxidx); + TRY_EXECUTE(cm_grpc_verify_uarch_reset_state_transition(managed_grpc_stub.get(), &root_hash, managed_log.get(), + &target_hash, managed_runtime_config.get(), true, err_msg)); + managed_log.reset(); + managed_runtime_config.reset(); + lua_pop(L, 2); + lua_pushnumber(L, 1); + return 1; +} + +/// \brief This is the machine.verify_uarch_step_state_transition() +/// static method implementation. +static int grpc_machine_class_verify_uarch_step_state_transition(lua_State *L) { const int stubidx = lua_upvalueindex(1); const int ctxidx = lua_upvalueindex(2); lua_settop(L, 5); @@ -77,9 +121,8 @@ static int grpc_machine_class_verify_state_transition(lua_State *L) { clua_check_cm_hash(L, 3, &target_hash); auto &managed_runtime_config = clua_push_to(L, clua_managed_cm_ptr(clua_opt_cm_machine_runtime_config(L, 4, {}, ctxidx)), ctxidx); - const bool one_based = lua_toboolean(L, 5); - TRY_EXECUTE(cm_grpc_verify_state_transition(managed_grpc_stub.get(), &root_hash, managed_log.get(), &target_hash, - managed_runtime_config.get(), one_based, err_msg)); + TRY_EXECUTE(cm_grpc_verify_uarch_step_state_transition(managed_grpc_stub.get(), &root_hash, managed_log.get(), + &target_hash, managed_runtime_config.get(), true, err_msg)); managed_log.reset(); managed_runtime_config.reset(); lua_pop(L, 2); @@ -121,8 +164,10 @@ static int grpc_machine_class_get_csr_address(lua_State *L) { /// \brief Contents of the machine class metatable __index table. static const auto grpc_machine_static_methods = cartesi::clua_make_luaL_Reg_array({ {"get_default_config", grpc_machine_class_get_default_config}, - {"verify_access_log", grpc_machine_class_verify_access_log}, - {"verify_state_transition", grpc_machine_class_verify_state_transition}, + {"verify_uarch_step_log", grpc_machine_class_verify_uarch_step_log}, + {"verify_uarch_step_state_transition", grpc_machine_class_verify_uarch_step_state_transition}, + {"verify_uarch_reset_log", grpc_machine_class_verify_uarch_reset_log}, + {"verify_uarch_reset_state_transition", grpc_machine_class_verify_uarch_reset_state_transition}, {"get_x_address", grpc_machine_class_get_x_address}, {"get_uarch_x_address", grpc_machine_class_get_uarch_x_address}, {"get_csr_address", grpc_machine_class_get_csr_address}, diff --git a/src/clua-i-virtual-machine.cpp b/src/clua-i-virtual-machine.cpp index bcbca0026..da946a93e 100644 --- a/src/clua-i-virtual-machine.cpp +++ b/src/clua-i-virtual-machine.cpp @@ -152,7 +152,6 @@ IMPL_MACHINE_OBJ_READ_WRITE(htif_iyield) IMPL_MACHINE_OBJ_READ_WRITE(clint_mtimecmp) IMPL_MACHINE_OBJ_READ_WRITE(uarch_cycle) IMPL_MACHINE_OBJ_READ_WRITE(uarch_pc) -IMPL_MACHINE_OBJ_READ(uarch_ram_length) /// \brief This is the machine:read_csr() method implementation. /// \param L Lua state. @@ -357,14 +356,25 @@ static int machine_obj_index_set_uarch_halt_flag(lua_State *L) { return 0; } -/// \brief This is the machine:reset_uarch_state() method implementation. +/// \brief This is the machine:reset_uarch() method implementation. /// \param L Lua state. -static int machine_obj_index_reset_uarch_state(lua_State *L) { +static int machine_obj_index_reset_uarch(lua_State *L) { auto &m = clua_check>(L, 1); - TRY_EXECUTE(cm_reset_uarch_state(m.get(), err_msg)); + TRY_EXECUTE(cm_reset_uarch(m.get(), err_msg)); return 0; } +/// \brief This is the machine:reset_uarch() method implementation. +/// \param L Lua state. +static int machine_obj_index_log_uarch_reset(lua_State *L) { + auto &m = clua_check>(L, 1); + auto &managed_log = clua_push_to(L, clua_managed_cm_ptr(nullptr)); + TRY_EXECUTE(cm_log_uarch_reset(m.get(), clua_check_cm_log_type(L, 2), true, &managed_log.get(), err_msg)); + clua_push_cm_access_log(L, managed_log.get()); + managed_log.reset(); + return 1; +} + /// \brief This is the machine:run_uarch() method implementation. /// \param L Lua state. static int machine_obj_index_run_uarch(lua_State *L) { @@ -376,12 +386,12 @@ static int machine_obj_index_run_uarch(lua_State *L) { return 1; } -/// \brief This is the machine:step_uarch() method implementation. +/// \brief This is the machine:log_uarch_step() method implementation. /// \param L Lua state. -static int machine_obj_index_step_uarch(lua_State *L) { +static int machine_obj_index_log_uarch_step(lua_State *L) { auto &m = clua_check>(L, 1); auto &managed_log = clua_push_to(L, clua_managed_cm_ptr(nullptr)); - TRY_EXECUTE(cm_step_uarch(m.get(), clua_check_cm_log_type(L, 2), true, &managed_log.get(), err_msg)); + TRY_EXECUTE(cm_log_uarch_step(m.get(), clua_check_cm_log_type(L, 2), true, &managed_log.get(), err_msg)); clua_push_cm_access_log(L, managed_log.get()); managed_log.reset(); return 1; @@ -547,7 +557,6 @@ static const auto machine_obj_index = cartesi::clua_make_luaL_Reg_array({ {"read_uarch_cycle", machine_obj_index_read_uarch_cycle}, {"read_uarch_pc", machine_obj_index_read_uarch_pc}, {"read_uarch_x", machine_obj_index_read_uarch_x}, - {"read_uarch_ram_length", machine_obj_index_read_uarch_ram_length}, {"read_iflags", machine_obj_index_read_iflags}, {"read_iflags_H", machine_obj_index_read_iflags_H}, {"read_iflags_Y", machine_obj_index_read_iflags_Y}, @@ -593,7 +602,7 @@ static const auto machine_obj_index = cartesi::clua_make_luaL_Reg_array({ {"read_f", machine_obj_index_read_f}, {"run", machine_obj_index_run}, {"run_uarch", machine_obj_index_run_uarch}, - {"step_uarch", machine_obj_index_step_uarch}, + {"log_uarch_step", machine_obj_index_log_uarch_step}, {"store", machine_obj_index_store}, {"verify_dirty_page_maps", machine_obj_index_verify_dirty_page_maps}, {"verify_merkle_tree", machine_obj_index_verify_merkle_tree}, @@ -645,7 +654,8 @@ static const auto machine_obj_index = cartesi::clua_make_luaL_Reg_array({ {"rollback", machine_obj_index_rollback}, {"read_uarch_halt_flag", machine_obj_index_read_uarch_halt_flag}, {"set_uarch_halt_flag", machine_obj_index_set_uarch_halt_flag}, - {"reset_uarch_state", machine_obj_index_reset_uarch_state}, + {"reset_uarch", machine_obj_index_reset_uarch}, + {"log_uarch_reset", machine_obj_index_log_uarch_reset}, }); /// \brief This is the machine __close metamethod implementation. diff --git a/src/clua-jsonrpc-machine.cpp b/src/clua-jsonrpc-machine.cpp index 1dce580e9..6bc60411b 100644 --- a/src/clua-jsonrpc-machine.cpp +++ b/src/clua-jsonrpc-machine.cpp @@ -42,9 +42,9 @@ static int jsonrpc_machine_class_get_default_config(lua_State *L) { return 1; } -/// \brief This is the machine.verify_access_log() +/// \brief This is the machine.verify_uarch_step_log() /// static method implementation. -static int jsonrpc_machine_class_verify_access_log(lua_State *L) { +static int jsonrpc_machine_class_verify_uarch_step_log(lua_State *L) { const int stubidx = lua_upvalueindex(1); const int ctxidx = lua_upvalueindex(2); lua_settop(L, 3); @@ -53,8 +53,8 @@ static int jsonrpc_machine_class_verify_access_log(lua_State *L) { clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_access_log(L, 1, ctxidx)), ctxidx); auto &managed_runtime_config = clua_push_to(L, clua_managed_cm_ptr(clua_opt_cm_machine_runtime_config(L, 2, {}, ctxidx)), ctxidx); - TRY_EXECUTE(cm_jsonrpc_verify_access_log(managed_jsonrpc_mg_mgr.get(), managed_log.get(), - managed_runtime_config.get(), lua_toboolean(L, 3), err_msg)); + TRY_EXECUTE(cm_jsonrpc_verify_uarch_step_log(managed_jsonrpc_mg_mgr.get(), managed_log.get(), + managed_runtime_config.get(), true, err_msg)); managed_log.reset(); managed_runtime_config.reset(); lua_pop(L, 2); @@ -62,9 +62,54 @@ static int jsonrpc_machine_class_verify_access_log(lua_State *L) { return 1; } -/// \brief This is the machine.verify_state_transition() +/// \brief This is the machine.verify_uarch_reset_log() /// static method implementation. -static int jsonrpc_machine_class_verify_state_transition(lua_State *L) { +static int jsonrpc_machine_class_verify_uarch_reset_log(lua_State *L) { + const int stubidx = lua_upvalueindex(1); + const int ctxidx = lua_upvalueindex(2); + lua_settop(L, 3); + auto &managed_jsonrpc_mg_mgr = clua_check>(L, stubidx, ctxidx); + auto &managed_log = + clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_access_log(L, 1, ctxidx)), ctxidx); + auto &managed_runtime_config = clua_push_to(L, + clua_managed_cm_ptr(clua_opt_cm_machine_runtime_config(L, 2, {}, ctxidx)), ctxidx); + TRY_EXECUTE(cm_jsonrpc_verify_uarch_reset_log(managed_jsonrpc_mg_mgr.get(), managed_log.get(), + managed_runtime_config.get(), true, err_msg)); + managed_log.reset(); + managed_runtime_config.reset(); + lua_pop(L, 2); + lua_pushnumber(L, 1); + return 1; +} + +/// \brief This is the machine.verify_uarch_step_state_transition() +/// static method implementation. +static int jsonrpc_machine_class_verify_uarch_step_state_transition(lua_State *L) { + const int stubidx = lua_upvalueindex(1); + const int ctxidx = lua_upvalueindex(2); + lua_settop(L, 5); + auto &managed_jsonrpc_mg_mgr = clua_check>(L, stubidx, ctxidx); + cm_hash root_hash{}; + clua_check_cm_hash(L, 1, &root_hash); + auto &managed_log = + clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_access_log(L, 2, ctxidx)), ctxidx); + cm_hash target_hash{}; + clua_check_cm_hash(L, 3, &target_hash); + auto &managed_runtime_config = clua_push_to(L, + clua_managed_cm_ptr(clua_opt_cm_machine_runtime_config(L, 4, {}, ctxidx)), ctxidx); + const bool one_based = lua_toboolean(L, 5); + TRY_EXECUTE(cm_jsonrpc_verify_uarch_step_state_transition(managed_jsonrpc_mg_mgr.get(), &root_hash, + managed_log.get(), &target_hash, managed_runtime_config.get(), one_based, err_msg)); + managed_log.reset(); + managed_runtime_config.reset(); + lua_pop(L, 2); + lua_pushnumber(L, 1); // result + return 1; +} + +/// \brief This is the machine.verify_uarch_reset_state_transition() +/// static method implementation. +static int jsonrpc_machine_class_verify_uarch_reset_state_transition(lua_State *L) { const int stubidx = lua_upvalueindex(1); const int ctxidx = lua_upvalueindex(2); lua_settop(L, 5); @@ -78,8 +123,8 @@ static int jsonrpc_machine_class_verify_state_transition(lua_State *L) { auto &managed_runtime_config = clua_push_to(L, clua_managed_cm_ptr(clua_opt_cm_machine_runtime_config(L, 4, {}, ctxidx)), ctxidx); const bool one_based = lua_toboolean(L, 5); - TRY_EXECUTE(cm_jsonrpc_verify_state_transition(managed_jsonrpc_mg_mgr.get(), &root_hash, managed_log.get(), - &target_hash, managed_runtime_config.get(), one_based, err_msg)); + TRY_EXECUTE(cm_jsonrpc_verify_uarch_reset_state_transition(managed_jsonrpc_mg_mgr.get(), &root_hash, + managed_log.get(), &target_hash, managed_runtime_config.get(), one_based, err_msg)); managed_log.reset(); managed_runtime_config.reset(); lua_pop(L, 2); @@ -132,8 +177,10 @@ static int jsonrpc_machine_class_get_csr_address(lua_State *L) { /// \brief Contents of the machine class metatable __index table. static const auto jsonrpc_machine_static_methods = cartesi::clua_make_luaL_Reg_array({ {"get_default_config", jsonrpc_machine_class_get_default_config}, - {"verify_access_log", jsonrpc_machine_class_verify_access_log}, - {"verify_state_transition", jsonrpc_machine_class_verify_state_transition}, + {"verify_uarch_step_log", jsonrpc_machine_class_verify_uarch_step_log}, + {"verify_uarch_step_state_transition", jsonrpc_machine_class_verify_uarch_step_state_transition}, + {"verify_uarch_reset_log", jsonrpc_machine_class_verify_uarch_reset_log}, + {"verify_uarch_reset_state_transition", jsonrpc_machine_class_verify_uarch_reset_state_transition}, {"get_x_address", jsonrpc_machine_class_get_x_address}, {"get_f_address", jsonrpc_machine_class_get_f_address}, {"get_uarch_x_address", jsonrpc_machine_class_get_uarch_x_address}, diff --git a/src/clua-machine-util.cpp b/src/clua-machine-util.cpp index 7fb41db86..581c0b0a9 100644 --- a/src/clua-machine-util.cpp +++ b/src/clua-machine-util.cpp @@ -407,11 +407,6 @@ static unsigned char *aux_cm_access_data_field(lua_State *L, int tabidx, const c return a; } -static unsigned char *check_cm_access_data_field(lua_State *L, int tabidx, const char *field, uint64_t log2_size, - size_t *data_size) { - return aux_cm_access_data_field(L, tabidx, field, log2_size, false, data_size); -} - static unsigned char *opt_cm_access_data_field(lua_State *L, int tabidx, const char *field, uint64_t log2_size, size_t *data_size) { return aux_cm_access_data_field(L, tabidx, field, log2_size, true, data_size); @@ -438,7 +433,16 @@ static void check_cm_access(lua_State *L, int tabidx, bool proofs, cm_access *a, a->proof = clua_check_cm_merkle_tree_proof(L, -1, ctxidx); lua_pop(L, 1); } - a->read_data = check_cm_access_data_field(L, tabidx, "read", a->log2_size, &a->read_data_size); + + lua_getfield(L, tabidx, "read_hash"); + clua_check_cm_hash(L, -1, &a->read_hash); + lua_pop(L, 1); + if (a->type == CM_ACCESS_WRITE) { + lua_getfield(L, tabidx, "written_hash"); + clua_check_cm_hash(L, -1, &a->written_hash); + lua_pop(L, 1); + } + a->read_data = opt_cm_access_data_field(L, tabidx, "read", a->log2_size, &a->read_data_size); a->written_data = opt_cm_access_data_field(L, tabidx, "written", a->log2_size, &a->written_data_size); } @@ -451,6 +455,7 @@ cm_access_log *clua_check_cm_access_log(lua_State *L, int tabidx, int ctxidx) { check_table_field(L, tabidx, "log_type"); log->log_type.proofs = opt_boolean_field(L, -1, "proofs"); log->log_type.annotations = opt_boolean_field(L, -1, "annotations"); + log->log_type.large_data = opt_boolean_field(L, -1, "large_data"); lua_pop(L, 1); check_table_field(L, tabidx, "accesses"); log->accesses.count = luaL_len(L, -1); @@ -551,7 +556,6 @@ CM_PROC_CSR clua_check_cm_proc_csr(lua_State *L, int idx) try { {"uarch_pc", CM_PROC_UARCH_PC}, {"uarch_cycle", CM_PROC_UARCH_CYCLE}, {"uarch_halt_flag", CM_PROC_UARCH_HALT_FLAG}, - {"uarch_ram_length", CM_PROC_UARCH_RAM_LENGTH} // clang-format on }; const char *name = luaL_checkstring(L, idx); @@ -584,6 +588,7 @@ static void push_cm_access_log_type(lua_State *L, const cm_access_log_type *log_ lua_newtable(L); clua_setbooleanfield(L, log_type->annotations, "annotations", -1); clua_setbooleanfield(L, log_type->proofs, "proofs", -1); + clua_setbooleanfield(L, log_type->large_data, "large_data", -1); } /// \brief Converts an CM_ACCESS_TYPE to a string. @@ -626,11 +631,19 @@ void clua_push_cm_access_log(lua_State *L, const cm_access_log *log) { clua_setstringfield(L, cm_access_type_name(a->type), "type", -1); clua_setintegerfield(L, a->address, "address", -1); clua_setintegerfield(L, a->log2_size, "log2_size", -1); - push_raw_data(L, a->read_data, a->read_data_size); - lua_setfield(L, -2, "read"); + clua_push_cm_hash(L, &a->read_hash); + lua_setfield(L, -2, "read_hash"); // read_hash + if (a->read_data != nullptr) { + push_raw_data(L, a->read_data, a->read_data_size); + lua_setfield(L, -2, "read"); + } if (a->type == CM_ACCESS_WRITE) { - push_raw_data(L, a->written_data, a->written_data_size); - lua_setfield(L, -2, "written"); + clua_push_cm_hash(L, &a->written_hash); + lua_setfield(L, -2, "written_hash"); + if (a->written_data != nullptr) { + push_raw_data(L, a->written_data, a->written_data_size); + lua_setfield(L, -2, "written"); + } } if (log->log_type.proofs && a->proof != nullptr) { clua_push_cm_proof(L, a->proof); @@ -695,7 +708,11 @@ void clua_push_cm_proof(lua_State *L, const cm_merkle_tree_proof *proof) { cm_access_log_type clua_check_cm_log_type(lua_State *L, int tabidx) { luaL_checktype(L, tabidx, LUA_TTABLE); - return cm_access_log_type{opt_boolean_field(L, tabidx, "proofs"), opt_boolean_field(L, tabidx, "annotations")}; + return cm_access_log_type{ + opt_boolean_field(L, tabidx, "proofs"), + opt_boolean_field(L, tabidx, "annotations"), + opt_boolean_field(L, tabidx, "large_data"), + }; } // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -859,7 +876,6 @@ static void push_cm_flash_drive_configs(lua_State *L, const cm_memory_range_conf /// \param r microarchitecture RAM configuration to be pushed. static void push_cm_uarch_ram_config(lua_State *L, const cm_uarch_ram_config *r) { lua_newtable(L); - clua_setintegerfield(L, r->length, "length", -1); if (r->image_filename != nullptr) { clua_setstringfield(L, r->image_filename, "image_filename", -1); } @@ -872,6 +888,7 @@ static void push_cm_uarch_processor_config(lua_State *L, const cm_uarch_processo lua_newtable(L); clua_setintegerfield(L, u->pc, "pc", -1); clua_setintegerfield(L, u->cycle, "cycle", -1); + clua_setbooleanfield(L, u->halt_flag, "halt_flag", -1); lua_newtable(L); for (int i = 1; i <= (UARCH_X_REG_COUNT - 1); i++) { lua_pushinteger(L, static_cast(u->x[i])); @@ -1161,7 +1178,6 @@ static void check_cm_uarch_ram_config(lua_State *L, int tabidx, cm_uarch_ram_con *r = *def; return; } - r->length = check_uint_field(L, -1, "length"); r->image_filename = opt_copy_string_field(L, -1, "image_filename"); lua_pop(L, 1); } @@ -1178,6 +1194,7 @@ static void check_cm_uarch_processor_config(lua_State *L, int tabidx, cm_uarch_p } p->pc = opt_uint_field(L, -1, "pc", def->pc); p->cycle = opt_uint_field(L, -1, "cycle", def->cycle); + p->halt_flag = opt_boolean_field(L, -1, "halt_flag", def->halt_flag); lua_getfield(L, -1, "x"); if (lua_istable(L, -1)) { for (int i = 1; i <= (UARCH_X_REG_COUNT - 1); i++) { diff --git a/src/clua-machine.cpp b/src/clua-machine.cpp index 7543b013c..b560b8b6d 100644 --- a/src/clua-machine.cpp +++ b/src/clua-machine.cpp @@ -35,21 +35,21 @@ static int machine_class_index_get_default_config(lua_State *L) { return 1; } -/// \brief This is the machine.verify_access_log() method implementation. -static int machine_class_index_verify_access_log(lua_State *L) { +/// \brief This is the machine.verify_uarch_step_log() method implementation. +static int machine_class_index_verify_uarch_step_log(lua_State *L) { lua_settop(L, 2); auto &managed_log = clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_access_log(L, 1))); auto &managed_runtime_config = clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_machine_runtime_config(L, 2))); - TRY_EXECUTE(cm_verify_access_log(managed_log.get(), managed_runtime_config.get(), true, err_msg)); + TRY_EXECUTE(cm_verify_uarch_step_log(managed_log.get(), managed_runtime_config.get(), true, err_msg)); lua_pushnumber(L, 1); managed_runtime_config.reset(); managed_log.reset(); return 1; } -/// \brief This is the machine.verify_state_transition() method implementation. -static int machine_class_index_verify_state_transition(lua_State *L) { +/// \brief This is the machine.verify_uarch_step_state_transition() method implementation. +static int machine_class_index_verify_uarch_step_state_transition(lua_State *L) { lua_settop(L, 4); cm_hash root_hash{}; clua_check_cm_hash(L, 1, &root_hash); @@ -58,8 +58,38 @@ static int machine_class_index_verify_state_transition(lua_State *L) { clua_check_cm_hash(L, 3, &target_hash); auto &managed_runtime_config = clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_machine_runtime_config(L, 4))); - TRY_EXECUTE(cm_verify_state_transition(&root_hash, managed_log.get(), &target_hash, managed_runtime_config.get(), - true, err_msg)); + TRY_EXECUTE(cm_verify_uarch_step_state_transition(&root_hash, managed_log.get(), &target_hash, + managed_runtime_config.get(), true, err_msg)); + lua_pushnumber(L, 1); + managed_log.reset(); + managed_runtime_config.reset(); + return 1; +} +/// \brief This is the machine.verify_uarch_reset_log() method implementation. +static int machine_class_index_verify_uarch_reset_log(lua_State *L) { + lua_settop(L, 2); + auto &managed_log = clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_access_log(L, 1))); + auto &managed_runtime_config = + clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_machine_runtime_config(L, 2))); + TRY_EXECUTE(cm_verify_uarch_reset_log(managed_log.get(), managed_runtime_config.get(), true, err_msg)); + lua_pushnumber(L, 1); + managed_runtime_config.reset(); + managed_log.reset(); + return 1; +} + +/// \brief This is the machine.verify_uarch_reset_state_transition() method implementation. +static int machine_class_index_verify_uarch_reset_state_transition(lua_State *L) { + lua_settop(L, 4); + cm_hash root_hash{}; + clua_check_cm_hash(L, 1, &root_hash); + auto &managed_log = clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_access_log(L, 2))); + cm_hash target_hash{}; + clua_check_cm_hash(L, 3, &target_hash); + auto &managed_runtime_config = + clua_push_to(L, clua_managed_cm_ptr(clua_check_cm_machine_runtime_config(L, 4))); + TRY_EXECUTE(cm_verify_uarch_reset_state_transition(&root_hash, managed_log.get(), &target_hash, + managed_runtime_config.get(), true, err_msg)); lua_pushnumber(L, 1); managed_log.reset(); managed_runtime_config.reset(); @@ -105,8 +135,10 @@ static int machine_class_index_get_csr_address(lua_State *L) { /// \brief Contents of the machine class metatable __index table. static const auto machine_class_index = cartesi::clua_make_luaL_Reg_array({ {"get_default_config", machine_class_index_get_default_config}, - {"verify_access_log", machine_class_index_verify_access_log}, - {"verify_state_transition", machine_class_index_verify_state_transition}, + {"verify_uarch_step_log", machine_class_index_verify_uarch_step_log}, + {"verify_uarch_step_state_transition", machine_class_index_verify_uarch_step_state_transition}, + {"verify_uarch_reset_log", machine_class_index_verify_uarch_reset_log}, + {"verify_uarch_reset_state_transition", machine_class_index_verify_uarch_reset_state_transition}, {"get_x_address", machine_class_index_get_x_address}, {"get_uarch_x_address", machine_class_index_get_uarch_x_address}, {"get_f_address", machine_class_index_get_f_address}, diff --git a/src/clua.cpp b/src/clua.cpp index f6299721a..7b80f5f7c 100644 --- a/src/clua.cpp +++ b/src/clua.cpp @@ -46,6 +46,12 @@ void clua_setstringfield(lua_State *L, const char *val, const char *name, int id lua_setfield(L, absidx, name); } +void clua_setlstringfield(lua_State *L, const char *val, size_t length, const char *name, int idx) { + auto absidx = lua_absindex(L, idx); + lua_pushlstring(L, val, length); + lua_setfield(L, absidx, name); +} + void clua_setbooleanfield(lua_State *L, bool val, const char *name, int idx) { auto absidx = lua_absindex(L, idx); lua_pushboolean(L, val); diff --git a/src/clua.h b/src/clua.h index 8278e34f4..76cb7e231 100644 --- a/src/clua.h +++ b/src/clua.h @@ -336,13 +336,21 @@ void clua_createnewtype(lua_State *L, int ctxidx) { /// \param ctxidx Index (or pseudo-index) of clua context void clua_setintegerfield(lua_State *L, uint64_t val, const char *name, int idx); -/// \brief Sets the lua named field to string value +/// \brief Sets the lua named field to C string value /// \param L Lua state /// \param val String value /// \param idx Index (or pseudo-index) of object in stack /// \param ctxidx Index (or pseudo-index) of clua context void clua_setstringfield(lua_State *L, const char *val, const char *name, int idx); +/// \brief Sets the lua named field to arbitrary string value +/// \param L Lua state +/// \param val String value +/// \param val String length +/// \param idx Index (or pseudo-index) of object in stack +/// \param ctxidx Index (or pseudo-index) of clua context +void clua_setlstringfield(lua_State *L, const char *val, size_t length, const char *name, int idx); + /// \brief Sets the lua named field to boolean value /// \param L Lua state /// \param val Boolean value diff --git a/src/compute-uarch-pristine-hash.cpp b/src/compute-uarch-pristine-hash.cpp new file mode 100644 index 000000000..cd7829599 --- /dev/null +++ b/src/compute-uarch-pristine-hash.cpp @@ -0,0 +1,127 @@ +// 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 "machine-merkle-tree.h" +#include "shadow-uarch-state.h" +#include "uarch-constants.h" +#include "uarch-state.h" +#include "unique-c-ptr.h" + +/// \file +/// \brief This program computes the hash of the pristine uarch state ad writes it to stdout + +using namespace cartesi; + +using tree_type = machine_merkle_tree; +using hash_type = tree_type::hash_type; +using hashertype = tree_type::hasher_type; +using proof_type = tree_type::proof_type; + +static_assert(PMA_PAGE_SIZE == tree_type::get_page_size(), "PMA and machine_merkle_tree page sizes must match"); +static_assert(sizeof(shadow_uarch_state) <= PMA_PAGE_SIZE, "shadow_uarch_state must fit in one page"); + +/// \brief Prints help message +static void help(const char *name) { + std::cerr << R"(Usage: + + )" << name << R"( [options] + +Computes the hash of the pristine uarch state. + +Options: + + --help + Prints this message and returns. +)"; + exit(0); +} + +int main(int argc, char *argv[]) try { + tree_type tree{}; + hashertype hasher{}; + hash_type hash{}; + + // Process command line arguments + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--help") == 0) { + help(argv[0]); + } else { + std::cerr << "unrecognized option '" << argv[i] << "'\n"; + exit(1); + } + } + + // Allocate scratch page buffer + auto scratch = unique_calloc(PMA_PAGE_SIZE, std::nothrow_t{}); + if (!scratch) { + throw std::runtime_error("Could not allocate scratch memory"); + } + + // Build pristine shadow uarch state + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *shadow = reinterpret_cast(scratch.get()); + memset(scratch.get(), 0, PMA_PAGE_SIZE); + shadow->halt_flag = UARCH_HALT_FLAG_INIT; + shadow->pc = UARCH_PC_INIT; + shadow->cycle = UARCH_CYCLE_INIT; + for (int i = 1; i < UARCH_X_REG_COUNT; i++) { + shadow->x[i] = UARCH_X_INIT; + } + + // Start updating merkle tree + tree.begin_update(); + // Add shadow uarch state to merkle tree + tree.get_page_node_hash(hasher, scratch.get(), hash); + if (!tree.update_page_node_hash(UARCH_SHADOW_START_ADDRESS, hash)) { + throw std::runtime_error("Could not update uarch shadow tree node hash"); + } + // Add all pages of uarch ram to merkle tree + for (uint32_t p = 0; p < uarch_pristine_ram_len; p += PMA_PAGE_SIZE) { + const auto *page_data = uarch_pristine_ram + p; + if (p + PMA_PAGE_SIZE > uarch_pristine_ram_len) { + memset(scratch.get(), 0, PMA_PAGE_SIZE); + memcpy(scratch.get(), uarch_pristine_ram + p, uarch_pristine_ram_len - p); + page_data = scratch.get(); + } + tree.get_page_node_hash(hasher, page_data, hash); + if (!tree.update_page_node_hash(UARCH_RAM_START_ADDRESS + p, hash)) { + throw std::runtime_error("Could not update uarch ram tree node hash"); + } + } + + // Get uarch root hash + if (!tree.end_update(hasher)) { + throw std::runtime_error("end_update merkle tree failed"); + } + proof_type proof = tree.get_proof(UARCH_STATE_START_ADDRESS, UARCH_STATE_LOG2_SIZE, nullptr); + auto &uarch_state_hash = proof.get_target_hash(); + + // Print hash + for (auto c : uarch_state_hash) { + std::cout << "0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(c) << ", "; + } + std::cout << std::endl; + return 0; +} catch (std::exception &e) { + std::cerr << "Caught exception: " << e.what() << '\n'; + return 1; +} catch (...) { + std::cerr << "Caught unknown exception\n"; + return 1; +} diff --git a/src/grpc-config.h b/src/grpc-config.h new file mode 100644 index 000000000..884824878 --- /dev/null +++ b/src/grpc-config.h @@ -0,0 +1,27 @@ +// 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 GRPC_CONFIG_H +#define GRPC_CONFIG_H + +namespace cartesi { + +/// \brief GRPC customization argument allowing larger messages +/// \brief Currently, the biggest message is reset uarch with large_data: 2x 4MB + small data + overhead +static constexpr int GRPC_MAX_RECEIVE_MESSAGE_SIZE = 0xc00000; + +} // namespace cartesi +#endif diff --git a/src/grpc-machine-c-api.cpp b/src/grpc-machine-c-api.cpp index b7a050778..4cc538557 100644 --- a/src/grpc-machine-c-api.cpp +++ b/src/grpc-machine-c-api.cpp @@ -128,18 +128,18 @@ int cm_grpc_get_default_config(const cm_grpc_machine_stub *stub, const cm_machin return cm_result_failure(err_msg); } -int cm_grpc_verify_access_log(const cm_grpc_machine_stub *stub, const cm_access_log *log, +int cm_grpc_verify_uarch_step_log(const cm_grpc_machine_stub *stub, const cm_access_log *log, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg) try { const auto *cpp_stub = convert_from_c(stub); const cartesi::access_log cpp_log = convert_from_c(log); const cartesi::machine_runtime_config cpp_runtime = convert_from_c(runtime_config); - cartesi::grpc_virtual_machine::verify_access_log(*cpp_stub, cpp_log, cpp_runtime, one_based); + cartesi::grpc_virtual_machine::verify_uarch_step_log(*cpp_stub, cpp_log, cpp_runtime, one_based); return cm_result_success(err_msg); } catch (...) { return cm_result_failure(err_msg); } -int cm_grpc_verify_state_transition(const cm_grpc_machine_stub *stub, const cm_hash *root_hash_before, +int cm_grpc_verify_uarch_step_state_transition(const cm_grpc_machine_stub *stub, const cm_hash *root_hash_before, const cm_access_log *log, const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg) try { const auto *cpp_stub = convert_from_c(stub); @@ -147,7 +147,33 @@ int cm_grpc_verify_state_transition(const cm_grpc_machine_stub *stub, const cm_h const cartesi::machine::hash_type cpp_root_hash_after = convert_from_c(root_hash_after); const cartesi::access_log cpp_log = convert_from_c(log); const cartesi::machine_runtime_config cpp_runtime = convert_from_c(runtime_config); - cartesi::grpc_virtual_machine::verify_state_transition(*cpp_stub, cpp_root_hash_before, cpp_log, + cartesi::grpc_virtual_machine::verify_uarch_step_state_transition(*cpp_stub, cpp_root_hash_before, cpp_log, + cpp_root_hash_after, cpp_runtime, one_based); + return cm_result_success(err_msg); +} catch (...) { + return cm_result_failure(err_msg); +} + +int cm_grpc_verify_uarch_reset_log(const cm_grpc_machine_stub *stub, const cm_access_log *log, + const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg) try { + const auto *cpp_stub = convert_from_c(stub); + const cartesi::access_log cpp_log = convert_from_c(log); + const cartesi::machine_runtime_config cpp_runtime = convert_from_c(runtime_config); + cartesi::grpc_virtual_machine::verify_uarch_reset_log(*cpp_stub, cpp_log, cpp_runtime, one_based); + return cm_result_success(err_msg); +} catch (...) { + return cm_result_failure(err_msg); +} + +int cm_grpc_verify_uarch_reset_state_transition(const cm_grpc_machine_stub *stub, const cm_hash *root_hash_before, + const cm_access_log *log, const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, + bool one_based, char **err_msg) try { + const auto *cpp_stub = convert_from_c(stub); + const cartesi::machine::hash_type cpp_root_hash_before = convert_from_c(root_hash_before); + const cartesi::machine::hash_type cpp_root_hash_after = convert_from_c(root_hash_after); + const cartesi::access_log cpp_log = convert_from_c(log); + const cartesi::machine_runtime_config cpp_runtime = convert_from_c(runtime_config); + cartesi::grpc_virtual_machine::verify_uarch_reset_state_transition(*cpp_stub, cpp_root_hash_before, cpp_log, cpp_root_hash_after, cpp_runtime, one_based); return cm_result_success(err_msg); } catch (...) { diff --git a/src/grpc-machine-c-api.h b/src/grpc-machine-c-api.h index 21191dae7..186afad5f 100644 --- a/src/grpc-machine-c-api.h +++ b/src/grpc-machine-c-api.h @@ -100,7 +100,34 @@ CM_API int cm_grpc_get_default_config(const cm_grpc_machine_stub *stub, const cm /// or NULL in case of successfull function execution. In case of failure error_msg /// must be deleted by the function caller using cm_7_error_message /// \returns 0 for success, non zero code for error -CM_API int cm_grpc_verify_access_log(const cm_grpc_machine_stub *stub, const cm_access_log *log, +CM_API int cm_grpc_verify_uarch_step_log(const cm_grpc_machine_stub *stub, const cm_access_log *log, + const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg); + +/// \brief Checks the internal consistency of an access log produced by cm_grpc_log_uarch_reset() +/// \param stub Cartesi grpc machine stub. Must be pointer to valid object +/// \param log State access log to be verified. +/// \param runtime_config Machine runtime configuration. Must be pointer to valid object +/// \param one_based Use 1-based indices when reporting errors. +/// \param err_msg Receives the error message if function execution fails +/// or NULL in case of successfull function execution. In case of failure error_msg +/// must be deleted by the function caller using cm_7_error_message +/// \returns 0 for success, non zero code for error +CM_API int cm_grpc_verify_uarch_reset_log(const cm_grpc_machine_stub *stub, const cm_access_log *log, + const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg); + +/// \brief Checks the validity of a state transition caused by resetting the uarch state +/// \param stub Cartesi grpc machine stub. Must be pointer to valid object +/// \param root_hash_before State hash before step +/// \param log Step state access log produced by log_uarch_reset +/// \param root_hash_after State hash after step +/// \param runtime_config Machine runtime configuration. Must be pointer to valid object +/// \param one_based Use 1-based indices when reporting errors +/// \param err_msg Receives the error message if function execution fails +/// or NULL in case of successfull function execution. In case of failure error_msg +/// must be deleted by the function caller using cm_delete_cstring +/// \returns 0 for successfull verification, non zero code for error +CM_API int cm_grpc_verify_uarch_reset_state_transition(const cm_grpc_machine_stub *stub, + const cm_hash *root_hash_before, const cm_access_log *log, const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg); /// \brief Checks the validity of a state transition @@ -114,7 +141,7 @@ CM_API int cm_grpc_verify_access_log(const cm_grpc_machine_stub *stub, const cm_ /// or NULL in case of successfull function execution. In case of failure error_msg /// must be deleted by the function caller using cm_delete_cstring /// \returns 0 for successfull verification, non zero code for error -CM_API int cm_grpc_verify_state_transition(const cm_grpc_machine_stub *stub, const cm_hash *root_hash_before, +CM_API int cm_grpc_verify_uarch_step_state_transition(const cm_grpc_machine_stub *stub, const cm_hash *root_hash_before, const cm_access_log *log, const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg); diff --git a/src/grpc-virtual-machine.cpp b/src/grpc-virtual-machine.cpp index 3832e25ac..d4a97727e 100644 --- a/src/grpc-virtual-machine.cpp +++ b/src/grpc-virtual-machine.cpp @@ -18,6 +18,7 @@ #include #include +#include "grpc-config.h" #include "grpc-virtual-machine.h" #include "protobuf-util.h" @@ -109,10 +110,18 @@ static std::string replace_port(const std::string &address, int port) { } } +/// \brief Returns the arguments used to customize the grpc channel +static grpc::ChannelArguments get_channel_arguments(void) { + grpc::ChannelArguments args; + args.SetMaxReceiveMessageSize(GRPC_MAX_RECEIVE_MESSAGE_SIZE); + return args; +} + grpc_machine_stub::grpc_machine_stub(std::string remote_address, std::string checkin_address) : m_remote_address(std::move(remote_address)), m_checkin_address(std::move(checkin_address)), - m_stub(Machine::NewStub(grpc::CreateChannel(m_remote_address, grpc::InsecureChannelCredentials()))) { + m_stub(Machine::NewStub( + grpc::CreateCustomChannel(m_remote_address, grpc::InsecureChannelCredentials(), get_channel_arguments()))) { if (!m_stub) { throw std::runtime_error("unable to create stub"); } @@ -277,20 +286,46 @@ void grpc_virtual_machine::shutdown(const grpc_machine_stub_ptr &stub) { check_status(stub->get_stub()->Shutdown(&context, request, &response)); } -void grpc_virtual_machine::verify_access_log(const grpc_machine_stub_ptr &stub, const access_log &log, +void grpc_virtual_machine::verify_uarch_step_log(const grpc_machine_stub_ptr &stub, const access_log &log, + const machine_runtime_config &r, bool one_based) { + VerifyUarchStepLogRequest request; + Void response; + ClientContext context; + set_proto_access_log(log, request.mutable_log()); + set_proto_machine_runtime_config(r, request.mutable_runtime()); + request.set_one_based(one_based); + check_status(stub->get_stub()->VerifyUarchStepLog(&context, request, &response)); +} + +void grpc_virtual_machine::verify_uarch_reset_log(const grpc_machine_stub_ptr &stub, const access_log &log, + const machine_runtime_config &r, bool one_based) { + VerifyUarchResetLogRequest request; + Void response; + ClientContext context; + set_proto_access_log(log, request.mutable_log()); + set_proto_machine_runtime_config(r, request.mutable_runtime()); + request.set_one_based(one_based); + check_status(stub->get_stub()->VerifyUarchResetLog(&context, request, &response)); +} + +void grpc_virtual_machine::verify_uarch_reset_state_transition(const grpc_machine_stub_ptr &stub, + const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after, const machine_runtime_config &r, bool one_based) { - VerifyAccessLogRequest request; + VerifyUarchResetStateTransitionRequest request; Void response; ClientContext context; + set_proto_hash(root_hash_before, request.mutable_root_hash_before()); set_proto_access_log(log, request.mutable_log()); + set_proto_hash(root_hash_after, request.mutable_root_hash_after()); set_proto_machine_runtime_config(r, request.mutable_runtime()); request.set_one_based(one_based); - check_status(stub->get_stub()->VerifyAccessLog(&context, request, &response)); + check_status(stub->get_stub()->VerifyUarchResetStateTransition(&context, request, &response)); } -void grpc_virtual_machine::verify_state_transition(const grpc_machine_stub_ptr &stub, const hash_type &root_hash_before, - const access_log &log, const hash_type &root_hash_after, const machine_runtime_config &r, bool one_based) { - VerifyStateTransitionRequest request; +void grpc_virtual_machine::verify_uarch_step_state_transition(const grpc_machine_stub_ptr &stub, + const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after, + const machine_runtime_config &r, bool one_based) { + VerifyUarchStepStateTransitionRequest request; Void response; ClientContext context; set_proto_hash(root_hash_before, request.mutable_root_hash_before()); @@ -298,7 +333,7 @@ void grpc_virtual_machine::verify_state_transition(const grpc_machine_stub_ptr & set_proto_hash(root_hash_after, request.mutable_root_hash_after()); set_proto_machine_runtime_config(r, request.mutable_runtime()); request.set_one_based(one_based); - check_status(stub->get_stub()->VerifyStateTransition(&context, request, &response)); + check_status(stub->get_stub()->VerifyUarchStepStateTransition(&context, request, &response)); } interpreter_break_reason grpc_virtual_machine::do_run(uint64_t mcycle_end) { @@ -810,14 +845,15 @@ void grpc_virtual_machine::do_replace_memory_range(const memory_range_config &ne check_status(m_stub->get_stub()->ReplaceMemoryRange(&context, request, &response)); } -access_log grpc_virtual_machine::do_step_uarch(const access_log::type &log_type, bool one_based) { - StepUarchRequest request; +access_log grpc_virtual_machine::do_log_uarch_step(const access_log::type &log_type, bool one_based) { + LogUarchStepRequest request; request.mutable_log_type()->set_proofs(log_type.has_proofs()); request.mutable_log_type()->set_annotations(log_type.has_annotations()); + request.mutable_log_type()->set_large_data(log_type.has_large_data()); request.set_one_based(one_based); - StepUarchResponse response; + LogUarchStepResponse response; ClientContext context; - check_status(m_stub->get_stub()->StepUarch(&context, request, &response)); + check_status(m_stub->get_stub()->LogUarchStep(&context, request, &response)); return get_proto_access_log(response.log()); } @@ -925,19 +961,27 @@ void grpc_virtual_machine::do_set_uarch_halt_flag() { write_csr(csr::uarch_halt_flag, true); } -void grpc_virtual_machine::do_reset_uarch_state() { +void grpc_virtual_machine::do_reset_uarch() { const Void request; Void response; ClientContext context; - check_status(m_stub->get_stub()->ResetUarchState(&context, request, &response)); + check_status(m_stub->get_stub()->ResetUarch(&context, request, &response)); } -bool grpc_virtual_machine::do_read_uarch_halt_flag(void) const { - return read_csr(csr::uarch_halt_flag); +access_log grpc_virtual_machine::do_log_uarch_reset(const access_log::type &log_type, bool one_based) { + LogUarchResetRequest request; + request.mutable_log_type()->set_proofs(log_type.has_proofs()); + request.mutable_log_type()->set_annotations(log_type.has_annotations()); + request.mutable_log_type()->set_large_data(log_type.has_large_data()); + request.set_one_based(one_based); + LogUarchResetResponse response; + ClientContext context; + check_status(m_stub->get_stub()->LogUarchReset(&context, request, &response)); + return get_proto_access_log(response.log()); } -uint64_t grpc_virtual_machine::do_read_uarch_ram_length(void) const { - return read_csr(csr::uarch_ram_length); +bool grpc_virtual_machine::do_read_uarch_halt_flag(void) const { + return read_csr(csr::uarch_halt_flag); } uarch_interpreter_break_reason grpc_virtual_machine::do_run_uarch(uint64_t uarch_cycle_end) { diff --git a/src/grpc-virtual-machine.h b/src/grpc-virtual-machine.h index d8a53359b..598af215e 100644 --- a/src/grpc-virtual-machine.h +++ b/src/grpc-virtual-machine.h @@ -95,13 +95,20 @@ class grpc_virtual_machine : public i_virtual_machine { static machine_config get_default_config(const grpc_machine_stub_ptr &stub); - static void verify_access_log(const grpc_machine_stub_ptr &stub, const access_log &log, + static void verify_uarch_step_log(const grpc_machine_stub_ptr &stub, const access_log &log, const machine_runtime_config &r = {}, bool one_based = false); - static void verify_state_transition(const grpc_machine_stub_ptr &stub, const hash_type &root_hash_before, + static void verify_uarch_step_state_transition(const grpc_machine_stub_ptr &stub, const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after, const machine_runtime_config &r = {}, bool one_based = false); + static void verify_uarch_reset_log(const grpc_machine_stub_ptr &stub, const access_log &log, + const machine_runtime_config &r = {}, bool one_based = false); + + static void verify_uarch_reset_state_transition(const grpc_machine_stub_ptr &stub, + const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after, + const machine_runtime_config &r = {}, bool one_based = false); + static uint64_t get_x_address(const grpc_machine_stub_ptr &stub, int i); static uint64_t get_f_address(const grpc_machine_stub_ptr &stub, int i); static uint64_t get_uarch_x_address(const grpc_machine_stub_ptr &stub, int i); @@ -206,7 +213,7 @@ class grpc_virtual_machine : public i_virtual_machine { void do_get_root_hash(hash_type &hash) const override; machine_merkle_tree::proof_type do_get_proof(uint64_t address, int log2_size) const override; void do_replace_memory_range(const memory_range_config &new_range) override; - access_log do_step_uarch(const access_log::type &log_type, bool /*one_based = false*/) override; + access_log do_log_uarch_step(const access_log::type &log_type, bool /*one_based = false*/) override; void do_destroy() override; void do_snapshot() override; void do_rollback() override; @@ -220,9 +227,9 @@ class grpc_virtual_machine : public i_virtual_machine { void do_write_uarch_pc(uint64_t val) override; uint64_t do_read_uarch_cycle(void) const override; void do_write_uarch_cycle(uint64_t val) override; - uint64_t do_read_uarch_ram_length(void) const override; void do_set_uarch_halt_flag() override; - void do_reset_uarch_state() override; + void do_reset_uarch() override; + access_log do_log_uarch_reset(const access_log::type &log_type, bool /*one_based = false*/) override; bool do_read_uarch_halt_flag(void) const override; uarch_interpreter_break_reason do_run_uarch(uint64_t uarch_cycle_end) override; diff --git a/src/i-state-access.h b/src/i-state-access.h index e6f5a9fa3..99571cb3e 100644 --- a/src/i-state-access.h +++ b/src/i-state-access.h @@ -664,10 +664,6 @@ class i_state_access { // CRTP return derived().do_write_device(pma, mcycle, offset, pval, log2_size); } - auto read_uarch_ram_length() { - return derived().do_read_uarch_ram_length(); - } - /// \brief Try to translate a virtual address to a host pointer through the TLB. /// \tparam ETYPE TLB entry type. /// \tparam T Type of word that would be read with the pointer. diff --git a/src/i-uarch-reset-state-access.h b/src/i-uarch-reset-state-access.h new file mode 100644 index 000000000..7b53a95e3 --- /dev/null +++ b/src/i-uarch-reset-state-access.h @@ -0,0 +1,61 @@ +// 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 I_UARCH_RESET_STATE_ACCESS_H +#define I_UARCH_RESET_STATE_ACCESS_H + +#include "bracket-note.h" +#include "pma.h" +#include + +namespace cartesi { + +// Interface for microarchitecture reset state access +template +class i_uarch_reset_state_access { // CRTP + + DERIVED &derived(void) { + return *static_cast(this); + } + + const DERIVED &derived(void) const { + return *static_cast(this); + } + +public: + /// \brief Adds an annotation bracket to the log + /// \param type Type of bracket + /// \param text String with the text for the annotation + void push_bracket(bracket_type type, const char *text) { + return derived().do_push_bracket(type, text); + } + + /// \brief Adds annotations to the state, bracketing a scope + /// \param text String with the text for the annotation + /// \returns An object that, when constructed and destroyed issues an annonation. + auto make_scoped_note(const char *text) { + return derived().do_make_scoped_note(text); + } + + /// \brief Resets uarch to pristine state + void reset_state(void) { + return derived().do_reset_state(); + } +}; + +} // namespace cartesi + +#endif diff --git a/src/i-uarch-state-access.h b/src/i-uarch-step-state-access.h similarity index 98% rename from src/i-uarch-state-access.h rename to src/i-uarch-step-state-access.h index a67191fa7..a2c9452c7 100644 --- a/src/i-uarch-state-access.h +++ b/src/i-uarch-step-state-access.h @@ -15,6 +15,7 @@ // #ifndef I_UARCH_STATE_ACCESS_H + #define I_UARCH_STATE_ACCESS_H #include "bracket-note.h" @@ -25,7 +26,7 @@ namespace cartesi { // Interface for microarchitecture state access template -class i_uarch_state_access { // CRTP +class i_uarch_step_state_access { // CRTP DERIVED &derived(void) { return *static_cast(this); diff --git a/src/i-virtual-machine.h b/src/i-virtual-machine.h index 04484088e..b9fc4ffa3 100644 --- a/src/i-virtual-machine.h +++ b/src/i-virtual-machine.h @@ -62,8 +62,8 @@ class i_virtual_machine { } /// \brief Runs the machine for one micro cycle logging all accesses to the state. - access_log step_uarch(const access_log::type &log_type, bool one_based = false) { - return do_step_uarch(log_type, one_based); + access_log log_uarch_step(const access_log::type &log_type, bool one_based = false) { + return do_log_uarch_step(log_type, one_based); } /// \brief Obtains the proof for a node in the Merkle tree. @@ -626,15 +626,17 @@ class i_virtual_machine { return do_set_uarch_halt_flag(); } - /// \brief Resets the microarchitecture halt flag - void reset_uarch_state() { - return do_reset_uarch_state(); + /// \brief Resets the microarchitecture state to pristine value + void reset_uarch() { + return do_reset_uarch(); } - /// \brief Reads the value of the microarchitecture ROM length - /// \returns The value of microarchitecture ROM length - uint64_t read_uarch_ram_length(void) const { - return do_read_uarch_ram_length(); + /// \brief Resets the microarchitecture state to pristine value and returns an access log + /// \param log_type Type of access log to generate. + /// \param one_based Use 1-based indices when reporting errors. + /// \returns The state access log. + access_log log_uarch_reset(const access_log::type &log_type, bool one_based = false) { + return do_log_uarch_reset(log_type, one_based); } /// \brief Runs the microarchitecture until the machine advances to the next mcycle or the current micro cycle @@ -646,7 +648,7 @@ class i_virtual_machine { private: virtual interpreter_break_reason do_run(uint64_t mcycle_end) = 0; virtual void do_store(const std::string &dir) = 0; - virtual access_log do_step_uarch(const access_log::type &log_type, bool one_based = false) = 0; + virtual access_log do_log_uarch_step(const access_log::type &log_type, bool one_based = false) = 0; virtual machine_merkle_tree::proof_type do_get_proof(uint64_t address, int log2_size) const = 0; virtual void do_get_root_hash(hash_type &hash) const = 0; virtual bool do_verify_merkle_tree(void) const = 0; @@ -757,8 +759,8 @@ class i_virtual_machine { virtual void do_write_uarch_cycle(uint64_t val) = 0; virtual bool do_read_uarch_halt_flag(void) const = 0; virtual void do_set_uarch_halt_flag() = 0; - virtual void do_reset_uarch_state() = 0; - virtual uint64_t do_read_uarch_ram_length(void) const = 0; + virtual void do_reset_uarch() = 0; + virtual access_log do_log_uarch_reset(const access_log::type &log_type, bool one_based = false) = 0; virtual uarch_interpreter_break_reason do_run_uarch(uint64_t uarch_cycle_end) = 0; }; diff --git a/src/interpret.cpp b/src/interpret.cpp index 3077de830..257770deb 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -5610,7 +5610,7 @@ interpreter_break_reason interpret(STATE_ACCESS &a, uint64_t mcycle_end) { static_assert(is_an_i_state_access::value, "not an i_state_access"); // This must be the first read because we assume the first log access is a - // mcycle read in machine::verify_state_transition + // mcycle read in machine::verify_uarch_step_state_transition const uint64_t mcycle = a.read_mcycle(); // If the cpu is halted, we are done diff --git a/src/json-util.cpp b/src/json-util.cpp index 5ef64354e..ae78a533c 100644 --- a/src/json-util.cpp +++ b/src/json-util.cpp @@ -88,9 +88,9 @@ static auto csr_from_name(const std::string &name) { {"htif_ihalt", csr::htif_ihalt}, {"htif_iconsole", csr::htif_iconsole}, {"htif_iyield", csr::htif_iyield}, + {"uarch_halt_flag", csr::uarch_halt_flag}, {"uarch_pc", csr::uarch_pc}, {"uarch_cycle", csr::uarch_cycle}, - {"uarch_ram_length", csr::uarch_ram_length}, }; auto got = g_csr_name.find(name); if (got == g_csr_name.end()) { @@ -178,8 +178,8 @@ static auto csr_to_name(machine::csr reg) { return "uarch_pc"; case csr::uarch_cycle: return "uarch_cycle"; - case csr::uarch_ram_length: - return "uarch_ram_length"; + case csr::uarch_halt_flag: + return "uarch_halt_flag"; default: throw std::domain_error{"invalid csr"}; break; @@ -460,6 +460,25 @@ void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_merkle_tree std::copy(bin.begin(), bin.end(), value.data()); } +template +void ju_get_opt_field(const nlohmann::json &j, const K &key, + not_default_constructible &optional, const std::string &path) { + optional = {}; + if (!contains(j, key)) { + return; + } + const auto &jk = j[key]; + if (!jk.is_string()) { + throw std::invalid_argument("field \""s + path + to_string(key) + "\" not a string"); + } + std::string bin = decode_base64(jk.template get()); + optional.emplace(); + if (bin.size() != optional.value().size()) { + throw std::invalid_argument("field \""s + path + to_string(key) + "\" not a base64-encoded 256-bit hash"); + } + std::copy(bin.begin(), bin.end(), optional.value().data()); +} + template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, machine_merkle_tree::proof_type::hash_type &value, const std::string &path); @@ -559,6 +578,22 @@ void ju_get_opt_field(const nlohmann::json &j, const K &key, access_data &data, std::copy(bin.begin(), bin.end(), std::back_inserter(data)); } +template +void ju_get_opt_field(const nlohmann::json &j, const K &key, not_default_constructible &optional, + const std::string &path) { + optional = {}; + if (!contains(j, key)) { + return; + } + const auto &jk = j[key]; + if (!jk.is_string()) { + throw std::invalid_argument("field \""s + path + to_string(key) + "\" not a string"); + } + const auto &bin = decode_base64(jk.template get()); + optional.emplace(); + std::copy(bin.begin(), bin.end(), std::back_inserter(optional.value())); +} + template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, access_data &value, const std::string &path); @@ -587,19 +622,34 @@ void ju_get_opt_field(const nlohmann::json &j, const K &key, access &access, con uint64_t address = 0; ju_get_field(jk, "address"s, address, new_path); access.set_address(address); - access_data read_data; - ju_get_field(jk, "read"s, read_data, new_path); - if (read_data.size() != (UINT64_C(1) << log2_size)) { - throw std::invalid_argument("field \""s + new_path + "read\" has wrong length"); + machine_merkle_tree::proof_type::hash_type read_hash; + ju_get_field(jk, "read_hash", read_hash, new_path); + access.set_read_hash(read_hash); + + not_default_constructible written_hash; + ju_get_opt_field(jk, "written_hash", written_hash, new_path); + if (written_hash.has_value()) { + access.set_written_hash(written_hash.value()); } - access.set_read(std::move(read_data)); - if (type == access_type::write) { - access_data write_data; - ju_get_field(jk, "written"s, write_data, new_path); - if (write_data.size() != (UINT64_C(1) << log2_size)) { + + not_default_constructible read; + ju_get_opt_field(jk, "read"s, read, new_path); + if (read.has_value()) { + if (read.value().size() != (UINT64_C(1) << log2_size)) { throw std::invalid_argument("field \""s + new_path + "written\" has wrong length"); } - access.set_written(std::move(write_data)); + access.set_read(std::move(read.value())); + } + + if (type == access_type::write) { + not_default_constructible written; + ju_get_opt_field(jk, "written"s, written, new_path); + if (written.has_value()) { + if (written.value().size() != (UINT64_C(1) << log2_size)) { + throw std::invalid_argument("field \""s + new_path + "written\" has wrong length"); + } + access.set_written(std::move(written.value())); + } } not_default_constructible proof; ju_get_opt_field(jk, "proof"s, proof, new_path); @@ -675,7 +725,9 @@ void ju_get_opt_field(const nlohmann::json &j, const K &key, not_default_constru ju_get_field(jk, "has_proofs"s, has_proofs, new_path); bool has_annotations = false; ju_get_field(jk, "has_annotations"s, has_annotations, new_path); - optional.emplace(has_proofs, has_annotations); + bool has_large_data = false; + ju_get_field(jk, "has_large_data"s, has_large_data, new_path); + optional.emplace(has_proofs, has_annotations, has_large_data); } template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, @@ -961,6 +1013,7 @@ void ju_get_opt_field(const nlohmann::json &j, const K &key, uarch_processor_con ju_get_opt_array_like_field(jconfig, "x"s, value.x, new_path); ju_get_opt_field(jconfig, "pc"s, value.pc, new_path); ju_get_opt_field(jconfig, "cycle"s, value.cycle, new_path); + ju_get_opt_field(jconfig, "halt_flag"s, value.halt_flag, new_path); } template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, uarch_processor_config &value, @@ -976,7 +1029,6 @@ void ju_get_opt_field(const nlohmann::json &j, const K &key, uarch_ram_config &v } const auto &jconfig = j[key]; const auto new_path = path + to_string(key) + "/"; - ju_get_opt_field(jconfig, "length"s, value.length, new_path); ju_get_opt_field(jconfig, "image_filename"s, value.image_filename, new_path); } @@ -1056,13 +1108,27 @@ void to_json(nlohmann::json &j, const access &a) { {"type", access_type_name(a.get_type())}, {"address", a.get_address()}, {"log2_size", a.get_log2_size()}, - {"read", encode_base64(a.get_read())}, }; + + j["read_hash"] = encode_base64(a.get_read_hash()); + if (a.get_read().has_value()) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + j["read"] = encode_base64(a.get_read().value()); + } + if (a.get_type() == access_type::write) { - j["written"] = encode_base64(a.get_written()); + if (a.get_written_hash().has_value()) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + j["written_hash"] = encode_base64(a.get_written_hash().value()); + } + if (a.get_written().has_value()) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + j["written"] = encode_base64(a.get_written().value()); + } } if (a.get_proof().has_value()) { - j["proof"] = a.get_proof().value(); // NOLINT(bugprone-unchecked-optional-access) + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + j["proof"] = a.get_proof().value(); } } @@ -1082,10 +1148,8 @@ void to_json(nlohmann::json &j, const std::vector &as) { } void to_json(nlohmann::json &j, const access_log::type &log_type) { - j = nlohmann::json{ - {"has_proofs", log_type.has_proofs()}, - {"has_annotations", log_type.has_annotations()}, - }; + j = nlohmann::json{{"has_proofs", log_type.has_proofs()}, {"has_annotations", log_type.has_annotations()}, + {"has_large_data", log_type.has_large_data()}}; } void to_json(nlohmann::json &j, const access_log &log) { @@ -1170,12 +1234,12 @@ void to_json(nlohmann::json &j, const uarch_processor_config &config) { {"x", config.x}, {"pc", config.pc}, {"cycle", config.cycle}, + {"halt_flag", config.halt_flag}, }; } void to_json(nlohmann::json &j, const uarch_ram_config &config) { j = nlohmann::json{ - {"length", config.length}, {"image_filename", config.image_filename}, }; } diff --git a/src/jsonrpc-discover.json b/src/jsonrpc-discover.json index 867c5360b..dd01993df 100644 --- a/src/jsonrpc-discover.json +++ b/src/jsonrpc-discover.json @@ -193,7 +193,7 @@ }, { - "name": "machine.step_uarch", + "name": "machine.log_uarch_step", "summary": "Runs the small emulator for one cycle and return a log of state accesses", "params": [ { "name":"log_type", @@ -221,7 +221,7 @@ }, { - "name": "machine.verify_access_log", + "name": "machine.verify_uarch_step_log", "summary": "Verifies an access log", "params": [ { "name":"log", @@ -256,7 +256,7 @@ }, { - "name": "machine.verify_state_transition", + "name": "machine.verify_uarch_step_state_transition", "summary": "Verifies a state transition", "params": [ { "name":"root_hash_before", @@ -304,6 +304,90 @@ } }, + { + "name": "machine.verify_uarch_reset_log", + "summary": "Verifies an access log produced by a log_uarch_reset", + "params": [ { + "name":"log", + "description": "Access log to verify", + "required": true, + "schema": { + "$ref": "#/components/schemas/AccessLog" + } + }, { + "name":"runtime", + "description": "Machine runtime configuration", + "required": false, + "schema": { + "$ref": "#/components/schemas/MachineRuntimeConfig" + } + }, { + "name":"one_based", + "description": "Whether messages use 1-based or 0-based indeces", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "result": { + "name": "status", + "description": "True when operation succeeded", + "schema": { + "type": "boolean" + } + } + }, + + { + "name": "machine.verify_uarch_reset_state_transition", + "summary": "Verifies a state transition caused by log_uarch_reset", + "params": [ { + "name":"root_hash_before", + "description": "State hash before transition described by access log", + "required": true, + "schema": { + "$ref": "#/components/schemas/AccessLog" + } + }, { + "name":"log", + "description": "Access log describing transition", + "required": true, + "schema": { + "$ref": "#/components/schemas/AccessLog" + } + }, { + "name":"root_hash_after", + "description": "State hash after transition described by access log", + "required": true, + "schema": { + "$ref": "#/components/schemas/AccessLog" + } + }, { + "name":"runtime", + "description": "Machine runtime configuration", + "required": false, + "schema": { + "$ref": "#/components/schemas/MachineRuntimeConfig" + } + }, { + "name":"one_based", + "description": "Whether messages use 1-based or 0-based indeces", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "result": { + "name": "status", + "description": "True when operation succeeded", + "schema": { + "type": "boolean" + } + } + }, + { "name": "machine.get_proof", "summary": "Obtains a Merkle proof for a span of memory in the machine state", @@ -951,8 +1035,8 @@ }, { - "name": "machine.reset_uarch_state", - "summary": "Restores small emulator to what it was at initialization", + "name": "machine.reset_uarch", + "summary": "Reset uarch to pristine state", "params": [], "result": { "name": "status", @@ -963,6 +1047,34 @@ } }, + { + "name": "machine.log_uarch_reset", + "summary": "Reset uarch to pristine state and return a log of state accesses", + "params": [ { + "name":"log_type", + "description": "The maximum value of the cycle counter", + "required": true, + "schema": { + "$ref": "#/components/schemas/AccessLogType" + } + }, { + "name":"one_based", + "description": "Whether messages use 1-based or 0-based indeces", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "result": { + "name": "access_log", + "description": "Log of state accesses", + "schema": { + "$ref": "#/components/schemas/AccessLog" + } + } + }, + { "name": "machine.get_initial_config", "summary": "Returns initial machine configuration for instance", @@ -1072,8 +1184,7 @@ "htif_iconsole", "htif_iyield", "uarch_pc", - "uarch_cycle", - "uarch_ram_length" + "uarch_cycle" ] }, @@ -1404,11 +1515,15 @@ }, "has_annotations": { "type": "boolean" + }, + "has_large_data": { + "type": "boolean" } }, "required": [ "has_proofs", - "has_annotations" + "has_annotations", + "has_large_data" ] }, diff --git a/src/jsonrpc-machine-c-api.cpp b/src/jsonrpc-machine-c-api.cpp index 8aacdc2f9..a2552699a 100644 --- a/src/jsonrpc-machine-c-api.cpp +++ b/src/jsonrpc-machine-c-api.cpp @@ -128,18 +128,18 @@ int cm_jsonrpc_get_default_config(const cm_jsonrpc_mg_mgr *mgr, const cm_machine return cm_result_failure(err_msg); } -int cm_jsonrpc_verify_access_log(const cm_jsonrpc_mg_mgr *mgr, const cm_access_log *log, +int cm_jsonrpc_verify_uarch_step_log(const cm_jsonrpc_mg_mgr *mgr, const cm_access_log *log, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg) try { const auto *cpp_mgr = convert_from_c(mgr); const cartesi::access_log cpp_log = convert_from_c(log); const cartesi::machine_runtime_config cpp_runtime = convert_from_c(runtime_config); - cartesi::jsonrpc_virtual_machine::verify_access_log(*cpp_mgr, cpp_log, cpp_runtime, one_based); + cartesi::jsonrpc_virtual_machine::verify_uarch_step_log(*cpp_mgr, cpp_log, cpp_runtime, one_based); return cm_result_success(err_msg); } catch (...) { return cm_result_failure(err_msg); } -int cm_jsonrpc_verify_state_transition(const cm_jsonrpc_mg_mgr *mgr, const cm_hash *root_hash_before, +int cm_jsonrpc_verify_uarch_step_state_transition(const cm_jsonrpc_mg_mgr *mgr, const cm_hash *root_hash_before, const cm_access_log *log, const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg) try { const auto *cpp_mgr = convert_from_c(mgr); @@ -147,7 +147,33 @@ int cm_jsonrpc_verify_state_transition(const cm_jsonrpc_mg_mgr *mgr, const cm_ha const cartesi::access_log cpp_log = convert_from_c(log); const cartesi::machine::hash_type cpp_root_hash_after = convert_from_c(root_hash_after); const cartesi::machine_runtime_config cpp_runtime = convert_from_c(runtime_config); - cartesi::jsonrpc_virtual_machine::verify_state_transition(*cpp_mgr, cpp_root_hash_before, cpp_log, + cartesi::jsonrpc_virtual_machine::verify_uarch_step_state_transition(*cpp_mgr, cpp_root_hash_before, cpp_log, + cpp_root_hash_after, cpp_runtime, one_based); + return cm_result_success(err_msg); +} catch (...) { + return cm_result_failure(err_msg); +} + +int cm_jsonrpc_verify_uarch_reset_log(const cm_jsonrpc_mg_mgr *mgr, const cm_access_log *log, + const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg) try { + const auto *cpp_mgr = convert_from_c(mgr); + const cartesi::access_log cpp_log = convert_from_c(log); + const cartesi::machine_runtime_config cpp_runtime = convert_from_c(runtime_config); + cartesi::jsonrpc_virtual_machine::verify_uarch_reset_log(*cpp_mgr, cpp_log, cpp_runtime, one_based); + return cm_result_success(err_msg); +} catch (...) { + return cm_result_failure(err_msg); +} + +int cm_jsonrpc_verify_uarch_reset_state_transition(const cm_jsonrpc_mg_mgr *mgr, const cm_hash *root_hash_before, + const cm_access_log *log, const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, + bool one_based, char **err_msg) try { + const auto *cpp_mgr = convert_from_c(mgr); + const cartesi::machine::hash_type cpp_root_hash_before = convert_from_c(root_hash_before); + const cartesi::access_log cpp_log = convert_from_c(log); + const cartesi::machine::hash_type cpp_root_hash_after = convert_from_c(root_hash_after); + const cartesi::machine_runtime_config cpp_runtime = convert_from_c(runtime_config); + cartesi::jsonrpc_virtual_machine::verify_uarch_reset_state_transition(*cpp_mgr, cpp_root_hash_before, cpp_log, cpp_root_hash_after, cpp_runtime, one_based); return cm_result_success(err_msg); } catch (...) { diff --git a/src/jsonrpc-machine-c-api.h b/src/jsonrpc-machine-c-api.h index fe36afcca..656365af3 100644 --- a/src/jsonrpc-machine-c-api.h +++ b/src/jsonrpc-machine-c-api.h @@ -98,9 +98,36 @@ CM_API int cm_jsonrpc_get_default_config(const cm_jsonrpc_mg_mgr *mgr, const cm_ /// or NULL in case of successfull function execution. In case of failure error_msg /// must be deleted by the function caller using cm_7_error_message /// \returns 0 for success, non zero code for error -CM_API int cm_jsonrpc_verify_access_log(const cm_jsonrpc_mg_mgr *mgr, const cm_access_log *log, +CM_API int cm_jsonrpc_verify_uarch_step_log(const cm_jsonrpc_mg_mgr *mgr, const cm_access_log *log, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg); +/// \brief Checks the internal consistency of an access log produced by cm_jsonrpc_log_uarch_reset +/// \param mgr Cartesi jsonrpc connection manager. Must be pointer to valid object +/// \param log State access log to be verified. +/// \param runtime_config Runtime config to be used +/// \param one_based Use 1-based indices when reporting errors. +/// \param err_msg Receives the error message if function execution fails +/// or NULL in case of successfull function execution. In case of failure error_msg +/// must be deleted by the function caller using cm_7_error_message +/// \returns 0 for success, non zero code for error +CM_API int cm_jsonrpc_verify_uarch_reset_log(const cm_jsonrpc_mg_mgr *mgr, const cm_access_log *log, + const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg); + +/// \brief Checks the validity of a state transition caused by uarch state reset +/// \param mgr Cartesi jsonrpc connection manager. Must be pointer to valid object +/// \param root_hash_before State hash before step +/// \param log Step state access log created by cm_jsonrpc_log_uarch_reset +/// \param root_hash_after State hash after step +/// \param runtime_config Runtime config to be used +/// \param one_based Use 1-based indices when reporting errors +/// \param err_msg Receives the error message if function execution fails +/// or NULL in case of successfull function execution. In case of failure error_msg +/// must be deleted by the function caller using cm_delete_cstring +/// \returns 0 for successfull verification, non zero code for error +CM_API int cm_jsonrpc_verify_uarch_reset_state_transition(const cm_jsonrpc_mg_mgr *mgr, const cm_hash *root_hash_before, + const cm_access_log *log, const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, + bool one_based, char **err_msg); + /// \brief Checks the validity of a state transition /// \param mgr Cartesi jsonrpc connection manager. Must be pointer to valid object /// \param root_hash_before State hash before step @@ -112,7 +139,7 @@ CM_API int cm_jsonrpc_verify_access_log(const cm_jsonrpc_mg_mgr *mgr, const cm_a /// or NULL in case of successfull function execution. In case of failure error_msg /// must be deleted by the function caller using cm_delete_cstring /// \returns 0 for successfull verification, non zero code for error -CM_API int cm_jsonrpc_verify_state_transition(const cm_jsonrpc_mg_mgr *mgr, const cm_hash *root_hash_before, +CM_API int cm_jsonrpc_verify_uarch_step_state_transition(const cm_jsonrpc_mg_mgr *mgr, const cm_hash *root_hash_before, const cm_access_log *log, const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg); diff --git a/src/jsonrpc-remote-machine.cpp b/src/jsonrpc-remote-machine.cpp index ad0805ff9..c5fd418a9 100644 --- a/src/jsonrpc-remote-machine.cpp +++ b/src/jsonrpc-remote-machine.cpp @@ -809,12 +809,12 @@ static json jsonrpc_machine_run_uarch_handler(const json &j, mg_connection *con, return jsonrpc_response_ok(j, uarch_interpreter_break_reason_name(reason)); } -/// \brief JSONRPC handler for the machine.step_uarch method +/// \brief JSONRPC handler for the machine.log_uarch_step method /// \param j JSON request object /// \param con Mongoose connection /// \param h Handler data /// \returns JSON response object -static json jsonrpc_machine_step_uarch_handler(const json &j, mg_connection *con, http_handler_data *h) { +static json jsonrpc_machine_log_uarch_step_handler(const json &j, mg_connection *con, http_handler_data *h) { (void) con; if (!h->machine) { return jsonrpc_response_invalid_request(j, "no machine"); @@ -827,11 +827,12 @@ static json jsonrpc_machine_step_uarch_handler(const json &j, mg_connection *con switch (count_args(args)) { case 1: // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - s = jsonrpc_response_ok(j, h->machine->step_uarch(std::get<0>(args).value())); + s = jsonrpc_response_ok(j, h->machine->log_uarch_step(std::get<0>(args).value())); break; case 2: - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - s = jsonrpc_response_ok(j, h->machine->step_uarch(std::get<0>(args).value(), std::get<1>(args).value())); + s = jsonrpc_response_ok(j, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + h->machine->log_uarch_step(std::get<0>(args).value(), std::get<1>(args).value())); break; default: throw std::runtime_error{"error detecting number of arguments"}; @@ -839,12 +840,12 @@ static json jsonrpc_machine_step_uarch_handler(const json &j, mg_connection *con return s; } -/// \brief JSONRPC handler for the machine.verify_access_log method +/// \brief JSONRPC handler for the machine.verify_uarch_step_log method /// \param j JSON request object /// \param con Mongoose connection /// \param h Handler data /// \returns JSON response object -static json jsonrpc_machine_verify_access_log_handler(const json &j, mg_connection *con, http_handler_data *h) { +static json jsonrpc_machine_verify_uarch_step_log_handler(const json &j, mg_connection *con, http_handler_data *h) { (void) con; (void) h; static const char *param_name[] = {"log", "runtime", "one_based"}; @@ -853,15 +854,15 @@ static json jsonrpc_machine_verify_access_log_handler(const json &j, mg_connecti switch (count_args(args)) { case 1: // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - cartesi::machine::verify_access_log(std::get<0>(args).value()); + cartesi::machine::verify_uarch_step_log(std::get<0>(args).value()); break; case 2: // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - cartesi::machine::verify_access_log(std::get<0>(args).value(), std::get<1>(args).value()); + cartesi::machine::verify_uarch_step_log(std::get<0>(args).value(), std::get<1>(args).value()); break; case 3: // NOLINTBEGIN(bugprone-unchecked-optional-access) - cartesi::machine::verify_access_log(std::get<0>(args).value(), std::get<1>(args).value(), + cartesi::machine::verify_uarch_step_log(std::get<0>(args).value(), std::get<1>(args).value(), std::get<2>(args).value()); // NOLINTEND(bugprone-unchecked-optional-access) break; @@ -871,12 +872,113 @@ static json jsonrpc_machine_verify_access_log_handler(const json &j, mg_connecti return jsonrpc_response_ok(j); } -/// \brief JSONRPC handler for the machine.verify_state_transition method +/// \brief JSONRPC handler for the machine.verify_uarch_reset_log method +/// \param j JSON request object +/// \param con Mongoose connection +/// \param h Handler data +/// \returns JSON response object +static json jsonrpc_machine_verify_uarch_reset_log_handler(const json &j, mg_connection *con, http_handler_data *h) { + (void) con; + (void) h; + static const char *param_name[] = {"log", "runtime", "one_based"}; + auto args = parse_args, + cartesi::optional_param, cartesi::optional_param>(j, param_name); + switch (count_args(args)) { + case 1: + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + cartesi::machine::verify_uarch_reset_log(std::get<0>(args).value()); + break; + case 2: + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + cartesi::machine::verify_uarch_reset_log(std::get<0>(args).value(), std::get<1>(args).value()); + break; + case 3: + // NOLINTBEGIN(bugprone-unchecked-optional-access) + cartesi::machine::verify_uarch_reset_log(std::get<0>(args).value(), std::get<1>(args).value(), + std::get<2>(args).value()); + // NOLINTEND(bugprone-unchecked-optional-access) + break; + default: + throw std::runtime_error{"error detecting number of arguments"}; + } + return jsonrpc_response_ok(j); +} + +/// \brief JSONRPC handler for the machine.log_uarch_step method +/// \param j JSON request object +/// \param con Mongoose connection +/// \param h Handler data +/// \returns JSON response object +static json jsonrpc_machine_log_uarch_reset_handler(const json &j, mg_connection *con, http_handler_data *h) { + (void) con; + if (!h->machine) { + return jsonrpc_response_invalid_request(j, "no machine"); + } + static const char *param_name[] = {"log_type", "one_based"}; + auto args = + parse_args, cartesi::optional_param>(j, + param_name); + json s; + switch (count_args(args)) { + case 1: + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + s = jsonrpc_response_ok(j, h->machine->log_uarch_reset(std::get<0>(args).value())); + break; + case 2: + s = jsonrpc_response_ok(j, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + h->machine->log_uarch_reset(std::get<0>(args).value(), std::get<1>(args).value())); + break; + default: + throw std::runtime_error{"error detecting number of arguments"}; + } + return s; +} + +/// \brief JSONRPC handler for the machine.verify_uarch_step_state_transition method +/// \param j JSON request object +/// \param con Mongoose connection +/// \param h Handler data +/// \returns JSON response object +static json jsonrpc_machine_verify_uarch_step_state_transition_handler(const json &j, mg_connection *con, + http_handler_data *h) { + (void) con; + (void) h; + static const char *param_name[] = {"root_hash_before", "log", "root_hash_after", "runtime", "one_based"}; + auto args = parse_args, cartesi::machine_merkle_tree::hash_type, + cartesi::optional_param, cartesi::optional_param>(j, param_name); + switch (count_args(args)) { + case 3: + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + cartesi::machine::verify_uarch_step_state_transition(std::get<0>(args), std::get<1>(args).value(), + std::get<2>(args)); + break; + case 4: + // NOLINTBEGIN(bugprone-unchecked-optional-access) + cartesi::machine::verify_uarch_step_state_transition(std::get<0>(args), std::get<1>(args).value(), + std::get<2>(args), std::get<3>(args).value()); + // NOLINTEND(bugprone-unchecked-optional-access) + break; + case 5: + // NOLINTBEGIN(bugprone-unchecked-optional-access) + cartesi::machine::verify_uarch_step_state_transition(std::get<0>(args), std::get<1>(args).value(), + std::get<2>(args), std::get<3>(args).value(), std::get<4>(args).value()); + // NOLINTEND(bugprone-unchecked-optional-access) + break; + default: + throw std::runtime_error{"error detecting number of arguments"}; + } + return jsonrpc_response_ok(j); +} + +/// \brief JSONRPC handler for the machine.verify_uarch_reset_state_transition method /// \param j JSON request object /// \param con Mongoose connection /// \param h Handler data /// \returns JSON response object -static json jsonrpc_machine_verify_state_transition_handler(const json &j, mg_connection *con, http_handler_data *h) { +static json jsonrpc_machine_verify_uarch_reset_state_transition_handler(const json &j, mg_connection *con, + http_handler_data *h) { (void) con; (void) h; static const char *param_name[] = {"root_hash_before", "log", "root_hash_after", "runtime", "one_based"}; @@ -886,18 +988,19 @@ static json jsonrpc_machine_verify_state_transition_handler(const json &j, mg_co switch (count_args(args)) { case 3: // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - cartesi::machine::verify_state_transition(std::get<0>(args), std::get<1>(args).value(), std::get<2>(args)); + cartesi::machine::verify_uarch_reset_state_transition(std::get<0>(args), std::get<1>(args).value(), + std::get<2>(args)); break; case 4: // NOLINTBEGIN(bugprone-unchecked-optional-access) - cartesi::machine::verify_state_transition(std::get<0>(args), std::get<1>(args).value(), std::get<2>(args), - std::get<3>(args).value()); + cartesi::machine::verify_uarch_reset_state_transition(std::get<0>(args), std::get<1>(args).value(), + std::get<2>(args), std::get<3>(args).value()); // NOLINTEND(bugprone-unchecked-optional-access) break; case 5: // NOLINTBEGIN(bugprone-unchecked-optional-access) - cartesi::machine::verify_state_transition(std::get<0>(args), std::get<1>(args).value(), std::get<2>(args), - std::get<3>(args).value(), std::get<4>(args).value()); + cartesi::machine::verify_uarch_reset_state_transition(std::get<0>(args), std::get<1>(args).value(), + std::get<2>(args), std::get<3>(args).value(), std::get<4>(args).value()); // NOLINTEND(bugprone-unchecked-optional-access) break; default: @@ -1420,18 +1523,18 @@ static json jsonrpc_machine_set_uarch_halt_flag_handler(const json &j, mg_connec return jsonrpc_response_ok(j); } -/// \brief JSONRPC handler for the machine.reset_uarch_state method +/// \brief JSONRPC handler for the machine.reset_uarch method /// \param j JSON request object /// \param con Mongoose connection /// \param h Handler data /// \returns JSON response object -static json jsonrpc_machine_reset_uarch_state_handler(const json &j, mg_connection *con, http_handler_data *h) { +static json jsonrpc_machine_reset_uarch_handler(const json &j, mg_connection *con, http_handler_data *h) { (void) con; if (!h->machine) { return jsonrpc_response_invalid_request(j, "no machine"); } jsonrpc_check_no_params(j); - h->machine->reset_uarch_state(); + h->machine->reset_uarch(); return jsonrpc_response_ok(j); } @@ -1540,9 +1643,13 @@ static json jsonrpc_dispatch_method(const json &j, mg_connection *con, http_hand {"machine.store", jsonrpc_machine_store_handler}, {"machine.run", jsonrpc_machine_run_handler}, {"machine.run_uarch", jsonrpc_machine_run_uarch_handler}, - {"machine.step_uarch", jsonrpc_machine_step_uarch_handler}, - {"machine.verify_access_log", jsonrpc_machine_verify_access_log_handler}, - {"machine.verify_state_transition", jsonrpc_machine_verify_state_transition_handler}, + {"machine.log_uarch_step", jsonrpc_machine_log_uarch_step_handler}, + {"machine.reset_uarch", jsonrpc_machine_reset_uarch_handler}, + {"machine.log_uarch_reset", jsonrpc_machine_log_uarch_reset_handler}, + {"machine.verify_uarch_reset_log", jsonrpc_machine_verify_uarch_reset_log_handler}, + {"machine.verify_uarch_reset_state_transition", jsonrpc_machine_verify_uarch_reset_state_transition_handler}, + {"machine.verify_uarch_step_log", jsonrpc_machine_verify_uarch_step_log_handler}, + {"machine.verify_uarch_step_state_transition", jsonrpc_machine_verify_uarch_step_state_transition_handler}, {"machine.get_proof", jsonrpc_machine_get_proof_handler}, {"machine.get_root_hash", jsonrpc_machine_get_root_hash_handler}, {"machine.read_word", jsonrpc_machine_read_word_handler}, @@ -1574,7 +1681,6 @@ static json jsonrpc_dispatch_method(const json &j, mg_connection *con, http_hand {"machine.read_iflags_PRV", jsonrpc_machine_read_iflags_PRV_handler}, {"machine.read_uarch_halt_flag", jsonrpc_machine_read_uarch_halt_flag_handler}, {"machine.set_uarch_halt_flag", jsonrpc_machine_set_uarch_halt_flag_handler}, - {"machine.reset_uarch_state", jsonrpc_machine_reset_uarch_state_handler}, {"machine.get_initial_config", jsonrpc_machine_get_initial_config_handler}, {"machine.get_default_config", jsonrpc_machine_get_default_config_handler}, {"machine.verify_merkle_tree", jsonrpc_machine_verify_merkle_tree_handler}, diff --git a/src/jsonrpc-virtual-machine.cpp b/src/jsonrpc-virtual-machine.cpp index 45c237e79..20883d036 100644 --- a/src/jsonrpc-virtual-machine.cpp +++ b/src/jsonrpc-virtual-machine.cpp @@ -20,6 +20,7 @@ #include #include + #include #include "jsonrpc-mg-mgr.h" @@ -286,19 +287,37 @@ void jsonrpc_virtual_machine::shutdown(const jsonrpc_mg_mgr_ptr &mgr) { mgr->shutdown(); } -void jsonrpc_virtual_machine::verify_access_log(const jsonrpc_mg_mgr_ptr &mgr, const access_log &log, +void jsonrpc_virtual_machine::verify_uarch_step_log(const jsonrpc_mg_mgr_ptr &mgr, const access_log &log, + const machine_runtime_config &runtime, bool one_based) { + bool result = false; + jsonrpc_request(mgr->get_mgr(), mgr->get_remote_address(), "machine.verify_uarch_step_log", + std::tie(log, runtime, one_based), result); +} + +void jsonrpc_virtual_machine::verify_uarch_step_state_transition(const jsonrpc_mg_mgr_ptr &mgr, + const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after, + const machine_runtime_config &runtime, bool one_based) { + bool result = false; + auto b64_root_hash_before = encode_base64(root_hash_before); + auto b64_root_hash_after = encode_base64(root_hash_after); + jsonrpc_request(mgr->get_mgr(), mgr->get_remote_address(), "machine.verify_uarch_step_state_transition", + std::tie(b64_root_hash_before, log, b64_root_hash_after, runtime, one_based), result); +} + +void jsonrpc_virtual_machine::verify_uarch_reset_log(const jsonrpc_mg_mgr_ptr &mgr, const access_log &log, const machine_runtime_config &runtime, bool one_based) { bool result = false; - jsonrpc_request(mgr->get_mgr(), mgr->get_remote_address(), "machine.verify_access_log", + jsonrpc_request(mgr->get_mgr(), mgr->get_remote_address(), "machine.verify_uarch_reset_log", std::tie(log, runtime, one_based), result); } -void jsonrpc_virtual_machine::verify_state_transition(const jsonrpc_mg_mgr_ptr &mgr, const hash_type &root_hash_before, - const access_log &log, const hash_type &root_hash_after, const machine_runtime_config &runtime, bool one_based) { +void jsonrpc_virtual_machine::verify_uarch_reset_state_transition(const jsonrpc_mg_mgr_ptr &mgr, + const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after, + const machine_runtime_config &runtime, bool one_based) { bool result = false; auto b64_root_hash_before = encode_base64(root_hash_before); auto b64_root_hash_after = encode_base64(root_hash_after); - jsonrpc_request(mgr->get_mgr(), mgr->get_remote_address(), "machine.verify_state_transition", + jsonrpc_request(mgr->get_mgr(), mgr->get_remote_address(), "machine.verify_uarch_reset_state_transition", std::tie(b64_root_hash_before, log, b64_root_hash_after, runtime, one_based), result); } @@ -690,9 +709,19 @@ void jsonrpc_virtual_machine::do_set_uarch_halt_flag(void) { jsonrpc_request(m_mgr->get_mgr(), m_mgr->get_remote_address(), "machine.set_uarch_halt_flag", std::tie(), result); } -void jsonrpc_virtual_machine::do_reset_uarch_state(void) { +void jsonrpc_virtual_machine::do_reset_uarch(void) { bool result = false; - jsonrpc_request(m_mgr->get_mgr(), m_mgr->get_remote_address(), "machine.reset_uarch_state", std::tie(), result); + jsonrpc_request(m_mgr->get_mgr(), m_mgr->get_remote_address(), "machine.reset_uarch", std::tie(), result); +} + +access_log jsonrpc_virtual_machine::do_log_uarch_reset(const access_log::type &log_type, bool one_based) { + not_default_constructible result; + jsonrpc_request(m_mgr->get_mgr(), m_mgr->get_remote_address(), "machine.log_uarch_reset", + std::tie(log_type, one_based), result); + if (!result.has_value()) { + throw std::runtime_error("jsonrpc server error: missing result"); + } + return std::move(result).value(); } void jsonrpc_virtual_machine::do_write_iflags(uint64_t val) { @@ -783,10 +812,10 @@ void jsonrpc_virtual_machine::do_replace_memory_range(const memory_range_config result); } -access_log jsonrpc_virtual_machine::do_step_uarch(const access_log::type &log_type, bool one_based) { +access_log jsonrpc_virtual_machine::do_log_uarch_step(const access_log::type &log_type, bool one_based) { not_default_constructible result; - jsonrpc_request(m_mgr->get_mgr(), m_mgr->get_remote_address(), "machine.step_uarch", std::tie(log_type, one_based), - result); + jsonrpc_request(m_mgr->get_mgr(), m_mgr->get_remote_address(), "machine.log_uarch_step", + std::tie(log_type, one_based), result); if (!result.has_value()) { throw std::runtime_error("jsonrpc server error: missing result"); } @@ -857,10 +886,6 @@ void jsonrpc_virtual_machine::do_rollback(void) { m_mgr->rollback(); } -uint64_t jsonrpc_virtual_machine::do_read_uarch_ram_length(void) const { - return read_csr(csr::uarch_ram_length); -} - uarch_interpreter_break_reason jsonrpc_virtual_machine::do_run_uarch(uint64_t uarch_cycle_end) { uarch_interpreter_break_reason result = uarch_interpreter_break_reason::reached_target_cycle; jsonrpc_request(m_mgr->get_mgr(), m_mgr->get_remote_address(), "machine.run_uarch", std::tie(uarch_cycle_end), diff --git a/src/jsonrpc-virtual-machine.h b/src/jsonrpc-virtual-machine.h index f96f8b5f0..adc22f96b 100644 --- a/src/jsonrpc-virtual-machine.h +++ b/src/jsonrpc-virtual-machine.h @@ -52,10 +52,17 @@ class jsonrpc_virtual_machine final : public i_virtual_machine { static machine_config get_default_config(const jsonrpc_mg_mgr_ptr &mgr); - static void verify_access_log(const jsonrpc_mg_mgr_ptr &mgr, const access_log &log, + static void verify_uarch_step_log(const jsonrpc_mg_mgr_ptr &mgr, const access_log &log, const machine_runtime_config &r = {}, bool one_based = false); - static void verify_state_transition(const jsonrpc_mg_mgr_ptr &mgr, const hash_type &root_hash_before, + static void verify_uarch_step_state_transition(const jsonrpc_mg_mgr_ptr &mgr, const hash_type &root_hash_before, + const access_log &log, const hash_type &root_hash_after, const machine_runtime_config &r = {}, + bool one_based = false); + + static void verify_uarch_reset_log(const jsonrpc_mg_mgr_ptr &mgr, const access_log &log, + const machine_runtime_config &r = {}, bool one_based = false); + + static void verify_uarch_reset_state_transition(const jsonrpc_mg_mgr_ptr &mgr, const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after, const machine_runtime_config &r = {}, bool one_based = false); @@ -146,7 +153,8 @@ class jsonrpc_virtual_machine final : public i_virtual_machine { void do_reset_iflags_X(void) override; bool do_read_uarch_halt_flag(void) const override; void do_set_uarch_halt_flag(void) override; - void do_reset_uarch_state(void) override; + void do_reset_uarch(void) override; + access_log do_log_uarch_reset(const access_log::type &log_type, bool /*one_based = false*/) override; void do_write_iflags(uint64_t val) override; uint64_t do_read_htif_tohost(void) const override; uint64_t do_read_htif_tohost_dev(void) const override; @@ -167,7 +175,7 @@ class jsonrpc_virtual_machine final : public i_virtual_machine { void do_get_root_hash(hash_type &hash) const override; machine_merkle_tree::proof_type do_get_proof(uint64_t address, int log2_size) const override; void do_replace_memory_range(const memory_range_config &new_range) override; - access_log do_step_uarch(const access_log::type &log_type, bool /*one_based = false*/) override; + access_log do_log_uarch_step(const access_log::type &log_type, bool /*one_based = false*/) override; void do_destroy() override; void do_snapshot() override; void do_rollback() override; @@ -181,7 +189,6 @@ class jsonrpc_virtual_machine final : public i_virtual_machine { void do_write_uarch_pc(uint64_t val) override; uint64_t do_read_uarch_cycle(void) const override; void do_write_uarch_cycle(uint64_t val) override; - uint64_t do_read_uarch_ram_length(void) const override; uarch_interpreter_break_reason do_run_uarch(uint64_t uarch_cycle_end) override; jsonrpc_mg_mgr_ptr m_mgr; diff --git a/src/machine-c-api.cpp b/src/machine-c-api.cpp index cc70975aa..dce5ff72b 100644 --- a/src/machine-c-api.cpp +++ b/src/machine-c-api.cpp @@ -311,14 +311,12 @@ static cm_rollup_config convert_to_c(const std::optional static cartesi::uarch_ram_config convert_from_c(const cm_uarch_ram_config *c_config) { cartesi::uarch_ram_config new_cpp_uarch_ram_config{}; - new_cpp_uarch_ram_config.length = c_config->length; new_cpp_uarch_ram_config.image_filename = null_to_empty(c_config->image_filename); return new_cpp_uarch_ram_config; } static cm_uarch_ram_config convert_to_c(const cartesi::uarch_ram_config &cpp_config) { cm_uarch_ram_config new_c_uarch_ram_config{}; - new_c_uarch_ram_config.length = cpp_config.length; new_c_uarch_ram_config.image_filename = convert_to_c(cpp_config.image_filename); return new_c_uarch_ram_config; } @@ -327,6 +325,7 @@ static cartesi::uarch_processor_config convert_from_c(const cm_uarch_processor_c cartesi::uarch_processor_config new_cpp_config{}; new_cpp_config.pc = c_config->pc; new_cpp_config.cycle = c_config->cycle; + new_cpp_config.halt_flag = c_config->halt_flag; for (size_t i = 0; i < CM_MACHINE_UARCH_X_REG_COUNT; i++) { new_cpp_config.x[i] = c_config->x[i]; } @@ -337,6 +336,7 @@ static cm_uarch_processor_config convert_to_c(const cartesi::uarch_processor_con cm_uarch_processor_config new_c_config{}; new_c_config.pc = cpp_config.pc; new_c_config.cycle = cpp_config.cycle; + new_c_config.halt_flag = cpp_config.halt_flag; for (size_t i = 0; i < CM_MACHINE_UARCH_X_REG_COUNT; i++) { new_c_config.x[i] = cpp_config.x[i]; } @@ -530,7 +530,7 @@ static cartesi::access_type convert_from_c(const CM_ACCESS_TYPE c_type) { } cartesi::access_log::type convert_from_c(const cm_access_log_type *type) { - cartesi::access_log::type cpp_type(type->proofs, type->annotations); + cartesi::access_log::type cpp_type(type->proofs, type->annotations, type->large_data); return cpp_type; } @@ -539,19 +539,35 @@ static cm_access convert_to_c(const cartesi::access &cpp_access) { new_access.type = convert_to_c(cpp_access.get_type()); new_access.address = cpp_access.get_address(); new_access.log2_size = cpp_access.get_log2_size(); - new_access.read_data_size = cpp_access.get_read().size(); - if (new_access.read_data_size > 0) { - new_access.read_data = new uint8_t[new_access.read_data_size]; - memcpy(new_access.read_data, cpp_access.get_read().data(), new_access.read_data_size); + memcpy(&new_access.read_hash, static_cast(cpp_access.get_read_hash().data()), sizeof(cm_hash)); + new_access.read_data = nullptr; + new_access.read_data_size = 0; + if (cpp_access.get_read().has_value()) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + const auto &read_value = cpp_access.get_read().value(); + new_access.read_data_size = read_value.size(); + if (new_access.read_data_size > 0) { + new_access.read_data = new uint8_t[new_access.read_data_size]; + memcpy(new_access.read_data, read_value.data(), new_access.read_data_size); + } + } + if (cpp_access.get_written_hash().has_value()) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + memcpy(&new_access.written_hash, static_cast(cpp_access.get_written_hash().value().data()), + sizeof(cm_hash)); } else { - new_access.read_data = nullptr; + memset(&new_access.written_hash, 0, sizeof(cm_hash)); } - new_access.written_data_size = cpp_access.get_written().size(); - if (new_access.written_data_size > 0) { - new_access.written_data = new uint8_t[new_access.written_data_size]; - memcpy(new_access.written_data, cpp_access.get_written().data(), new_access.written_data_size); - } else { - new_access.written_data = nullptr; + new_access.written_data = nullptr; + new_access.written_data_size = 0; + if (cpp_access.get_written().has_value()) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + const auto &written_value = cpp_access.get_written().value(); + new_access.written_data_size = written_value.size(); + if (new_access.written_data_size > 0) { + new_access.written_data = new uint8_t[new_access.written_data_size]; + memcpy(new_access.written_data, written_value.data(), new_access.written_data_size); + } } if (cpp_access.get_proof().has_value()) { @@ -574,15 +590,17 @@ static cartesi::access convert_from_c(const cm_access *c_access) { cpp_access.set_proof(proof); } + cpp_access.set_read_hash(convert_from_c(&c_access->read_hash)); if (c_access->read_data_size > 0) { cpp_access.set_read(cartesi::access_data{c_access->read_data, c_access->read_data + c_access->read_data_size}); } - + if (c_access->type == CM_ACCESS_WRITE) { + cpp_access.set_written_hash(convert_from_c(&c_access->written_hash)); + } if (c_access->written_data_size > 0) { cpp_access.set_written( cartesi::access_data{c_access->written_data, c_access->written_data + c_access->written_data_size}); } - return cpp_access; } @@ -657,6 +675,7 @@ cm_access_log *convert_to_c(const cartesi::access_log &cpp_access_log) { new_access_log->log_type.annotations = cpp_access_log.get_log_type().has_annotations(); new_access_log->log_type.proofs = cpp_access_log.get_log_type().has_proofs(); + new_access_log->log_type.large_data = cpp_access_log.get_log_type().has_large_data(); return new_access_log; } @@ -818,9 +837,23 @@ CM_API int cm_set_uarch_halt_flag(cm_machine *m, char **err_msg) try { return cm_result_failure(err_msg); } -CM_API int cm_reset_uarch_state(cm_machine *m, char **err_msg) try { +CM_API int cm_reset_uarch(cm_machine *m, char **err_msg) try { auto *cpp_machine = convert_from_c(m); - cpp_machine->reset_uarch_state(); + cpp_machine->reset_uarch(); + return cm_result_success(err_msg); +} catch (...) { + return cm_result_failure(err_msg); +} + +int cm_log_uarch_reset(cm_machine *m, cm_access_log_type log_type, bool one_based, cm_access_log **access_log, + char **err_msg) try { + if (access_log == nullptr) { + throw std::invalid_argument("invalid access log output"); + } + auto *cpp_machine = convert_from_c(m); + cartesi::access_log::type cpp_log_type{log_type.proofs, log_type.annotations, log_type.large_data}; + cartesi::access_log cpp_access_log = cpp_machine->log_uarch_reset(cpp_log_type, one_based); + *access_log = convert_to_c(cpp_access_log); return cm_result_success(err_msg); } catch (...) { return cm_result_failure(err_msg); @@ -838,14 +871,14 @@ int cm_machine_run_uarch(cm_machine *m, uint64_t uarch_cycle_end, CM_UARCH_BREAK return cm_result_failure(err_msg); } -int cm_step_uarch(cm_machine *m, cm_access_log_type log_type, bool one_based, cm_access_log **access_log, +int cm_log_uarch_step(cm_machine *m, cm_access_log_type log_type, bool one_based, cm_access_log **access_log, char **err_msg) try { if (access_log == nullptr) { throw std::invalid_argument("invalid access log output"); } auto *cpp_machine = convert_from_c(m); cartesi::access_log::type cpp_log_type{log_type.proofs, log_type.annotations}; - cartesi::access_log cpp_access_log = cpp_machine->step_uarch(cpp_log_type, one_based); + cartesi::access_log cpp_access_log = cpp_machine->log_uarch_step(cpp_log_type, one_based); *access_log = convert_to_c(cpp_access_log); return cm_result_success(err_msg); } catch (...) { @@ -872,25 +905,49 @@ void cm_delete_access_log(cm_access_log *acc_log) { delete acc_log; } -int cm_verify_access_log(const cm_access_log *log, const cm_machine_runtime_config *runtime_config, bool one_based, +int cm_verify_uarch_step_log(const cm_access_log *log, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg) try { const cartesi::access_log cpp_log = convert_from_c(log); const cartesi::machine_runtime_config cpp_runtime_config = convert_from_c(runtime_config); - cartesi::machine::verify_access_log(cpp_log, cpp_runtime_config, one_based); + cartesi::machine::verify_uarch_step_log(cpp_log, cpp_runtime_config, one_based); + return cm_result_success(err_msg); +} catch (...) { + return cm_result_failure(err_msg); +} + +int cm_verify_uarch_reset_log(const cm_access_log *log, const cm_machine_runtime_config *runtime_config, bool one_based, + char **err_msg) try { + const cartesi::access_log cpp_log = convert_from_c(log); + const cartesi::machine_runtime_config cpp_runtime_config = convert_from_c(runtime_config); + cartesi::machine::verify_uarch_reset_log(cpp_log, cpp_runtime_config, one_based); + return cm_result_success(err_msg); +} catch (...) { + return cm_result_failure(err_msg); +} + +int cm_verify_uarch_step_state_transition(const cm_hash *root_hash_before, const cm_access_log *log, + const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, bool one_based, + char **err_msg) try { + const cartesi::machine::hash_type cpp_root_hash_before = convert_from_c(root_hash_before); + const cartesi::machine::hash_type cpp_root_hash_after = convert_from_c(root_hash_after); + const cartesi::access_log cpp_log = convert_from_c(log); + const cartesi::machine_runtime_config cpp_runtime_config = convert_from_c(runtime_config); + cartesi::machine::verify_uarch_step_state_transition(cpp_root_hash_before, cpp_log, cpp_root_hash_after, + cpp_runtime_config, one_based); return cm_result_success(err_msg); } catch (...) { return cm_result_failure(err_msg); } -int cm_verify_state_transition(const cm_hash *root_hash_before, const cm_access_log *log, +int cm_verify_uarch_reset_state_transition(const cm_hash *root_hash_before, const cm_access_log *log, const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg) try { const cartesi::machine::hash_type cpp_root_hash_before = convert_from_c(root_hash_before); const cartesi::machine::hash_type cpp_root_hash_after = convert_from_c(root_hash_after); const cartesi::access_log cpp_log = convert_from_c(log); const cartesi::machine_runtime_config cpp_runtime_config = convert_from_c(runtime_config); - cartesi::machine::verify_state_transition(cpp_root_hash_before, cpp_log, cpp_root_hash_after, cpp_runtime_config, - one_based); + cartesi::machine::verify_uarch_reset_state_transition(cpp_root_hash_before, cpp_log, cpp_root_hash_after, + cpp_runtime_config, one_based); return cm_result_success(err_msg); } catch (...) { return cm_result_failure(err_msg); @@ -1158,7 +1215,6 @@ IMPL_MACHINE_READ_WRITE(htif_iyield) IMPL_MACHINE_READ_WRITE(clint_mtimecmp) IMPL_MACHINE_READ_WRITE(uarch_cycle) IMPL_MACHINE_READ_WRITE(uarch_pc) -IMPL_MACHINE_READ(uarch_ram_length) // clang-format-on uint64_t cm_packed_iflags(int PRV, int X, int Y, int H) { diff --git a/src/machine-c-api.h b/src/machine-c-api.h index 88e9c3682..533f91bd6 100644 --- a/src/machine-c-api.h +++ b/src/machine-c-api.h @@ -139,7 +139,6 @@ typedef enum { // NOLINT(modernize-use-using) CM_PROC_UARCH_PC, CM_PROC_UARCH_CYCLE, CM_PROC_UARCH_HALT_FLAG, - CM_PROC_UARCH_RAM_LENGTH, CM_PROC_UNKNOWN } CM_PROC_CSR; @@ -242,7 +241,6 @@ typedef struct { // NOLINT(modernize-use-using) /// \brief microarchitecture RAM configuration typedef struct { // NOLINT(modernize-use-using) - uint64_t length; ///< RAM length const char *image_filename; ///< RAM image file name } cm_uarch_ram_config; @@ -296,6 +294,7 @@ typedef enum { // NOLINT(modernize-use-using) typedef struct { // NOLINT(modernize-use-using) bool proofs; ///< Includes proofs bool annotations; ///< Includes annotations + bool large_data; ///< Includes data bigger than 8 bytes } cm_access_log_type; /// \brief Bracket type @@ -316,8 +315,10 @@ typedef struct { // NOLINT(modernize-use-using) CM_ACCESS_TYPE type; ///< Type of access uint64_t address; ///< Address of access int log2_size; ///< Log2 of size of access + cm_hash read_hash; ///< Hash of data before access uint8_t *read_data; ///< Data before access size_t read_data_size; ///< Size of data before access in bytes + cm_hash written_hash; ///< Hash of data after access (if writing) uint8_t *written_data; ///< Data after access (if writing) size_t written_data_size; ///< Size of data after access in bytes cm_merkle_tree_proof *proof; ///< Proof of data before access @@ -460,7 +461,7 @@ CM_API int cm_machine_run(cm_machine *m, uint64_t mcycle_end, CM_BREAK_REASON *b /// must be deleted by the function caller using cm_delete_cstring. /// err_msg can be NULL, meaning the error message won't be received. /// \returns 0 for success, non zero code for error -CM_API int cm_step_uarch(cm_machine *m, cm_access_log_type log_type, bool one_based, cm_access_log **access_log, +CM_API int cm_log_uarch_step(cm_machine *m, cm_access_log_type log_type, bool one_based, cm_access_log **access_log, char **err_msg); /// \brief Deletes the instance of cm_access_log acquired from cm_step @@ -476,7 +477,7 @@ CM_API void cm_delete_access_log(cm_access_log *acc_log); /// must be deleted by the function caller using cm_delete_cstring. /// err_msg can be NULL, meaning the error message won't be received. /// \returns 0 for success, non zero code for error -CM_API int cm_verify_access_log(const cm_access_log *log, const cm_machine_runtime_config *runtime_config, +CM_API int cm_verify_uarch_step_log(const cm_access_log *log, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg); /// \brief Checks the validity of a state transition @@ -490,9 +491,35 @@ CM_API int cm_verify_access_log(const cm_access_log *log, const cm_machine_runti /// must be deleted by the function caller using cm_delete_cstring. /// err_msg can be NULL, meaning the error message won't be received. /// \returns 0 for successful verification, non zero code for error -CM_API int cm_verify_state_transition(const cm_hash *root_hash_before, const cm_access_log *log, +CM_API int cm_verify_uarch_step_state_transition(const cm_hash *root_hash_before, const cm_access_log *log, + const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg); + +/// \brief Checks the validity of a state transition caused by a uarch state reset +/// \param root_hash_before State hash before step +/// \param log Step state access log produced by cm_log_uarch_reset +/// \param root_hash_after State hash after step +/// \param runtime_config Machine runtime configuration to use during verification. Must be pointer to valid object +/// \param one_based Use 1-based indices when reporting errors +/// \param err_msg Receives the error message if function execution fails +/// or NULL in case of successful function execution. In case of failure error_msg +/// must be deleted by the function caller using cm_delete_cstring. +/// err_msg can be NULL, meaning the error message won't be received. +/// \returns 0 for successful verification, non zero code for error +CM_API int cm_verify_uarch_reset_state_transition(const cm_hash *root_hash_before, const cm_access_log *log, const cm_hash *root_hash_after, const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg); +/// \brief Checks the internal consistency of an access log produced by cm_log_uarch_reset +/// \param log State access log to be verified +/// \param r Machine runtime configuration to use during verification. Must be pointer to valid object +/// \param one_based Use 1-based indices when reporting errors +/// \param err_msg Receives the error message if function execution fails +/// or NULL in case of successful function execution. In case of failure error_msg +/// must be deleted by the function caller using cm_delete_cstring. +/// err_msg can be NULL, meaning the error message won't be received. +/// \returns 0 for success, non zero code for error +CM_API int cm_verify_uarch_reset_log(const cm_access_log *log, const cm_machine_runtime_config *runtime_config, + bool one_based, char **err_msg); + /// \brief Obtains the proof for a node in the Merkle tree /// \param m Pointer to valid machine instance /// \param address Address of target node. Must be aligned to a 2log2_size boundary @@ -1652,16 +1679,6 @@ CM_API int cm_write_uarch_pc(cm_machine *m, uint64_t val, char **err_msg); /// \returns 0 for success, non zero code for error CM_API int cm_read_uarch_cycle(const cm_machine *m, uint64_t *val, char **err_msg); -/// \brief Reads the value of the microarchitecture RAM length -/// \param m Pointer to valid machine instance -/// \param val Receives value of the microarchitecture RAM length. -/// \param err_msg Receives the error message if function execution fails -/// or NULL in case of successful function execution. In case of failure error_msg -/// must be deleted by the function caller using cm_delete_cstring. -/// err_msg can be NULL, meaning the error message won't be received. -/// \returns 0 for success, non zero code for error -CM_API int cm_read_uarch_ram_length(const cm_machine *m, uint64_t *val, char **err_msg); - /// \brief Writes the value of the microarchitecture cycle register. /// \param m Pointer to valid machine instance /// \param val New register value. @@ -1695,7 +1712,19 @@ CM_API int cm_set_uarch_halt_flag(cm_machine *m, char **err_msg); /// or NULL in case of successfull function execution. In case of failure error_msg /// must be deleted by the function caller using cm_delete_cstring /// \returns 0 for success, non zero code for error -CM_API int cm_reset_uarch_state(cm_machine *m, char **err_msg); +CM_API int cm_reset_uarch(cm_machine *m, char **err_msg); + +/// \brief Resets the value of the microarchitecture halt flag. +/// \param m Pointer to valid machine instance +/// \param log_type Type of access log to generate. +/// \param one_based Use 1-based indices when reporting errors. +/// \param access_log Receives the state access log. +/// \param err_msg Receives the error message if function execution fails +/// or NULL in case of successfull function execution. In case of failure error_msg +/// must be deleted by the function caller using cm_delete_cstring +/// \returns 0 for success, non zero code for error +CM_API int cm_log_uarch_reset(cm_machine *m, cm_access_log_type log_type, bool one_based, cm_access_log **access_log, + char **err_msg); /// \brief Runs the machine in the microarchitecture until the mcycle advances by one unit or the micro cycles counter /// (uarch_cycle) reaches uarch_cycle_end \param m Pointer to valid machine instance \param mcycle_end End cycle value diff --git a/src/machine-config.cpp b/src/machine-config.cpp index 4ef3d19ad..883061d41 100644 --- a/src/machine-config.cpp +++ b/src/machine-config.cpp @@ -59,10 +59,6 @@ static void adjust_image_filenames(machine_config &c, const std::string &dir) { r.voucher_hashes.image_filename = c.get_image_filename(dir, r.voucher_hashes); r.notice_hashes.image_filename = c.get_image_filename(dir, r.notice_hashes); } - - if (c.uarch.ram.length > 0) { - c.uarch.ram.image_filename = c.get_image_filename(dir, PMA_UARCH_RAM_START, c.uarch.ram.length); - } } machine_config machine_config::load(const std::string &dir) { diff --git a/src/machine.cpp b/src/machine.cpp index 35678e0cc..e2b188dab 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -37,14 +37,17 @@ #include "shadow-pmas-factory.h" #include "shadow-state-factory.h" #include "shadow-tlb-factory.h" -#include "shadow-uarch-state-factory.h" #include "state-access.h" #include "strict-aliasing.h" #include "translate-virtual-address.h" #include "uarch-interpret.h" -#include "uarch-record-state-access.h" -#include "uarch-replay-state-access.h" -#include "uarch-state-access.h" +#include "uarch-record-reset-state-access.h" +#include "uarch-record-step-state-access.h" +#include "uarch-replay-reset-state-access.h" +#include "uarch-replay-step-state-access.h" +#include "uarch-reset-state-access.h" +#include "uarch-reset-state.h" +#include "uarch-step-state-access.h" #include "uarch-step.h" #include "unique-c-ptr.h" @@ -424,9 +427,6 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : // Initialize PMA extension metadata on ROM rom_init(m_c, rom.get_memory().get_host_memory(), PMA_ROM_LENGTH); - // Register uarch shadow state - register_pma_entry(make_shadow_uarch_state_pma_entry(PMA_SHADOW_UARCH_STATE_START, PMA_SHADOW_UARCH_STATE_LENGTH)); - // Add sentinel to PMA vector register_pma_entry(make_empty_pma_entry("sentinel"s, 0, 0)); @@ -436,7 +436,8 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : m_pmas.push_back(&pma); } - // Second, push the uarch ram, which is visible only to the microarchitecture interpreter + // Second, push uarch pmas that are visible only to the microarchitecture interpreter + m_pmas.push_back(&m_uarch.get_state().shadow_state); m_pmas.push_back(&m_uarch.get_state().ram); // Last, add sentinel @@ -1151,8 +1152,6 @@ uint64_t machine::read_csr(csr r) const { return read_uarch_halt_flag(); case csr::uarch_pc: return read_uarch_pc(); - case csr::uarch_ram_length: - return read_uarch_ram_length(); default: throw std::invalid_argument{"unknown CSR"}; return 0; // never reached @@ -1239,8 +1238,6 @@ void machine::write_csr(csr csr, uint64_t value) { [[fallthrough]]; case csr::mimpid: throw std::invalid_argument{"CSR is read-only"}; - case csr::uarch_ram_length: - throw std::invalid_argument{"CSR is read-only"}; default: throw std::invalid_argument{"unknown CSR"}; } @@ -1326,8 +1323,6 @@ uint64_t machine::get_csr_address(csr csr) { return shadow_uarch_state_get_csr_abs_addr(shadow_uarch_state_csr::cycle); case csr::uarch_halt_flag: return shadow_uarch_state_get_csr_abs_addr(shadow_uarch_state_csr::halt_flag); - case csr::uarch_ram_length: - return shadow_uarch_state_get_csr_abs_addr(shadow_uarch_state_csr::ram_length); default: throw std::invalid_argument{"unknown CSR"}; } @@ -1725,15 +1720,7 @@ void machine::write_memory(uint64_t address, const unsigned char *data, size_t l if (!pma.get_istart_M() || pma.get_istart_E()) { throw std::invalid_argument{"address range not entirely in memory PMA"}; } - constexpr const auto log2_page_size = PMA_constants::PMA_PAGE_SIZE_LOG2; - uint64_t page_in_range = ((address - pma.get_start()) >> log2_page_size) << log2_page_size; - constexpr const auto page_size = PMA_constants::PMA_PAGE_SIZE; - auto npages = (length + page_size - 1) / page_size; - for (decltype(npages) i = 0; i < npages; ++i) { - pma.mark_dirty_page(page_in_range); - page_in_range += page_size; - } - memcpy(pma.get_memory().get_host_memory() + (address - pma.get_start()), data, length); + pma.write_memory(address, data, length); } void machine::read_virtual_memory(uint64_t vaddr_start, unsigned char *data, uint64_t length) { @@ -1852,41 +1839,97 @@ void machine::write_uarch_cycle(uint64_t val) { return m_uarch.write_cycle(val); } -/// \brief Reads the value of the microarchitecture halt flag. -/// \returns The current microarchitecture halt value. bool machine::read_uarch_halt_flag(void) const { return m_uarch.read_halt_flag(); } -/// \brief Sets the value ofthe microarchitecture halt flag. void machine::set_uarch_halt_flag() { m_uarch.set_halt_flag(); } -void machine::reset_uarch_state() { - m_uarch.reset_state(); +void machine::reset_uarch() { + uarch_reset_state_access a(m_uarch.get_state()); + uarch_reset_state(a); } -uint64_t machine::read_uarch_ram_length(void) const { - return m_uarch.read_ram_length(); +access_log machine::log_uarch_reset(const access_log::type &log_type, bool one_based) { + hash_type root_hash_before; + if (log_type.has_proofs()) { + update_merkle_tree(); + get_root_hash(root_hash_before); + } + // Call uarch_reset_state with a uarch_record_reset_state_access object + uarch_record_reset_state_access a(m_uarch.get_state(), *this, log_type); + a.push_bracket(bracket_type::begin, "reset uarch state"); + uarch_reset_state(a); + a.push_bracket(bracket_type::end, "reset uarch state"); + // Verify access log before returning + if (log_type.has_proofs()) { + hash_type root_hash_after; + update_merkle_tree(); + get_root_hash(root_hash_after); + verify_uarch_reset_state_transition(root_hash_before, *a.get_log(), root_hash_after, m_r, one_based); + } else { + verify_uarch_reset_log(*a.get_log(), m_r, one_based); + } + return std::move(*a.get_log()); } -void machine::verify_access_log(const access_log &log, const machine_runtime_config &r, bool one_based) { +void machine::verify_uarch_step_log(const access_log &log, const machine_runtime_config &r, bool one_based) { (void) r; // There must be at least one access in log if (log.get_accesses().empty()) { throw std::invalid_argument{"too few accesses in log"}; } - uarch_replay_state_access a(log, log.get_log_type().has_proofs(), one_based); + uarch_replay_step_state_access a(log, log.get_log_type().has_proofs(), one_based); uarch_step(a); a.finish(); } +void machine::verify_uarch_reset_log(const access_log &log, const machine_runtime_config &r, bool one_based) { + (void) r; + uarch_replay_reset_state_access a(log, log.get_log_type().has_proofs(), one_based); + uarch_reset_state(a); + a.finish(); +} + +void machine::verify_uarch_reset_state_transition(const hash_type &root_hash_before, const access_log &log, + const hash_type &root_hash_after, const machine_runtime_config &r, bool one_based) { + (void) r; + // We need proofs in order to verify the state transition + if (!log.get_log_type().has_proofs()) { + throw std::invalid_argument{"log has no proofs"}; + } + // There must be at least one access in log + if (log.get_accesses().empty()) { + throw std::invalid_argument{"too few accesses in log"}; + } + // It must contain proofs + if (!log.get_accesses().front().get_proof().has_value()) { + throw std::invalid_argument{"access has no proof"}; + } + // Make sure the access log starts from the same root hash as the state + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + if (log.get_accesses().front().get_proof().value().get_root_hash() != root_hash_before) { + throw std::invalid_argument{"mismatch in root hash before replay"}; + } + // Verify all intermediate state transitions + uarch_replay_reset_state_access a(log, log.get_log_type().has_proofs(), one_based); + uarch_reset_state(a); + a.finish(); + // Make sure the access log ends at the same root hash as the state + hash_type obtained_root_hash; + a.get_root_hash(obtained_root_hash); + if (obtained_root_hash != root_hash_after) { + throw std::invalid_argument{"mismatch in root hash after replay"}; + } +} + machine_config machine::get_default_config(void) { return machine_config{}; } -void machine::verify_state_transition(const hash_type &root_hash_before, const access_log &log, +void machine::verify_uarch_step_state_transition(const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after, const machine_runtime_config &r, bool one_based) { (void) r; // We need proofs in order to verify the state transition @@ -1907,7 +1950,7 @@ void machine::verify_state_transition(const hash_type &root_hash_before, const a throw std::invalid_argument{"mismatch in root hash before replay"}; } // Verify all intermediate state transitions - uarch_replay_state_access a(log, true /* verify proofs! */, one_based); + uarch_replay_step_state_access a(log, true /* verify proofs! */, one_based); uarch_step(a); a.finish(); // Make sure the access log ends at the same root hash as the state @@ -1918,7 +1961,7 @@ void machine::verify_state_transition(const hash_type &root_hash_before, const a } } -access_log machine::step_uarch(const access_log::type &log_type, bool one_based) { +access_log machine::log_uarch_step(const access_log::type &log_type, bool one_based) { if (m_uarch.get_state().ram.get_istart_E()) { throw std::runtime_error("microarchitecture RAM is not present"); } @@ -1928,7 +1971,7 @@ access_log machine::step_uarch(const access_log::type &log_type, bool one_based) get_root_hash(root_hash_before); } // Call interpret with a logged state access object - uarch_record_state_access a(m_uarch.get_state(), *this, log_type); + uarch_record_step_state_access a(m_uarch.get_state(), *this, log_type); a.push_bracket(bracket_type::begin, "step"); uarch_step(a); a.push_bracket(bracket_type::end, "step"); @@ -1937,9 +1980,9 @@ access_log machine::step_uarch(const access_log::type &log_type, bool one_based) hash_type root_hash_after; update_merkle_tree(); get_root_hash(root_hash_after); - verify_state_transition(root_hash_before, *a.get_log(), root_hash_after, m_r, one_based); + verify_uarch_step_state_transition(root_hash_before, *a.get_log(), root_hash_after, m_r, one_based); } else { - verify_access_log(*a.get_log(), m_r, one_based); + verify_uarch_step_log(*a.get_log(), m_r, one_based); } return std::move(*a.get_log()); } @@ -1949,7 +1992,7 @@ uarch_interpreter_break_reason machine::run_uarch(uint64_t uarch_cycle_end) { if (m_uarch.get_state().ram.get_istart_E()) { throw std::runtime_error("microarchitecture RAM is not present"); } - uarch_state_access a(m_uarch.get_state(), get_state()); + uarch_step_state_access a(m_uarch.get_state(), get_state()); return uarch_interpret(a, uarch_cycle_end); } diff --git a/src/machine.h b/src/machine.h index 44ae9a591..35d1b1d89 100644 --- a/src/machine.h +++ b/src/machine.h @@ -173,7 +173,6 @@ class machine final { uarch_pc, uarch_cycle, uarch_halt_flag, - uarch_ram_length, last }; @@ -216,20 +215,27 @@ class machine final { /// \param uarch_cycle_end uarch_cycle limit uarch_interpreter_break_reason run_uarch(uint64_t uarch_cycle_end); + /// \brief Advances one micro step and returns a state access log. + /// \param log_type Type of access log to generate. + /// \param one_based Use 1-based indices when reporting errors. + /// \returns The state access log. + access_log log_uarch_step(const access_log::type &log_type, bool one_based = false); + /// \brief Resets the microarchitecture state - void reset_uarch_state(); + void reset_uarch(); - /// \brief Runs the machine for one micro cycle logging all accesses to the state. + /// \brief Resets the microarchitecture state and returns an access log /// \param log_type Type of access log to generate. /// \param one_based Use 1-based indices when reporting errors. + /// \param log_data If true, access data is recorded in the log, otherwise only hashes. The default is false. /// \returns The state access log. - access_log step_uarch(const access_log::type &log_type, bool one_based = false); + access_log log_uarch_reset(const access_log::type &log_type, bool one_based = false); /// \brief Checks the internal consistency of an access log. /// \param log State access log to be verified. /// \param runtime Machine runtime configuration to use during verification. /// \param one_based Use 1-based indices when reporting errors. - static void verify_access_log(const access_log &log, const machine_runtime_config &runtime = {}, + static void verify_uarch_step_log(const access_log &log, const machine_runtime_config &runtime = {}, bool one_based = false); /// \brief Checks the validity of a state transition. @@ -238,7 +244,23 @@ class machine final { /// \param root_hash_after State hash after step. /// \param runtime Machine runtime configuration to use during verification. /// \param one_based Use 1-based indices when reporting errors. - static void verify_state_transition(const hash_type &root_hash_before, const access_log &log, + static void verify_uarch_step_state_transition(const hash_type &root_hash_before, const access_log &log, + const hash_type &root_hash_after, const machine_runtime_config &runtime = {}, bool one_based = false); + + /// \brief Checks the internal consistency of an access log produced by log_uarch_reset + /// \param log State access log to be verified. + /// \param runtime Machine runtime configuration to use during verification. + /// \param one_based Use 1-based indices when reporting errors. + static void verify_uarch_reset_log(const access_log &log, const machine_runtime_config &runtime = {}, + bool one_based = false); + + /// \brief Checks the validity of a state transition. caused by log_uarch_reset + /// \param root_hash_before State hash before uarch reset + /// \param log Step state access log. + /// \param root_hash_after State hash after uarch reset. + /// \param runtime Machine runtime configuration to use during verification. + /// \param one_based Use 1-based indices when reporting errors. + static void verify_uarch_reset_state_transition(const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after, const machine_runtime_config &runtime = {}, bool one_based = false); static machine_config get_default_config(void); @@ -804,10 +826,6 @@ class machine final { /// \brief Writes the value ofthe microarchitecture cycle counter register. /// \param value New register value. void write_uarch_cycle(uint64_t value); - - /// \brief Reads the value of the microarchitecture RAM length - /// \returns The value of the microarchitecture RAM length - uint64_t read_uarch_ram_length(void) const; }; } // namespace cartesi diff --git a/src/pma.cpp b/src/pma.cpp index 5fc23b650..3321d7bf8 100644 --- a/src/pma.cpp +++ b/src/pma.cpp @@ -190,6 +190,31 @@ uint64_t pma_entry::get_ilength(void) const { return m_length; } +void pma_entry::write_memory(uint64_t paddr, const unsigned char *data, uint64_t size) { + if (!get_istart_M() || get_istart_E()) { + throw std::invalid_argument{"address range not entirely in memory PMA"}; + } + if (!contains(paddr, size)) { + throw std::invalid_argument{"range not contained in pma"}; + } + if (!data) { + throw std::invalid_argument{"invalid data buffer"}; + } + memcpy(get_memory().get_host_memory() + (paddr - get_start()), data, size); + mark_dirty_pages(paddr, size); +} + +void pma_entry::set_memory(uint64_t paddr, unsigned char value, uint64_t size) { + if (!get_istart_M() || get_istart_E()) { + throw std::invalid_argument{"address range not entirely in memory PMA"}; + } + if (!contains(paddr, size)) { + throw std::invalid_argument{"range not contained in pma"}; + } + memset(get_memory().get_host_memory() + (paddr - get_start()), value, size); + mark_dirty_pages(paddr, size); +} + bool pma_peek_error(const pma_entry &, const machine &, uint64_t, const unsigned char **, unsigned char *) { return false; } diff --git a/src/pma.h b/src/pma.h index 1c952f114..c6fbb370a 100644 --- a/src/pma.h +++ b/src/pma.h @@ -433,6 +433,28 @@ class pma_entry final { m_dirty_page_map[map_index] |= (1 << (page_number & 7)); } } + /// \brief Mark all pages in rage as dirty + /// \param address Start address + /// \param size Size of range + void mark_dirty_pages(uint64_t address, uint64_t size) { + if (m_dirty_page_map.empty()) { + return; + } + if (!get_istart_M() || get_istart_E()) { + throw std::invalid_argument{"address range not entirely in memory PMA"}; + } + if (!contains(address, size)) { + throw std::invalid_argument{"range not contained in pma"}; + } + constexpr const auto log2_page_size = PMA_constants::PMA_PAGE_SIZE_LOG2; + uint64_t page_in_range = ((address - get_start()) >> log2_page_size) << log2_page_size; + constexpr const auto page_size = PMA_constants::PMA_PAGE_SIZE; + auto npages = (size + page_size - 1) / page_size; + for (decltype(npages) i = 0; i < npages; ++i) { + mark_dirty_page(page_in_range); + page_in_range += page_size; + } + } /// \brief Mark a given page as clean /// \param address_in_range Any address within page in range @@ -480,6 +502,18 @@ class pma_entry final { } return address >= get_start() && get_length() >= length && address - get_start() <= get_length() - length; } + + /// \brief Writes data to pma memory + /// \param paddr Destination address within pma range + /// \param data Source data + /// \param size Data size + void write_memory(uint64_t paddr, const unsigned char *data, uint64_t size); + + /// \brief Sets pma memory to a given value + /// \param paddr Destination address within pma range + /// \param value Value to write + /// \param size Data size + void set_memory(uint64_t paddr, unsigned char value, uint64_t size); }; /// \brief Creates a PMA entry for a new memory range initially filled with zeros. diff --git a/src/protobuf-util.cpp b/src/protobuf-util.cpp index 65c37fc39..f16cd84d6 100644 --- a/src/protobuf-util.cpp +++ b/src/protobuf-util.cpp @@ -196,8 +196,8 @@ void set_proto_machine_config(const machine_config &c, CartesiMachine::MachineCo proto_up->set_x31(c.uarch.processor.x[31]); proto_up->set_pc(c.uarch.processor.pc); proto_up->set_cycle(c.uarch.processor.cycle); + proto_up->set_halt_flag(c.uarch.processor.halt_flag); auto *proto_uarch_ram = proto_u->mutable_ram(); - proto_uarch_ram->set_length(c.uarch.ram.length); proto_uarch_ram->set_image_filename(c.uarch.ram.image_filename); } @@ -207,7 +207,7 @@ void set_proto_machine_runtime_config(const machine_runtime_config &r, CartesiMa } access_log::type get_proto_log_type(const CartesiMachine::AccessLogType &proto_lt) { - return access_log::type{proto_lt.proofs(), proto_lt.annotations()}; + return access_log::type{proto_lt.proofs(), proto_lt.annotations(), proto_lt.large_data()}; } void set_proto_hash(const machine_merkle_tree::hash_type &h, CartesiMachine::Hash *proto_h) { @@ -254,6 +254,7 @@ void set_proto_merkle_tree_proof(const machine_merkle_tree::proof_type &p, Carte void set_proto_access_log(const access_log &al, CartesiMachine::AccessLog *proto_al) { proto_al->mutable_log_type()->set_annotations(al.get_log_type().has_annotations()); proto_al->mutable_log_type()->set_proofs(al.get_log_type().has_proofs()); + proto_al->mutable_log_type()->set_large_data(al.get_log_type().has_large_data()); for (const auto &a : al.get_accesses()) { auto *proto_a = proto_al->add_accesses(); switch (a.get_type()) { @@ -269,8 +270,18 @@ void set_proto_access_log(const access_log &al, CartesiMachine::AccessLog *proto } proto_a->set_log2_size(a.get_log2_size()); proto_a->set_address(a.get_address()); - proto_a->set_read(a.get_read().data(), a.get_read().size()); - proto_a->set_written(a.get_written().data(), a.get_written().size()); + set_proto_hash(a.get_read_hash(), proto_a->mutable_read_hash()); + if (a.get_read().has_value()) { + auto &value_read = a.get_read().value(); + proto_a->set_read(value_read.data(), value_read.size()); + } + if (a.get_written_hash().has_value()) { + set_proto_hash(a.get_written_hash().value(), proto_a->mutable_written_hash()); + } + if (a.get_written().has_value()) { + auto &value_written = a.get_written().value(); + proto_a->set_written(value_written.data(), value_written.size()); + } if (al.get_log_type().has_proofs() && a.get_proof().has_value()) { // NOLINTNEXTLINE(bugprone-unchecked-optional-access) set_proto_merkle_tree_proof(a.get_proof().value(), proto_a->mutable_proof()); @@ -328,7 +339,8 @@ access_log get_proto_access_log(const CartesiMachine::AccessLog &proto_al) { const bool has_annotations = proto_al.log_type().annotations(); const bool has_proofs = proto_al.log_type().proofs(); - auto al = access_log(access_log::type{has_proofs, has_annotations}); + const bool has_large_data = proto_al.log_type().large_data(); + auto al = access_log(access_log::type{has_proofs, has_annotations, has_large_data}); const auto &proto_accesses = proto_al.accesses(); const auto &proto_brackets = proto_al.brackets(); @@ -337,31 +349,41 @@ access_log get_proto_access_log(const CartesiMachine::AccessLog &proto_al) { auto pnt = proto_notes.begin(); auto pac = proto_accesses.begin(); uint64_t iac = 0; // curent access index - while (pac != proto_accesses.end() && pbr != proto_brackets.end()) { + while (pac != proto_accesses.end()) { while (pbr != proto_brackets.end() && pbr->where() == iac) { // bracket note points to current access al.push_bracket(get_proto_bracket_type(pbr->type()), pbr->text().c_str()); assert(pbr->where() == al.get_brackets().back().where); pbr++; } - if (pac != proto_accesses.end()) { - access a; - a.set_type(get_proto_access_type(pac->type())); - a.set_address(pac->address()); - a.set_log2_size(static_cast(pac->log2_size())); - a.get_read().insert(a.get_read().end(), pac->read().begin(), pac->read().end()); - a.get_written().insert(a.get_written().end(), pac->written().begin(), pac->written().end()); - std::string note; - if (has_annotations) { - note = *pnt++; - } - if (has_proofs) { - a.set_proof(get_proto_merkle_tree_proof(pac->proof())); - } - al.push_access(a, note.c_str()); - pac++; - iac++; + access a; + a.set_type(get_proto_access_type(pac->type())); + a.set_address(pac->address()); + a.set_log2_size(static_cast(pac->log2_size())); + a.set_read_hash(get_proto_hash(pac->read_hash())); + if (pac->has_read()) { + access_data read_value; + read_value.insert(read_value.end(), pac->read().begin(), pac->read().end()); + a.set_read(read_value); + } + if (pac->has_written_hash()) { + a.set_written_hash(get_proto_hash(pac->written_hash())); } + if (pac->has_written()) { + access_data written_value; + written_value.insert(written_value.end(), pac->written().begin(), pac->written().end()); + a.set_written(written_value); + } + std::string note; + if (has_annotations) { + note = *pnt++; + } + if (has_proofs) { + a.set_proof(get_proto_merkle_tree_proof(pac->proof())); + } + al.push_access(a, note.c_str()); + pac++; + iac++; } // push closing bracket notes while (pbr != proto_brackets.end()) { @@ -676,7 +698,6 @@ static uarch_config get_proto_uarch_config(const CartesiMachine::UarchConfig &pr using CartesiMachine::UarchConfig; uarch_config c; if (proto_c.has_ram()) { - c.ram.length = proto_c.ram().length(); c.ram.image_filename = proto_c.ram().image_filename(); } if (proto_c.has_processor()) { @@ -781,6 +802,9 @@ static uarch_config get_proto_uarch_config(const CartesiMachine::UarchConfig &pr if (proto_p.has_cycle()) { p.cycle = proto_p.cycle(); } + if (proto_p.has_halt_flag()) { + p.halt_flag = proto_p.halt_flag(); + } } return c; } diff --git a/src/remote-machine-proxy.cpp b/src/remote-machine-proxy.cpp index 884496051..8d4e43c02 100644 --- a/src/remote-machine-proxy.cpp +++ b/src/remote-machine-proxy.cpp @@ -18,6 +18,8 @@ #include #include +#include "grpc-config.h" + using namespace std::string_literals; #pragma GCC diagnostic push @@ -264,16 +266,29 @@ static auto new_RunUarch_handler(handler_context &hctx) { }); } -static auto new_ResetUarchState_handler(handler_context &hctx) { +static auto new_ResetUarch_handler(handler_context &hctx) { return new_handler( - "ResetUarchState", + "ResetUarch", + [&hctx](auto &server_context, auto &request, auto &writer, auto self) { + auto *cq = hctx.completion_queue.get(); + hctx.async_service.RequestResetUarch(&server_context, &request, &writer, cq, cq, self); + }, + [&hctx](auto &client_context, auto &request) { + auto *cq = hctx.completion_queue.get(); + return hctx.stub->AsyncResetUarch(&client_context, request, cq); + }); +} + +static auto new_LogUarchReset_handler(handler_context &hctx) { + return new_handler( + "LogUarchReset", [&hctx](auto &server_context, auto &request, auto &writer, auto self) { auto *cq = hctx.completion_queue.get(); - hctx.async_service.RequestResetUarchState(&server_context, &request, &writer, cq, cq, self); + hctx.async_service.RequestLogUarchReset(&server_context, &request, &writer, cq, cq, self); }, [&hctx](auto &client_context, auto &request) { auto *cq = hctx.completion_queue.get(); - return hctx.stub->AsyncResetUarchState(&client_context, request, cq); + return hctx.stub->AsyncLogUarchReset(&client_context, request, cq); }); } @@ -343,16 +358,16 @@ static auto new_Shutdown_handler(handler_context &hctx) { side_effect::shutdown); } -static auto new_StepUarch_handler(handler_context &hctx) { - return new_handler( - "StepUarch", +static auto new_LogUarchStep_handler(handler_context &hctx) { + return new_handler( + "LogUarchStep", [&hctx](auto &server_context, auto &request, auto &writer, auto self) { auto *cq = hctx.completion_queue.get(); - hctx.async_service.RequestStepUarch(&server_context, &request, &writer, cq, cq, self); + hctx.async_service.RequestLogUarchStep(&server_context, &request, &writer, cq, cq, self); }, [&hctx](auto &client_context, auto &request) { auto *cq = hctx.completion_queue.get(); - return hctx.stub->AsyncStepUarch(&client_context, request, cq); + return hctx.stub->AsyncLogUarchStep(&client_context, request, cq); }); } @@ -629,37 +644,40 @@ static auto new_GetDefaultConfig_handler(handler_context &hctx) { }); } -static auto new_VerifyAccessLog_handler(handler_context &hctx) { - return new_handler( - "VerifyAccessLog", +static auto new_VerifyUarchStepLog_handler(handler_context &hctx) { + return new_handler( + "VerifyUarchStepLog", [&hctx](auto &server_context, auto &request, auto &writer, auto self) { auto *cq = hctx.completion_queue.get(); - hctx.async_service.RequestVerifyAccessLog(&server_context, &request, &writer, cq, cq, self); + hctx.async_service.RequestVerifyUarchStepLog(&server_context, &request, &writer, cq, cq, self); }, [&hctx](auto &client_context, auto &request) { auto *cq = hctx.completion_queue.get(); - return hctx.stub->AsyncVerifyAccessLog(&client_context, request, cq); + return hctx.stub->AsyncVerifyUarchStepLog(&client_context, request, cq); }); } -static auto new_VerifyStateTransition_handler(handler_context &hctx) { - return new_handler( - "VerifyStateTransition", +static auto new_VerifyUarchStepStateTransition_handler(handler_context &hctx) { + return new_handler( + "VerifyUarchStepStateTransition", [&hctx](auto &server_context, auto &request, auto &writer, auto self) { auto *cq = hctx.completion_queue.get(); - hctx.async_service.RequestVerifyStateTransition(&server_context, &request, &writer, cq, cq, self); + hctx.async_service.RequestVerifyUarchStepStateTransition(&server_context, &request, &writer, cq, cq, self); }, [&hctx](auto &client_context, auto &request) { auto *cq = hctx.completion_queue.get(); - return hctx.stub->AsyncVerifyStateTransition(&client_context, request, cq); + return hctx.stub->AsyncVerifyUarchStepStateTransition(&client_context, request, cq); }); } static bool forward_checkin(handler_context &hctx) { if (hctx.checkin.has_value()) { + grpc::ChannelArguments args; + args.SetMaxReceiveMessageSize(cartesi::GRPC_MAX_RECEIVE_MESSAGE_SIZE); auto stub = MachineCheckIn::NewStub( - grpc::CreateChannel(hctx.checkin.value().checkin_address, grpc::InsecureChannelCredentials())); + grpc::CreateCustomChannel(hctx.checkin.value().checkin_address, grpc::InsecureChannelCredentials(), args)); CheckInRequest request; + request.set_session_id(hctx.checkin.value().session_id); request.set_address(hctx.proxy_address); Void response; @@ -777,42 +795,43 @@ static auto build_proxy(const char *address, handler_context &hctx) { } static void enable_server_handlers(handler_context &hctx) { - new_GetVersion_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_SetCheckInTarget_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_Machine_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_Run_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_RunUarch_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_ResetUarchState_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_Store_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_Destroy_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_Snapshot_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_Rollback_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_Shutdown_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_StepUarch_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_ReadMemory_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_WriteMemory_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_ReadWord_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_GetRootHash_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_GetProof_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_ReplaceMemoryRange_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_GetXAddress_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_ReadX_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_WriteX_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_GetUarchXAddress_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_ReadUarchX_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_WriteUarchX_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_ResetIflagsY_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_GetCsrAddress_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_ReadCsr_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_WriteCsr_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_GetInitialConfig_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_VerifyMerkleTree_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_VerifyDirtyPageMaps_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_DumpPmas_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_GetDefaultConfig_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_VerifyAccessLog_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_VerifyStateTransition_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) - new_CheckIn_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_GetVersion_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_SetCheckInTarget_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_Machine_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_Run_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_RunUarch_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_ResetUarch_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_LogUarchReset_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_Store_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_Destroy_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_Snapshot_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_Rollback_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_Shutdown_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_LogUarchStep_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_ReadMemory_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_WriteMemory_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_ReadWord_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_GetRootHash_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_GetProof_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_ReplaceMemoryRange_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_GetXAddress_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_ReadX_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_WriteX_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_GetUarchXAddress_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_ReadUarchX_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_WriteUarchX_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_ResetIflagsY_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_GetCsrAddress_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_ReadCsr_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_WriteCsr_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_GetInitialConfig_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_VerifyMerkleTree_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_VerifyDirtyPageMaps_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_DumpPmas_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_GetDefaultConfig_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_VerifyUarchStepLog_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_VerifyUarchStepStateTransition_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) + new_CheckIn_handler(hctx); // NOLINT: cannot leak (pointer is in completion queue) } // NOLINT: cannot leak (pointer is in completion queue) static void drain_completion_queue(grpc::ServerCompletionQueue *completion_queue) { diff --git a/src/remote-machine.cpp b/src/remote-machine.cpp index b32eaf784..ecf6e802d 100644 --- a/src/remote-machine.cpp +++ b/src/remote-machine.cpp @@ -36,6 +36,7 @@ #include "cartesi-machine.grpc.pb.h" #pragma GCC diagnostic pop +#include "grpc-config.h" #include "machine.h" #include "protobuf-util.h" #include "unique-c-ptr.h" @@ -547,11 +548,11 @@ class handler_Shutdown final : public handler { } }; -class handler_ResetUarchState final : public handler { +class handler_ResetUarch final : public handler { side_effect prepare(handler_context &hctx, ServerContext *sctx, Void *req, ServerAsyncResponseWriter *writer) override { - hctx.s->RequestResetUarchState(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); + hctx.s->RequestResetUarch(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); return side_effect::none; } @@ -560,39 +561,65 @@ class handler_ResetUarchState final : public handler { if (!hctx.m) { return finish_with_error_no_machine(writer); } - hctx.m->reset_uarch_state(); + hctx.m->reset_uarch(); const Void resp; return finish_ok(writer, resp); } public: - handler_ResetUarchState(handler_context &hctx) { + handler_ResetUarch(handler_context &hctx) { advance(hctx); } }; -class handler_StepUarch final : public handler { +class handler_LogUarchReset final : public handler { - side_effect prepare(handler_context &hctx, ServerContext *sctx, StepUarchRequest *req, - ServerAsyncResponseWriter *writer) override { - hctx.s->RequestStepUarch(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); + side_effect prepare(handler_context &hctx, ServerContext *sctx, LogUarchResetRequest *req, + ServerAsyncResponseWriter *writer) override { + hctx.s->RequestLogUarchReset(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); return side_effect::none; } - side_effect go(handler_context &hctx, StepUarchRequest *req, - ServerAsyncResponseWriter *writer) override { + side_effect go(handler_context &hctx, LogUarchResetRequest *req, + ServerAsyncResponseWriter *writer) override { if (!hctx.m) { return finish_with_error_no_machine(writer); } const AccessLog proto_log; - StepUarchResponse resp; - set_proto_access_log(hctx.m->step_uarch(get_proto_log_type(req->log_type()), req->one_based()), + LogUarchResetResponse resp; + set_proto_access_log(hctx.m->log_uarch_reset(get_proto_log_type(req->log_type()), req->one_based()), resp.mutable_log()); return finish_ok(writer, resp); } public: - handler_StepUarch(handler_context &hctx) { + handler_LogUarchReset(handler_context &hctx) { + advance(hctx); + } +}; + +class handler_LogUarchStep final : public handler { + + side_effect prepare(handler_context &hctx, ServerContext *sctx, LogUarchStepRequest *req, + ServerAsyncResponseWriter *writer) override { + hctx.s->RequestLogUarchStep(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); + return side_effect::none; + } + + side_effect go(handler_context &hctx, LogUarchStepRequest *req, + ServerAsyncResponseWriter *writer) override { + if (!hctx.m) { + return finish_with_error_no_machine(writer); + } + const AccessLog proto_log; + LogUarchStepResponse resp; + set_proto_access_log(hctx.m->log_uarch_step(get_proto_log_type(req->log_type()), req->one_based()), + resp.mutable_log()); + return finish_ok(writer, resp); + } + +public: + handler_LogUarchStep(handler_context &hctx) { advance(hctx); } }; @@ -1201,48 +1228,96 @@ class handler_GetDefaultConfig final : public handler { +class handler_VerifyUarchStepLog final : public handler { - side_effect prepare(handler_context &hctx, ServerContext *sctx, VerifyAccessLogRequest *req, + side_effect prepare(handler_context &hctx, ServerContext *sctx, VerifyUarchStepLogRequest *req, ServerAsyncResponseWriter *writer) override { - hctx.s->RequestVerifyAccessLog(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); + hctx.s->RequestVerifyUarchStepLog(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); return side_effect::none; } - side_effect go(handler_context &hctx, VerifyAccessLogRequest *req, + side_effect go(handler_context &hctx, VerifyUarchStepLogRequest *req, ServerAsyncResponseWriter *writer) override { (void) hctx; const Void resp; - machine::verify_access_log(get_proto_access_log(req->log()), get_proto_machine_runtime_config(req->runtime()), - req->one_based()); + machine::verify_uarch_step_log(get_proto_access_log(req->log()), + get_proto_machine_runtime_config(req->runtime()), req->one_based()); return finish_ok(writer, resp); } public: - handler_VerifyAccessLog(handler_context &hctx) { + handler_VerifyUarchStepLog(handler_context &hctx) { advance(hctx); } }; -class handler_VerifyStateTransition final : public handler { +class handler_VerifyUarchStepStateTransition final : public handler { - side_effect prepare(handler_context &hctx, ServerContext *sctx, VerifyStateTransitionRequest *req, + side_effect prepare(handler_context &hctx, ServerContext *sctx, VerifyUarchStepStateTransitionRequest *req, ServerAsyncResponseWriter *writer) override { - hctx.s->RequestVerifyStateTransition(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); + hctx.s->RequestVerifyUarchStepStateTransition(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); return side_effect::none; } - side_effect go(handler_context &hctx, VerifyStateTransitionRequest *req, + side_effect go(handler_context &hctx, VerifyUarchStepStateTransitionRequest *req, ServerAsyncResponseWriter *writer) override { (void) hctx; - machine::verify_state_transition(get_proto_hash(req->root_hash_before()), get_proto_access_log(req->log()), - get_proto_hash(req->root_hash_after()), get_proto_machine_runtime_config(req->runtime()), req->one_based()); + machine::verify_uarch_step_state_transition(get_proto_hash(req->root_hash_before()), + get_proto_access_log(req->log()), get_proto_hash(req->root_hash_after()), + get_proto_machine_runtime_config(req->runtime()), req->one_based()); const Void resp; return finish_ok(writer, resp); // NOLINT: suppress warning caused by gRPC } public: - handler_VerifyStateTransition(handler_context &hctx) { + handler_VerifyUarchStepStateTransition(handler_context &hctx) { + advance(hctx); + } +}; + +class handler_VerifyUarchResetStateTransition final : public handler { + + side_effect prepare(handler_context &hctx, ServerContext *sctx, VerifyUarchResetStateTransitionRequest *req, + ServerAsyncResponseWriter *writer) override { + hctx.s->RequestVerifyUarchResetStateTransition(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); + return side_effect::none; + } + + side_effect go(handler_context &hctx, VerifyUarchResetStateTransitionRequest *req, + ServerAsyncResponseWriter *writer) override { + (void) hctx; + machine::verify_uarch_reset_state_transition(get_proto_hash(req->root_hash_before()), + get_proto_access_log(req->log()), get_proto_hash(req->root_hash_after()), + get_proto_machine_runtime_config(req->runtime()), req->one_based()); + const Void resp; + return finish_ok(writer, resp); // NOLINT: suppress warning caused by gRPC + } + +public: + handler_VerifyUarchResetStateTransition(handler_context &hctx) { + advance(hctx); + } +}; + +class handler_VerifyUarchResetLog final : public handler { + + side_effect prepare(handler_context &hctx, ServerContext *sctx, VerifyUarchResetLogRequest *req, + ServerAsyncResponseWriter *writer) override { + hctx.s->RequestVerifyUarchResetLog(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); + return side_effect::none; + } + + side_effect go(handler_context &hctx, VerifyUarchResetLogRequest *req, + ServerAsyncResponseWriter *writer) override { + (void) hctx; + const Void resp; + machine::verify_uarch_reset_log(get_proto_access_log(req->log()), + get_proto_machine_runtime_config(req->runtime()), req->one_based()); + return finish_ok(writer, resp); + } + +public: + handler_VerifyUarchResetLog(handler_context &hctx) { advance(hctx); } }; @@ -1266,6 +1341,7 @@ std::unique_ptr build_server(const char *server_address, handler_context SLOG(debug) << "Building new GRPC server"; hctx.s = std::make_unique(); ServerBuilder builder; + builder.SetMaxReceiveMessageSize(GRPC_MAX_RECEIVE_MESSAGE_SIZE); builder.AddChannelArgument(GRPC_ARG_ALLOW_REUSEPORT, 0); int server_port = 0; builder.AddListeningPort(server_address, grpc::InsecureServerCredentials(), &server_port); @@ -1339,13 +1415,14 @@ static void server_loop(const char *server_address, const char *session_id, cons const handler_Machine hMachine(hctx); const handler_Run hRun(hctx); const handler_RunUarch hRunUarch(hctx); - const handler_ResetUarchState hResetUarchState(hctx); + const handler_ResetUarch hResetUarch(hctx); + const handler_LogUarchReset hLogUarchReset(hctx); const handler_Store hStore(hctx); const handler_Destroy hDestroy(hctx); const handler_Snapshot hSnapshot(hctx); const handler_Rollback hRollback(hctx); const handler_Shutdown hShutdown(hctx); - const handler_StepUarch hStepUarch(hctx); + const handler_LogUarchStep hLogUarchStep(hctx); const handler_ReadMemory hReadMemory(hctx); const handler_WriteMemory hWriteMemory(hctx); const handler_ReadVirtualMemory hReadVirtualMemory(hctx); @@ -1369,8 +1446,10 @@ static void server_loop(const char *server_address, const char *session_id, cons const handler_VerifyDirtyPageMaps hVerifyDirtyPageMaps(hctx); const handler_DumpPmas hDumpPmas(hctx); const handler_GetDefaultConfig hGetDefaultConfig(hctx); - const handler_VerifyAccessLog hVerifyAccessLog(hctx); - const handler_VerifyStateTransition hVerifyStateTransition(hctx); + const handler_VerifyUarchStepLog hVerifyUarchStepLog(hctx); + const handler_VerifyUarchStepStateTransition hVerifyUarchStepStateTransition(hctx); + const handler_VerifyUarchResetLog hVerifyUarchResetLog(hctx); + const handler_VerifyUarchResetStateTransition hVerifyUarchResetStateTransition(hctx); // The invariant before and after snapshot/rollbacks is that all handlers // are in waiting mode diff --git a/src/riscv-constants.h b/src/riscv-constants.h index 207a48329..ebfc8bfe3 100644 --- a/src/riscv-constants.h +++ b/src/riscv-constants.h @@ -464,8 +464,11 @@ enum CARTESI_init : uint64_t { TOHOST_INIT = UINT64_C(0), ///< Initial value for tohost MENVCFG_INIT = UINT64_C(0), ///< Initial value for menvcfg SENVCFG_INIT = UINT64_C(0), ///< Initial value for senvcfg + UARCH_HALT_FLAG_INIT = UINT64_C(0), ///< Initial value for microarchitecture halt flag + UARCH_X_INIT = UINT64_C(0), ///< Initial value for microarchitecture general purpose register x UARCH_PC_INIT = static_cast(PMA_UARCH_RAM_START_DEF), ///< Initial value for microarchitecture pc UARCH_CYCLE_INIT = UINT64_C(0), ///< Initial value for microarchitecture cycle + UARCH_RAM_LENGTH_INIT = PMA_UARCH_RAM_LENGTH_DEF, ///< Initial value for microarchitecture ram length }; /// \brief Mapping between CSR names and addresses diff --git a/src/shadow-uarch-state-factory.cpp b/src/shadow-uarch-state-factory.cpp index 741db1668..98e9d1c33 100644 --- a/src/shadow-uarch-state-factory.cpp +++ b/src/shadow-uarch-state-factory.cpp @@ -49,7 +49,6 @@ static bool shadow_uarch_state_peek(const pma_entry &pma, const machine &m, uint s->halt_flag = m.read_uarch_halt_flag(); s->cycle = m.read_uarch_cycle(); s->pc = m.read_uarch_pc(); - s->ram_length = m.get_initial_config().uarch.ram.length; for (int i = 0; i < UARCH_X_REG_COUNT; ++i) { s->x[i] = m.read_uarch_x(i); } diff --git a/src/shadow-uarch-state.cpp b/src/shadow-uarch-state.cpp index 6e960cf0c..b00421d35 100644 --- a/src/shadow-uarch-state.cpp +++ b/src/shadow-uarch-state.cpp @@ -25,11 +25,6 @@ namespace cartesi { -static constexpr uint64_t uarch_ram_length_abs_addr = - shadow_uarch_state_get_csr_abs_addr(shadow_uarch_state_csr::ram_length); - -extern "C" const uint64_t shadow_uarch_state_uarch_ram_length_abs_addr = uarch_ram_length_abs_addr; - const pma_driver shadow_uarch_state_driver = {"SHADOW UARCH", device_read_error, device_write_error}; } // namespace cartesi diff --git a/src/shadow-uarch-state.h b/src/shadow-uarch-state.h index 2493c429d..377f66181 100644 --- a/src/shadow-uarch-state.h +++ b/src/shadow-uarch-state.h @@ -37,7 +37,6 @@ struct shadow_uarch_state { uint64_t halt_flag; uint64_t cycle; uint64_t pc; - uint64_t ram_length; uint64_t x[UARCH_X_REG_COUNT]; }; #pragma pack(pop) @@ -50,7 +49,6 @@ enum class shadow_uarch_state_csr { halt_flag = offsetof(shadow_uarch_state, halt_flag), cycle = offsetof(shadow_uarch_state, cycle), pc = offsetof(shadow_uarch_state, pc), - ram_length = offsetof(shadow_uarch_state, ram_length) }; /// \brief Obtains the relative address of a CSR in shadow uarch state memory. @@ -79,10 +77,6 @@ static inline uint64_t shadow_uarch_state_get_x_abs_addr(int reg) { return PMA_SHADOW_UARCH_STATE_START + shadow_uarch_state_get_x_rel_addr(reg); } -/// \brief Absolute address of shadow_uarch_csr::uarch_ram_length. This symbol is used by the microarchitecture boostrap -/// to detect the RAM size -extern "C" const uint64_t shadow_uarch_state_uarch_ram_length_abs_addr; - } // namespace cartesi #endif diff --git a/src/state-access.h b/src/state-access.h index 11a97585c..c9fb91437 100644 --- a/src/state-access.h +++ b/src/state-access.h @@ -511,10 +511,6 @@ class state_access : public i_state_access { log2_size); } - uint64_t do_read_uarch_ram_length() { - return m_m.get_initial_config().uarch.ram.length; - } - template inline bool do_translate_vaddr_via_tlb(uint64_t vaddr, unsigned char **phptr) { const uint64_t eidx = tlb_get_entry_index(vaddr); diff --git a/src/test-machine-c-api.cpp b/src/test-machine-c-api.cpp index 2143e9674..169787eb0 100644 --- a/src/test-machine-c-api.cpp +++ b/src/test-machine-c-api.cpp @@ -33,6 +33,7 @@ #include "machine-c-api.h" #include "riscv-constants.h" #include "test-utils.h" +#include "uarch-constants.h" #include "uarch-solidity-compat.h" // NOLINTNEXTLINE @@ -49,8 +50,8 @@ static hash_type get_verification_root_hash(cm_machine *machine) { "0000000002000000--00000000000c0000.bin", // clint "0000000040008000--0000000000001000.bin", // htif "0000000080000000--0000000000100000.bin", // ram - "0000000070000000--0000000000001000.bin", // shadow uarch state - "0000000078000000--0000000000080000.bin", // uarch ram + "0000000000400000--0000000000001000.bin", // shadow uarch state + "0000000000600000--0000000000200000.bin", // uarch ram }; char *err_msg{}; @@ -238,7 +239,6 @@ class incomplete_machine_fixture : public default_machine_fixture { target->uarch.processor = source->uarch.processor; target->uarch.ram.image_filename = new_cstr(source->uarch.ram.image_filename); - target->uarch.ram.length = source->uarch.ram.length; } static void _cleanup_machine_config(cm_machine_config *config) { @@ -1146,7 +1146,6 @@ CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, marchid) CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, mimpid) CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, uarch_cycle) CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, uarch_pc) -CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, uarch_ram_length) CHECK_READER_FAILS_ON_nullptr_MACHINE(bool, iflags_Y) CHECK_READER_FAILS_ON_nullptr_MACHINE(bool, iflags_X) CHECK_READER_FAILS_ON_nullptr_MACHINE(bool, iflags_H) @@ -1536,8 +1535,8 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(dump_pmas_null_placeholder_test, flash_drive_mach "0000000002000000--00000000000c0000.bin", // clint "0000000040008000--0000000000001000.bin", // htif "0000000080000000--0000000000100000.bin", // ram - "0000000070000000--0000000000001000.bin", // shadow uarch state - "0000000078000000--0000000000080000.bin", // uarch ram + "0000000000400000--0000000000001000.bin", // shadow uarch state + "0000000000600000--0000000000200000.bin", // uarch ram "0080000000000000--0000000003c00000.bin" // flash drive }; @@ -1559,8 +1558,8 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(dump_pmas_basic_test, flash_drive_machine_fixture "0000000002000000--00000000000c0000.bin", // clint "0000000040008000--0000000000001000.bin", // htif "0000000080000000--0000000000100000.bin", // ram - "0000000070000000--0000000000001000.bin", // shadow uarch state - "0000000078000000--0000000000080000.bin", // uarch ram + "0000000000400000--0000000000001000.bin", // shadow uarch state + "0000000000600000--0000000000200000.bin", // uarch ram "0080000000000000--0000000003c00000.bin" // flash drive }; @@ -1826,7 +1825,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(read_write_uarch_x_basic_test, ordinary_machine_f BOOST_CHECK_EQUAL(err_msg, nullptr); BOOST_CHECK_EQUAL(uarch_x_origin, uarch_x_read); - BOOST_CHECK_EQUAL(static_cast(cartesi::PMA_SHADOW_UARCH_STATE_START + 48), cm_get_uarch_x_address(2)); + BOOST_CHECK_EQUAL(static_cast(cartesi::PMA_SHADOW_UARCH_STATE_START + 40), cm_get_uarch_x_address(2)); } BOOST_AUTO_TEST_CASE_NOLINT(read_csr_null_machine_test) { @@ -1909,9 +1908,9 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(verify_merkle_tree_basic_test, ordinary_machine_f BOOST_CHECK(ret); } -BOOST_FIXTURE_TEST_CASE_NOLINT(verify_access_log_null_log_test, default_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(verify_uarch_step_log_null_log_test, default_machine_fixture) { char *err_msg{}; - int error_code = cm_verify_access_log(nullptr, &_runtime_config, false, &err_msg); + int error_code = cm_verify_uarch_step_log(nullptr, &_runtime_config, false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); std::string result = err_msg; @@ -1924,12 +1923,12 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(verify_access_log_null_log_test, default_machine_ class access_log_machine_fixture : public machine_rom_fixture { public: access_log_machine_fixture() { - _log_type = {true, true}; + _log_type = {true, true, false}; _machine_dir_path = (std::filesystem::temp_directory_path() / "661b6096c377cdc07756df488059f4407c8f4").string(); uint32_t test_uarch_ram[] = { 0x07b00513, // li a0,123 - 0x700002b7, // li t0,UARCH_HALT_FLAG_SHADDOW_ADDR Address of uarch halt flag + 0x004002b7, // li t0,UARCH_HALT_FLAG_SHADDOW_ADDR Address of uarch halt flag 0x00100313, // li t1,1 0x0062b023, // sd t1,0(t0) Halt microarchitecture at uarch cycle 4 }; @@ -1937,7 +1936,6 @@ class access_log_machine_fixture : public machine_rom_fixture { of.write(static_cast(static_cast(&test_uarch_ram)), sizeof(test_uarch_ram)); of.close(); _set_uarch_ram_image(_uarch_ram_path); - _machine_config.uarch.ram.length = cartesi::PMA_UARCH_RAM_LENGTH; char *err_msg{}; cm_create_machine(&_machine_config, &_runtime_config, &_machine, &err_msg); @@ -1960,13 +1958,13 @@ class access_log_machine_fixture : public machine_rom_fixture { cm_access_log_type _log_type{}; }; -BOOST_FIXTURE_TEST_CASE_NOLINT(verify_access_log_null_rt_config_test, access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(verify_uarch_step_log_null_rt_config_test, access_log_machine_fixture) { char *err_msg{}; - int error_code = cm_step_uarch(_machine, _log_type, false, &_access_log, &err_msg); + int error_code = cm_log_uarch_step(_machine, _log_type, false, &_access_log, &err_msg); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); - error_code = cm_verify_access_log(_access_log, nullptr, false, &err_msg); + error_code = cm_verify_uarch_step_log(_access_log, nullptr, false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); std::string result = err_msg; @@ -1977,39 +1975,40 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(verify_access_log_null_rt_config_test, access_log cm_delete_access_log(_access_log); } -BOOST_FIXTURE_TEST_CASE_NOLINT(verify_access_log_null_error_placeholder_test, access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(verify_uarch_step_log_null_error_placeholder_test, access_log_machine_fixture) { char *err_msg{}; - int error_code = cm_step_uarch(_machine, _log_type, false, &_access_log, &err_msg); + int error_code = cm_log_uarch_step(_machine, _log_type, false, &_access_log, &err_msg); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); - error_code = cm_verify_access_log(_access_log, &_runtime_config, false, nullptr); + error_code = cm_verify_uarch_step_log(_access_log, &_runtime_config, false, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); cm_delete_access_log(_access_log); } BOOST_FIXTURE_TEST_CASE_NOLINT(step_null_machine_test, access_log_machine_fixture) { - int error_code = cm_step_uarch(nullptr, _log_type, false, &_access_log, nullptr); + int error_code = cm_log_uarch_step(nullptr, _log_type, false, &_access_log, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); } BOOST_FIXTURE_TEST_CASE_NOLINT(step_null_access_log_test, access_log_machine_fixture) { - int error_code = cm_step_uarch(_machine, _log_type, false, nullptr, nullptr); + int error_code = cm_log_uarch_step(_machine, _log_type, false, nullptr, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); } BOOST_FIXTURE_TEST_CASE_NOLINT(step_null_error_placeholder_test, access_log_machine_fixture) { - int error_code = cm_step_uarch(_machine, _log_type, false, &_access_log, nullptr); + int error_code = cm_log_uarch_step(_machine, _log_type, false, &_access_log, nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); cm_delete_access_log(_access_log); } -BOOST_FIXTURE_TEST_CASE_NOLINT(verify_state_transition_null_hash0_test, access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(verify_uarch_step_state_transition_null_hash0_test, access_log_machine_fixture) { char *err_msg{}; cm_hash hash1; - int error_code = cm_verify_state_transition(nullptr, _access_log, &hash1, &_runtime_config, false, &err_msg); + int error_code = + cm_verify_uarch_step_state_transition(nullptr, _access_log, &hash1, &_runtime_config, false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); std::string result = err_msg; @@ -2019,10 +2018,11 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(verify_state_transition_null_hash0_test, access_l cm_delete_cstring(err_msg); } -BOOST_FIXTURE_TEST_CASE_NOLINT(verify_state_transition_null_hash1_test, access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(verify_uarch_step_state_transition_null_hash1_test, access_log_machine_fixture) { char *err_msg{}; cm_hash hash0; - int error_code = cm_verify_state_transition(&hash0, _access_log, nullptr, &_runtime_config, false, &err_msg); + int error_code = + cm_verify_uarch_step_state_transition(&hash0, _access_log, nullptr, &_runtime_config, false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); std::string result = err_msg; @@ -2032,11 +2032,11 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(verify_state_transition_null_hash1_test, access_l cm_delete_cstring(err_msg); } -BOOST_FIXTURE_TEST_CASE_NOLINT(verify_state_transition_null_access_log_test, access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(verify_uarch_step_state_transition_null_access_log_test, access_log_machine_fixture) { char *err_msg{}; cm_hash hash0; cm_hash hash1; - int error_code = cm_verify_state_transition(&hash0, nullptr, &hash1, &_runtime_config, false, &err_msg); + int error_code = cm_verify_uarch_step_state_transition(&hash0, nullptr, &hash1, &_runtime_config, false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); std::string result = err_msg; @@ -2046,15 +2046,15 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(verify_state_transition_null_access_log_test, acc cm_delete_cstring(err_msg); } -BOOST_FIXTURE_TEST_CASE_NOLINT(verify_state_transition_null_rt_config_test, access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(verify_uarch_step_state_transition_null_rt_config_test, access_log_machine_fixture) { char *err_msg{}; - int error_code = cm_step_uarch(_machine, _log_type, false, &_access_log, &err_msg); + int error_code = cm_log_uarch_step(_machine, _log_type, false, &_access_log, &err_msg); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); cm_hash hash0; cm_hash hash1; - error_code = cm_verify_state_transition(&hash0, _access_log, &hash1, nullptr, false, &err_msg); + error_code = cm_verify_uarch_step_state_transition(&hash0, _access_log, &hash1, nullptr, false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); std::string result = err_msg; @@ -2065,30 +2065,30 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(verify_state_transition_null_rt_config_test, acce cm_delete_access_log(_access_log); } -BOOST_FIXTURE_TEST_CASE_NOLINT(step_uarch_complex_test_null_error_placeholder_test, access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(log_uarch_step_complex_test_null_error_placeholder_test, access_log_machine_fixture) { cm_hash hash0; cm_hash hash1; int error_code = cm_get_root_hash(_machine, &hash0, nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); - error_code = cm_step_uarch(_machine, _log_type, false, &_access_log, nullptr); + error_code = cm_log_uarch_step(_machine, _log_type, false, &_access_log, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); - error_code = cm_verify_access_log(_access_log, &_runtime_config, false, nullptr); + error_code = cm_verify_uarch_step_log(_access_log, &_runtime_config, false, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); error_code = cm_get_root_hash(_machine, &hash1, nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); - error_code = cm_verify_state_transition(&hash0, _access_log, &hash1, &_runtime_config, false, nullptr); + error_code = cm_verify_uarch_step_state_transition(&hash0, _access_log, &hash1, &_runtime_config, false, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); cm_delete_access_log(_access_log); } // sunda -BOOST_FIXTURE_TEST_CASE_NOLINT(step_uarch_until_halt, access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(log_uarch_step_until_halt, access_log_machine_fixture) { cm_hash hash0{}; cm_hash hash1{}; cm_hash hash2{}; @@ -2114,53 +2114,53 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(step_uarch_until_halt, access_log_machine_fixture BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); // step 1 - error_code = cm_step_uarch(_machine, _log_type, false, &_access_log, nullptr); + error_code = cm_log_uarch_step(_machine, _log_type, false, &_access_log, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); - error_code = cm_verify_access_log(_access_log, &_runtime_config, false, nullptr); + error_code = cm_verify_uarch_step_log(_access_log, &_runtime_config, false, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); // get hash after step error_code = cm_get_root_hash(_machine, &hash1, nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); // verify - error_code = cm_verify_state_transition(&hash0, _access_log, &hash1, &_runtime_config, false, nullptr); + error_code = cm_verify_uarch_step_state_transition(&hash0, _access_log, &hash1, &_runtime_config, false, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); cm_delete_access_log(_access_log); // step 2 - error_code = cm_step_uarch(_machine, _log_type, false, &_access_log, nullptr); + error_code = cm_log_uarch_step(_machine, _log_type, false, &_access_log, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); - error_code = cm_verify_access_log(_access_log, &_runtime_config, false, nullptr); + error_code = cm_verify_uarch_step_log(_access_log, &_runtime_config, false, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); // get hash after step error_code = cm_get_root_hash(_machine, &hash2, nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); // verify - error_code = cm_verify_state_transition(&hash1, _access_log, &hash2, &_runtime_config, false, nullptr); + error_code = cm_verify_uarch_step_state_transition(&hash1, _access_log, &hash2, &_runtime_config, false, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); cm_delete_access_log(_access_log); // step 3 - error_code = cm_step_uarch(_machine, _log_type, false, &_access_log, nullptr); + error_code = cm_log_uarch_step(_machine, _log_type, false, &_access_log, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); - error_code = cm_verify_access_log(_access_log, &_runtime_config, false, nullptr); + error_code = cm_verify_uarch_step_log(_access_log, &_runtime_config, false, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); // get hash after step error_code = cm_get_root_hash(_machine, &hash3, nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); // verify - error_code = cm_verify_state_transition(&hash2, _access_log, &hash3, &_runtime_config, false, nullptr); + error_code = cm_verify_uarch_step_state_transition(&hash2, _access_log, &hash3, &_runtime_config, false, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); cm_delete_access_log(_access_log); // step 4 - error_code = cm_step_uarch(_machine, _log_type, false, &_access_log, nullptr); + error_code = cm_log_uarch_step(_machine, _log_type, false, &_access_log, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); - error_code = cm_verify_access_log(_access_log, &_runtime_config, false, nullptr); + error_code = cm_verify_uarch_step_log(_access_log, &_runtime_config, false, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); // get hash after step error_code = cm_get_root_hash(_machine, &hash4, nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); // verify - error_code = cm_verify_state_transition(&hash3, _access_log, &hash4, &_runtime_config, false, nullptr); + error_code = cm_verify_uarch_step_state_transition(&hash3, _access_log, &hash4, &_runtime_config, false, nullptr); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); cm_delete_access_log(_access_log); @@ -2184,11 +2184,11 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(step_complex_test, access_log_machine_fixture) { BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); - error_code = cm_step_uarch(_machine, _log_type, false, &_access_log, &err_msg); + error_code = cm_log_uarch_step(_machine, _log_type, false, &_access_log, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); BOOST_CHECK_EQUAL(err_msg, nullptr); - error_code = cm_verify_access_log(_access_log, &_runtime_config, false, &err_msg); + error_code = cm_verify_uarch_step_log(_access_log, &_runtime_config, false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); BOOST_CHECK_EQUAL(err_msg, nullptr); @@ -2196,7 +2196,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(step_complex_test, access_log_machine_fixture) { BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); - error_code = cm_verify_state_transition(&hash0, _access_log, &hash1, &_runtime_config, false, &err_msg); + error_code = cm_verify_uarch_step_state_transition(&hash0, _access_log, &hash1, &_runtime_config, false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); BOOST_CHECK_EQUAL(err_msg, nullptr); @@ -2206,7 +2206,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(step_complex_test, access_log_machine_fixture) { BOOST_FIXTURE_TEST_CASE_NOLINT(step_hash_test, access_log_machine_fixture) { char *err_msg{}; - int error_code = cm_step_uarch(_machine, _log_type, false, &_access_log, &err_msg); + int error_code = cm_log_uarch_step(_machine, _log_type, false, &_access_log, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); BOOST_CHECK_EQUAL(err_msg, nullptr); @@ -2390,11 +2390,13 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(machine_run_uarch_advance_until_halt, access_log_ BOOST_REQUIRE_EQUAL(halt, 1); } -BOOST_FIXTURE_TEST_CASE_NOLINT(machine_reset_uarch_state, access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(machine_reset_uarch, ordinary_machine_fixture) { char *err_msg{}; // ensure that uarch cycle is 0 + uint64_t halt_cycle{}; uint64_t cycle{}; int error_code = cm_read_csr(_machine, CM_PROC_UARCH_CYCLE, &cycle, &err_msg); + BOOST_REQUIRE_EQUAL(err_msg, nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); BOOST_REQUIRE_EQUAL(cycle, 0); @@ -2407,15 +2409,15 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(machine_reset_uarch_state, access_log_machine_fix BOOST_REQUIRE_EQUAL(halt, 0); // save initial uarch ram - std::vector initial_uarch_ram(_machine_config.uarch.ram.length); - error_code = cm_read_memory(_machine, cartesi::PMA_UARCH_RAM_START, initial_uarch_ram.data(), + std::vector initial_uarch_ram(cartesi::UARCH_RAM_LENGTH); + error_code = cm_read_memory(_machine, cartesi::UARCH_RAM_START_ADDRESS, initial_uarch_ram.data(), initial_uarch_ram.size(), &err_msg); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); - // run until halts + // run until halt auto status{CM_UARCH_BREAK_REASON_UARCH_HALTED}; - error_code = cm_machine_run_uarch(_machine, 100, &status, &err_msg); + error_code = cm_machine_run_uarch(_machine, -1, &status, &err_msg); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); BOOST_REQUIRE_EQUAL(status, CM_UARCH_BREAK_REASON_UARCH_HALTED); @@ -2426,41 +2428,38 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(machine_reset_uarch_state, access_log_machine_fix BOOST_REQUIRE_EQUAL(err_msg, nullptr); BOOST_REQUIRE_EQUAL(halt, 1); - // alted at micro cycle 4 - error_code = cm_read_csr(_machine, CM_PROC_UARCH_CYCLE, &cycle, nullptr); + // save halt cycle + error_code = cm_read_csr(_machine, CM_PROC_UARCH_CYCLE, &halt_cycle, nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); - BOOST_REQUIRE_EQUAL(cycle, 4); + BOOST_REQUIRE(halt_cycle > 0); - // try run_uarch past micro cycle 4 - error_code = cm_machine_run_uarch(_machine, 100, &status, nullptr); + // try run_uarch past the halted cycle + error_code = cm_machine_run_uarch(_machine, halt_cycle + 1, &status, nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(status, CM_UARCH_BREAK_REASON_UARCH_HALTED); - // should stay at micro cycle 4 + // should stay at halt cycle error_code = cm_read_csr(_machine, CM_PROC_UARCH_CYCLE, &cycle, nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); - BOOST_REQUIRE_EQUAL(cycle, 4); + BOOST_REQUIRE_EQUAL(cycle, halt_cycle); // change the uarch ram in order to confirm if reset will restore it to the initial value std::array random_bytes{1, 2, 3, 4, 5, 6, 7, 8}; error_code = - cm_write_memory(_machine, cartesi::PMA_UARCH_RAM_START, random_bytes.data(), random_bytes.size(), &err_msg); + cm_write_memory(_machine, cartesi::PMA_UARCH_RAM_START, random_bytes.data(), random_bytes.size(), nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); - BOOST_REQUIRE_EQUAL(err_msg, nullptr); - BOOST_REQUIRE_EQUAL(err_msg, nullptr); // grab the modified ram bytes - std::vector modified_uarch_ram(_machine_config.uarch.ram.length); - error_code = cm_read_memory(_machine, cartesi::PMA_UARCH_RAM_START, modified_uarch_ram.data(), - modified_uarch_ram.size(), &err_msg); + std::vector modified_uarch_ram(cartesi::PMA_UARCH_RAM_LENGTH); + error_code = cm_read_memory(_machine, cartesi::UARCH_RAM_START_ADDRESS, modified_uarch_ram.data(), + modified_uarch_ram.size(), nullptr); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); - BOOST_REQUIRE_EQUAL(err_msg, nullptr); // ensure that modified ram is diferent from the one initially saved BOOST_REQUIRE(initial_uarch_ram != modified_uarch_ram); // reset state - error_code = cm_reset_uarch_state(_machine, &err_msg); + error_code = cm_reset_uarch(_machine, &err_msg); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); @@ -2471,20 +2470,14 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(machine_reset_uarch_state, access_log_machine_fix BOOST_REQUIRE_EQUAL(halt, 0); // grab the ram after reset - std::vector reset_uarch_ram(_machine_config.uarch.ram.length); - error_code = cm_read_memory(_machine, cartesi::PMA_UARCH_RAM_START, reset_uarch_ram.data(), reset_uarch_ram.size(), - &err_msg); + std::vector reset_uarch_ram(cartesi::UARCH_RAM_LENGTH); + error_code = cm_read_memory(_machine, cartesi::UARCH_RAM_START_ADDRESS, reset_uarch_ram.data(), + reset_uarch_ram.size(), &err_msg); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); // confirm ram was restored to initial state BOOST_REQUIRE(initial_uarch_ram == reset_uarch_ram); - - // refuse to reset state because it is not halted - error_code = cm_reset_uarch_state(_machine, &err_msg); - BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_RUNTIME_ERROR); - BOOST_REQUIRE_EQUAL(err_msg, "reset uarch state is not allowed when uarch is not halted"); - cm_delete_cstring(err_msg); } BOOST_FIXTURE_TEST_CASE_NOLINT(machine_verify_merkle_tree_root_updates_test, ordinary_machine_fixture) { @@ -2597,7 +2590,7 @@ class grpc_machine_fixture_with_server : public grpc_machine_fixture { class grpc_access_log_machine_fixture : public grpc_machine_fixture { public: - grpc_access_log_machine_fixture() : _access_log{}, _log_type{true, true} {} + grpc_access_log_machine_fixture() : _access_log{}, _log_type{true, true, true} {} ~grpc_access_log_machine_fixture() override = default; grpc_access_log_machine_fixture(const grpc_access_log_machine_fixture &) = delete; grpc_access_log_machine_fixture(grpc_access_log_machine_fixture &&) = delete; @@ -2804,11 +2797,12 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_get_version_wrong_addr_test, grpc_machine_fi cm_delete_semantic_version(version); } -BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_verify_state_transition_null_hash0_test, grpc_access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_verify_uarch_step_state_transition_null_hash0_test, + grpc_access_log_machine_fixture) { char *err_msg{}; cm_hash hash1; - int error_code = - cm_grpc_verify_state_transition(m_stub, nullptr, _access_log, &hash1, &_runtime_config, false, &err_msg); + int error_code = cm_grpc_verify_uarch_step_state_transition(m_stub, nullptr, _access_log, &hash1, &_runtime_config, + false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); std::string result = err_msg; std::string origin("invalid hash"); @@ -2816,11 +2810,12 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_verify_state_transition_null_hash0_test, grp cm_delete_cstring(err_msg); } -BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_verify_state_transition_null_hash1_test, grpc_access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_verify_uarch_step_state_transition_null_hash1_test, + grpc_access_log_machine_fixture) { char *err_msg{}; cm_hash hash0{}; - int error_code = - cm_grpc_verify_state_transition(m_stub, &hash0, _access_log, nullptr, &_runtime_config, false, &err_msg); + int error_code = cm_grpc_verify_uarch_step_state_transition(m_stub, &hash0, _access_log, nullptr, &_runtime_config, + false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); std::string result = err_msg; std::string origin("invalid hash"); @@ -2828,12 +2823,13 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_verify_state_transition_null_hash1_test, grp cm_delete_cstring(err_msg); } -BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_verify_state_transition_null_ljog_test, grpc_access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_verify_uarch_step_state_transition_null_ljog_test, + grpc_access_log_machine_fixture) { char *err_msg{}; cm_hash hash0{}; cm_hash hash1{}; int error_code = - cm_grpc_verify_state_transition(m_stub, &hash0, nullptr, &hash1, &_runtime_config, false, &err_msg); + cm_grpc_verify_uarch_step_state_transition(m_stub, &hash0, nullptr, &hash1, &_runtime_config, false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); std::string result = err_msg; std::string origin("invalid access log"); @@ -2841,9 +2837,9 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_verify_state_transition_null_ljog_test, grpc cm_delete_cstring(err_msg); } -BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_verify_access_log_null_log_test, grpc_access_log_machine_fixture) { +BOOST_FIXTURE_TEST_CASE_NOLINT(grpc_verify_uarch_step_log_null_log_test, grpc_access_log_machine_fixture) { char *err_msg{}; - int error_code = cm_grpc_verify_access_log(m_stub, nullptr, &_runtime_config, false, &err_msg); + int error_code = cm_grpc_verify_uarch_step_log(m_stub, nullptr, &_runtime_config, false, &err_msg); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); std::string result = err_msg; std::string origin("invalid access log"); diff --git a/src/test-utils.h b/src/test-utils.h index 23cfebae1..d3f56b876 100644 --- a/src/test-utils.h +++ b/src/test-utils.h @@ -355,7 +355,7 @@ static hash_type calculate_emulator_hash(const std::vector &pmas_f shadow_rom.insert(shadow_rom.end(), shadow_pmas.begin(), shadow_pmas.end()); hash_type shadow_rom_tlb_space_hash; - hash_type shadow_rom_tlb_clint_hash; + hash_type shadow_rom_tlb_uarch_clint_hash; hash_type left; hash_type used_space_hash; @@ -370,35 +370,37 @@ static hash_type calculate_emulator_hash(const std::vector &pmas_f tlb_space_hash = extend_region_hash(tlb_space_hash, PMA_SHADOW_TLB_START, tlb_size_log2, 17); get_concat_hash(h, shadow_rom_space_hash, tlb_space_hash, shadow_rom_tlb_space_hash); // 18 - shadow_rom_tlb_space_hash = extend_region_hash(shadow_rom_tlb_space_hash, 0, 18, 25); - - auto clint_size_log2 = ceil_log2(PMA_CLINT_LENGTH); - auto clint_space_hash = calculate_region_hash(clint, (clint.size() + PMA_PAGE_SIZE - 1) / PMA_PAGE_SIZE, - PMA_PAGE_SIZE_LOG2, clint_size_log2); - clint_space_hash = extend_region_hash(clint_space_hash, PMA_CLINT_START, clint_size_log2, 25); - - get_concat_hash(h, shadow_rom_tlb_space_hash, clint_space_hash, shadow_rom_tlb_clint_hash); // 26 - shadow_rom_tlb_clint_hash = extend_region_hash(shadow_rom_tlb_clint_hash, 0, 26, 29); - - uint64_t htif_size_log2 = ceil_log2(htif.size()); - auto htif_space_hash = calculate_region_hash_2(PMA_HTIF_START, htif, htif_size_log2, 29); - get_concat_hash(h, shadow_rom_tlb_clint_hash, htif_space_hash, left); // 30 + shadow_rom_tlb_space_hash = extend_region_hash(shadow_rom_tlb_space_hash, 0, 18, 22); // uarch shadow state auto shadow_uarch_size_log2 = ceil_log2(PMA_SHADOW_UARCH_STATE_LENGTH); auto shadow_uarch_space_hash = - calculate_region_hash_2(PMA_SHADOW_UARCH_STATE_START, shadow_uarch, shadow_uarch_size_log2, 27); + calculate_region_hash_2(PMA_SHADOW_UARCH_STATE_START, shadow_uarch, shadow_uarch_size_log2, 21); // uarch ram auto uarch_ram_size_log2 = ceil_log2(uarch_ram.size()); auto uarch_ram_space_hash = calculate_region_hash(uarch_ram, (uarch_ram.size() + PMA_PAGE_SIZE - 1) / PMA_PAGE_SIZE, PMA_PAGE_SIZE_LOG2, uarch_ram_size_log2); - uarch_ram_space_hash = extend_region_hash(uarch_ram_space_hash, PMA_UARCH_RAM_START, uarch_ram_size_log2, 27); + uarch_ram_space_hash = extend_region_hash(uarch_ram_space_hash, PMA_UARCH_RAM_START, uarch_ram_size_log2, 21); // shadow uarch state + uarch ram hash_type uarch_space_hash; - get_concat_hash(h, shadow_uarch_space_hash, uarch_ram_space_hash, uarch_space_hash); // 28 - uarch_space_hash = extend_region_hash(uarch_space_hash, PMA_SHADOW_UARCH_STATE_START, 28, 30); + get_concat_hash(h, shadow_uarch_space_hash, uarch_ram_space_hash, uarch_space_hash); // 22 - get_concat_hash(h, left, uarch_space_hash, left); // 31 + hash_type shadow_rom_tlb_uarch_space_hash; + get_concat_hash(h, shadow_rom_tlb_space_hash, uarch_space_hash, shadow_rom_tlb_uarch_space_hash); // 23 + shadow_rom_tlb_uarch_space_hash = extend_region_hash(shadow_rom_tlb_uarch_space_hash, 0, 23, 25); + + auto clint_size_log2 = ceil_log2(PMA_CLINT_LENGTH); + auto clint_space_hash = calculate_region_hash(clint, (clint.size() + PMA_PAGE_SIZE - 1) / PMA_PAGE_SIZE, + PMA_PAGE_SIZE_LOG2, clint_size_log2); + clint_space_hash = extend_region_hash(clint_space_hash, PMA_CLINT_START, clint_size_log2, 25); + + get_concat_hash(h, shadow_rom_tlb_uarch_space_hash, clint_space_hash, shadow_rom_tlb_uarch_clint_hash); // 26 + shadow_rom_tlb_uarch_clint_hash = extend_region_hash(shadow_rom_tlb_uarch_clint_hash, 0, 26, 29); + + uint64_t htif_size_log2 = ceil_log2(htif.size()); + auto htif_space_hash = calculate_region_hash_2(PMA_HTIF_START, htif, htif_size_log2, 29); + get_concat_hash(h, shadow_rom_tlb_uarch_clint_hash, htif_space_hash, left); // 30 + left = extend_region_hash(left, 0, 30, 31); uint64_t ram_size_log2 = ceil_log2(ram.size()); auto ram_space_hash = calculate_region_hash_2(PMA_RAM_START, ram, ram_size_log2, 31); diff --git a/src/tests/htif-yield.lua b/src/tests/htif-yield.lua index 81bcfe409..dd10da3f5 100755 --- a/src/tests/htif-yield.lua +++ b/src/tests/htif-yield.lua @@ -19,9 +19,6 @@ where options are: --uarch-ram-image= name of file containing microarchitecture RAM image. - --uarch-ram-length= - set microarchitecture RAM length. - ]=], arg[0] )) @@ -29,7 +26,6 @@ where options are: end local uarch = false -local uarch_ram_length = nil local uarch_ram_image_filename = nil -- List of supported options @@ -63,14 +59,6 @@ local options = { return true end, }, - { - "^%-%-uarch%-ram%-length%=(.+)$", - function(n) - if not n then return false end - uarch_ram_length = assert(util.parse_number(n), "invalid microarchitecture RAM length " .. n) - return true - end, - }, { ".*", function(all) error("unrecognized option " .. all .. ". Use --help to obtain a list of supported options.") end, @@ -102,11 +90,7 @@ local config_base = { }, } -if uarch_ram_length then config_base.uarch = { ram = { length = uarch_ram_length } } end -if uarch_ram_image_filename then - assert(uarch_ram_length, "--uarch-ram-length was not specified") - config_base.uarch.ram.image_filename = uarch_ram_image_filename -end +if uarch_ram_image_filename then config_base.uarch.ram.image_filename = uarch_ram_image_filename end local YIELD_MANUAL = cartesi.machine.HTIF_YIELD_MANUAL local YIELD_AUTOMATIC = cartesi.machine.HTIF_YIELD_AUTOMATIC @@ -145,7 +129,7 @@ local function run_machine_with_uarch(machine) while true do local ubr = machine:run_uarch() if ubr == cartesi.UARCH_BREAK_REASON_UARCH_HALTED then - machine:reset_uarch_state() + machine:reset_uarch() if machine:read_iflags_H() then -- iflags.H was set during the last mcycle return cartesi.BREAK_REASON_HALTED diff --git a/src/tests/log-with-mtime-transition.lua b/src/tests/log-with-mtime-transition.lua index 0e4c17f99..4e0477619 100644 --- a/src/tests/log-with-mtime-transition.lua +++ b/src/tests/log-with-mtime-transition.lua @@ -16,14 +16,11 @@ local config = { rom = { image_filename = "", }, - uarch = { - ram = { length = 1 << 19, image_filename = test_util.create_test_uarch_program() }, - }, } local machine = cartesi.machine(config) local old_hash = machine:get_root_hash() -local access_log = machine:step_uarch({ proofs = true }) +local access_log = machine:log_uarch_step({ proofs = true }) local new_hash = machine:get_root_hash() -cartesi.machine.verify_state_transition(old_hash, access_log, new_hash, {}) +cartesi.machine.verify_uarch_step_state_transition(old_hash, access_log, new_hash, {}) print("ok") diff --git a/src/tests/machine-bind.lua b/src/tests/machine-bind.lua index e17de9ab2..6ba877f8f 100755 --- a/src/tests/machine-bind.lua +++ b/src/tests/machine-bind.lua @@ -275,10 +275,10 @@ local pmas_file_names = { "0000000002000000--00000000000c0000.bin", -- clint "0000000040008000--0000000000001000.bin", -- htif "0000000080000000--0000000000100000.bin", -- ram - "0000000070000000--0000000000001000.bin", -- shadow uarch state - "0000000078000000--0000000000001000.bin", -- uarch ram + "0000000000400000--0000000000001000.bin", -- shadow uarch state + "0000000000600000--0000000000200000.bin", -- uarch ram } -local pmas_sizes = { 4096, 61440, 4096, 24576, 786432, 4096, 1048576, 4096, 4096 } +local pmas_sizes = { 4096, 61440, 4096, 24576, 786432, 4096, 1048576, 4096, 0x200000 } local remote @@ -410,7 +410,7 @@ end) print("\n\ntesting get_x_uarch_address function binding") do_test("should return address value for uarch x registers", function() - local SHADOW_UARCH_XBASE = test_util.PMA_SHADOW_UARCH_STATE_START + 32 + local SHADOW_UARCH_XBASE = test_util.PMA_SHADOW_UARCH_STATE_START + 24 local module = cartesi if machine_type == "grpc" then if not remote then remote = connect() end @@ -614,7 +614,7 @@ do_test("mcycle value should match", function(machine) local log_type = {} local uarch_cycle_initial_value = machine:read_csr("uarch_cycle") - machine:step_uarch(log_type) + machine:log_uarch_step(log_type) -- Check mcycle increment local uarch_cycle_current_value = machine:read_csr("uarch_cycle") @@ -711,8 +711,8 @@ do_test("written and read values should match", function(machine) assert(memory_read == "mydataol12345678") end) -print("\n\n dump log to console") -do_test("dumped log content should match", function() +print("\n\n dump step log to console") +do_test("dumped step log content should match", function() -- Dump log and check values local lua_code = [[ " local cartesi = require 'cartesi' @@ -726,12 +726,12 @@ do_test("dumped log content should match", function() ram = {length = 1 << 20}, rom = {image_filename = test_util.images_path .. 'rom.bin'}, uarch = { - ram = { length = 1 << 16, image_filename = uarch_ram_path } + ram = { image_filename = uarch_ram_path } } } os.remove(uarch_ram_path) local log_type = {proofs = false, annotations = true} - local log = machine:step_uarch(log_type) + local log = machine:log_uarch_step(log_type) cartesi_util.dump_log(log, io.stdout) " 2>&1]] @@ -739,16 +739,16 @@ do_test("dumped log content should match", function() local output = p:read(2000) p:close() local expected_output = "begin step\n" - .. " 1: read uarch.cycle@0x70000008(1879048200): 0x0(0)\n" - .. " 2: read uarch.halt_flag@0x70000000(1879048192): 0x0(0)\n" - .. " 3: read uarch.pc@0x70000010(1879048208): 0x78000000(2013265920)\n" - .. " 4: read memory@0x78000000(2013265920): 0x700002b707b00513(8070453517379175699)\n" + .. " 1: read uarch.cycle@0x400008(4194312): 0x0(0)\n" + .. " 2: read uarch.halt_flag@0x400000(4194304): 0x0(0)\n" + .. " 3: read uarch.pc@0x400010(4194320): 0x600000(6291456)\n" + .. " 4: read memory@0x600000(6291456): 0x4002b707b00513(18017383640728851)\n" .. " begin addi\n" - .. " 5: read uarch.x@0x70000020(1879048224): 0x0(0)\n" - .. " 6: write uarch.x@0x70000070(1879048304): 0x0(0) -> 0x7b(123)\n" - .. " 7: write uarch.pc@0x70000010(1879048208): 0x78000000(2013265920) -> 0x78000004(2013265924)\n" + .. " 5: read uarch.x@0x400018(4194328): 0x0(0)\n" + .. " 6: write uarch.x@0x400068(4194408): 0x0(0) -> 0x7b(123)\n" + .. " 7: write uarch.pc@0x400010(4194320): 0x600000(6291456) -> 0x600004(6291460)\n" .. " end addi\n" - .. " 8: write uarch.cycle@0x70000008(1879048200): 0x0(0) -> 0x1(1)\n" + .. " 8: write uarch.cycle@0x400008(4194312): 0x0(0) -> 0x1(1)\n" .. "end step\n" print("Output of dump log:") @@ -766,10 +766,10 @@ do_test("machine step should pass verifications", function(machine) module = remote end local initial_hash = machine:get_root_hash() - local log = machine:step_uarch({ proofs = true, annotations = true }) + local log = machine:log_uarch_step({ proofs = true, annotations = true }) local final_hash = machine:get_root_hash() - module.machine.verify_state_transition(initial_hash, log, final_hash, {}) - module.machine.verify_access_log(log, {}) + module.machine.verify_uarch_step_state_transition(initial_hash, log, final_hash, {}) + module.machine.verify_uarch_step_log(log, {}) end) do_test("step when uarch cycle is max", function(machine) @@ -781,12 +781,12 @@ do_test("step when uarch cycle is max", function(machine) machine:write_uarch_cycle(MAX_UARCH_CYCLE) assert(machine:read_uarch_cycle() == MAX_UARCH_CYCLE) local initial_hash = machine:get_root_hash() - local log = machine:step_uarch({ proofs = true, annotations = true }) + local log = machine:log_uarch_step({ proofs = true, annotations = true }) assert(machine:read_uarch_cycle() == MAX_UARCH_CYCLE) local final_hash = machine:get_root_hash() assert(final_hash == initial_hash) - module.machine.verify_state_transition(initial_hash, log, final_hash, {}) - module.machine.verify_access_log(log, {}) + module.machine.verify_uarch_step_state_transition(initial_hash, log, final_hash, {}) + module.machine.verify_uarch_step_log(log, {}) end) local uarch_proof_step_program = { @@ -801,7 +801,7 @@ local uarch_proof_step_program = { test_util.make_do_test(build_machine, machine_type, { uarch = { - ram = { length = 1 << 16, image_filename = test_util.create_test_uarch_program(uarch_proof_step_program) }, + ram = { image_filename = test_util.create_test_uarch_program(uarch_proof_step_program) }, }, })("merkle tree must be consistent when stepping alternating with and without proofs", function(machine) local t0 = 5 @@ -811,41 +811,32 @@ test_util.make_do_test(build_machine, machine_type, { local with_proofs = { proofs = true } local without_proofs = {} - machine:step_uarch(with_proofs) -- auipc t0,0x0 - machine:step_uarch(with_proofs) -- addi t0,t0,256 # 0x100 + machine:log_uarch_step(with_proofs) -- auipc t0,0x0 + machine:log_uarch_step(with_proofs) -- addi t0,t0,256 # 0x100 assert(machine:read_uarch_x(t0) == uarch_ram_start + 0x100) - machine:step_uarch(with_proofs) -- li t1,0xca + machine:log_uarch_step(with_proofs) -- li t1,0xca assert(machine:read_uarch_x(t1) == 0xca) - machine:step_uarch(with_proofs) -- li t2,0xfe + machine:log_uarch_step(with_proofs) -- li t2,0xfe assert(machine:read_uarch_x(t2) == 0xfe) -- sd and assert stored correctly - machine:step_uarch(with_proofs) -- sd t1,0(t0) [0xca] + machine:log_uarch_step(with_proofs) -- sd t1,0(t0) [0xca] assert(string.unpack("I8", machine:read_memory(uarch_ram_start + 0x100, 8)) == 0xca) -- sd and assert stored correctly - machine:step_uarch(without_proofs) -- t2,0(t0) [0xfe] + machine:log_uarch_step(without_proofs) -- t2,0(t0) [0xfe] assert(string.unpack("I8", machine:read_memory(uarch_ram_start + 0x100, 8)) == 0xfe) -- This step should run successfully -- The previous unproven step should have marked the updated pages dirty, allowing -- the tree to be updated correctly in the next proved step - machine:step_uarch(with_proofs) -- sd t1,0(t0) [0xca] + machine:log_uarch_step(with_proofs) -- sd t1,0(t0) [0xca] assert(string.unpack("I8", machine:read_memory(uarch_ram_start + 0x100, 8)) == 0xca) end) -test_util.make_do_test(build_machine, machine_type, { uarch = { ram = { length = 0x1000 } } })( - "It should initialize uarch ram with zeros when only uarch ram length is provided", - function(machine) - local m = machine:read_memory(test_util.PMA_UARCH_RAM_START, 0x1000) - assert(m == string.rep("\0", 0x1000)) - assert(machine:read_uarch_ram_length() == 0x1000) - end -) - test_util.make_do_test(build_machine, machine_type, { uarch = { - ram = { length = 0x1000, image_filename = test_util.create_test_uarch_program(uarch_proof_step_program) }, + ram = { image_filename = test_util.create_test_uarch_program(uarch_proof_step_program) }, }, })("It should load the uarch ram image from a file", function(machine) local expected_ram_image = "" @@ -857,7 +848,6 @@ test_util.make_do_test(build_machine, machine_type, { local ram_image = machine:read_memory(test_util.PMA_UARCH_RAM_START, 0x1000) assert(ram_image == expected_ram_image) - assert(machine:read_uarch_ram_length() == 0x1000) end) test_util.make_do_test(build_machine, machine_type, { processor = { mcycle = 1 }, uarch = {} })( @@ -871,7 +861,175 @@ test_util.make_do_test(build_machine, machine_type, { processor = { mcycle = 1 } machine:run_uarch() assert(machine:read_mcycle() == 2) - assert(machine:read_uarch_ram_length() == test_util.PMA_UARCH_RAM_LENGTH) + end +) + +print("\n\n testing reset uarch") + +local test_reset_uarch_config = { + processor = { + halt_flag = true, + cycle = 1, + pc = 0, + x = get_cpu_uarch_xreg_test_values(), + }, +} + +local function test_reset_uarch(machine, with_log, with_proofs, with_annotations) + -- assert initial fixture state + assert(machine:read_uarch_halt_flag() == true) + assert(machine:read_uarch_cycle() == 1) + assert(machine:read_uarch_pc() == 0) + local xreg_test_values = get_cpu_uarch_xreg_test_values() + for i = 1, 31 do + assert(machine:read_uarch_x(i) == xreg_test_values[i]) + end + -- modify uarch ram + local gibberish = "mydataol12345678" + machine:write_memory(cartesi.UARCH_RAM_START_ADDRESS, gibberish, #gibberish) + assert(machine:read_memory(cartesi.UARCH_RAM_START_ADDRESS, #gibberish) == gibberish) + -- assert uarch state hash is not pristine + local uarch_state_hash = test_util.calculate_uarch_state_hash(machine) + assert(uarch_state_hash ~= cartesi.UARCH_PRISTINE_STATE_HASH) + -- reset uarch state + if with_log then + local log = machine:log_uarch_reset({ proofs = with_proofs, annotations = with_annotations }) + assert(#log.accesses == 1) + local access = log.accesses[1] + if with_proofs then + assert(access.proof ~= nil) + assert(test_util.check_proof(access.proof), "uarch reset proof failed") + else + assert(access.proof == nil) + end + assert(access.address == cartesi.UARCH_SHADOW_START_ADDRESS) + assert(access.log2_size == cartesi.UARCH_STATE_LOG2_SIZE) + assert(access.written_hash == cartesi.UARCH_PRISTINE_STATE_HASH) + assert(access.written == nil) + assert(access.read_hash ~= nil) + assert(access.read == nil) + else + machine:reset_uarch() + end + --- assert registers are reset to pristine values + assert(machine:read_uarch_halt_flag() == false) + assert(machine:read_uarch_cycle() == 0) + assert(machine:read_uarch_pc() == test_util.PMA_UARCH_RAM_START) + for i = 1, 31 do + assert(machine:read_uarch_x(i) == 0) + end + -- assert that gibberish was removed from uarch ram + assert(machine:read_memory(cartesi.UARCH_RAM_START_ADDRESS, #gibberish) ~= gibberish) + -- compute current uarch state hash + uarch_state_hash = test_util.calculate_uarch_state_hash(machine) + -- assert computed and pristine hash match + assert(uarch_state_hash == cartesi.UARCH_PRISTINE_STATE_HASH) +end + +test_util.make_do_test(build_machine, machine_type, { uarch = test_reset_uarch_config })( + "Testing reset_uarch without logging", + function(machine) test_reset_uarch(machine, false, false, false) end +) +for _, with_proofs in ipairs({ true, false }) do + for _, with_annotations in ipairs({ true, false }) do + test_util.make_do_test(build_machine, machine_type, { uarch = test_reset_uarch_config })( + "Testing reset_uarch with logging, proofs=" + .. tostring(with_proofs) + .. ", annotations=" + .. tostring(with_annotations), + function(machine) test_reset_uarch(machine, true, with_proofs, withj_annotations) end + ) + end +end + +test_util.make_do_test(build_machine, machine_type, { uarch = test_reset_uarch_config })( + "Testing verify_uarch_reset_state_transition", + function(machine) + local module = cartesi + if machine_type ~= "local" then module = remote end + local initial_hash = machine:get_root_hash() + local log = machine:log_uarch_reset({ proofs = true, annotations = true }) + local final_hash = machine:get_root_hash() + -- verify happy path + module.machine.verify_uarch_reset_state_transition(initial_hash, log, final_hash, {}) + -- verifying incorrect initial hash + local wrong_hash = string.rep("0", 32) + local _, err = pcall(module.machine.verify_uarch_reset_state_transition, wrong_hash, log, final_hash, {}) + assert(err:match("mismatch in root hash before replay")) + -- verifying incorrect final hash + _, err = pcall(module.machine.verify_uarch_reset_state_transition, initial_hash, log, wrong_hash, {}) + assert(err:match("mismatch in root hash after replay")) + end +) + +test_util.make_do_test(build_machine, machine_type, { uarch = test_reset_uarch_config })( + "Testing verify_uarch_reset_log", + function(machine) + local module = cartesi + if machine_type ~= "local" then module = remote end + local log = machine:log_uarch_reset({ proofs = true, annotations = true }) + module.machine.verify_uarch_reset_log(log, {}) + end +) + +test_util.make_do_test(build_machine, machine_type, { uarch = test_reset_uarch_config })( + "Dump of log produced by log_uarch_reset should match", + function(machine) + local module = cartesi + if machine_type ~= "local" then module = remote end + local log = machine:log_uarch_reset({ proofs = true, annotations = true }) + local expected_dump = "begin reset uarch state\n" + .. " hash 2f28c75a\n" + .. ' 1: write uarch_state@0x400000(4194304): hash:"aa5c42d9"(2^22 bytes) -> hash:"d9809f3f"(2^22 bytes)\n' + .. "end reset uarch state\n" + + local tmpname = os.tmpname() + __gc = function() os.remove(tmpname) end + local tmp = io.open(tmpname, "w+") + util.dump_log(log, tmp) + tmp:seek("set", 0) + local actual_dump = tmp:read("*all") + + print("Output of reset_uarch log dump:") + print("--------------------------") + print(actual_dump) + print("--------------------------") + assert( + actual_dump == expected_dump, + "Dump of uarch_reset_state does not match expected output:\n" .. expected_dump + ) + end +) + +test_util.make_do_test(build_machine, machine_type, { uarch = test_reset_uarch_config })( + "Log uarch reset with large_data option set must have consistent read and written data", + function(machine) + local module = cartesi + if machine_type ~= "local" then + module = remote + -- return -- TODO: fix grpc and jsorpc failing due to large data + end + -- reset uarch and get log + local log = machine:log_uarch_reset({ proofs = true, annotations = true, large_data = true }) + assert(#log.accesses == 1, "log should have 1 access") + local access = log.accesses[1] + -- in debug mode, the log must include read and written data + assert(access.read ~= nil, "read data should not be nil") + assert(access.written ~= nil, "written data should not be nil") + -- verify returned log + module.machine.verify_uarch_reset_log(log, {}) + -- save logged read and written data + local original_read = access.read + local original_written = access.written + -- tamper with read data to produce a hash mismatch + access.read = "X" .. access.read:sub(2) + local _, err = pcall(module.machine.verify_uarch_reset_log, log, {}) + assert(err:match("hash of read data and read hash at access 1 does not match read hash")) + -- restore read and change written data to produce a hash mismatch + access.read = original_read + access.written = "X" .. access.written:sub(2) + _, err = pcall(module.machine.verify_uarch_reset_log, log, {}) + assert(err:match("hash of written data and written hash at access 1 does not match written hash")) end ) diff --git a/src/tests/machine-test.lua b/src/tests/machine-test.lua index 813176202..a1c936a78 100755 --- a/src/tests/machine-test.lua +++ b/src/tests/machine-test.lua @@ -160,8 +160,8 @@ local pmas_file_names = { "0000000002000000--00000000000c0000.bin", -- clint "0000000040008000--0000000000001000.bin", -- htif "0000000080000000--0000000000100000.bin", -- ram - "0000000070000000--0000000000001000.bin", -- shadow uarch state - "0000000078000000--0000000000080000.bin", -- uarch ram + "0000000000400000--0000000000001000.bin", -- shadow uarch state + "0000000000600000--0000000000200000.bin", -- uarch ram } local remote @@ -271,7 +271,7 @@ do_test("machine root hash after step one should match", function(machine) -- Perform step, dump address space to file, calculate emulator root hash -- and check if maches local log_type = {} - machine:step_uarch(log_type) + machine:log_uarch_step(log_type) local root_hash_step1 = machine:get_root_hash() machine:dump_pmas() @@ -289,7 +289,7 @@ end) print("\n\ntesting proof after step one") do_test("proof check should pass", function(machine) local log_type = {} - machine:step_uarch(log_type) + machine:log_uarch_step(log_type) -- Dump RAM memory to file, calculate hash of file -- get proof of ram using get_proof and check if @@ -514,7 +514,7 @@ do_test("register values should match", function(machine) local uarch_cycle_before = machine:read_uarch_cycle() local log_type = {} - machine:step_uarch(log_type) + machine:log_uarch_step(log_type) local uarch_pc_after = machine:read_uarch_pc() local uarch_cycle_after = machine:read_uarch_cycle() diff --git a/src/tests/mcycle-overflow.lua b/src/tests/mcycle-overflow.lua index 0463ffdb6..a4d0bbf60 100755 --- a/src/tests/mcycle-overflow.lua +++ b/src/tests/mcycle-overflow.lua @@ -86,7 +86,7 @@ end) for _, proofs in ipairs({ true, false }) do do_test("machine step should do nothing on max mcycle [proofs=" .. tostring(proofs) .. "]", function(machine) machine:write_uarch_cycle(MAX_UARCH_CYCLE) - local log = machine:step_uarch({ proofs = proofs }) + local log = machine:log_uarch_step({ proofs = proofs }) assert(machine:read_uarch_cycle() == MAX_UARCH_CYCLE) assert(#log.accesses == 1) assert(log.accesses[1].type == "read") diff --git a/src/tests/mtime-interrupt.lua b/src/tests/mtime-interrupt.lua index dcc2b03a3..bcb602901 100755 --- a/src/tests/mtime-interrupt.lua +++ b/src/tests/mtime-interrupt.lua @@ -58,9 +58,9 @@ do_test("machine:run should interrupt for mtime", function(machine) check_state(machine) end) -test_util.disabled_test("machine:step_uarch should interrupt for mtime", function(machine) +test_util.disabled_test("machine:log_uarch_step should interrupt for mtime", function(machine) for _ = 1, EXPECTED_MCYCLE do - machine:step_uarch({}) + machine:log_uarch_step({}) if machine:read_iflags_H() then break end end check_state(machine) diff --git a/src/tests/util.lua b/src/tests/util.lua index b90c2bbcd..3e49821bd 100644 --- a/src/tests/util.lua +++ b/src/tests/util.lua @@ -104,7 +104,7 @@ local test_util = { test_util.uarch_programs = { halt = { - 0x700002b7, -- li t0,UARCH_HALT_FLAG_SHADDOW_ADDR Address of uarch halt flag + 0x004002b7, -- li t0,UARCH_HALT_FLAG_SHADDOW_ADDR Address of uarch halt flag 0x00100313, -- li t1,1 UARCH_MMIO_HALT_VALUE 0x0062b023, -- sd t1,0(t0) Halt uarch }, @@ -246,6 +246,24 @@ function test_util.calculate_root_hash(data, log2_size) end end +function test_util.calculate_uarch_state_hash(machine) + local shadow_data = machine:read_memory(cartesi.UARCH_SHADOW_START_ADDRESS, cartesi.UARCH_SHADOW_LENGTH) + local ram_data = machine:read_memory(cartesi.UARCH_RAM_START_ADDRESS, cartesi.UARCH_RAM_LENGTH) + local shadow_hash = test_util.calculate_region_hash_2( + cartesi.UARCH_SHADOW_START_ADDRESS, + shadow_data, + math.log(#shadow_data, 2), + cartesi.UARCH_STATE_LOG2_SIZE - 1 + ) + local ram_hash = test_util.calculate_region_hash_2( + cartesi.UARCH_RAM_START_ADDRESS, + ram_data, + math.log(#ram_data, 2), + cartesi.UARCH_STATE_LOG2_SIZE - 1 + ) + return cartesi.keccak(shadow_hash, ram_hash) +end + -- Taking memory region in buffer data_buffer, and occuping data_number_of_pages -- of page size page_log2_size -- calculate merke hash for region of up to tree_log2_size, @@ -333,10 +351,10 @@ test_util.PMA_SHADOW_TLB_LENGTH = 0x6000 test_util.PMA_CLINT_START = 0x2000000 test_util.PMA_CLINT_LENGTH = 0xC0000 test_util.PMA_HTIF_START = 0x40008000 -test_util.PMA_SHADOW_UARCH_STATE_START = 0x70000000 +test_util.PMA_SHADOW_UARCH_STATE_START = 0x400000 test_util.PMA_SHADOW_UARCH_STATE_LENGTH = 0x1000 -test_util.PMA_UARCH_RAM_START = 0x78000000 -test_util.PMA_UARCH_RAM_LENGTH = 0x80000 +test_util.PMA_UARCH_RAM_START = 0x600000 +test_util.PMA_UARCH_RAM_LENGTH = 0x200000 test_util.PMA_RAM_START = 0x80000000 test_util.PMA_PAGE_SIZE_LOG2 = 12 test_util.PMA_PAGE_SIZE = 1 << test_util.PMA_PAGE_SIZE_LOG2 @@ -384,28 +402,12 @@ function test_util.calculate_emulator_hash(test_path, pmas_files) tlb_space_hash = extend_region_hash(tlb_space_hash, test_util.PMA_SHADOW_TLB_START, tlb_size_log2, 17) local shadow_rom_tlb_space_hash = cartesi.keccak(shadow_rom_space_hash, tlb_space_hash) -- 18 - shadow_rom_tlb_space_hash = extend_region_hash(shadow_rom_tlb_space_hash, 0, 18, 25) - - local clint_size_log2 = ceil_log2(test_util.PMA_CLINT_LENGTH) - local clint_space_hash = calculate_region_hash( - clint, - (#clint + test_util.PMA_PAGE_SIZE - 1) // test_util.PMA_PAGE_SIZE, - test_util.PMA_PAGE_SIZE_LOG2, - clint_size_log2 - ) - clint_space_hash = extend_region_hash(clint_space_hash, test_util.PMA_CLINT_START, clint_size_log2, 25) - - local shadow_rom_tlb_clint_hash = cartesi.keccak(shadow_rom_tlb_space_hash, clint_space_hash) -- 26 - shadow_rom_tlb_clint_hash = extend_region_hash(shadow_rom_tlb_clint_hash, 0, 26, 29) - - local htif_size_log2 = ceil_log2(#htif) - local htif_space_hash = calculate_region_hash_2(test_util.PMA_HTIF_START, htif, htif_size_log2, 29) - local left = cartesi.keccak(shadow_rom_tlb_clint_hash, htif_space_hash) -- 30 + shadow_rom_tlb_space_hash = extend_region_hash(shadow_rom_tlb_space_hash, 0, 18, 22) -- shadow uarch state local shadow_uarch_size_log2 = ceil_log2(#shadow_uarch) local shadow_uarch_space_hash = - calculate_region_hash_2(test_util.PMA_SHADOW_UARCH_STATE_START, shadow_uarch, shadow_uarch_size_log2, 27) + calculate_region_hash_2(test_util.PMA_SHADOW_UARCH_STATE_START, shadow_uarch, shadow_uarch_size_log2, 21) -- uarch ram local uarch_ram_size_log2 = ceil_log2(#uarch_ram) local uarch_ram_space_hash = calculate_region_hash( @@ -415,12 +417,28 @@ function test_util.calculate_emulator_hash(test_path, pmas_files) uarch_ram_size_log2 ) uarch_ram_space_hash = - extend_region_hash(uarch_ram_space_hash, test_util.PMA_UARCH_RAM_START, uarch_ram_size_log2, 27) + extend_region_hash(uarch_ram_space_hash, test_util.PMA_UARCH_RAM_START, uarch_ram_size_log2, 21) -- shadow uarch state + uarch ram - local uarch_space_hash = cartesi.keccak(shadow_uarch_space_hash, uarch_ram_space_hash) -- 28 - uarch_space_hash = test_util.extend_region_hash(uarch_space_hash, test_util.PMA_SHADOW_UARCH_STATE_START, 28, 30) + local uarch_space_hash = cartesi.keccak(shadow_uarch_space_hash, uarch_ram_space_hash) -- 22 + local shadow_rom_tlb_uarch_space_hash = cartesi.keccak(shadow_rom_tlb_space_hash, uarch_space_hash) -- 23 + shadow_rom_tlb_uarch_space_hash = test_util.extend_region_hash(shadow_rom_tlb_uarch_space_hash, 0, 23, 25) + + local clint_size_log2 = ceil_log2(test_util.PMA_CLINT_LENGTH) + local clint_space_hash = calculate_region_hash( + clint, + (#clint + test_util.PMA_PAGE_SIZE - 1) // test_util.PMA_PAGE_SIZE, + test_util.PMA_PAGE_SIZE_LOG2, + clint_size_log2 + ) + clint_space_hash = extend_region_hash(clint_space_hash, test_util.PMA_CLINT_START, clint_size_log2, 25) - left = cartesi.keccak(left, uarch_space_hash) -- 31 + local shadow_rom_tlb_uarch_clint_hash = cartesi.keccak(shadow_rom_tlb_uarch_space_hash, clint_space_hash) -- 26 + shadow_rom_tlb_uarch_clint_hash = extend_region_hash(shadow_rom_tlb_uarch_clint_hash, 0, 26, 29) + + local htif_size_log2 = ceil_log2(#htif) + local htif_space_hash = calculate_region_hash_2(test_util.PMA_HTIF_START, htif, htif_size_log2, 29) + local left = cartesi.keccak(shadow_rom_tlb_uarch_clint_hash, htif_space_hash) -- 30 + left = extend_region_hash(left, 0, 30, 31) local ram_size_log2 = ceil_log2(#ram) local ram_space_hash = calculate_region_hash_2(test_util.PMA_RAM_START, ram, ram_size_log2, 31) diff --git a/src/uarch-bridge.h b/src/uarch-bridge.h index dcb593026..9f98d4683 100644 --- a/src/uarch-bridge.h +++ b/src/uarch-bridge.h @@ -359,8 +359,6 @@ class uarch_bridge { switch (static_cast(paddr - PMA_SHADOW_UARCH_STATE_START)) { case shadow_uarch_state_csr::halt_flag: return "uarch.halt_flag"; - case shadow_uarch_state_csr::ram_length: - return "uarch.ram_length"; default: break; } @@ -662,9 +660,6 @@ class uarch_bridge { case shadow_uarch_state_csr::halt_flag: *data = us.halt_flag; return true; - case shadow_uarch_state_csr::ram_length: - *data = us.ram.get_length(); - return true; default: break; } diff --git a/src/uarch-config.h b/src/uarch-config.h index 2ebc63335..75c431501 100644 --- a/src/uarch-config.h +++ b/src/uarch-config.h @@ -28,7 +28,6 @@ namespace cartesi { /// \brief RAM state configuration for the microarchitecture struct uarch_ram_config final { // NOLINT(bugprone-exception-escape) - uint64_t length{0}; ///< RAM length std::string image_filename{}; ///< RAM image file name }; diff --git a/src/uarch-constants.h b/src/uarch-constants.h index 257869b2d..980324584 100644 --- a/src/uarch-constants.h +++ b/src/uarch-constants.h @@ -17,11 +17,50 @@ #ifndef UARCH_CONSTANTS_H #define UARCH_CONSTANTS_H +#include "meta.h" +#include "pma-constants.h" #include #include namespace cartesi { +/// \brief uarch state constants +enum uarch_state_constants : uint64_t { + UARCH_STATE_START_ADDRESS = + EXPAND_UINT64_C(UARCH_STATE_START_ADDRESS_DEF), ///< Start address of the uarch state: shadow + ram + UARCH_STATE_LOG2_SIZE = EXPAND_UINT64_C(UARCH_STATE_LOG2_SIZE_DEF), ///< Log2 size of the uarch state: shadow + ram + UARCH_STATE_CHILD_LOG2_SIZE = UARCH_STATE_LOG2_SIZE - 1, ///< Log2 size of a uarch state child: shadow or ram + UARCH_SHADOW_START_ADDRESS = + EXPAND_UINT64_C(PMA_SHADOW_UARCH_STATE_START_DEF), ///< Start address of the shadow uarch state + UARCH_SHADOW_LENGTH = EXPAND_UINT64_C(PMA_SHADOW_UARCH_STATE_LENGTH_DEF), ///< Length of the shadow uarch state + UARCH_RAM_START_ADDRESS = EXPAND_UINT64_C(PMA_UARCH_RAM_START_DEF), ///< Start address of the uarch ram + UARCH_RAM_LENGTH = EXPAND_UINT64_C(PMA_UARCH_RAM_LENGTH_DEF), ///< Length of the uarch ram + UARCH_STATE_ALIGN_MASK = (EXPAND_UINT64_C(1) << UARCH_STATE_LOG2_SIZE) - 1, ///< Mask for uarch state alignment + UARCH_STATE_MASK = ~UARCH_STATE_ALIGN_MASK, ///< Mask for uarch state address space + UARCH_STATE_CHILD_ALIGN_MASK = + (EXPAND_UINT64_C(1) << UARCH_STATE_CHILD_LOG2_SIZE) - 1 ///< Mask for uarch state child alignment +}; + +static_assert((UARCH_STATE_START_ADDRESS & UARCH_STATE_ALIGN_MASK) == 0, + "UARCH_STATE_START_ADDRESS must be aligned to UARCH_STATE_LOG2_SIZE"); +static_assert((UARCH_SHADOW_START_ADDRESS & UARCH_STATE_MASK) == UARCH_STATE_START_ADDRESS, + "UARCH_SHADOW_START_ADDRESS must be within uarch state address space"); +static_assert((UARCH_RAM_START_ADDRESS & UARCH_STATE_MASK) == UARCH_STATE_START_ADDRESS, + "UARCH_RAM_START_ADDRESS must be within uarch state address space"); +static_assert((UARCH_SHADOW_START_ADDRESS & UARCH_STATE_CHILD_ALIGN_MASK) == 0, + "UARCH_SHADOW_START_ADDRESS must be aligned to UARCH_STATE_LOG2_SIZE -1"); +static_assert((UARCH_RAM_START_ADDRESS & UARCH_STATE_CHILD_ALIGN_MASK) == 0, + "UARCH_RAM_START_ADDRESS must be aligned to UARCH_STATE_LOG2_SIZE -1"); +static_assert(UARCH_RAM_LENGTH <= (static_cast(1) << UARCH_STATE_CHILD_LOG2_SIZE), + "UARCH_RAM_LENGTH is too big"); +static_assert(UARCH_SHADOW_LENGTH <= (static_cast(1) << UARCH_STATE_CHILD_LOG2_SIZE), + "UARCH_SHADOW_LENGTH is too big"); +static_assert(UARCH_SHADOW_START_ADDRESS < UARCH_RAM_START_ADDRESS, + "UARCH_SHADOW_START_ADDRESS must be smaller than UARCH_RAN_START_ADDRESS"); +static_assert((UARCH_SHADOW_LENGTH & (PMA_PAGE_SIZE - 1)) == 0, + "UARCH_SHADOW_LENGTH must be multiple of PMA_PAGE_SIZE"); +static_assert((UARCH_RAM_LENGTH & (PMA_PAGE_SIZE - 1)) == 0, "UARCH_RAM_LENGTH must be multiple of PMA_PAGE_SIZE"); + /// \briefThe value that halts the microarchitecture when written to shadow_state_csr::uarch_halt_flag: constexpr uint64_t uarch_halt_flag_halt_value = UARCH_HALT_FLAG_HALT_VALUE_DEF; diff --git a/src/uarch-interpret.cpp b/src/uarch-interpret.cpp index b2f49afd6..d48b3996f 100644 --- a/src/uarch-interpret.cpp +++ b/src/uarch-interpret.cpp @@ -19,7 +19,7 @@ namespace cartesi { -uarch_interpreter_break_reason uarch_interpret(uarch_state_access &a, uint64_t cycle_end) { +uarch_interpreter_break_reason uarch_interpret(uarch_step_state_access &a, uint64_t cycle_end) { uint64_t cycle = a.read_cycle(); if (cycle_end < cycle) { throw std::invalid_argument{"uarch_cycle is past"}; diff --git a/src/uarch-interpret.h b/src/uarch-interpret.h index 75165e86e..914538ca6 100644 --- a/src/uarch-interpret.h +++ b/src/uarch-interpret.h @@ -17,14 +17,14 @@ #ifndef UARCH_INTERPRET_H #define UARCH_INTERPRET_H -#include "uarch-state-access.h" +#include "uarch-step-state-access.h" namespace cartesi { enum class uarch_interpreter_break_reason : int { reached_target_cycle, uarch_halted }; // Run the microarchitecture interpreter until cycle hits a target or a fixed point is reached -uarch_interpreter_break_reason uarch_interpret(uarch_state_access &a, uint64_t uarch_cycle_end); +uarch_interpreter_break_reason uarch_interpret(uarch_step_state_access &a, uint64_t uarch_cycle_end); } // namespace cartesi diff --git a/src/uarch-machine.cpp b/src/uarch-machine.cpp index 18cd887d5..edfaa4397 100644 --- a/src/uarch-machine.cpp +++ b/src/uarch-machine.cpp @@ -16,18 +16,14 @@ #include +#include "shadow-uarch-state-factory.h" +#include "uarch-constants.h" #include "uarch-machine.h" namespace cartesi { using namespace std::string_literals; -/// \brief Embedded uarch ram image. Symbol created by "xxd -i uarch-ram.bin". -extern "C" const unsigned char embedded_uarch_ram[]; - -/// \brief Length of the embedded uarch ram image. Symbol created by "xxd -i uarch-ram.bin". -extern "C" const uint32_t embedded_uarch_ram_len; - const pma_entry::flags ram_flags{ true, // R true, // W @@ -37,19 +33,7 @@ const pma_entry::flags ram_flags{ PMA_ISTART_DID::memory // DID }; -uarch_machine::uarch_machine(uarch_config c) : m_s{}, m_c{std::move(c)} { - load_config(m_c); -} - -/// \brief Resets the value of halt flag -void uarch_machine::reset_state(void) { - if (!m_s.halt_flag) { - throw std::runtime_error("reset uarch state is not allowed when uarch is not halted"); - } - load_config(m_c); -} - -void uarch_machine::load_config(uarch_config &c) { +uarch_machine::uarch_machine(uarch_config c) : m_s{}, m_c{c} { m_s.pc = c.processor.pc; m_s.cycle = c.processor.cycle; m_s.halt_flag = c.processor.halt_flag; @@ -57,23 +41,24 @@ void uarch_machine::load_config(uarch_config &c) { for (int i = 1; i < UARCH_X_REG_COUNT; i++) { m_s.x[i] = c.processor.x[i]; } - - // Register RAM pma + // Register shadow state + m_s.shadow_state = make_shadow_uarch_state_pma_entry(PMA_SHADOW_UARCH_STATE_START, PMA_SHADOW_UARCH_STATE_LENGTH); + // Register RAM + constexpr auto ram_description = "uarch RAM"; if (!c.ram.image_filename.empty()) { // Load RAM image from file - m_s.ram = make_callocd_memory_pma_entry("uarch RAM", PMA_UARCH_RAM_START, c.ram.length, c.ram.image_filename) - .set_flags(ram_flags); - } else if (c.ram.length > 0) { - // Allocate zero-filled RAM - m_s.ram = make_callocd_memory_pma_entry("uarch RAM", PMA_UARCH_RAM_START, c.ram.length).set_flags(ram_flags); + m_s.ram = + make_callocd_memory_pma_entry(ram_description, PMA_UARCH_RAM_START, UARCH_RAM_LENGTH, c.ram.image_filename) + .set_flags(ram_flags); } else { - // Load embedded RAM image - if (embedded_uarch_ram_len > PMA_UARCH_RAM_LENGTH) { - throw std::runtime_error("Embedded uarch RAM image is too big"); // LCOV_EXCL_LINE + // Load embedded pristine RAM image + m_s.ram = make_callocd_memory_pma_entry(ram_description, PMA_UARCH_RAM_START, PMA_UARCH_RAM_LENGTH) + .set_flags(ram_flags); + if (uarch_pristine_ram_len > m_s.ram.get_length()) { + throw std::runtime_error("embedded uarch ram image does not fit in uarch ram pma"); } - m_s.ram = - make_callocd_memory_pma_entry("uarch RAM", PMA_UARCH_RAM_START, PMA_UARCH_RAM_LENGTH).set_flags(ram_flags); - memcpy(m_s.ram.get_memory().get_host_memory(), embedded_uarch_ram, embedded_uarch_ram_len); + memset(m_s.ram.get_memory().get_host_memory(), 0, m_s.ram.get_length()); + memcpy(m_s.ram.get_memory().get_host_memory(), uarch_pristine_ram, uarch_pristine_ram_len); } } @@ -89,12 +74,10 @@ uint64_t uarch_machine::read_pc(void) const { return m_s.pc; } -/// \brief Gets the value of halt flag bool uarch_machine::read_halt_flag(void) const { return m_s.halt_flag; } -/// \brief Sets the value of halt flag void uarch_machine::set_halt_flag(void) { m_s.halt_flag = true; } diff --git a/src/uarch-machine.h b/src/uarch-machine.h index 5536cdf09..29c65bce3 100644 --- a/src/uarch-machine.h +++ b/src/uarch-machine.h @@ -33,9 +33,6 @@ class uarch_machine final { uarch_state m_s; ///< Opaque microarchitecture machine state uarch_config m_c; ///< Copy of initialization config - // Setup the machine with the provided configuration - void load_config(uarch_config &c); - public: /// \brief Constructor from machine configuration // I will deal with clang-tidy later. @@ -54,9 +51,6 @@ class uarch_machine final { /// \brief No move assignment uarch_machine &operator=(uarch_machine &&other) = delete; - // Reset machine state to initial configuration - void reset_state(void); - /// \brief Returns machine state for direct access. uarch_state &get_state(void) { return m_s; diff --git a/src/uarch-pristine-state-hash.cpp b/src/uarch-pristine-state-hash.cpp new file mode 100644 index 000000000..e6c78b727 --- /dev/null +++ b/src/uarch-pristine-state-hash.cpp @@ -0,0 +1,8 @@ +// This file is auto-generated and should not be modified +// clang-format off +#include "uarch-pristine-state-hash.h" +namespace cartesi { + const machine_merkle_tree::hash_type uarch_pristine_state_hash{ +0xd9, 0x80, 0x9f, 0x3f, 0xba, 0xae, 0x44, 0x7c, 0xb0, 0x07, 0xc2, 0xe4, 0x14, 0xea, 0x07, 0x2b, 0x4b, 0x13, 0x5c, 0xeb, 0xfd, 0x02, 0x8d, 0x9c, 0xac, 0xe0, 0xd8, 0x63, 0x24, 0xa9, 0x42, 0x8d, + }; +} // namespace cartesi diff --git a/src/uarch-pristine-state-hash.h b/src/uarch-pristine-state-hash.h new file mode 100644 index 000000000..7dff6cc8b --- /dev/null +++ b/src/uarch-pristine-state-hash.h @@ -0,0 +1,30 @@ +// 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 UARCH_STATE_HASH_H +#define UARCH_STATE_HASH_H + +#include "machine-merkle-tree.h" + +namespace cartesi { + +/// \brief Hash of the pristine uarch state. +/// \details This hash is computed at compile time by the program compute-uarch-pristine-hash.cpp +extern const machine_merkle_tree::hash_type uarch_pristine_state_hash; + +} // namespace cartesi + +#endif diff --git a/src/uarch-record-reset-state-access.h b/src/uarch-record-reset-state-access.h new file mode 100644 index 000000000..e33543bb2 --- /dev/null +++ b/src/uarch-record-reset-state-access.h @@ -0,0 +1,196 @@ +// 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 UARCH_RECORD_RESET_STATE_ACCESS +#define UARCH_RECORD_RESET_STATE_ACCESS + +/// \file +/// \brief State access implementation that record and logs all accesses + +#include "i-uarch-reset-state-access.h" +#include "machine.h" +#include "uarch-constants.h" +#include "uarch-machine.h" +#include "uarch-pristine-state-hash.h" +#include "unique-c-ptr.h" + +namespace cartesi { + +/// \brief Records a uarch state reset operation into an access log. +/// \details The reset operation is logged as a single write access to the entire uarch memory range. +/// This write access restores all registers and ram to their initial values. +class uarch_record_reset_state_access : public i_uarch_reset_state_access { + using tree_type = machine_merkle_tree; + using hash_type = tree_type::hash_type; + using hashertype = tree_type::hasher_type; + using proof_type = tree_type::proof_type; + + ///< uarch state + uarch_state &m_us; + ///< big machine + machine &m_m; + ///< Pointer to access log + std::shared_ptr m_log; + +public: + /// \brief Constructor from machine and uarch states. + /// \param us Reference to uarch state. + /// \param m Reference to machine. + /// \param log_type Type of access log to be created. + explicit uarch_record_reset_state_access(uarch_state &us, machine &m, access_log::type log_type) : + m_us(us), + m_m(m), + m_log(std::make_shared(log_type)) {} + + /// \brief No copy constructor + uarch_record_reset_state_access(const uarch_record_reset_state_access &) = delete; + /// \brief No copy assignment + uarch_record_reset_state_access &operator=(const uarch_record_reset_state_access &) = delete; + /// \brief No move constructor + uarch_record_reset_state_access(uarch_record_reset_state_access &&) = delete; + /// \brief No move assignment + uarch_record_reset_state_access &operator=(uarch_record_reset_state_access &&) = delete; + /// \brief Default destructor + ~uarch_record_reset_state_access() = default; + + /// \brief Returns const pointer to access log. + std::shared_ptr get_log(void) const { + return m_log; + } + + /// \brief Returns pointer to access log. + std::shared_ptr get_log(void) { + return m_log; + } + + /// \brief Adds annotations to the state, bracketing a scope + class scoped_note { + std::shared_ptr m_log; ///< Pointer to log receiving annotations + std::string m_text; ///< String with the text for the annotation + + public: + /// \brief Constructor adds the "begin" bracketting note + /// \param log Pointer to access log receiving annotations + /// \param text Pointer to annotation text + /// \details A note is added at the moment of construction + scoped_note(std::shared_ptr log, const char *text) : m_log(std::move(log)), m_text(text) { + if (m_log) { + m_log->push_bracket(bracket_type::begin, text); + } + } + + /// \brief No copy constructors + scoped_note(const scoped_note &) = delete; + + /// \brief No copy assignment + scoped_note &operator=(const scoped_note &) = delete; + + /// \brief Default move constructor + /// \details This is OK because the shared_ptr to log will be + /// empty afterwards and we explicitly test for this + /// condition before writing the "end" bracketting note + scoped_note(scoped_note &&) = default; + + /// \brief Default move assignment + /// \details This is OK because the shared_ptr to log will be + /// empty afterwards and we explicitly test for this + /// condition before writing the "end" bracketting note + scoped_note &operator=(scoped_note &&) = default; + + /// \brief Destructor adds the "end" bracketting note + /// if the log shared_ptr is not empty + /// NOLINTNEXTLINE(bugprone-exception-escape) + ~scoped_note() { + if (m_log) { + m_log->push_bracket(bracket_type::end, m_text.c_str()); + } + } + }; + +private: + // Declare interface as friend to it can forward calls to the "overriden" methods. + friend i_uarch_reset_state_access; + + void do_push_bracket(bracket_type &type, const char *text) { + m_log->push_bracket(type, text); + } + + scoped_note do_make_scoped_note(const char *text) { + return scoped_note{m_log, text}; + } + + void do_reset_state(void) { + // The pristine uarch state decided at compile time and never changes. + // We set all uarch registers and RAM to their initial values and + // log a single write access to the entire uarch memory range. + // This write access does not contain any data, just hashes, unless log_type.large_data is enabled. + access a; + a.set_type(access_type::write); + a.set_address(UARCH_STATE_START_ADDRESS); + a.set_log2_size(UARCH_STATE_LOG2_SIZE); + + // Always compute the proof, even if we are not logging it, because: + // (1) we always need to compute read_hash. + // (2) Depending on the log type, we may also need to compute the proof. + // (3) proof.target_hash is the value that we need for a.read_hash in (1). + auto proof = m_m.get_proof(UARCH_STATE_START_ADDRESS, UARCH_STATE_LOG2_SIZE); + a.set_read_hash(proof.get_target_hash()); + if (m_log->get_log_type().has_large_data()) { + // log read data, if debug info is enabled + a.get_read().emplace(get_uarch_state_image()); + } + if (m_log->get_log_type().has_proofs()) { + a.set_proof(std::move(proof)); + } + a.set_written_hash(uarch_pristine_state_hash); + + // Restore uarch to pristine state + m_us.halt_flag = false; + m_us.pc = UARCH_PC_INIT; + m_us.cycle = UARCH_CYCLE_INIT; + for (int i = 1; i < UARCH_X_REG_COUNT; i++) { + m_us.x[i] = UARCH_X_INIT; + } + m_us.ram.set_memory(m_us.ram.get_start(), 0, m_us.ram.get_length()); + m_us.ram.write_memory(m_us.ram.get_start(), uarch_pristine_ram, uarch_pristine_ram_len); + if (m_log->get_log_type().has_large_data()) { + // log written data, if debug info is enabled + a.get_written().emplace(get_uarch_state_image()); + } + m_log->push_access(a, "uarch_state"); + } + + /// \brief Returns the image of the entire uarch state + /// \return access_data containing the image of the current uarch state + access_data get_uarch_state_image(void) { + constexpr int uarch_data_len = uint64_t{1} << UARCH_STATE_LOG2_SIZE; + access_data data(uarch_data_len, 0); + constexpr auto ram_offset = UARCH_RAM_START_ADDRESS - UARCH_STATE_START_ADDRESS; + // copy shadow state data + const unsigned char *page_data = nullptr; + auto peek = m_us.shadow_state.get_peek(); + if (!peek(m_us.shadow_state, m_m, 0, &page_data, data.data())) { + throw std::runtime_error{"peek failed"}; + } + // copy ram data + memcpy(data.data() + ram_offset, m_us.ram.get_memory().get_host_memory(), m_us.ram.get_length()); + return data; + } +}; + +} // namespace cartesi + +#endif diff --git a/src/uarch-record-state-access.h b/src/uarch-record-step-state-access.h similarity index 84% rename from src/uarch-record-state-access.h rename to src/uarch-record-step-state-access.h index edc4c5278..2f050eba4 100644 --- a/src/uarch-record-state-access.h +++ b/src/uarch-record-step-state-access.h @@ -20,7 +20,8 @@ /// \file /// \brief State access implementation that record and logs all accesses -#include "i-uarch-state-access.h" +#include "i-uarch-step-state-access.h" +#include "machine-merkle-tree.h" #include "machine.h" #include "uarch-bridge.h" #include "uarch-constants.h" @@ -28,8 +29,11 @@ namespace cartesi { -/// \details The uarch_record_state_access logs all access to the machine state. -class uarch_record_state_access : public i_uarch_state_access { +/// \details The uarch_record_step_state_access logs all access to the machine state. +class uarch_record_step_state_access : public i_uarch_step_state_access { + using hasher_type = machine_merkle_tree::hasher_type; + using hash_type = machine_merkle_tree::hash_type; + uarch_state &m_us; machine &m_m; ///< Macro machine machine_state &m_s; @@ -61,11 +65,35 @@ class uarch_record_state_access : public i_uarch_state_access get_log(void) const { @@ -150,14 +178,20 @@ class uarch_record_state_access : public i_uarch_state_accessget_log_type().has_proofs()) { // We can skip updating the merkle tree while getting the proof because we assume that: - // 1) A full merkle tree update was called at the beginning of machine::step_uarch() + // 1) A full merkle tree update was called at the beginning of machine::log_uarch_step() // 2) We called update_merkle_tree_page on all write accesses a.set_proof(m_m.get_proof(paligned, machine_merkle_tree::get_log2_word_size(), skip_merkle_tree_update)); } a.set_type(access_type::read); a.set_address(paligned); a.set_log2_size(machine_merkle_tree::get_log2_word_size()); - set_word_access_data(val, a.get_read()); + a.get_read().emplace(); + set_word_access_data(val, a.get_read().value()); + + hash_type read_hash; + get_hash(a.get_read().value(), read_hash); + a.set_read_hash(read_hash); + m_log->push_access(std::move(a), text); return val; } @@ -174,15 +208,23 @@ class uarch_record_state_access : public i_uarch_state_accessget_log_type().has_proofs()) { // We can skip updating the merkle tree while getting the proof because we assume that: - // 1) A full merkle tree update was called at the beginning of machine::step_uarch() + // 1) A full merkle tree update was called at the beginning of machine::log_uarch_step() // 2) We called update_merkle_tree_page on all write accesses a.set_proof(m_m.get_proof(paligned, machine_merkle_tree::get_log2_word_size(), skip_merkle_tree_update)); } a.set_type(access_type::write); a.set_address(paligned); a.set_log2_size(machine_merkle_tree::get_log2_word_size()); - set_word_access_data(dest, a.get_read()); - set_word_access_data(val, a.get_written()); + + a.get_read().emplace(); + set_word_access_data(dest, a.get_read().value()); + get_hash(a.get_read().value(), a.get_read_hash()); + + a.get_written().emplace(); + set_word_access_data(val, a.get_written().value()); + a.get_written_hash().emplace(); + get_hash(a.get_written().value(), a.get_written_hash().value()); + m_log->push_access(std::move(a), text); } @@ -213,10 +255,11 @@ class uarch_record_state_access : public i_uarch_state_access; + friend i_uarch_step_state_access; void do_push_bracket(bracket_type &type, const char *text) { m_log->push_bracket(type, text); @@ -259,7 +302,7 @@ class uarch_record_state_access : public i_uarch_state_access. +// + +#ifndef UARCH_REPLAY_RESET_STATE_ACCESS_H +#define UARCH_REPLAY_RESET_STATE_ACCESS_H + +/// \file +/// \brief State access implementation that replays recorded state accesses + +#include +#include +#include +#include +#include +#include + +#include "i-uarch-reset-state-access.h" +#include "uarch-pristine-state-hash.h" + +namespace cartesi { + +/// \brief Allows replaying a uarch reset operation from an access log. +class uarch_replay_reset_state_access : public i_uarch_reset_state_access { + using tree_type = machine_merkle_tree; + using hash_type = tree_type::hash_type; + using hasher_type = tree_type::hasher_type; + using proof_type = tree_type::proof_type; + + ///< Access log generated by log_uarch_reset + const std::vector &m_accesses; + ///< Whether to verify proofs in access log + bool m_verify_proofs; + ///< Index of next access to ne consumed + unsigned m_next_access; + ///< Add to indices reported in errors + int m_one_based; + ///< Root hash before next access + machine_merkle_tree::hash_type m_root_hash; + ///< Hasher needed to verify proofs + machine_merkle_tree::hasher_type m_hasher; + +public: + /// \brief Constructor from access log + /// \param log Access log to be replayed + /// \param verify_proofs Whether to verify proofs in access log + /// \param one_based Whether to add one to indices reported in errors + explicit uarch_replay_reset_state_access(const access_log &log, bool verify_proofs, bool one_based) : + m_accesses(log.get_accesses()), + m_verify_proofs(verify_proofs), + m_next_access{0}, + m_one_based{one_based}, + m_root_hash{}, + m_hasher{} { + if (m_verify_proofs && !log.get_log_type().has_proofs()) { + throw std::invalid_argument{"log has no proofs"}; + } + if (log.get_accesses().size() != 1) { + throw std::invalid_argument{"there must be one access in log"}; + } + if (m_verify_proofs) { + const auto &access = m_accesses.front(); + if (!access.get_proof().has_value()) { + throw std::invalid_argument{"initial access has no proof"}; + } + m_root_hash = access.get_proof().value().get_root_hash(); // NOLINT(bugprone-unchecked-optional-access) + } + } + + /// \brief No copy constructor + uarch_replay_reset_state_access(const uarch_replay_reset_state_access &) = delete; + /// \brief No copy assignment + uarch_replay_reset_state_access &operator=(const uarch_replay_reset_state_access &) = delete; + /// \brief No move constructor + uarch_replay_reset_state_access(uarch_replay_reset_state_access &&) = delete; + /// \brief No move assignment + uarch_replay_reset_state_access &operator=(uarch_replay_reset_state_access &&) = delete; + /// \brief Default destructor + ~uarch_replay_reset_state_access() = default; + + void get_root_hash(machine_merkle_tree::hash_type &hash) const { + hash = m_root_hash; + } + + /// \brief Checks if access log was fully consumed after reset operation is finished + void finish(void) { + if (m_next_access != m_accesses.size()) { + throw std::invalid_argument{"access log was not fully consumed"}; + } + } + +private: + static void roll_hash_up_tree(machine_merkle_tree::hasher_type &hasher, + const machine_merkle_tree::proof_type &proof, machine_merkle_tree::hash_type &rolling_hash) { + for (int log2_size = proof.get_log2_target_size(); log2_size < proof.get_log2_root_size(); ++log2_size) { + const int bit = (proof.get_target_address() & (UINT64_C(1) << log2_size)) != 0; + const auto &sibling_hash = proof.get_sibling_hash(log2_size); + hasher.begin(); + if (bit) { + hasher.add_data(sibling_hash.data(), sibling_hash.size()); + hasher.add_data(rolling_hash.data(), rolling_hash.size()); + } else { + hasher.add_data(rolling_hash.data(), rolling_hash.size()); + hasher.add_data(sibling_hash.data(), sibling_hash.size()); + } + hasher.end(rolling_hash); + } + } + + friend i_uarch_reset_state_access; + + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + void do_push_bracket(bracket_type type, const char *text) { + (void) type; + (void) text; + } + + int do_make_scoped_note(const char *text) { // NOLINT(readability-convert-member-functions-to-static) + (void) text; + return 0; + } + + auto access_to_report(void) const { + return m_next_access + m_one_based; + } + + void do_reset_state(void) { + hasher_type hasher; + auto text = std::string("uarchState"); + if (m_next_access >= m_accesses.size()) { + throw std::invalid_argument{"too few accesses in log"}; + } + const auto &access = m_accesses[m_next_access]; + if (access.get_type() != access_type::write) { + if (access.get_address() != UARCH_STATE_START_ADDRESS) { + throw std::invalid_argument{"expected address of access " + std::to_string(access_to_report()) + + " to be the start address of the uarch state"}; + } + throw std::invalid_argument{"expected access " + std::to_string(access_to_report()) + " to write " + text}; + } + if (access.get_address() != UARCH_STATE_START_ADDRESS) { + throw std::invalid_argument{"expected address of access " + std::to_string(access_to_report()) + + " to be the start address of the uarch state"}; + } + if (access.get_log2_size() != UARCH_STATE_LOG2_SIZE) { + throw std::invalid_argument{"expected access " + std::to_string(access_to_report()) + " to write 2^" + + std::to_string(UARCH_STATE_LOG2_SIZE) + " bytes from " + text}; + } + if (access.get_read().has_value()) { + // if read data is available then its hash and the logged read hash must match + hash_type computed_hash; + get_hash(hasher, access.get_read().value().data(), access.get_read().value().size(), computed_hash); + if (computed_hash != access.get_read_hash()) { + throw std::invalid_argument{"hash of read data and read hash at access " + + std::to_string(access_to_report()) + " does not match read hash"}; + } + } + if (!access.get_written_hash().has_value()) { + throw std::invalid_argument{"write access " + std::to_string(access_to_report()) + " has no written hash"}; + } + if (access.get_written().has_value()) { + // if written data is available then its hash and the logged written hash must match + hash_type computed_hash; + get_hash(hasher, access.get_written().value().data(), access.get_written().value().size(), computed_hash); + if (computed_hash != access.get_written_hash().value()) { + throw std::invalid_argument{"hash of written data and written hash at access " + + std::to_string(access_to_report()) + " does not match written hash"}; + } + } + if (m_verify_proofs) { + if (!access.get_proof().has_value()) { + throw std::invalid_argument{"write access " + std::to_string(access_to_report()) + " has no proof"}; + } + const auto &proof = access.get_proof().value(); // NOLINT(bugprone-unchecked-optional-access) + if (proof.get_target_address() != access.get_address()) { + throw std::invalid_argument{"mismatch in write access " + std::to_string(access_to_report()) + + " address and its proof address"}; + } + if (m_root_hash != proof.get_root_hash()) { + throw std::invalid_argument{ + "mismatch in write access " + std::to_string(access_to_report()) + " root hash"}; + } + auto rolling_hash = access.get_read_hash(); + if (rolling_hash != proof.get_target_hash()) { + throw std::invalid_argument{ + "value before write access " + std::to_string(access_to_report()) + " does not match target hash"}; + } + roll_hash_up_tree(m_hasher, proof, rolling_hash); + if (rolling_hash != proof.get_root_hash()) { + throw std::invalid_argument{ + "value before write access " + std::to_string(access_to_report()) + " fails proof"}; + } + m_root_hash = access.get_written_hash().value(); + roll_hash_up_tree(m_hasher, proof, m_root_hash); + } + if (access.get_written_hash().value() != uarch_pristine_state_hash) { + throw std::invalid_argument{"expected written hash of access " + std::to_string(access_to_report()) + + " to be the start hash of the pristine uarch state"}; + } + m_next_access++; + } + + static void get_hash(hasher_type &hasher, const unsigned char *data, size_t len, hash_type &hash) { + if (len <= 8) { + assert(len == 8); + hasher.begin(); + hasher.add_data(data, len); + hasher.end(hash); + } else { + assert((len & 1) == 0); + len = len / 2; + hash_type left; + get_hash(hasher, data, len, left); + get_hash(hasher, data + len, len, hash); + hasher.begin(); + hasher.add_data(left.data(), left.size()); + hasher.add_data(hash.data(), hash.size()); + hasher.end(hash); + } + } +}; + +} // namespace cartesi + +#endif diff --git a/src/uarch-replay-state-access.h b/src/uarch-replay-step-state-access.h similarity index 91% rename from src/uarch-replay-state-access.h rename to src/uarch-replay-step-state-access.h index 3b0fae3cf..7123e7b88 100644 --- a/src/uarch-replay-state-access.h +++ b/src/uarch-replay-step-state-access.h @@ -14,8 +14,8 @@ // with this program (see COPYING). If not, see . // -#ifndef UARCH_REPLAY_MACHINE_H -#define UARCH_REPLAY_MACHINE_H +#ifndef UARCH_REPLAY_STATE_ACCESS_H +#define UARCH_REPLAY_STATE_ACCESS_H /// \file /// \brief State access implementation that replays recorded state accesses @@ -27,13 +27,13 @@ #include #include -#include "i-uarch-state-access.h" +#include "i-uarch-step-state-access.h" #include "shadow-state.h" #include "uarch-bridge.h" namespace cartesi { -class uarch_replay_state_access : public i_uarch_state_access { +class uarch_replay_step_state_access : public i_uarch_step_state_access { ///< Access log generated by step const std::vector &m_accesses; ///< Whether to verify proofs in access log @@ -46,20 +46,17 @@ class uarch_replay_state_access : public i_uarch_state_access m_mock_pmas; public: /// \brief Constructor from log of word accesses. /// \param accesses Reference to word access vector. - explicit uarch_replay_state_access(const access_log &log, bool verify_proofs, bool one_based) : + explicit uarch_replay_step_state_access(const access_log &log, bool verify_proofs, bool one_based) : m_accesses(log.get_accesses()), m_verify_proofs(verify_proofs), m_next_access{0}, m_one_based{one_based}, m_root_hash{}, - m_hasher{}, - m_mock_pmas{} { + m_hasher{} { if (m_verify_proofs && !log.get_log_type().has_proofs()) { throw std::invalid_argument{"log has no proofs"}; } @@ -73,15 +70,15 @@ class uarch_replay_state_access : public i_uarch_state_access; + friend i_uarch_step_state_access; // NOLINTNEXTLINE(readability-convert-member-functions-to-static) void do_push_bracket(bracket_type type, const char *text) { diff --git a/src/uarch-reset-state-access.h b/src/uarch-reset-state-access.h new file mode 100644 index 000000000..068113634 --- /dev/null +++ b/src/uarch-reset-state-access.h @@ -0,0 +1,84 @@ +// 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 UARCH_RESET_STATE_ACCESS +#define UARCH_RESET_STATE_ACCESS + +/// \file +/// \brief State access implementation that record and logs all accesses + +#include "i-uarch-reset-state-access.h" +#include "uarch-constants.h" +#include "uarch-machine.h" + +namespace cartesi { + +/// \details The uarch_reset_state_access logs all access to the machine state. +class uarch_reset_state_access : public i_uarch_reset_state_access { + uarch_state &m_us; + +public: + /// \brief Constructor from machine and uarch states. + /// \param um Reference to uarch state. + /// \param m Reference to machine state. + explicit uarch_reset_state_access(uarch_state &us) : m_us(us) {} + + /// \brief No copy constructor + uarch_reset_state_access(const uarch_reset_state_access &) = delete; + /// \brief No copy assignment + uarch_reset_state_access &operator=(const uarch_reset_state_access &) = delete; + /// \brief No move constructor + uarch_reset_state_access(uarch_reset_state_access &&) = delete; + /// \brief No move assignment + uarch_reset_state_access &operator=(uarch_reset_state_access &&) = delete; + /// \brief Default destructor + ~uarch_reset_state_access() = default; + +private: + // Declare interface as friend to it can forward calls to the "overriden" methods. + friend i_uarch_reset_state_access; + + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + void do_push_bracket(bracket_type type, const char *text) { + (void) type; + (void) text; + } + + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + int do_make_scoped_note(const char *text) { + (void) text; + return 0; + } + + void do_reset_state(void) { + m_us.halt_flag = false; + m_us.pc = UARCH_PC_INIT; + m_us.cycle = UARCH_CYCLE_INIT; + for (int i = 1; i < UARCH_X_REG_COUNT; i++) { + m_us.x[i] = UARCH_X_INIT; + } + // Load embedded pristine RAM image + if (uarch_pristine_ram_len > m_us.ram.get_length()) { + throw std::runtime_error("embedded uarch ram image does not fit in uarch ram pma"); + } + memset(m_us.ram.get_memory().get_host_memory(), 0, m_us.ram.get_length()); + memcpy(m_us.ram.get_memory().get_host_memory(), uarch_pristine_ram, uarch_pristine_ram_len); + } +}; + +} // namespace cartesi + +#endif diff --git a/src/uarch-reset-state.cpp b/src/uarch-reset-state.cpp new file mode 100644 index 000000000..a6a206468 --- /dev/null +++ b/src/uarch-reset-state.cpp @@ -0,0 +1,45 @@ +// 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 . +// + +// NOLINTBEGIN(google-readability-casting, misc-const-correctness) + +#include + +#include "riscv-constants.h" +#include "uarch-record-reset-state-access.h" +#include "uarch-replay-reset-state-access.h" +#include "uarch-reset-state-access.h" +#include "uarch-reset-state.h" +#include "uarch-solidity-compat.h" + +namespace cartesi { + +template +void uarch_reset_state(UarchState &a) { + a.reset_state(); +} + +// Explicit instantiation for uarch_step_state_access +template void uarch_reset_state(uarch_reset_state_access &a); + +// Explicit instantiation for uarch_record_reset_state_access +template void uarch_reset_state(uarch_record_reset_state_access &a); + +// Explicit instantiation for uarch_replay_step_state_access +template void uarch_reset_state(uarch_replay_reset_state_access &a); + +} // namespace cartesi +// NOLINTEND(google-readability-casting, misc-const-correctness) diff --git a/src/uarch-reset-state.h b/src/uarch-reset-state.h new file mode 100644 index 000000000..b46aad9d4 --- /dev/null +++ b/src/uarch-reset-state.h @@ -0,0 +1,46 @@ +// 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 UARCH_RESET_STATE_H +#define UARCH_RESET_STATE_H + +namespace cartesi { + +/// \brief Reset uarch to pristine state +/// \tparam STATE_ACCESS state accessor type +/// \param a state accessor instance +template +void uarch_reset_state(STATE_ACCESS &a); + +class uarch_reset_state_access; +class uarch_record_step_state_access; +class uarch_replay_step_state_access; + +// Declaration of explicit instantiation in module uarch-reset-state.cpp +extern template void uarch_reset_state(uarch_reset_state_access &a); + +// Declaration of explicit instantiation in module uarch-reset-state.cpp +extern template void uarch_reset_state(uarch_record_step_state_access &a); + +// Declaration of explicit instantiation in module uarch-reset-state.cpp +extern template void uarch_reset_state(uarch_record_reset_state_access &a); + +// Declaration of explicit instantiation in module uarch-reset-state.cpp +extern template void uarch_reset_state(uarch_replay_step_state_access &a); + +} // namespace cartesi + +#endif diff --git a/src/uarch-riscv-tests.lua b/src/uarch-riscv-tests.lua index 4f4ec104b..9f4c71533 100755 --- a/src/uarch-riscv-tests.lua +++ b/src/uarch-riscv-tests.lua @@ -206,7 +206,6 @@ local function build_machine(test_name) uarch = { ram = { image_filename = test_path .. "/" .. test_name, - length = 0x20000, }, }, } @@ -396,7 +395,7 @@ local function run_machine_writing_json_logs(machine, ctx) local step_count = 0 while math.ult(machine:read_uarch_cycle(), max_cycle) do local log_type = { proofs = should_log_proofs() } - local log = machine:step_uarch(log_type) + local log = machine:log_uarch_step(log_type) total_steps_counter = total_steps_counter + 1 step_count = step_count + 1 local halted = machine:read_uarch_halt_flag() diff --git a/src/uarch-state.h b/src/uarch-state.h index 52ae63511..9f4d1e1f0 100644 --- a/src/uarch-state.h +++ b/src/uarch-state.h @@ -20,12 +20,18 @@ /// \file /// \brief Cartesi microarchitecture machine state structure definition. +#include #include #include #include "pma.h" #include "riscv-constants.h" -#include + +/// \brief Embedded pristine uarch ram image. This symbol is created by "xxd" +extern "C" const unsigned char uarch_pristine_ram[]; + +/// \brief Length of the embedded pristine uarch ram image. This symbol is created by "xxd" +extern "C" const uint32_t uarch_pristine_ram_len; namespace cartesi { @@ -44,8 +50,9 @@ struct uarch_state { std::array x; ///< Register file. uint64_t cycle; ///< Cycles counter bool halt_flag; - pma_entry ram; ///< Memory range for micro RAM - pma_entry empty_pma; ///< Empty range fallback + pma_entry shadow_state; ///< Shadow uarch state + pma_entry ram; ///< Memory range for micro RAM + pma_entry empty_pma; ///< Empty range fallback }; } // namespace cartesi diff --git a/src/uarch-state-access.h b/src/uarch-step-state-access.h similarity index 90% rename from src/uarch-state-access.h rename to src/uarch-step-state-access.h index 8d238306a..7f8561271 100644 --- a/src/uarch-state-access.h +++ b/src/uarch-step-state-access.h @@ -17,7 +17,7 @@ #ifndef UARCH_STATE_ACCESS_H #define UARCH_STATE_ACCESS_H -#include "i-uarch-state-access.h" +#include "i-uarch-step-state-access.h" #include "machine-state.h" #include "shadow-state.h" #include "uarch-bridge.h" @@ -26,7 +26,7 @@ namespace cartesi { -class uarch_state_access : public i_uarch_state_access { +class uarch_step_state_access : public i_uarch_step_state_access { uarch_state &m_us; machine_state &m_s; @@ -60,23 +60,23 @@ class uarch_state_access : public i_uarch_state_access { /// \brief Constructor from machine and uarch states. /// \param um Reference to uarch state. /// \param m Reference to machine state. - explicit uarch_state_access(uarch_state &us, machine_state &s) : m_us(us), m_s(s) { + explicit uarch_step_state_access(uarch_state &us, machine_state &s) : m_us(us), m_s(s) { ; } /// \brief No copy constructor - uarch_state_access(const uarch_state_access &) = delete; + uarch_step_state_access(const uarch_step_state_access &) = delete; /// \brief No copy assignment - uarch_state_access &operator=(const uarch_state_access &) = delete; + uarch_step_state_access &operator=(const uarch_step_state_access &) = delete; /// \brief No move constructor - uarch_state_access(uarch_state_access &&) = delete; + uarch_step_state_access(uarch_step_state_access &&) = delete; /// \brief No move assignment - uarch_state_access &operator=(uarch_state_access &&) = delete; + uarch_step_state_access &operator=(uarch_step_state_access &&) = delete; /// \brief Default destructor - ~uarch_state_access() = default; + ~uarch_step_state_access() = default; private: - friend i_uarch_state_access; + friend i_uarch_step_state_access; // NOLINTNEXTLINE(readability-convert-member-functions-to-static) void do_push_bracket(bracket_type type, const char *text) { diff --git a/src/uarch-step.cpp b/src/uarch-step.cpp index e6a512451..8634f2fe4 100644 --- a/src/uarch-step.cpp +++ b/src/uarch-step.cpp @@ -19,10 +19,10 @@ #include #include "riscv-constants.h" -#include "uarch-record-state-access.h" -#include "uarch-replay-state-access.h" +#include "uarch-record-step-state-access.h" +#include "uarch-replay-step-state-access.h" #include "uarch-solidity-compat.h" -#include "uarch-state-access.h" +#include "uarch-step-state-access.h" #include "uarch-step.h" namespace cartesi { @@ -1092,7 +1092,7 @@ static inline void executeInsn(UarchState &a, uint32 insn, uint64 pc) { template UArchStepStatus uarch_step(UarchState &a) { - // This must be the first read in order to match the first log access in machine::verify_state_transition + // This must be the first read in order to match the first log access in machine::verify_uarch_step_state_transition uint64 cycle = readCycle(a); // do not advance if cycle will overflow if (cycle == UINT64_MAX) { @@ -1111,14 +1111,14 @@ UArchStepStatus uarch_step(UarchState &a) { return UArchStepStatus::Success; } -// Explicit instantiation for uarch_state_access -template UArchStepStatus uarch_step(uarch_state_access &a); +// Explicit instantiation for uarch_step_state_access +template UArchStepStatus uarch_step(uarch_step_state_access &a); -// Explicit instantiation for uarch_record_state_access -template UArchStepStatus uarch_step(uarch_record_state_access &a); +// Explicit instantiation for uarch_record_step_state_access +template UArchStepStatus uarch_step(uarch_record_step_state_access &a); -// Explicit instantiation for uarch_replay_state_access -template UArchStepStatus uarch_step(uarch_replay_state_access &a); +// Explicit instantiation for uarch_replay_step_state_access +template UArchStepStatus uarch_step(uarch_replay_step_state_access &a); } // namespace cartesi // NOLINTEND(google-readability-casting, misc-const-correctness) diff --git a/src/uarch-step.h b/src/uarch-step.h index 83ba3d76a..f1d5b4670 100644 --- a/src/uarch-step.h +++ b/src/uarch-step.h @@ -33,18 +33,18 @@ enum class UArchStepStatus : int { template UArchStepStatus uarch_step(STATE_ACCESS &a); -class uarch_state_access; -class uarch_record_state_access; -class uarch_replay_state_access; +class uarch_step_state_access; +class uarch_record_step_state_access; +class uarch_replay_step_state_access; // Declaration of explicit instantiation in module uarch-step.cpp -extern template UArchStepStatus uarch_step(uarch_state_access &a); +extern template UArchStepStatus uarch_step(uarch_step_state_access &a); // Declaration of explicit instantiation in module uarch-step.cpp -extern template UArchStepStatus uarch_step(uarch_record_state_access &a); +extern template UArchStepStatus uarch_step(uarch_record_step_state_access &a); // Declaration of explicit instantiation in module uarch-step.cpp -extern template UArchStepStatus uarch_step(uarch_replay_state_access &a); +extern template UArchStepStatus uarch_step(uarch_replay_step_state_access &a); } // namespace cartesi diff --git a/src/virtual-machine.cpp b/src/virtual-machine.cpp index eb2ebcc70..9c52f4219 100644 --- a/src/virtual-machine.cpp +++ b/src/virtual-machine.cpp @@ -36,8 +36,8 @@ interpreter_break_reason virtual_machine::do_run(uint64_t mcycle_end) { return m_machine->run(mcycle_end); } -access_log virtual_machine::do_step_uarch(const access_log::type &log_type, bool one_based) { - return m_machine->step_uarch(log_type, one_based); +access_log virtual_machine::do_log_uarch_step(const access_log::type &log_type, bool one_based) { + return m_machine->log_uarch_step(log_type, one_based); } machine_merkle_tree::proof_type virtual_machine::do_get_proof(uint64_t address, int log2_size) const { @@ -476,16 +476,16 @@ void virtual_machine::do_set_uarch_halt_flag() { m_machine->set_uarch_halt_flag(); } -void virtual_machine::do_reset_uarch_state() { - m_machine->reset_uarch_state(); +void virtual_machine::do_reset_uarch() { + m_machine->reset_uarch(); } -bool virtual_machine::do_read_uarch_halt_flag(void) const { - return m_machine->read_uarch_halt_flag(); +access_log virtual_machine::do_log_uarch_reset(const access_log::type &log_type, bool one_based) { + return m_machine->log_uarch_reset(log_type, one_based); } -uint64_t virtual_machine::do_read_uarch_ram_length(void) const { - return m_machine->read_uarch_ram_length(); +bool virtual_machine::do_read_uarch_halt_flag(void) const { + return m_machine->read_uarch_halt_flag(); } uarch_interpreter_break_reason virtual_machine::do_run_uarch(uint64_t uarch_cycle_end) { diff --git a/src/virtual-machine.h b/src/virtual-machine.h index da5249ad2..b43588341 100644 --- a/src/virtual-machine.h +++ b/src/virtual-machine.h @@ -42,7 +42,7 @@ class virtual_machine : public i_virtual_machine { private: void do_store(const std::string &dir) override; interpreter_break_reason do_run(uint64_t mcycle_end) override; - access_log do_step_uarch(const access_log::type &log_type, bool one_based = false) override; + access_log do_log_uarch_step(const access_log::type &log_type, bool one_based = false) override; machine_merkle_tree::proof_type do_get_proof(uint64_t address, int log2_size) const override; void do_get_root_hash(hash_type &hash) const override; bool do_verify_merkle_tree(void) const override; @@ -151,9 +151,9 @@ class virtual_machine : public i_virtual_machine { void do_write_uarch_pc(uint64_t val) override; uint64_t do_read_uarch_cycle(void) const override; void do_write_uarch_cycle(uint64_t val) override; - uint64_t do_read_uarch_ram_length(void) const override; void do_set_uarch_halt_flag() override; - void do_reset_uarch_state() override; + void do_reset_uarch() override; + access_log do_log_uarch_reset(const access_log::type &log_type, bool one_based = false) override; bool do_read_uarch_halt_flag(void) const override; uarch_interpreter_break_reason do_run_uarch(uint64_t uarch_cycle_end) override; }; diff --git a/third-party/riscv-arch-tests/src/run-rv64i-arch-test.lua b/third-party/riscv-arch-tests/src/run-rv64i-arch-test.lua index b40dc24e5..fa486b3ed 100755 --- a/third-party/riscv-arch-tests/src/run-rv64i-arch-test.lua +++ b/third-party/riscv-arch-tests/src/run-rv64i-arch-test.lua @@ -39,8 +39,7 @@ end local uarch_ram_image_filename = arg[1] local output_signature_file = arg[2] -local uarch_ram_start = 0x78000000 -local uarch_ram_length = 0x1000000 +local uarch_ram_start = cartesi.UARCH_RAM_START_ADDRESS local dummy_rom_filename = os.tmpname() io.open(dummy_rom_filename, "w"):close() local deleter = {} @@ -49,8 +48,7 @@ setmetatable(deleter, { __gc = function() os.remove(dummy_rom_filename) end }) local config = { uarch = { ram = { - image_filename = uarch_ram_image_filename, - length = uarch_ram_length, + image_filename = uarch_ram_image_filename }, }, processor = {}, @@ -64,7 +62,7 @@ local machine = assert(cartesi.machine(config)) machine:run_uarch() -- extract test result signature from microarchitecture RAM -local mem = machine:read_memory(uarch_ram_start, uarch_ram_length) +local mem = machine:read_memory(uarch_ram_start, cartesi.UARCH_RAM_LENGTH) local _, e1 = string.find(mem, "BEGIN_CTSI_SIGNATURE____") local s2, _ = string.find(mem, "END_CTSI_SIGNATURE______") local sig = string.sub(mem, e1 + 1, s2 - 1) diff --git a/uarch/uarch-ram-entry.S b/uarch/uarch-ram-entry.S index 709ac0637..81fc85960 100644 --- a/uarch/uarch-ram-entry.S +++ b/uarch/uarch-ram-entry.S @@ -23,11 +23,8 @@ _start: // Initialize stack li sp, PMA_UARCH_RAM_START_DEF - la t0, shadow_uarch_state_uarch_ram_length_abs_addr // t0=pointer to address of uarch_ram_length in shadow uarch state - ld t0, 0(t0) // t0=address of uarch_ram_length in state shadow - ld t0, 0(t0) // t0=value of uarch_ram_length + li t0, PMA_UARCH_RAM_LENGTH_DEF add sp, sp, t0 // stack pointer at the end of RAM - call interpret_next_mcycle_with_uarch