diff --git a/src/clua-i-virtual-machine.cpp b/src/clua-i-virtual-machine.cpp index bcbca0026..7fa1198b2 100644 --- a/src/clua-i-virtual-machine.cpp +++ b/src/clua-i-virtual-machine.cpp @@ -365,6 +365,17 @@ static int machine_obj_index_reset_uarch_state(lua_State *L) { return 0; } +/// \brief This is the machine:get_memory_ranges() method implementation. +/// \param L Lua state. +static int machine_obj_index_get_memory_ranges(lua_State *L) { + auto &m = clua_check>(L, 1); + auto &managed_mrds = clua_push_to(L, clua_managed_cm_ptr(nullptr)); + TRY_EXECUTE(cm_get_memory_ranges(m.get(), &managed_mrds.get(), err_msg)); + clua_push_cm_memory_range_descr_array(L, managed_mrds.get()); + managed_mrds.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) { @@ -646,6 +657,7 @@ static const auto machine_obj_index = cartesi::clua_make_luaL_Reg_array({ {"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}, + {"get_memory_ranges", machine_obj_index_get_memory_ranges}, }); /// \brief This is the machine __close metamethod implementation. diff --git a/src/clua-machine-util.cpp b/src/clua-machine-util.cpp index 76c3ff26e..254cad0b0 100644 --- a/src/clua-machine-util.cpp +++ b/src/clua-machine-util.cpp @@ -81,6 +81,12 @@ void cm_delete(cm_memory_range_config *ptr) { cm_delete_memory_range_config(ptr); } +/// \brief Deleter for C api memory range description array +template <> +void cm_delete(cm_memory_range_descr_array *ptr) { + cm_delete_memory_range_descr_array(ptr); +} + static char *copy_lua_str(lua_State *L, int idx) { const char *lua_str = lua_tostring(L, idx); auto size = strlen(lua_str) + 1; @@ -693,6 +699,18 @@ void clua_push_cm_proof(lua_State *L, const cm_merkle_tree_proof *proof) { lua_setfield(L, -2, "target_hash"); // proof } +void clua_push_cm_memory_range_descr_array(lua_State *L, const cm_memory_range_descr_array *mrds) { + lua_newtable(L); // array + for (size_t i = 0; i < mrds->count; ++i) { + const auto &mrd = mrds->entry[i]; + lua_newtable(L); // array config + clua_setintegerfield(L, mrd.start, "start", -1); // array config + clua_setintegerfield(L, mrd.length, "length", -1); // array config + clua_setstringfield(L, mrd.description, "description", -1); // array config + lua_rawseti(L, -2, i + 1); // array + } +} + 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")}; diff --git a/src/clua-machine-util.h b/src/clua-machine-util.h index f09a6fb8a..96d69fda8 100644 --- a/src/clua-machine-util.h +++ b/src/clua-machine-util.h @@ -96,6 +96,10 @@ void cm_delete(cm_memory_range_config *p); template <> void cm_delete(const cm_semantic_version *p); +/// \brief Deleter for C api memory range description array +template <> +void cm_delete(cm_memory_range_descr_array *p); + // clua_managed_cm_ptr is a smart pointer, // however we don't use all its functionally, therefore we exclude it from code coverage. // LCOV_EXCL_START @@ -175,6 +179,11 @@ void clua_push_cm_hash(lua_State *L, const cm_hash *hash); /// \param c Machine configuration to be pushed void clua_push_cm_machine_config(lua_State *L, const cm_machine_config *c); +/// \brief Pushes a C api cm_memory_range_descr_array to the Lua stack +/// \param L Lua state +/// \param mrds Memory range description array to be pushed +void clua_push_cm_memory_range_descr_array(lua_State *L, const cm_memory_range_descr_array *mrds); + #if 0 /// \brief Pushes a cm_machine_runtime_config to the Lua stack /// \param L Lua state diff --git a/src/clua-machine.cpp b/src/clua-machine.cpp index 7543b013c..8529cb49e 100644 --- a/src/clua-machine.cpp +++ b/src/clua-machine.cpp @@ -149,6 +149,7 @@ int clua_machine_init(lua_State *L, int ctxidx) { clua_createnewtype>(L, ctxidx); clua_createnewtype>(L, ctxidx); clua_createnewtype>(L, ctxidx); + clua_createnewtype>(L, ctxidx); if (!clua_typeexists(L, ctxidx)) { clua_createtype(L, "cartesi machine class", ctxidx); clua_setmethods(L, machine_class_index.data(), 0, ctxidx); diff --git a/src/grpc-virtual-machine.cpp b/src/grpc-virtual-machine.cpp index 3832e25ac..41dcca75b 100644 --- a/src/grpc-virtual-machine.cpp +++ b/src/grpc-virtual-machine.cpp @@ -952,4 +952,16 @@ uarch_interpreter_break_reason grpc_virtual_machine::do_run_uarch(uint64_t uarch return uarch_interpreter_break_reason::reached_target_cycle; } +machine_memory_range_descrs grpc_virtual_machine::do_get_memory_ranges(void) const { + machine_memory_range_descrs mrds; + const Void request; + GetMemoryRangesResponse response; + ClientContext context; + check_status(m_stub->get_stub()->GetMemoryRanges(&context, request, &response)); + for (const auto &proto_m: response.memory_range()) { + mrds.push_back(get_proto_memory_range_descr(proto_m)); + } + return mrds; +} + } // namespace cartesi diff --git a/src/grpc-virtual-machine.h b/src/grpc-virtual-machine.h index d8a53359b..aeea2d2d5 100644 --- a/src/grpc-virtual-machine.h +++ b/src/grpc-virtual-machine.h @@ -225,6 +225,7 @@ class grpc_virtual_machine : public i_virtual_machine { void do_reset_uarch_state() override; bool do_read_uarch_halt_flag(void) const override; uarch_interpreter_break_reason do_run_uarch(uint64_t uarch_cycle_end) override; + machine_memory_range_descrs do_get_memory_ranges(void) const override; grpc_machine_stub_ptr m_stub; }; diff --git a/src/i-virtual-machine.h b/src/i-virtual-machine.h index 5cf5c18d3..f4bf47ace 100644 --- a/src/i-virtual-machine.h +++ b/src/i-virtual-machine.h @@ -643,6 +643,11 @@ class i_virtual_machine { return do_run_uarch(uarch_cycle_end); } + /// \brief Returns a list of descriptions for all PMA entries registered in the machine, sorted by start + virtual machine_memory_range_descrs get_memory_ranges(void) const { + return do_get_memory_ranges(); + } + private: virtual interpreter_break_reason do_run(uint64_t mcycle_end) = 0; virtual void do_store(const std::string &dir) = 0; @@ -760,6 +765,7 @@ class i_virtual_machine { virtual void do_reset_uarch_state() = 0; virtual uint64_t do_read_uarch_ram_length(void) const = 0; virtual uarch_interpreter_break_reason do_run_uarch(uint64_t uarch_cycle_end) = 0; + virtual machine_memory_range_descrs do_get_memory_ranges(void) const = 0; }; } // namespace cartesi diff --git a/src/json-util.cpp b/src/json-util.cpp index e4ebd1215..0cf61b098 100644 --- a/src/json-util.cpp +++ b/src/json-util.cpp @@ -1027,6 +1027,37 @@ template void ju_get_opt_field(const nlohmann::json &j, const uint64_t template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, machine_config &value, const std::string &path); +template +void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_memory_range_descr &value, + const std::string &path) { + if (!contains(j, key)) { + return; + } + 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, "start"s, value.start, new_path); + ju_get_opt_field(jconfig, "description"s, value.description, new_path); +} + +template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, + machine_memory_range_descr &value, const std::string &path); + +template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, + machine_memory_range_descr &value, const std::string &path); + +template +void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_memory_range_descrs &value, + const std::string &path) { + ju_get_opt_vector_like_field(j, key, value, path); +} + +template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, + machine_memory_range_descrs &value, const std::string &path); + +template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, + machine_memory_range_descrs &value, const std::string &path); + void to_json(nlohmann::json &j, const machine::csr &csr) { j = csr_to_name(csr); } @@ -1224,4 +1255,14 @@ void to_json(nlohmann::json &j, const machine_runtime_config &runtime) { }; } +void to_json(nlohmann::json &j, const machine_memory_range_descr &mrd) { + j = nlohmann::json{{"length", mrd.length}, {"start", mrd.start}, {"description", mrd.description}}; +} + +void to_json(nlohmann::json &j, const machine_memory_range_descrs &mrds) { + j = nlohmann::json::array(); + std::transform(mrds.cbegin(), mrds.cend(), std::back_inserter(j), + [](const auto &a) -> nlohmann::json { return a; }); +} + } // namespace cartesi diff --git a/src/json-util.h b/src/json-util.h index 0d22c4efd..519d852f2 100644 --- a/src/json-util.h +++ b/src/json-util.h @@ -394,6 +394,26 @@ template void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_config &value, const std::string &path = "params/"); +/// \brief Attempts to load a machine_memory_range_descr object from a field in a JSON object +/// \tparam K Key type (explicit extern declarations for uint64_t and std::string are provided) +/// \param j JSON object to load from +/// \param key Key to load value from +/// \param value Object to store value +/// \param path Path to j +template +void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_memory_range_descr &value, + const std::string &path = "params/"); + +/// \brief Attempts to load a machine_memory_range_descrs object from a field in a JSON object +/// \tparam K Key type (explicit extern declarations for uint64_t and std::string are provided) +/// \param j JSON object to load from +/// \param key Key to load value from +/// \param value Object to store value +/// \param path Path to j +template +void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_memory_range_descrs &value, + const std::string &path = "params/"); + /// \brief Attempts to load an array from a field in a JSON object /// \tparam K Key type (explicit extern declarations for uint64_t and std::string are provided) /// \param j JSON object to load from @@ -554,6 +574,7 @@ void to_json(nlohmann::json &j, const concurrency_runtime_config &config); void to_json(nlohmann::json &j, const htif_runtime_config &config); void to_json(nlohmann::json &j, const machine_runtime_config &runtime); void to_json(nlohmann::json &j, const machine::csr &csr); +void to_json(nlohmann::json &j, const machine_memory_range_descrs &mrds); // Extern template declarations extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, std::string &value, @@ -692,6 +713,14 @@ extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &k const std::string &base = "params/"); extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, machine_config &value, const std::string &base = "params/"); +extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, + machine_memory_range_descr &value, const std::string &base = "params/"); +extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, + machine_memory_range_descr &value, const std::string &base = "params/"); +extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, + machine_memory_range_descrs &value, const std::string &base = "params/"); +extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, + machine_memory_range_descrs &value, const std::string &base = "params/"); } // namespace cartesi diff --git a/src/jsonrpc-discover.json b/src/jsonrpc-discover.json index d13236f2b..ac8fd1562 100644 --- a/src/jsonrpc-discover.json +++ b/src/jsonrpc-discover.json @@ -1026,6 +1026,19 @@ "type": "boolean" } } + }, + + { + "name": "machine.get_memory_ranges", + "summary": "Returns a list with descriptions for all of the machine's memory ranges", + "params": [], + "result": { + "name": "ranges", + "description": "Array of memory range descriptions", + "schema": { + "$ref": "#/components/schemas/MemoryRangeDescriptionArray" + } + } } ], @@ -1668,7 +1681,32 @@ "type": "boolean" } } + }, + + "MemoryRangeDescription": { + "title": "MemoryRangeDescription", + "type": "object", + "properties": { + "start": { + "$ref": "#/components/schemas/UnsignedInteger" + }, + "length": { + "$ref": "#/components/schemas/UnsignedInteger" + }, + "description": { + "type": "string" + } + } + }, + + "MemoryRangeDescriptionArray": { + "title": "MemoryRangeDescriptionArray", + "type": "array", + "items": { + "$ref": "#/components/schemas/MemoryRangeDescription" + } } + } } } diff --git a/src/jsonrpc-remote-machine.cpp b/src/jsonrpc-remote-machine.cpp index ad0805ff9..81842a221 100644 --- a/src/jsonrpc-remote-machine.cpp +++ b/src/jsonrpc-remote-machine.cpp @@ -1504,6 +1504,20 @@ static json jsonrpc_machine_dump_pmas_handler(const json &j, mg_connection *con, return jsonrpc_response_ok(j); } +/// \brief JSONRPC handler for the machine.get_memory_ranges method +/// \param j JSON request object +/// \param con Mongoose connection +/// \param h Handler data +/// \returns JSON response object +static json jsonrpc_machine_get_memory_ranges_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); + return jsonrpc_response_ok(j, h->machine->get_memory_ranges()); +} + /// \brief Sends a JSONRPC response through the Mongoose connection /// \param con Mongoose connection /// \param j JSON response object @@ -1580,6 +1594,7 @@ static json jsonrpc_dispatch_method(const json &j, mg_connection *con, http_hand {"machine.verify_merkle_tree", jsonrpc_machine_verify_merkle_tree_handler}, {"machine.verify_dirty_page_maps", jsonrpc_machine_verify_dirty_page_maps_handler}, {"machine.dump_pmas", jsonrpc_machine_dump_pmas_handler}, + {"machine.get_memory_ranges", jsonrpc_machine_get_memory_ranges_handler}, }; auto method = j["method"].get(); SLOG(debug) << h->server_address << " handling \"" << method << "\" method"; diff --git a/src/jsonrpc-virtual-machine.cpp b/src/jsonrpc-virtual-machine.cpp index 45c237e79..d2e5c0306 100644 --- a/src/jsonrpc-virtual-machine.cpp +++ b/src/jsonrpc-virtual-machine.cpp @@ -868,6 +868,12 @@ uarch_interpreter_break_reason jsonrpc_virtual_machine::do_run_uarch(uint64_t ua return result; } +machine_memory_range_descrs jsonrpc_virtual_machine::do_get_memory_ranges(void) const { + machine_memory_range_descrs result; + jsonrpc_request(m_mgr->get_mgr(), m_mgr->get_remote_address(), "machine.get_memory_ranges", std::tie(), result); + return result; +} + #pragma GCC diagnostic pop } // namespace cartesi diff --git a/src/jsonrpc-virtual-machine.h b/src/jsonrpc-virtual-machine.h index f96f8b5f0..9aba44432 100644 --- a/src/jsonrpc-virtual-machine.h +++ b/src/jsonrpc-virtual-machine.h @@ -183,6 +183,7 @@ class jsonrpc_virtual_machine final : public i_virtual_machine { 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; + machine_memory_range_descrs do_get_memory_ranges(void) const override; jsonrpc_mg_mgr_ptr m_mgr; }; diff --git a/src/machine-c-api.cpp b/src/machine-c-api.cpp index 0808d5b7b..2807976b2 100644 --- a/src/machine-c-api.cpp +++ b/src/machine-c-api.cpp @@ -683,6 +683,27 @@ cartesi::access_log convert_from_c(const cm_access_log *c_acc_log) { return new_cpp_acc_log; } +// -------------------------------------------- +// Memory range description conversion functions +// -------------------------------------------- +cm_memory_range_descr convert_to_c(const cartesi::machine_memory_range_descr &cpp_mrd) { + cm_memory_range_descr new_mrd{}; + new_mrd.start = cpp_mrd.start; + new_mrd.length = cpp_mrd.length; + new_mrd.description = convert_to_c(cpp_mrd.description); + return new_mrd; +} + +cm_memory_range_descr_array *convert_to_c(const cartesi::machine_memory_range_descrs &cpp_mrds) { + auto *new_mrda = new cm_memory_range_descr_array{}; + new_mrda->count = cpp_mrds.size(); + new_mrda->entry = new cm_memory_range_descr[new_mrda->count]; + for (size_t i = 0; i < new_mrda->count; ++i) { + new_mrda->entry[i] = convert_to_c(cpp_mrds[i]); + } + return new_mrda; +} + // ----------------------------------------------------- // Public API functions for generation of default configs // ----------------------------------------------------- @@ -1349,3 +1370,25 @@ int cm_rollback(cm_machine *m, char **err_msg) try { } catch (...) { return cm_result_failure(err_msg); } + +CM_API int cm_get_memory_ranges(cm_machine *m, cm_memory_range_descr_array **mrds, char **err_msg) try { + if (mrds == nullptr) { + throw std::invalid_argument("invalid memory range output"); + } + auto *cpp_machine = convert_from_c(m); + *mrds = convert_to_c(cpp_machine->get_memory_ranges()); + return cm_result_success(err_msg); +} catch (...) { + return cm_result_failure(err_msg); +} + +CM_API void cm_delete_memory_range_descr_array(cm_memory_range_descr_array *mrds) { + if (mrds == nullptr) { + return; + } + for (size_t i = 0; i < mrds->count; ++i) { + delete[] mrds->entry[i].description; + } + delete[] mrds->entry; + delete mrds; +} diff --git a/src/machine-c-api.h b/src/machine-c-api.h index 7c411e89f..dc13071d2 100644 --- a/src/machine-c-api.h +++ b/src/machine-c-api.h @@ -384,6 +384,19 @@ typedef struct { // NOLINT(modernize-use-using) const char *build; } cm_semantic_version; +/// \brief Memory range description +typedef struct { // NOLINT(modernize-use-using) + uint64_t start; + uint64_t length; + const char *description; +} cm_memory_range_descr; + +/// \brief Memory range description array +typedef struct { // NOLINT(modernize-use-using) + cm_memory_range_descr *entry; + size_t count; +} cm_memory_range_descr_array; + // --------------------------------- // API function definitions // --------------------------------- @@ -1698,7 +1711,9 @@ CM_API int cm_set_uarch_halt_flag(cm_machine *m, char **err_msg); CM_API int cm_reset_uarch_state(cm_machine *m, 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 +/// (uarch_cycle) reaches uarch_cycle_end +/// \param m Pointer to valid machine instance +/// \param mcycle_end End cycle value /// \param status_result Receives status of machine run_uarch when not NULL /// \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 @@ -1708,6 +1723,22 @@ CM_API int cm_reset_uarch_state(cm_machine *m, char **err_msg); CM_API int cm_machine_run_uarch(cm_machine *m, uint64_t uarch_cycle_end, CM_UARCH_BREAK_REASON *status_result, char **err_msg); +/// \brief Returns an array with the description of each memory range in the machine. +/// \param m Pointer to valid machine instance +/// \param mrda Receives pointer to array of memory range descriptions. Must be deleted by the function caller using +/// cm_delete_memory_range_descr_array. +/// \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_get_memory_ranges(cm_machine *m, cm_memory_range_descr_array **mrda, char **err_msg); + +/// \brief Delete memory range description array acquired from cm_get_memory_ranges. +/// \param mrda Pointer to array of memory range descriptions to delete. +/// \returns void +CM_API void cm_delete_memory_range_descr_array(cm_memory_range_descr_array *mrda); + #ifdef __cplusplus } #endif diff --git a/src/machine-memory-range-descr.h b/src/machine-memory-range-descr.h new file mode 100644 index 000000000..245c450f8 --- /dev/null +++ b/src/machine-memory-range-descr.h @@ -0,0 +1,38 @@ +// 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 MACHINE_MEMORY_RANGE_DESCR_H +#define MACHINE_MEMORY_RANGE_DESCR_H + +#include +#include +#include + +namespace cartesi { + +/// \brief Description of memory range used for instrospection (i.e., get_memory_ranges()) +struct machine_memory_range_descr { + uint64_t start; ///< Start of memory range + uint64_t length; ///< Length of memory range + std::string description; ///< User-friendly description for memory range +}; + +/// \brief List of memory range descriptions used for instrospection (i.e., get_memory_ranges()) +using machine_memory_range_descrs = std::vector; + +} // namespace cartesi + +#endif diff --git a/src/machine.cpp b/src/machine.cpp index c48271340..2173178fa 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -468,6 +468,16 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : if (m_c.htif.console_getchar) { tty_initialize(); } + + // Initialize memory range descriptions returned by get_memory_ranges method + for (auto *pma : m_pmas) { + if (pma->get_length() != 0) { + m_mrds.push_back(machine_memory_range_descr{pma->get_start(), pma->get_length(), pma->get_description()}); + } + } + // Sort it by increasing start address + std::sort(m_mrds.begin(), m_mrds.end(), + [](const machine_memory_range_descr &a, const machine_memory_range_descr &b) { return a.start < b.start; }); } static void load_hash(const std::string &dir, machine::hash_type &h) { diff --git a/src/machine.h b/src/machine.h index b2d1cb0df..5d23ca0c6 100644 --- a/src/machine.h +++ b/src/machine.h @@ -26,6 +26,7 @@ #include "htif.h" #include "interpret.h" #include "machine-config.h" +#include "machine-memory-range-descr.h" #include "machine-merkle-tree.h" #include "machine-runtime-config.h" #include "machine-state.h" @@ -45,19 +46,20 @@ constexpr skip_merkle_tree_update_t skip_merkle_tree_update; /// \class machine /// \brief Cartesi Machine implementation class machine final { - +private: //??D Ideally, we would hold a unique_ptr to the state. This // would allow us to remove the machine-state.h include and // therefore hide its contents from anyone who includes only // machine.h. Maybe the compiler can do a good job we we are // not constantly going through the extra indirection. We // should test this. - mutable machine_state m_s; ///< Opaque machine state - mutable machine_merkle_tree m_t; ///< Merkle tree of state - std::vector m_pmas; ///< Combines uarch PMAs and machine state PMAs. - machine_config m_c; ///< Copy of initialization config - uarch_machine m_uarch; ///< Microarchitecture machine - machine_runtime_config m_r; ///< Copy of initialization runtime config + mutable machine_state m_s; ///< Opaque machine state + mutable machine_merkle_tree m_t; ///< Merkle tree of state + std::vector m_pmas; ///< Combines uarch PMAs and machine state PMAs for use with Merkle tree. + machine_config m_c; ///< Copy of initialization config + uarch_machine m_uarch; ///< Microarchitecture machine + machine_runtime_config m_r; ///< Copy of initialization runtime config + machine_memory_range_descrs m_mrds; ///< List of memory ranges returned by get_memory_ranges(). static const pma_entry::flags m_dtb_flags; ///< PMA flags used for DTB static const pma_entry::flags m_ram_flags; ///< PMA flags used for RAM @@ -253,6 +255,11 @@ class machine final { return m_s; } + /// \brief Returns a list of descriptions for all PMA entries registered in the machine, sorted by start + machine_memory_range_descrs get_memory_ranges(void) const { + return m_mrds; + } + /// \brief Destructor. ~machine(); diff --git a/src/protobuf-util.cpp b/src/protobuf-util.cpp index ba22f7818..5bbff4b9d 100644 --- a/src/protobuf-util.cpp +++ b/src/protobuf-util.cpp @@ -374,7 +374,7 @@ access_log get_proto_access_log(const CartesiMachine::AccessLog &proto_al) { processor_config get_proto_processor_config(const CartesiMachine::ProcessorConfig &proto_p) { using CartesiMachine::ProcessorConfig; - processor_config p; + processor_config p{}; if (proto_p.has_x1()) { p.x[1] = proto_p.x1(); } @@ -658,7 +658,7 @@ processor_config get_proto_processor_config(const CartesiMachine::ProcessorConfi } memory_range_config get_proto_memory_range_config(const CartesiMachine::MemoryRangeConfig &proto_m) { - memory_range_config m; + memory_range_config m{}; m.start = proto_m.start(); m.image_filename = proto_m.image_filename(); m.length = proto_m.length(); @@ -667,14 +667,14 @@ memory_range_config get_proto_memory_range_config(const CartesiMachine::MemoryRa } machine_runtime_config get_proto_machine_runtime_config(const CartesiMachine::MachineRuntimeConfig &proto_r) { - machine_runtime_config r; + machine_runtime_config r{}; r.concurrency.update_merkle_tree = proto_r.concurrency().update_merkle_tree(); return r; } static uarch_config get_proto_uarch_config(const CartesiMachine::UarchConfig &proto_c) { using CartesiMachine::UarchConfig; - uarch_config c; + uarch_config c{}; if (proto_c.has_ram()) { c.ram.length = proto_c.ram().length(); c.ram.image_filename = proto_c.ram().image_filename(); @@ -786,7 +786,7 @@ static uarch_config get_proto_uarch_config(const CartesiMachine::UarchConfig &pr } rollup_config get_proto_rollup_config(const CartesiMachine::RollupConfig &proto_r) { - rollup_config r; + rollup_config r{}; if (proto_r.has_rx_buffer()) { r.rx_buffer = get_proto_memory_range_config(proto_r.rx_buffer()); } @@ -806,7 +806,7 @@ rollup_config get_proto_rollup_config(const CartesiMachine::RollupConfig &proto_ } machine_config get_proto_machine_config(const CartesiMachine::MachineConfig &proto_c) { - machine_config c; + machine_config c{}; if (proto_c.has_processor()) { c.processor = get_proto_processor_config(proto_c.processor()); } @@ -855,4 +855,18 @@ machine_config get_proto_machine_config(const CartesiMachine::MachineConfig &pro return c; } +void set_proto_memory_range_descr(const machine_memory_range_descr &d, CartesiMachine::MemoryRangeDescription *proto_d) { + proto_d->set_start(d.start); + proto_d->set_length(d.length); + proto_d->set_description(d.description); +} + +machine_memory_range_descr get_proto_memory_range_descr(const CartesiMachine::MemoryRangeDescription &proto_d) { + machine_memory_range_descr mrd{}; + mrd.start = proto_d.start(); + mrd.length = proto_d.length(); + mrd.description = proto_d.description(); + return mrd; +} + } // namespace cartesi diff --git a/src/protobuf-util.h b/src/protobuf-util.h index bf5af2b2f..b871591f4 100644 --- a/src/protobuf-util.h +++ b/src/protobuf-util.h @@ -26,6 +26,7 @@ #include "access-log.h" #include "machine-c-defines.h" #include "machine-config.h" +#include "machine-memory-range-descr.h" #include "machine-runtime-config.h" #include "semantic-version.h" @@ -114,4 +115,15 @@ CM_API access_type get_proto_access_type(CartesiMachine::AccessType proto_at); /// \returns Converted C++ semantic_version CM_API semantic_version get_proto_semantic_version(const Versioning::SemanticVersion &proto_v); +/// \brief Converts C++ machine_memory_range_descr to proto MemoryRangeDescription +/// \param d C++ machine_memory_range_descr to convert +/// \param proto_d Pointer to proto repeated MemoryRangeDescription receiving result of conversion +CM_API void set_proto_memory_range_descr(const machine_memory_range_descr &d, + CartesiMachine::MemoryRangeDescription *proto_d); + +/// \brief Converts proto MemoryRangeDescription to C++ machine_memory_range_descr +/// \param proto_d Proto MemoryRangeDescription to convert +/// \returns Converted C++ machine_memory_range_descr +CM_API machine_memory_range_descr get_proto_memory_range_descr(const CartesiMachine::MemoryRangeDescription &proto_d); + } // namespace cartesi diff --git a/src/remote-machine.cpp b/src/remote-machine.cpp index b32eaf784..5b04f4c0a 100644 --- a/src/remote-machine.cpp +++ b/src/remote-machine.cpp @@ -1247,6 +1247,33 @@ class handler_VerifyStateTransition final : public handler { + + side_effect prepare(handler_context &hctx, ServerContext *sctx, Void *req, + ServerAsyncResponseWriter *writer) override { + hctx.s->RequestGetMemoryRanges(sctx, req, writer, hctx.cq.get(), hctx.cq.get(), this); + return side_effect::none; + } + + side_effect go(handler_context &hctx, Void *req, + ServerAsyncResponseWriter *writer) override { + (void) req; + if (!hctx.m) { + return finish_with_error_no_machine(writer); + } + GetMemoryRangesResponse resp; + for (auto &mrd: hctx.m->get_memory_ranges()) { + set_proto_memory_range_descr(mrd, resp.add_memory_range()); + } + return finish_ok(writer, resp); + } + +public: + handler_GetMemoryRanges(handler_context &hctx) { + advance(hctx); + } +}; + static std::string replace_port(const std::string &address, int port) { // Unix address? if (address.find("unix:") == 0) { @@ -1371,6 +1398,7 @@ static void server_loop(const char *server_address, const char *session_id, cons const handler_GetDefaultConfig hGetDefaultConfig(hctx); const handler_VerifyAccessLog hVerifyAccessLog(hctx); const handler_VerifyStateTransition hVerifyStateTransition(hctx); + const handler_GetMemoryRanges hGetMemoryRanges(hctx); // The invariant before and after snapshot/rollbacks is that all handlers // are in waiting mode diff --git a/src/test-machine-c-api.cpp b/src/test-machine-c-api.cpp index 2ff68aaf5..fc2a2cc7b 100644 --- a/src/test-machine-c-api.cpp +++ b/src/test-machine-c-api.cpp @@ -34,36 +34,6 @@ // NOLINTNEXTLINE #define BOOST_FIXTURE_TEST_CASE_NOLINT(...) BOOST_FIXTURE_TEST_CASE(__VA_ARGS__) -static hash_type get_verification_root_hash(cm_machine *machine) { - std::vector dump_list{{ - "0000000000000000--0000000000001000.bin", // shadow state - "0000000000010000--0000000000001000.bin", // shadow pmas - "0000000000020000--0000000000006000.bin", // shadow tlb - "0000000002000000--00000000000c0000.bin", // clint - "0000000040008000--0000000000001000.bin", // htif - "000000007ff00000--0000000000100000.bin", // dtb - "0000000080000000--0000000000100000.bin", // ram - }}; - char *err_msg{}; - - int error_code = cm_dump_pmas(machine, &err_msg); - BOOST_CHECK_EQUAL(error_code, CM_ERROR_OK); - BOOST_CHECK_EQUAL(err_msg, nullptr); - - const cm_machine_config *cfg{nullptr}; - BOOST_CHECK_EQUAL(cm_get_initial_config(machine, &cfg, &err_msg), CM_ERROR_OK); - if (cfg->uarch.ram.length) { - dump_list.push_back("0000000070000000--0000000000100000.bin"); // uarch ram - } - cm_delete_machine_config(cfg); - - auto hash = calculate_emulator_hash(dump_list); - for (const auto &file : dump_list) { - std::filesystem::remove(file); - } - return hash; -} - BOOST_AUTO_TEST_CASE_NOLINT(delete_machine_config_null_test) { BOOST_CHECK_NO_THROW(cm_delete_machine_config(nullptr)); } @@ -666,7 +636,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(get_root_hash_machine_hash_test, ordinary_machine BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); - auto verification = get_verification_root_hash(_machine); + auto verification = calculate_emulator_hash(_machine); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), result_hash, result_hash + sizeof(cm_hash)); } @@ -749,7 +719,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(get_proof_machine_hash_test, ordinary_machine_fix auto verification = calculate_proof_root_hash(p); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), p->root_hash, p->root_hash + sizeof(cm_hash)); - verification = get_verification_root_hash(_machine); + verification = calculate_emulator_hash(_machine); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), p->root_hash, p->root_hash + sizeof(cm_hash)); BOOST_CHECK_EQUAL(p->log2_root_size, static_cast(64)); @@ -2169,7 +2139,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(step_hash_test, access_log_machine_fixture) { BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); - auto verification = get_verification_root_hash(_machine); + auto verification = calculate_emulator_hash(_machine); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), hash1, hash1 + sizeof(cm_hash)); cm_delete_access_log(_access_log); @@ -2206,7 +2176,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(machine_run_1000_cycle_test, ordinary_machine_fix BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); - auto verification = get_verification_root_hash(_machine); + auto verification = calculate_emulator_hash(_machine); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), hash_1000, hash_1000 + sizeof(cm_hash)); } @@ -2252,7 +2222,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(machine_run_long_cycle_test, ordinary_machine_fix BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); - auto verification = get_verification_root_hash(_machine); + auto verification = calculate_emulator_hash(_machine); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), hash_end, hash_end + sizeof(cm_hash)); } @@ -2448,7 +2418,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(machine_verify_merkle_tree_root_updates_test, ord int error_code = cm_get_root_hash(_machine, &start_hash, &err_msg); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); - auto verification = get_verification_root_hash(_machine); + auto verification = calculate_emulator_hash(_machine); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), start_hash, start_hash + sizeof(cm_hash)); error_code = cm_machine_run(_machine, 1000, nullptr, &err_msg); @@ -2459,7 +2429,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(machine_verify_merkle_tree_root_updates_test, ord error_code = cm_get_root_hash(_machine, &end_hash, &err_msg); BOOST_REQUIRE_EQUAL(error_code, CM_ERROR_OK); BOOST_REQUIRE_EQUAL(err_msg, nullptr); - verification = get_verification_root_hash(_machine); + verification = calculate_emulator_hash(_machine); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), end_hash, end_hash + sizeof(cm_hash)); } @@ -2473,7 +2443,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(machine_verify_merkle_tree_proof_updates_test, or auto verification = calculate_proof_root_hash(start_proof); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), start_proof->root_hash, start_proof->root_hash + sizeof(cm_hash)); - verification = get_verification_root_hash(_machine); + verification = calculate_emulator_hash(_machine); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), start_proof->root_hash, start_proof->root_hash + sizeof(cm_hash)); cm_delete_merkle_tree_proof(start_proof); @@ -2489,7 +2459,7 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(machine_verify_merkle_tree_proof_updates_test, or verification = calculate_proof_root_hash(end_proof); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), end_proof->root_hash, end_proof->root_hash + sizeof(cm_hash)); - verification = get_verification_root_hash(_machine); + verification = calculate_emulator_hash(_machine); BOOST_CHECK_EQUAL_COLLECTIONS(verification.begin(), verification.end(), end_proof->root_hash, end_proof->root_hash + sizeof(cm_hash)); cm_delete_merkle_tree_proof(end_proof); diff --git a/src/test-utils.h b/src/test-utils.h index 4d7f67fb7..1a40a5060 100644 --- a/src/test-utils.h +++ b/src/test-utils.h @@ -48,6 +48,7 @@ static hash_type merkle_hash(cartesi::keccak_256_hasher &h, const std::string_vi } return result; } + } // namespace detail static hash_type merkle_hash(const std::string_view &data, int log2_size) { @@ -105,46 +106,35 @@ static int ceil_log2(uint64_t x) { return static_cast(std::ceil(std::log2(static_cast(x)))); } -static hash_type calculate_emulator_hash(const std::vector &pmas_files) { - struct pma_entry { - std::string path; - uint64_t start; - uint64_t length; - std::string data; - }; - std::vector pma_entries; - std::transform(pmas_files.begin(), pmas_files.end(), std::back_inserter(pma_entries), [](const std::string &path) { - uint64_t start; - uint64_t length; - int end = 0; - if (sscanf(path.data(), "%" SCNx64 "--%" SCNx64 ".bin%n", &start, &length, &end) != 2 || - static_cast(path.size()) != end) { - throw std::invalid_argument("PMA filename '" + path + "' does not match '%x--%x.bin'"); - } - if ((length >> detail::PAGE_LOG2_SIZE) << detail::PAGE_LOG2_SIZE != length) { - throw std::invalid_argument("PMA '" + path + "' length not multiple of page length"); - } - if ((start >> detail::PAGE_LOG2_SIZE) << detail::PAGE_LOG2_SIZE != start) { - throw std::invalid_argument("PMA '" + path + "' start not page-aligned"); - } - auto data = load_file(path); - if (data.length() != length) { - throw std::invalid_argument("PMA '" + path + "' length does not match filename"); - } - return pma_entry{path, start, length, std::move(data)}; - }); - std::sort(pma_entries.begin(), pma_entries.end(), - [](const pma_entry &a, const pma_entry &b) { return a.start < b.start; }); +static hash_type calculate_emulator_hash(cm_machine *machine) { cartesi::back_merkle_tree tree(64, 12, 3); + std::string page; + page.resize(detail::PAGE_SIZE); + cm_memory_range_descr_array *mrds = nullptr; + auto mrds_deleter = [](cm_memory_range_descr_array **mrds) { + cm_delete_memory_range_descr_array(*mrds); + }; + std::unique_ptr auto_mrds(&mrds, mrds_deleter); + char *err_msg = nullptr; + auto err_msg_deleter = [](char **str) { cm_delete_cstring(*str); }; + std::unique_ptr auto_err_msg(&err_msg, err_msg_deleter); + if (cm_get_memory_ranges(machine, &mrds, &err_msg) != 0) { + throw std::runtime_error{err_msg}; + } uint64_t last = 0; - for (const auto &e : pma_entries) { - tree.pad_back((e.start - last) >> detail::PAGE_LOG2_SIZE); - for (uint64_t s = 0; s < e.length; s += detail::PAGE_SIZE) { - std::string_view page{e.data.data() + s, detail::PAGE_SIZE}; + for (size_t i = 0; i < mrds->count; ++i) { + const auto &m = mrds->entry[i]; + tree.pad_back((m.start - last) >> detail::PAGE_LOG2_SIZE); + auto end = m.start + m.length; + for (uint64_t s = m.start; s < end; s += detail::PAGE_SIZE) { + if (cm_read_memory(machine, s, reinterpret_cast(page.data()), page.size(), &err_msg) != + 0) { + throw std::runtime_error{err_msg}; + } auto page_hash = merkle_hash(page, detail::PAGE_LOG2_SIZE); tree.push_back(page_hash); } - last = e.start + e.length; + last = end; } return tree.get_root_hash(); } diff --git a/src/tests/machine-bind.lua b/src/tests/machine-bind.lua index 65bb2bbe5..2049eb899 100755 --- a/src/tests/machine-bind.lua +++ b/src/tests/machine-bind.lua @@ -490,7 +490,7 @@ do_test("should return expected value", function(machine) print("Root hash: ", test_util.tohex(root_hash)) machine:dump_pmas() - local calculated_root_hash = test_util.calculate_emulator_hash(pmas_file_names) + local calculated_root_hash = test_util.calculate_emulator_hash(machine) for _, file_name in pairs(pmas_file_names) do os.remove(test_path .. file_name) end diff --git a/src/tests/machine-test.lua b/src/tests/machine-test.lua index dfe2f6eae..f3812f03c 100755 --- a/src/tests/machine-test.lua +++ b/src/tests/machine-test.lua @@ -148,27 +148,6 @@ local function connect() return remote, version end -local pmas_file_names = { - "0000000000000000--0000000000001000.bin", -- shadow state - "0000000000010000--0000000000001000.bin", -- shadow pmas - "0000000000020000--0000000000006000.bin", -- shadow tlb - "0000000002000000--00000000000c0000.bin", -- clint - "0000000040008000--0000000000001000.bin", -- htif - "000000007ff00000--0000000000100000.bin", -- dtb - "0000000080000000--0000000000100000.bin", -- ram -} - -local pmas_file_names_with_uarch = { - "0000000000000000--0000000000001000.bin", -- shadow state - "0000000000010000--0000000000001000.bin", -- shadow pmas - "0000000000020000--0000000000006000.bin", -- shadow tlb - "0000000002000000--00000000000c0000.bin", -- clint - "0000000040008000--0000000000001000.bin", -- htif - "000000007ff00000--0000000000100000.bin", -- dtb - "0000000080000000--0000000000100000.bin", -- ram - "0000000070000000--0000000000100000.bin", -- uarch ram -} - local remote local function build_machine(type, config) config = config or { @@ -207,12 +186,6 @@ end local do_test = test_util.make_do_test(build_machine, machine_type) -local function remove_files(file_names) - for _, file_name in pairs(file_names) do - os.remove(test_path .. file_name) - end -end - print("Testing machine for type " .. machine_type) print("\n\ntesting getting machine intial config and iflags") @@ -230,29 +203,23 @@ do_test("machine halt and yield flags and config matches", function(machine) end) print("\n\ntesting memory pmas dump to files") -do_test("dumped file hashes should match memory data hashes", function(machine) - -- Dump memory regions to files - -- Calculate hash for memory regions - -- Check if match memory data hash +do_test("dumped files should match memory contents", function(machine) + -- Dump memory ranges to files machine:dump_pmas() - - for _, file_name in pairs(pmas_file_names) do - print("Checking dump file " .. file_name) - - local temp = test_util.split_string(file_name, "--.") - local data_region_start = tonumber(temp[1], 16) - local data_region_size = tonumber(temp[2], 16) - - local dump = assert(io.open(file_name, "rb")) - local dump_hash = md5.sumhexa(dump:read("*all")) - dump:close() - - local memory_read = machine:read_memory(data_region_start, data_region_size) - local memory_hash = md5.sumhexa(memory_read) - - assert(dump_hash == memory_hash, "hash does not match for dump file " .. file_name) + -- Obtain list of memory ranges from machine + local pmas = machine:get_memory_ranges() + for _, v in ipairs(pmas) do + -- Add corresponding expected dumped filename + v.filename = string.format("%016x--%016x.bin", v.start, v.length) + end + -- Read directly from machine and compare with file contents + for _, v in ipairs(pmas) do + local dump = assert(io.open(v.filename, "rb")) + local dump_read = dump:read("*all") + local memory_read = machine:read_memory(v.start, v.length) + assert(dump_read == memory_read, "dump file does not match memory read (" .. v.filename .. ")") + os.remove(v.filename) end - remove_files(pmas_file_names) end) print("\n\ntesting if machine initial hash is correct") @@ -260,14 +227,11 @@ do_test("machine initial hash should match", function(machine) -- Get starting root hash local root_hash = machine:get_root_hash() - machine:dump_pmas() - local calculated_root_hash = test_util.calculate_emulator_hash(pmas_file_names) + local calculated_root_hash = test_util.calculate_emulator_hash(machine) print("Root hash:", test_util.tohex(root_hash), " calculated root hash:", test_util.tohex(calculated_root_hash)) - assert(test_util.tohex(root_hash) == test_util.tohex(calculated_root_hash), "Initial root hash does not match") - - remove_files(pmas_file_names) + assert(root_hash == calculated_root_hash, "Initial root hash does not match") end) print("\n\ntesting root hash after step one") @@ -278,11 +242,9 @@ test_util.make_do_test(build_uarch_machine, machine_type)( local root_hash = machine:get_root_hash() print("Root hash:", test_util.tohex(root_hash)) - machine:dump_pmas() - local calculated_root_hash = test_util.calculate_emulator_hash(pmas_file_names_with_uarch) - remove_files(pmas_file_names) + local calculated_root_hash = test_util.calculate_emulator_hash(machine) - assert(test_util.tohex(root_hash) == test_util.tohex(calculated_root_hash), "Initial root hash does not match") + assert(root_hash == calculated_root_hash, "Initial root hash does not match") -- Perform step, dump address space to file, calculate emulator root hash -- and check if maches @@ -290,16 +252,9 @@ test_util.make_do_test(build_uarch_machine, machine_type)( machine:step_uarch(log_type) local root_hash_step1 = machine:get_root_hash() - machine:dump_pmas() - local calculated_root_hash_step1 = test_util.calculate_emulator_hash(pmas_file_names_with_uarch) - - -- Remove dumped pmas files - remove_files(pmas_file_names) + local calculated_root_hash_step1 = test_util.calculate_emulator_hash(machine) - assert( - test_util.tohex(root_hash_step1) == test_util.tohex(calculated_root_hash_step1), - "hash after first step does not match" - ) + assert(root_hash_step1 == calculated_root_hash_step1, "hash after first step does not match") end ) @@ -307,34 +262,24 @@ print("\n\ntesting proof after step one") test_util.make_do_test(build_uarch_machine, machine_type)("proof check should pass", function(machine) local log_type = {} machine:step_uarch(log_type) - - -- Dump RAM memory to file, calculate hash of file - -- get proof of ram using get_proof and check if - -- hashes match - machine:dump_pmas() - local ram_file_name = pmas_file_names[7] - local ram = test_util.load_file(ram_file_name) - - remove_files(pmas_file_names) - - local ram_address_start = tonumber(test_util.split_string(ram_file_name, "--.")[1], 16) - local ram_log2_size = math.ceil(math.log(#ram, 2)) - local calculated_ram_hash = test_util.merkle_hash(ram, 0, ram_log2_size) - - local ram_proof = machine:get_proof(ram_address_start, ram_log2_size) + -- find ram memory range + local ram + for _, v in ipairs(machine:get_memory_ranges()) do + print(v.description) + if v.description == "RAM" then ram = v end + end + local ram_log2_size = math.ceil(math.log(ram.length, 2)) + local calculated_ram_hash = test_util.merkle_hash(machine:read_memory(ram.start, ram.length), 0, ram_log2_size) + local ram_proof = machine:get_proof(ram.start, ram_log2_size) local root_hash = machine:get_root_hash() - - assert(test_util.tohex(root_hash) == test_util.tohex(ram_proof.root_hash), "root hash in proof does not match") + assert(root_hash == ram_proof.root_hash, "root hash in proof does not match") print( "target hash:", test_util.tohex(ram_proof.target_hash), " calculated target hash:", test_util.tohex(calculated_ram_hash) ) - assert( - test_util.tohex(calculated_ram_hash) == test_util.tohex(ram_proof.target_hash), - "target hash in proof does not match" - ) + assert(calculated_ram_hash == ram_proof.target_hash, "target hash in proof does not match") end) print("\n\nrun machine to 1000 mcycle and check for mcycle and root hash") @@ -350,16 +295,10 @@ do_test("mcycle and root hash should match", function(machine) local root_hash = machine:get_root_hash() - machine:dump_pmas() - local calculated_root_hash_1000 = test_util.calculate_emulator_hash(pmas_file_names) - -- Remove dumped pmas files - remove_files(pmas_file_names) + local calculated_root_hash_1000 = test_util.calculate_emulator_hash(machine) print("1000 cycle hash: ", test_util.tohex(root_hash)) - assert( - test_util.tohex(root_hash) == test_util.tohex(calculated_root_hash_1000), - "machine hash does not match after 1000 cycles" - ) + assert(root_hash == calculated_root_hash_1000, "machine hash does not match after 1000 cycles") end) print("\n\nrun machine to end mcycle and check for mcycle, hash and halt flag") @@ -381,15 +320,9 @@ do_test("mcycle and root hash should match", function(machine) local root_hash = machine:get_root_hash() print("End hash: ", test_util.tohex(root_hash)) - machine:dump_pmas() - local calculated_end_hash = test_util.calculate_emulator_hash(pmas_file_names) - -- Remove dumped pmas files - remove_files(pmas_file_names) + local calculated_end_hash = test_util.calculate_emulator_hash(machine) - assert( - test_util.tohex(root_hash) == test_util.tohex(calculated_end_hash), - "machine hash does not match after on end cycle" - ) + assert(root_hash == calculated_end_hash, "machine hash does not match after on end cycle") end) print("\n\nwrite something to ram memory and check if hash and proof matches") @@ -401,10 +334,7 @@ do_test("proof and root hash should match", function(machine) -- Calculate hash local initial_memory_read = machine:read_memory(ram_address_start, 2 ^ 10) local initial_calculated_hash = test_util.merkle_hash(initial_memory_read, 0, 10) - assert( - test_util.tohex(initial_ram_proof.target_hash) == test_util.tohex(initial_calculated_hash), - "initial hash does not match" - ) + assert(initial_ram_proof.target_hash == initial_calculated_hash, "initial hash does not match") print( "initial target hash:", @@ -431,20 +361,11 @@ do_test("proof and root hash should match", function(machine) test_util.tohex(calculated_hash) ) - assert( - test_util.tohex(initial_ram_proof.target_hash) ~= test_util.tohex(ram_proof.target_hash), - "hash is same after memory is written" - ) + assert(initial_ram_proof.target_hash ~= ram_proof.target_hash, "hash is same after memory is written") - assert( - test_util.tohex(initial_calculated_hash) ~= test_util.tohex(calculated_hash), - "calculated hash is same after memory is written" - ) + assert(initial_calculated_hash ~= calculated_hash, "calculated hash is same after memory is written") - assert( - test_util.tohex(ram_proof.target_hash) == test_util.tohex(calculated_hash), - "hash does not match after memory is written" - ) + assert(ram_proof.target_hash == calculated_hash, "hash does not match after memory is written") end) print("\n\n check dirty page maps") diff --git a/src/tests/util.lua b/src/tests/util.lua index 53b834db7..7fcd8f3e2 100644 --- a/src/tests/util.lua +++ b/src/tests/util.lua @@ -244,27 +244,17 @@ test_util.merkle_hash = merkle_hash -- Take data from dumped memory files -- and calculate root hash of the machine -function test_util.calculate_emulator_hash(pmas_files) - local pmas = {} - for _, pma_file in ipairs(pmas_files) do - local start, length = string.match(pma_file, "^(%x+)%-%-(%x+).bin$") - pmas[#pmas + 1] = { - path = pma_file, - start = assert(tonumber(start, 16), "invalid PMA start in dumped filename"), - length = assert(tonumber(length, 16), "invalid PMA length in dumped filename"), - data = test_util.load_file(pma_file), - } - end - table.sort(pmas, function(a, b) return a.start < b.start end) +function test_util.calculate_emulator_hash(machine) local tree = test_util.new_back_merkle_tree(64, PAGE_LOG2_SIZE) local last = 0 - for _, v in ipairs(pmas) do + for _, v in ipairs(machine:get_memory_ranges()) do tree:pad_back((v.start - last) >> PAGE_LOG2_SIZE) - for j = 0, v.length - 1, PAGE_SIZE do - local page_hash = merkle_hash(v.data, j, PAGE_LOG2_SIZE) + local finish = v.start + v.length + for j = v.start, finish - 1, PAGE_SIZE do + local page_hash = merkle_hash(machine:read_memory(j, PAGE_SIZE), 0, PAGE_LOG2_SIZE) tree:push_back(page_hash) end - last = v.start + v.length + last = finish end return tree:get_root_hash() end diff --git a/src/virtual-machine.cpp b/src/virtual-machine.cpp index eb2ebcc70..61d225a16 100644 --- a/src/virtual-machine.cpp +++ b/src/virtual-machine.cpp @@ -492,4 +492,8 @@ uarch_interpreter_break_reason virtual_machine::do_run_uarch(uint64_t uarch_cycl return m_machine->run_uarch(uarch_cycle_end); } +machine_memory_range_descrs virtual_machine::do_get_memory_ranges(void) const { + return m_machine->get_memory_ranges(); +} + } // namespace cartesi diff --git a/src/virtual-machine.h b/src/virtual-machine.h index da5249ad2..261c4c42b 100644 --- a/src/virtual-machine.h +++ b/src/virtual-machine.h @@ -156,6 +156,7 @@ class virtual_machine : public i_virtual_machine { void do_reset_uarch_state() override; bool do_read_uarch_halt_flag(void) const override; uarch_interpreter_break_reason do_run_uarch(uint64_t uarch_cycle_end) override; + machine_memory_range_descrs do_get_memory_ranges(void) const override; }; } // namespace cartesi