From 83f72e11f0fd5a28fccb8e0108c51ee719f0f92d Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Tue, 9 Jan 2024 13:26:49 -0300 Subject: [PATCH] feat!: add VirtIO configs to C/Lua APIs --- src/clua-machine-util.cpp | 148 ++++++++++++++++++++++++++++++++++++++ src/dtb.cpp | 23 +----- src/machine-c-api.cpp | 125 ++++++++++++++++++++++++++++++++ src/machine-c-api.h | 57 +++++++++++++++ src/machine-c-defines.h | 10 +-- src/machine-config.h | 54 ++++++++++++-- src/machine.cpp | 66 +++++++++++------ 7 files changed, 429 insertions(+), 54 deletions(-) diff --git a/src/clua-machine-util.cpp b/src/clua-machine-util.cpp index b6478383a..6940360ea 100644 --- a/src/clua-machine-util.cpp +++ b/src/clua-machine-util.cpp @@ -885,6 +885,16 @@ static void push_cm_memory_range_config(lua_State *L, const cm_memory_range_conf clua_setbooleanfield(L, m->shared, "shared", -1); } +/// \brief Pushes cm_virtio_hostfwd_config to the Lua stack +/// \param L Lua state. +/// \param m VirtIO host forward config to be pushed. +static void push_cm_virtio_hostfwd_config(lua_State *L, const cm_virtio_hostfwd_config *m) { + lua_newtable(L); + clua_setbooleanfield(L, m->is_udp, "is_udp", -1); + clua_setintegerfield(L, m->host_port, "host_port", -1); + clua_setintegerfield(L, m->guest_port, "guest_port", -1); +} + /// \brief Pushes cm_rollup_config to the Lua stack /// \param L Lua state. /// \param r Rollup config to be pushed. @@ -913,6 +923,57 @@ static void push_cm_flash_drive_configs(lua_State *L, const cm_memory_range_conf } } +/// \brief Pushes cm_virtio_hostfwd_configs to the Lua stack +/// \param L Lua state. +/// \param virtio_hostfwds VirtIO host forward configuration array to be pushed. +static void push_cm_virtio_hostfwd_configs(lua_State *L, const cm_virtio_hostfwd_config_array *virtio_hostfwds) { + lua_newtable(L); + for (size_t j = 0; j < virtio_hostfwds->count; ++j) { + push_cm_virtio_hostfwd_config(L, &virtio_hostfwds->entry[j]); + lua_rawseti(L, -2, static_cast(j) + 1); + } +} + +/// \brief Pushes cm_virtio_device_config to the Lua stack +/// \param L Lua state. +/// \param m VirtIO device config to be pushed. +static void push_cm_virtio_device_config(lua_State *L, const cm_virtio_device_config *v) { + lua_newtable(L); + switch (v->type) { + case CM_VIRTIO_DEVICE_CONSOLE: + clua_setstringfield(L, "console", "type", -1); + break; + case CM_VIRTIO_DEVICE_P9FS: + clua_setstringfield(L, "p9fs", "type", -1); + clua_setstringfield(L, v->device.p9fs.tag, "tag", -1); + clua_setstringfield(L, v->device.p9fs.host_directory, "host_directory", -1); + break; + case CM_VIRTIO_DEVICE_NET_USER: + clua_setstringfield(L, "net-user", "type", -1); + push_cm_virtio_hostfwd_configs(L, &v->device.net_user.hostfwd); + lua_setfield(L, -2, "hostfwd"); + break; + case CM_VIRTIO_DEVICE_NET_TUNTAP: + clua_setstringfield(L, "net-tuntap", "type", -1); + clua_setstringfield(L, v->device.net_tuntap.iface, "iface", -1); + break; + default: + luaL_error(L, "invalid virtio device config type"); + break; + } +} + +/// \brief Pushes cm_virtio_config_array to the Lua stack +/// \param L Lua state. +/// \param virtio VirtIO configuration array to be pushed. +static void push_cm_virtio_configs(lua_State *L, const cm_virtio_config_array *virtio) { + lua_newtable(L); + for (size_t j = 0; j < virtio->count; ++j) { + push_cm_virtio_device_config(L, &virtio->entry[j]); + lua_rawseti(L, -2, static_cast(j) + 1); + } +} + /// \brief Pushes a cm_uarch_ram_config to the Lua stack /// \param L Lua state. /// \param r microarchitecture RAM configuration to be pushed. @@ -964,6 +1025,8 @@ void clua_push_cm_machine_config(lua_State *L, const cm_machine_config *c) { lua_setfield(L, -2, "plic"); // config push_cm_flash_drive_configs(L, &c->flash_drive); // config flash_drive lua_setfield(L, -2, "flash_drive"); // config + push_cm_virtio_configs(L, &c->virtio); // config virtio + lua_setfield(L, -2, "virtio"); // config push_cm_ram_config(L, &c->ram); // config ram lua_setfield(L, -2, "ram"); // config push_cm_dtb_config(L, &c->dtb); // config dtb @@ -1030,6 +1093,17 @@ cm_memory_range_config *clua_check_cm_memory_range_config(lua_State *L, int tabi return m; } +cm_virtio_hostfwd_config *clua_check_cm_virtio_hostfwd_config(lua_State *L, int tabidx, const char *what, + cm_virtio_hostfwd_config *m) { + if (!lua_istable(L, tabidx)) { + luaL_error(L, "%s virtio hostfwd not a table", what); + } + m->is_udp = opt_boolean_field(L, tabidx, "is_udp"); + m->guest_port = check_uint_field(L, tabidx, "guest_port"); + m->host_port = check_uint_field(L, tabidx, "host_port"); + return m; +} + /// \brief Loads rollup config from Lua to cm_rollup_config /// \param L Lua state /// \param tabidx Config stack index @@ -1082,6 +1156,79 @@ static void check_cm_flash_drive_configs(lua_State *L, int tabidx, cm_memory_ran lua_pop(L, 1); } +/// \brief Loads a C api VirtIO host forward configs from a Lua machine config +/// \param L Lua state +/// \param tabidx Machine config stack index +/// \param fs Receives allocated array of VirtIO host forward configs +static void check_cm_virtio_hostfwd_configs(lua_State *L, int tabidx, cm_virtio_hostfwd_config_array *hostfwds) { + memset(hostfwds, 0, sizeof(cm_virtio_hostfwd_config_array)); + if (!opt_table_field(L, tabidx, "hostfwd")) { + return; + } + auto virtio_hostfwd_table_idx = lua_gettop(L); + const size_t count = luaL_len(L, virtio_hostfwd_table_idx); + if (count > CM_VIRTIO_HOSTFWD_CONFIGS_MAX_SIZE) { + luaL_error(L, "too many host forwards (expected max %d, got %d)", CM_VIRTIO_HOSTFWD_CONFIGS_MAX_SIZE, + static_cast(hostfwds->count)); + } + hostfwds->count = count; + hostfwds->entry = new cm_virtio_hostfwd_config[count]{}; + for (unsigned i = 1; i <= hostfwds->count; ++i) { + lua_geti(L, virtio_hostfwd_table_idx, i); + clua_check_cm_virtio_hostfwd_config(L, -1, "hostfwd", &hostfwds->entry[i - 1]); + lua_pop(L, 1); + } + lua_pop(L, 1); +} + +cm_virtio_device_config *clua_check_cm_virtio_device_config(lua_State *L, int tabidx, cm_virtio_device_config *m) { + if (!lua_istable(L, tabidx)) { + luaL_error(L, "virtio device not a table"); + } + const std::string type = check_string_field(L, tabidx, "type"); + if (type == "console") { + m->type = CM_VIRTIO_DEVICE_CONSOLE; + } else if (type == "p9fs") { + m->type = CM_VIRTIO_DEVICE_P9FS; + m->device.p9fs.tag = opt_copy_string_field(L, tabidx, "tag"); + m->device.p9fs.host_directory = opt_copy_string_field(L, tabidx, "host_directory"); + } else if (type == "net-user") { + m->type = CM_VIRTIO_DEVICE_NET_USER; + check_cm_virtio_hostfwd_configs(L, tabidx, &m->device.net_user.hostfwd); + } else if (type == "net-tuntap") { + m->type = CM_VIRTIO_DEVICE_NET_TUNTAP; + m->device.net_tuntap.iface = opt_copy_string_field(L, tabidx, "iface"); + } else { + luaL_error(L, "invalid virtio device type '%s'", type.c_str()); + } + return m; +} + +/// \brief Loads a C api virtio configs from a Lua machine config +/// \param L Lua state +/// \param tabidx Machine config stack index +/// \param virtio Receives allocated array of virtio configs +static void check_cm_virtio_configs(lua_State *L, int tabidx, cm_virtio_config_array *virtio) { + memset(virtio, 0, sizeof(cm_virtio_config_array)); + if (!opt_table_field(L, tabidx, "virtio")) { + return; + } + auto virtio_table_idx = lua_gettop(L); + const size_t count = luaL_len(L, virtio_table_idx); + if (count > CM_VIRTIO_CONFIGS_MAX_SIZE) { + luaL_error(L, "too many virtio devices (expected max %d, got %d)", CM_VIRTIO_CONFIGS_MAX_SIZE, + static_cast(virtio->count)); + } + virtio->count = count; + virtio->entry = new cm_virtio_device_config[count]{}; + for (unsigned i = 1; i <= virtio->count; ++i) { + lua_geti(L, virtio_table_idx, i); + clua_check_cm_virtio_device_config(L, -1, &virtio->entry[i - 1]); + lua_pop(L, 1); + } + lua_pop(L, 1); +} + /// \brief Loads processor config from a Lua to C api machine config /// \param L Lua state /// \param tabidx Config stack index @@ -1294,6 +1441,7 @@ cm_machine_config *clua_check_cm_machine_config(lua_State *L, int tabidx, int ct check_cm_uarch_config(L, tabidx, &config->uarch); check_cm_rollup_config(L, tabidx, &config->rollup); check_cm_flash_drive_configs(L, tabidx, &config->flash_drive); + check_cm_virtio_configs(L, tabidx, &config->virtio); managed.release(); lua_pop(L, 1); //??DD I don't think lua_pop can throw, but we should check return config; diff --git a/src/dtb.cpp b/src/dtb.cpp index 178c8cdaf..d3091972e 100644 --- a/src/dtb.cpp +++ b/src/dtb.cpp @@ -141,28 +141,7 @@ void dtb_init(const machine_config &c, unsigned char *dtb_start, uint64_t dtb_le fdt.prop_u32_list<2>("interrupts-extended", {INTC_PHANDLE, X_HOST}); fdt.end_node(); } - if (c.htif.console_getchar) { // virtio console - const uint32_t virtio_idx = 0; - const uint64_t virtio_paddr = PMA_FIRST_VIRTIO_START + virtio_idx * PMA_VIRTIO_LENGTH; - const uint32_t plic_irq_id = virtio_idx + 1; - fdt.begin_node_num("virtio", virtio_paddr); - fdt.prop_string("compatible", "virtio,mmio"); - fdt.prop_u64_list<2>("reg", {virtio_paddr, PMA_VIRTIO_LENGTH}); - fdt.prop_u32_list<2>("interrupts-extended", {PLIC_PHANDLE, plic_irq_id}); - fdt.end_node(); - } - if (c.htif.console_getchar) { // virtio 9p - const uint32_t virtio_idx = 1; - const uint64_t virtio_paddr = PMA_FIRST_VIRTIO_START + virtio_idx * PMA_VIRTIO_LENGTH; - const uint32_t plic_irq_id = virtio_idx + 1; - fdt.begin_node_num("virtio", virtio_paddr); - fdt.prop_string("compatible", "virtio,mmio"); - fdt.prop_u64_list<2>("reg", {virtio_paddr, PMA_VIRTIO_LENGTH}); - fdt.prop_u32_list<2>("interrupts-extended", {PLIC_PHANDLE, plic_irq_id}); - fdt.end_node(); - } - if (c.htif.console_getchar) { // virtio net - const uint32_t virtio_idx = 2; + for (uint32_t virtio_idx = 0; virtio_idx < c.virtio.size(); ++virtio_idx) { // virtio const uint64_t virtio_paddr = PMA_FIRST_VIRTIO_START + virtio_idx * PMA_VIRTIO_LENGTH; const uint32_t plic_irq_id = virtio_idx + 1; fdt.begin_node_num("virtio", virtio_paddr); diff --git a/src/machine-c-api.cpp b/src/machine-c-api.cpp index 2282dc370..cd16a2375 100644 --- a/src/machine-c-api.cpp +++ b/src/machine-c-api.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "i-virtual-machine.h" #include "machine-c-api-internal.h" @@ -220,6 +222,94 @@ static cm_memory_range_config convert_to_c(const cartesi::memory_range_config &c return new_c_memory_range_config; } +// ---------------------------------------------- +// VirtIO host forward configuration conversion functions +// ---------------------------------------------- +static cartesi::virtio_hostfwd_config convert_from_c(const cm_virtio_hostfwd_config *c_config) { + if (c_config == nullptr) { + throw std::invalid_argument("invalid memory range configuration"); + } + cartesi::virtio_hostfwd_config new_cpp_virtio_hostfwd_config{c_config->is_udp, c_config->host_port, + c_config->guest_port}; + return new_cpp_virtio_hostfwd_config; +} + +static cm_virtio_hostfwd_config convert_to_c(const cartesi::virtio_hostfwd_config &cpp_config) { + cm_virtio_hostfwd_config new_c_virtio_hostfwd_config{}; + new_c_virtio_hostfwd_config.is_udp = cpp_config.is_udp; + new_c_virtio_hostfwd_config.guest_port = cpp_config.guest_port; + new_c_virtio_hostfwd_config.host_port = cpp_config.host_port; + return new_c_virtio_hostfwd_config; +} + +// ---------------------------------------------- +// VirtIO device configuration conversion functions +// ---------------------------------------------- +static cartesi::virtio_device_config convert_from_c(const cm_virtio_device_config *c_config) { + if (c_config == nullptr) { + throw std::invalid_argument("invalid virtio device configuration"); + } + switch (c_config->type) { + case CM_VIRTIO_DEVICE_CONSOLE: + return cartesi::virtio_console_config{}; + case CM_VIRTIO_DEVICE_P9FS: { + cartesi::virtio_p9fs_config new_cpp_virtio_device_config{}; + new_cpp_virtio_device_config.tag = null_to_empty(c_config->device.p9fs.tag); + new_cpp_virtio_device_config.host_directory = null_to_empty(c_config->device.p9fs.host_directory); + return new_cpp_virtio_device_config; + } + case CM_VIRTIO_DEVICE_NET_USER: { + cartesi::virtio_net_user_config new_cpp_virtio_device_config{}; + for (size_t i = 0; i < c_config->device.net_user.hostfwd.count; ++i) { + new_cpp_virtio_device_config.hostfwd.push_back( + convert_from_c(&(c_config->device.net_user.hostfwd.entry[i]))); + } + return new_cpp_virtio_device_config; + } + case CM_VIRTIO_DEVICE_NET_TUNTAP: { + cartesi::virtio_net_tuntap_config new_cpp_virtio_device_config{}; + new_cpp_virtio_device_config.iface = null_to_empty(c_config->device.net_tuntap.iface); + return new_cpp_virtio_device_config; + } + default: + throw std::invalid_argument("invalid virtio device configuration"); + } +} + +static cm_virtio_device_config convert_to_c(const cartesi::virtio_device_config &cpp_config) { + return std::visit( + [](const auto &cpp_virtio_device_config) -> cm_virtio_device_config { + using T = std::decay_t; + cm_virtio_device_config new_c_virtio_device_config{}; + if constexpr (std::is_same_v) { + new_c_virtio_device_config.type = CM_VIRTIO_DEVICE_CONSOLE; + } else if constexpr (std::is_same_v) { + new_c_virtio_device_config.type = CM_VIRTIO_DEVICE_P9FS; + new_c_virtio_device_config.device.p9fs.tag = convert_to_c(cpp_virtio_device_config.tag); + new_c_virtio_device_config.device.p9fs.host_directory = + convert_to_c(cpp_virtio_device_config.host_directory); + } else if constexpr (std::is_same_v) { + cm_virtio_hostfwd_config_array new_c_hostfdw; + new_c_hostfdw.count = cpp_virtio_device_config.hostfwd.size(); + new_c_hostfdw.entry = new cm_virtio_hostfwd_config[new_c_hostfdw.count]; + memset(new_c_hostfdw.entry, 0, sizeof(cm_virtio_hostfwd_config) * new_c_hostfdw.count); + for (size_t i = 0; i < new_c_hostfdw.count; ++i) { + new_c_hostfdw.entry[i] = convert_to_c(cpp_virtio_device_config.hostfwd[i]); + } + + new_c_virtio_device_config.type = CM_VIRTIO_DEVICE_NET_USER; + new_c_virtio_device_config.device.net_user.hostfwd = new_c_hostfdw; + } else if constexpr (std::is_same_v) { + new_c_virtio_device_config.type = CM_VIRTIO_DEVICE_NET_TUNTAP; + new_c_virtio_device_config.device.net_tuntap.iface = convert_to_c(cpp_virtio_device_config.iface); + } else { + throw std::invalid_argument("invalid virtio device configuration"); + } + return new_c_virtio_device_config; + }, + cpp_config); +} + // ---------------------------------------------- // TLB configuration conversion functions // ---------------------------------------------- @@ -411,6 +501,10 @@ cartesi::machine_config convert_from_c(const cm_machine_config *c_config) { new_cpp_machine_config.flash_drive.push_back(convert_from_c(&(c_config->flash_drive.entry[i]))); } + for (size_t i = 0; i < c_config->virtio.count; ++i) { + new_cpp_machine_config.virtio.push_back(convert_from_c(&(c_config->virtio.entry[i]))); + } + return new_cpp_machine_config; } @@ -425,12 +519,23 @@ cm_memory_range_config_array convert_to_c(const cartesi::flash_drive_configs &fl return new_flash_drive; } +cm_virtio_config_array convert_to_c(const cartesi::virtio_configs &virtio) { + cm_virtio_config_array new_virtio; + new_virtio.count = virtio.size(); + new_virtio.entry = new cm_virtio_device_config[virtio.size()]{}; + for (size_t i = 0; i < new_virtio.count; ++i) { + new_virtio.entry[i] = convert_to_c(virtio[i]); + } + return new_virtio; +} + cm_machine_config *convert_to_c(const cartesi::machine_config &cpp_config) { auto *new_machine_config = new cm_machine_config{}; new_machine_config->processor = convert_to_c(cpp_config.processor); new_machine_config->ram = convert_to_c(cpp_config.ram); new_machine_config->dtb = convert_to_c(cpp_config.dtb); new_machine_config->flash_drive = convert_to_c(cpp_config.flash_drive); + new_machine_config->virtio = convert_to_c(cpp_config.virtio); new_machine_config->tlb = convert_to_c(cpp_config.tlb); new_machine_config->clint = convert_to_c(cpp_config.clint); new_machine_config->plic = convert_to_c(cpp_config.plic); @@ -765,6 +870,26 @@ void cm_delete_machine_config(const cm_machine_config *config) { delete[] config->flash_drive.entry[i].image_filename; } delete[] config->flash_drive.entry; + + for (size_t i = 0; i < config->virtio.count; ++i) { + const cm_virtio_device_config &entry = config->virtio.entry[i]; + switch (entry.type) { + case CM_VIRTIO_DEVICE_NET_USER: + delete[] entry.device.net_user.hostfwd.entry; + break; + case CM_VIRTIO_DEVICE_P9FS: + delete[] entry.device.p9fs.tag; + delete[] entry.device.p9fs.host_directory; + break; + case CM_VIRTIO_DEVICE_NET_TUNTAP: + delete[] entry.device.net_tuntap.iface; + break; + default: + break; + } + } + delete[] config->virtio.entry; + delete[] config->dtb.image_filename; delete[] config->dtb.bootargs; delete[] config->dtb.init; diff --git a/src/machine-c-api.h b/src/machine-c-api.h index a374203b7..05939c36d 100644 --- a/src/machine-c-api.h +++ b/src/machine-c-api.h @@ -238,6 +238,62 @@ typedef struct { // NOLINT(modernize-use-using) bool yield_automatic; ///< Make yield automatic available? } cm_htif_config; +/// \brief VirtIO device type +typedef enum { // NOLINT(modernize-use-using) + CM_VIRTIO_DEVICE_CONSOLE, + CM_VIRTIO_DEVICE_P9FS, + CM_VIRTIO_DEVICE_NET_USER, + CM_VIRTIO_DEVICE_NET_TUNTAP +} CM_VIRTIO_DEVICE_TYPE; + +/// \brief VirtIO Plan 9 filesystem device state configuration +typedef struct { + const char *tag; ///< Guest mount tag + const char *host_directory; ///< Path to the host shared directory +} cm_virtio_p9fs_config; + +/// \brief VirtIO host forward state config +typedef struct cm_virtio_hostfwd_config { + bool is_udp; + uint16_t host_port; + uint16_t guest_port; +} cm_virtio_hostfwd_config; + +/// \brief VirtIO host forward configuration array +typedef struct { + cm_virtio_hostfwd_config *entry; + size_t count; +} cm_virtio_hostfwd_config_array; + +/// \brief VirtIO user network device state configuration +typedef struct { + cm_virtio_hostfwd_config_array hostfwd; +} cm_virtio_net_user_config; + +/// \brief VirtIO TUN/TAP network device state configuration +typedef struct { + const char *iface; ///< Host's tap network interface (e.g "tap0") +} cm_virtio_net_tuntap_config; + +/// \brief VirtIO device union +typedef union { + cm_virtio_p9fs_config p9fs; ///< Plan 9 filesystem + cm_virtio_net_user_config net_user; ///< User-mode networking + cm_virtio_net_tuntap_config net_tuntap; ///< TUN/TAP networking +} cm_virtio_device_config_union; + +/// \brief VirtIO device state configuration +typedef struct { + CM_VIRTIO_DEVICE_TYPE type; ///< VirtIO device type + cm_virtio_device_config_union device; ///< VirtIO device config +} cm_virtio_device_config; + +/// \brief VirtIO device configuration array +typedef struct { // NOLINT(modernize-use-using) + cm_virtio_device_config *entry; + size_t count; +} cm_virtio_config_array; + /// \brief Rollup state configuration typedef struct { // NOLINT(modernize-use-using) bool has_value; ///< Represents whether the rest of the struct have been filled @@ -277,6 +333,7 @@ typedef struct { // NOLINT(modernize-use-using) cm_clint_config clint; cm_plic_config plic; cm_htif_config htif; + cm_virtio_config_array virtio; cm_rollup_config rollup; cm_uarch_config uarch; } cm_machine_config; diff --git a/src/machine-c-defines.h b/src/machine-c-defines.h index bbb9820dc..b899e1b36 100644 --- a/src/machine-c-defines.h +++ b/src/machine-c-defines.h @@ -27,10 +27,12 @@ #define CM_MACHINE_F_REG_COUNT 32 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) #define CM_MACHINE_UARCH_X_REG_COUNT 32 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) -#define CM_TREE_LOG2_WORD_SIZE 3 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) -#define CM_TREE_LOG2_PAGE_SIZE 12 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) -#define CM_TREE_LOG2_ROOT_SIZE 64 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) -#define CM_FLASH_DRIVE_CONFIGS_MAX_SIZE 8 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) +#define CM_TREE_LOG2_WORD_SIZE 3 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) +#define CM_TREE_LOG2_PAGE_SIZE 12 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) +#define CM_TREE_LOG2_ROOT_SIZE 64 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) +#define CM_FLASH_DRIVE_CONFIGS_MAX_SIZE 8 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) +#define CM_VIRTIO_CONFIGS_MAX_SIZE 16 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) +#define CM_VIRTIO_HOSTFWD_CONFIGS_MAX_SIZE 16 // NOLINT(cppcoreguidelines-macro-usage, modernize-macro-to-enum) #include "machine-c-version.h" diff --git a/src/machine-config.h b/src/machine-config.h index 12e403f9d..4e08b89b8 100644 --- a/src/machine-config.h +++ b/src/machine-config.h @@ -22,12 +22,20 @@ #include #include #include +#include #include "riscv-constants.h" #include "uarch-config.h" namespace cartesi { +/// \brief Machine config constants +enum machine_config_constants { + FLASH_DRIVE_MAX = 8, ///< Maximum number of flash drives + VIRTIO_DEVICE_MAX = 16, ///< Maximum number of virtio devices + VIRTIO_HOSTFWD_MAX = 16, ///< Maximum number of virtio net user host forward ports +}; + /// \brief Processor state configuration struct processor_config final { std::array x{REG_X0, REG_X1, REG_X2, REG_X3, REG_X4, REG_X5, REG_X6, REG_X7, REG_X8, REG_X9, @@ -89,11 +97,6 @@ struct memory_range_config final { // NOLINT(bugprone-exception-escape) std::string image_filename{}; ///< Memory range image file name }; -/// \brief Flash constants -enum FLASH_DRIVE_constants { - FLASH_DRIVE_MAX = 8 ///< Maximum number of flash drives -}; - /// \brief List of flash drives using flash_drive_configs = boost::container::static_vector; @@ -122,6 +125,45 @@ struct htif_config final { bool yield_automatic{true}; ///< Make yield automatic available? }; +/// \brief VirtIO console device state config +struct virtio_console_config final {}; + +/// \brief VirtIO Plan 9 filesystem device state config +struct virtio_p9fs_config final { + std::string tag{}; ///< Guest mount tag + std::string host_directory{}; ///< Path to the host shared directory +}; + +/// \brief VirtIO host forward state config +struct virtio_hostfwd_config final { + bool is_udp{false}; + uint16_t host_port{0}; + uint16_t guest_port{0}; +}; + +/// \brief List of VirtIO host forwards +using virtio_hostfwd_configs = boost::container::static_vector; + +/// \brief VirtIO user network device state config +struct virtio_net_user_config final { + virtio_hostfwd_configs hostfwd{}; +}; + +/// \brief VirtIO TUN/TAP network device state config +struct virtio_net_tuntap_config final { + std::string iface{}; ///< Host's tap network interface (e.g "tap0") +}; + +/// \brief VirtIO device state config +using virtio_device_config = std::variant; + +/// \brief List of VirtIO devices +using virtio_configs = boost::container::static_vector; + /// \brief Rollup configuration struct rollup_config { // NOLINT(bugprone-exception-escape) memory_range_config rx_buffer{0x60000000, 2 << 20}; ///< RX buffer @@ -134,7 +176,6 @@ struct rollup_config { // NOLINT(bugprone- /// \brief Machine state configuration /// NOLINTNEXTLINE(bugprone-exception-escape) struct machine_config final { - processor_config processor{}; ///< Processor state ram_config ram{}; ///< RAM state dtb_config dtb{}; ///< DTB state @@ -143,6 +184,7 @@ struct machine_config final { clint_config clint{}; ///< CLINT device state plic_config plic{}; ///< PLIC device state htif_config htif{}; ///< HTIF device state + virtio_configs virtio{}; ///< VirtIO devices state uarch_config uarch{}; ///< microarchitecture configuration std::optional rollup{}; ///< Rollup state diff --git a/src/machine.cpp b/src/machine.cpp index 63b73d068..40caf7dbf 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -444,28 +444,44 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : // Register pma board shadow device register_pma_entry(make_shadow_pmas_pma_entry(PMA_SHADOW_PMAS_START, PMA_SHADOW_PMAS_LENGTH)); - // TODO(edubart): user should be able to configure these devices - if (m_c.htif.console_getchar) { - // Register VirtIO console device - auto vdev_console = std::make_unique(m_vdevs.size()); - register_pma_entry( - make_virtio_pma_entry(PMA_FIRST_VIRTIO_START + vdev_console->get_virtio_index() * PMA_VIRTIO_LENGTH, - PMA_VIRTIO_LENGTH, "VirtIO console device", &virtio_driver, vdev_console.get())); - m_vdevs.push_back(std::move(vdev_console)); - - // Register VirtIO Plan 9 filesystem device - auto vdev_p9fs = std::make_unique(m_vdevs.size(), "vfs0", "/tmp"); - register_pma_entry( - make_virtio_pma_entry(PMA_FIRST_VIRTIO_START + vdev_p9fs->get_virtio_index() * PMA_VIRTIO_LENGTH, - PMA_VIRTIO_LENGTH, "VirtIO p9fs device", &virtio_driver, vdev_p9fs.get())); - m_vdevs.push_back(std::move(vdev_p9fs)); - - // Register VirtIO network device - auto vdev_net = std::make_unique(m_vdevs.size(), std::make_unique()); - register_pma_entry( - make_virtio_pma_entry(PMA_FIRST_VIRTIO_START + vdev_net->get_virtio_index() * PMA_VIRTIO_LENGTH, - PMA_VIRTIO_LENGTH, "VirtIO network device", &virtio_driver, vdev_net.get())); - m_vdevs.push_back(std::move(vdev_net)); + // Initialize VirtIO devices + if (!m_c.virtio.empty()) { + // VirtIO devices are disallowed when interactive mode is disabled + if (!m_c.htif.console_getchar) { + throw std::invalid_argument{"virtio devices are only supported while in interactive mode"}; + } + + for (const auto &vdev_config_entry : m_c.virtio) { + std::visit( + [&](const auto &vdev_config) { + using T = std::decay_t; + std::string pma_name = "VirtIO device"; + std::unique_ptr vdev; + if constexpr (std::is_same_v) { + pma_name = "VirtIO Console"; + vdev = std::make_unique(m_vdevs.size()); + } else if constexpr (std::is_same_v) { + pma_name = "VirtIO 9P"; + vdev = std::make_unique(m_vdevs.size(), vdev_config.tag, + vdev_config.host_directory); + } else if constexpr (std::is_same_v) { + pma_name = "VirtIO Net User"; + vdev = std::make_unique(m_vdevs.size(), + std::make_unique(vdev_config)); + } else if constexpr (std::is_same_v) { + pma_name = "VirtIO Net TUN/TAP"; + vdev = std::make_unique(m_vdevs.size(), + std::make_unique(vdev_config.iface)); + } else { + throw std::invalid_argument("invalid virtio device configuration"); + } + register_pma_entry( + make_virtio_pma_entry(PMA_FIRST_VIRTIO_START + vdev->get_virtio_index() * PMA_VIRTIO_LENGTH, + PMA_VIRTIO_LENGTH, pma_name, &virtio_driver, vdev.get())); + m_vdevs.push_back(std::move(vdev)); + }, + vdev_config_entry); + } } // Initialize DTB @@ -654,6 +670,9 @@ machine_config machine::get_serialization_config(void) const { for (auto &f : c.flash_drive) { f.image_filename.clear(); } + if (!c.virtio.empty()) { + throw std::runtime_error{"machine config with VirtIO devices cannot be serialized"}; + } if (c.rollup.has_value()) { auto &r = c.rollup.value(); r.rx_buffer.image_filename.clear(); @@ -753,6 +772,9 @@ static inline T &deref(T *t) { } void machine::store_pmas(const machine_config &c, const std::string &dir) const { + if (!c.virtio.empty()) { + throw std::runtime_error{"machine with VirtIO devices cannot be stored"}; + } store_memory_pma(find_pma_entry(PMA_DTB_START), dir); store_memory_pma(find_pma_entry(PMA_RAM_START), dir); store_device_pma(*this, find_pma_entry(PMA_SHADOW_TLB_START), dir);