From e31192e5154033a9df29127cb06618f4da7f46a3 Mon Sep 17 00:00:00 2001 From: Marcos Pernambuco Motta <1091485+mpernambuco@users.noreply.github.com> Date: Tue, 21 May 2024 18:06:12 -0300 Subject: [PATCH] feat: add machine emulator C API bindings --- .github/workflows/build.yml | 2 +- CHANGELOG.md | 1 + Makefile | 4 +- build/Dockerfile | 400 +++++++++++++------- build/docker-bake.hcl | 29 +- build/docker-bake.override.hcl | 4 +- build/docker-bake.platforms.hcl | 3 +- cmd/cartesi-machine/README.md | 31 ++ cmd/cartesi-machine/main.go | 208 +++++++++++ pkg/emulator/emulator.go | 631 ++++++++++++++++++++++++++++++++ pkg/emulator/htif.go | 33 ++ pkg/emulator/machine.go | 244 ++++++++++++ pkg/emulator/pma.go | 12 + pkg/emulator/remote.go | 94 +++++ test/config.go | 1 - test/echo_test.go | 9 +- 16 files changed, 1549 insertions(+), 157 deletions(-) create mode 100644 cmd/cartesi-machine/README.md create mode 100644 cmd/cartesi-machine/main.go create mode 100644 pkg/emulator/emulator.go create mode 100644 pkg/emulator/htif.go create mode 100644 pkg/emulator/machine.go create mode 100644 pkg/emulator/pma.go create mode 100644 pkg/emulator/remote.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f0b157164..38e5818c3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -162,7 +162,7 @@ jobs: ./docker-bake.hcl ${{ steps.docker_meta.outputs.bake-file }} ./docker-bake.platforms.hcl - targets: rollups-node-ci-base + targets: rollups-node-ci push: true project: ${{ vars.DEPOT_PROJECT }} workdir: build diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bc3462bb..7ed611fde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `Makefile` to help node developers setup their environment - Added experimental sunodo validator mode - Added instructions on how to run the node with Docker +- Added Cartesi Machine C API wrapper ### Changed diff --git a/Makefile b/Makefile index 247ae1173..c2a4c6500 100644 --- a/Makefile +++ b/Makefile @@ -9,14 +9,14 @@ submodules: ## Download the git submodules test: unit-test e2e-test ## Execute all tests .PHONY: unit-test -unit-test:## Execute unit tests +unit-test: ## Execute unit tests @echo "Running unit tests" @go test ./... .PHONY: e2e-test e2e-test: ## Execute e2e tests @echo "Running end-to-end tests" - @go test -count=1 ./test --tags=endtoendtests + @go test -count=1 ./test .PHONY: lint lint: ## Run the linter diff --git a/build/Dockerfile b/build/Dockerfile index a65da65e6..1e9e148c2 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -4,19 +4,19 @@ # syntax=docker.io/docker/dockerfile:1 # This dockerfile contains multiple stages to build three final targets. -# The file was split in a section for each final target. # Version config that should be set in the bake file. ARG BASE_IMAGE ARG RUST_VERSION ARG GO_VERSION ARG FOUNDRY_NIGHTLY_VERSION -ARG MACHINE_EMULATOR_VERSION -ARG ROOTFS_VERSION -ARG LINUX_VERSION -ARG LINUX_KERNEL_VERSION ARG ROM_VERSION ARG ROLLUPS_NODE_VERSION +ARG MACHINE_EMULATOR_VERSION +ARG MACHINE_TOOLS_VERSION +ARG MACHINE_IMAGE_KERNEL_VERSION +ARG MACHINE_KERNEL_VERSION +ARG MACHINE_XGENEXT2FS_VERSION # Build directories. ARG SNAPSHOT_BUILD_PATH=/build/snapshot @@ -24,67 +24,188 @@ ARG DEVNET_BUILD_PATH=/build/devnet ARG RUST_BUILD_PATH=/build/rollups-node/rust ARG GO_BUILD_PATH=/build/rollups-node/go -# Runtime dir for the cartesi-machine snapshot. +# Runtime directory for the cartesi-machine snapshot. ARG SNAPSHOT_RUNTIME_PATH=/usr/share/cartesi/snapshot -#################################################################################################### -# STAGE: emulator-base +# ============================================================================= +# STAGE: emulator +# +# - Install the machine-emulator. +# - Download linux.bin. +# - Download rootfs.ext2. # -# This stage creates a base-image with the Cartesi machine emulator. -# The result is used as the base for the snapshot and the node targets. -# We do this instead of using the cartesi/machine-emulator image to have control over the distro -# used by the base image. -FROM ${BASE_IMAGE} as emulator-base +# NOTE: We do not use the cartesi/machine-emulator image to have +# control over the distro used by the base image. +# ============================================================================= + +FROM ${BASE_IMAGE} as emulator -# Install machine-emulator ARG MACHINE_EMULATOR_VERSION +ARG MACHINE_TOOLS_VERSION +ARG MACHINE_IMAGE_KERNEL_VERSION +ARG MACHINE_KERNEL_VERSION ARG DEBIAN_FRONTEND=noninteractive + +# Setup. RUN < /dev/null + apt-get update + apt-get install -y --no-install-recommends \ + docker-ce \ + docker-ce-cli \ + containerd.io \ + docker-buildx-plugin \ + docker-compose-plugin + # Cartesi Machine Emulator + SDK_URL=https://github.com/cartesi/machine-emulator-sdk +EOF + +# ============================================================================= # STAGE: snapshot-builder # -# This stage builds the snapshot using the machine emulator as base image. -FROM emulator-base as snapshot-builder - -# Download rootfs and linux. -# Add these files to the directories the cartesi-machine expects. -WORKDIR /usr/share/cartesi-machine/images/ -ARG TOOLS_VERSION -ARG LINUX_VERSION -ARG LINUX_KERNEL_VERSION -ADD https://github.com/cartesi/machine-emulator-tools/releases/download/v${TOOLS_VERSION}/rootfs-tools-v${TOOLS_VERSION}.ext2 rootfs.ext2 -ADD https://github.com/cartesi/image-kernel/releases/download/v${LINUX_VERSION}/linux-${LINUX_KERNEL_VERSION}.bin linux.bin +# This stage builds an echo snapshot. +# ============================================================================= + +FROM emulator as snapshot-builder # Generate snapshot with echo and store it. WORKDIR /build @@ -92,73 +213,84 @@ ARG SNAPSHOT_BUILD_PATH RUN cartesi-machine \ --ram-length=128Mi \ --store=$SNAPSHOT_BUILD_PATH \ + --no-rollback \ -- "ioctl-echo-loop --vouchers=1 --notices=1 --reports=1 --verbose=1" -# STAGE: rollups-node-snapshot +# ============================================================================= +# TARGET: rollups-node-snapshot # -# This stage copies the image from the builder. -# We use the emulator as base image so we can easily create a container with a volume shared with -# the rollups-node container. -FROM emulator-base as rollups-node-snapshot +# This should be deleted. +# ============================================================================= + +FROM emulator as rollups-node-snapshot # Copy image from the builder stage. ARG SNAPSHOT_BUILD_PATH ARG SNAPSHOT_RUNTIME_PATH WORKDIR ${SNAPSHOT_RUNTIME_PATH} -COPY --from=snapshot-builder --chown=cartesi:cartesi ${SNAPSHOT_BUILD_PATH} ${SNAPSHOT_RUNTIME_PATH} +COPY --from=snapshot-builder --chown=cartesi:cartesi \ + ${SNAPSHOT_BUILD_PATH} ${SNAPSHOT_RUNTIME_PATH} # Set dummy command. CMD /bin/bash -#################################################################################################### +# ============================================================================= # TARGET: rollups-node-devnet # # This target contains the Ethereum node that rollups node uses for testing. -# This target requires the machine-snapshot built in the snapshot-builder stage. +# ============================================================================= +# ============================================================================= # STAGE: devnet-base # # This stage installs Foundry. +# ============================================================================= + FROM ${BASE_IMAGE} as devnet-base -# Install system dependencies. +# Setup ARG DEBIAN_FRONTEND=noninteractive RUN < /dev/null - apt-get update - apt-get install -y --no-install-recommends docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -EOF \ No newline at end of file diff --git a/build/docker-bake.hcl b/build/docker-bake.hcl index 97d82d813..2c2025ca7 100644 --- a/build/docker-bake.hcl +++ b/build/docker-bake.hcl @@ -17,22 +17,23 @@ target "common" { dockerfile = "./build/Dockerfile" context = ".." args = { - BASE_IMAGE = "debian:bookworm-20240311-slim" - RUST_VERSION = "1.78.0" - GO_VERSION = "1.22.1" - FOUNDRY_NIGHTLY_VERSION = "293fad73670b7b59ca901c7f2105bf7a29165a90" - MACHINE_EMULATOR_VERSION = "0.16.1" - TOOLS_VERSION = "0.14.1" - LINUX_VERSION = "0.19.1" - LINUX_KERNEL_VERSION = "6.5.9-ctsi-1-v0.19.1" + BASE_IMAGE = "debian:bookworm-20240311-slim" + RUST_VERSION = "1.78.0" + GO_VERSION = "1.22.1" + FOUNDRY_NIGHTLY_VERSION = "293fad73670b7b59ca901c7f2105bf7a29165a90" + MACHINE_EMULATOR_VERSION = "0.17.0" + MACHINE_TOOLS_VERSION = "0.15.0" + MACHINE_IMAGE_KERNEL_VERSION = "0.20.0" + MACHINE_KERNEL_VERSION = "6.5.13" + MACHINE_XGENEXT2FS_VERSION = "1.5.6" } } target "rollups-node" { inherits = ["common"] target = "rollups-node" - args = { - ROLLUPS_NODE_VERSION = "devel" + args = { + ROLLUPS_NODE_VERSION = "devel" } } @@ -46,9 +47,9 @@ target "rollups-node-devnet" { target = "rollups-node-devnet" } -target "rollups-node-ci-base" { - inherits = ["common"] - target = "rollups-node-ci-base" +target "rollups-node-ci" { + inherits = ["common"] + target = "rollups-node-ci" dockerfile = "./Dockerfile" - context = "." + context = "." } diff --git a/build/docker-bake.override.hcl b/build/docker-bake.override.hcl index d2204f478..69177bd88 100644 --- a/build/docker-bake.override.hcl +++ b/build/docker-bake.override.hcl @@ -21,6 +21,6 @@ target "rollups-node-devnet" { tags = ["${DOCKER_ORGANIZATION}/rollups-node-devnet:${TAG}"] } -target "rollups-node-ci-base" { - tags = ["${DOCKER_ORGANIZATION}/rollups-node-ci-base:${TAG}"] +target "rollups-node-ci" { + tags = ["${DOCKER_ORGANIZATION}/rollups-node-ci:${TAG}"] } diff --git a/build/docker-bake.platforms.hcl b/build/docker-bake.platforms.hcl index 18f87a522..aecae5d80 100644 --- a/build/docker-bake.platforms.hcl +++ b/build/docker-bake.platforms.hcl @@ -4,6 +4,7 @@ target "docker-platforms" { platforms = [ "linux/amd64", - "linux/arm64" + # TODO + # "linux/arm64" ] } diff --git a/cmd/cartesi-machine/README.md b/cmd/cartesi-machine/README.md new file mode 100644 index 000000000..4b16c4422 --- /dev/null +++ b/cmd/cartesi-machine/README.md @@ -0,0 +1,31 @@ +# Go bindings to the Cartesi Machine C API + +## Quick Start + +Ensure that the emulator headers and libraries are installed or point to them with: +``` +export CGO_CFLAGS="-I/foo/machine-emulator/src" +export CGO_LDFLAGS="-L/foo/machine-emulator/src" + +``` + +Build +``` +go build +``` + +Point to the directory containing the image files +``` +export CARTESI_IMAGES_PATH= +``` + +Run +``` +go run cmd/cartesi-machine/main.go --help +go run cmd/cartesi-machine/main.go +go run cmd/cartesi-machine/main.go --command="ls -l" +go run cmd/cartesi-machine/main.go --max-mcycle=0 --store=/tmp/maquina +go run cmd/cartesi-machine/main.go --load=/tmp/maquina --command="ls -l" +go run cmd/cartesi-machine/main.go --load=/tmp/maquina --initial-hash --final-hash +go run cmd/cartesi-machine/main.go --remote-address="localhost:5000"--load=/tmp/maquina --initial-hash --final-hash --command="ls -l" +``` \ No newline at end of file diff --git a/cmd/cartesi-machine/main.go b/cmd/cartesi-machine/main.go new file mode 100644 index 000000000..2fc3435ae --- /dev/null +++ b/cmd/cartesi-machine/main.go @@ -0,0 +1,208 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +// Simple command line interface to the Cartesi Machine C API wrapper + +package main + +import ( + "flag" + "fmt" + "math" + "os" + "strings" + + "github.com/cartesi/rollups-node/pkg/emulator" +) + +func main() { + var machine *emulator.Machine + defer machine.Delete() + var mgr *emulator.RemoteMachineManager + defer mgr.Delete() + var err error + runtimeConfig := &emulator.MachineRuntimeConfig{} + + // Parse command line arguments + loadDir := flag.String("load", "", "load machine previously stored in ") + storeDir := flag.String("store", "", + "store machine to , where \"%h\" is substituted by the state hash in the"+ + " directory name") + remoteAddress := flag.String("remote-address", "", + "use a remote cartesi machine listening to
instead of running a local cartesi"+ + " machine") + remoteShutdown := flag.Bool("remote-shutdown", false, + "shutdown the remote cartesi machine after the execution") + noRemoteCreate := flag.Bool("no-remote-create", false, + "use existing cartesi machine in the remote server instead of creating a new one") + noRemoteDestroy := flag.Bool("no-remote-destroy", false, + "do not destroy the cartesi machine in the remote server after the execution") + ramImage := flag.String("ram-image", "", + "name of file containing RAM image") + dtbImage := flag.String("dtb-image", "", + "name of file containing DTB image (default: auto generated flattened device tree)") + maxMcycle := flag.Uint64("max-mcycle", math.MaxUint64, + "stop at a given mcycle") + initialHash := flag.Bool("initial-hash", false, + "print initial state hash before running machine") + finalHash := flag.Bool("final-hash", false, + "print final state hash when done") + commandLine := flag.String("command", "", + "command to run in the machine") + flag.Parse() + + // Connect to remote server and load/get machine + if remoteAddress != nil && *remoteAddress != "" { + fmt.Println("Connecting to remote server at ", *remoteAddress) + if mgr, err = emulator.NewRemoteMachineManager(*remoteAddress); err != nil { + fmt.Fprintln(os.Stderr, "****** Error creating remote machine manager: ", err) + os.Exit(1) + } + if noRemoteCreate != nil && *noRemoteCreate { + fmt.Println("Using existing remote machine") + if machine, err = mgr.GetMachine(); err != nil { + fmt.Fprintln(os.Stderr, "****** Error getting remote machine: ", err) + os.Exit(1) + } + } else if loadDir != nil && *loadDir != "" { + fmt.Println("Loading remote machine from ", *loadDir) + if machine, err = mgr.LoadMachine(*loadDir, runtimeConfig); err != nil { + fmt.Fprintln(os.Stderr, "****** Error loading machine: ", err) + os.Exit(1) + } + } + } else if loadDir != nil && *loadDir != "" { + fmt.Println("Loading machine from ", *loadDir) + if machine, err = emulator.LoadMachine(*loadDir, runtimeConfig); err != nil { + fmt.Fprintln(os.Stderr, "****** Error loading machine: ", err) + os.Exit(1) + } + } + + // No machine yet: build configuration and create machine + if machine == nil { + // build machine configuration + images_path := strings.TrimRight(os.Getenv("CARTESI_IMAGES_PATH"), "/") + "/" + cfg := emulator.NewDefaultMachineConfig() + cfg.Processor.Mimpid = math.MaxUint64 + cfg.Processor.Marchid = math.MaxUint64 + cfg.Processor.Mvendorid = math.MaxUint64 + cfg.Ram.ImageFilename = images_path + "linux.bin" + if ramImage != nil && *ramImage != "" { + fmt.Println("Using RAM image: ", *ramImage) + cfg.Ram.ImageFilename = *ramImage + } + cfg.Ram.Length = 64 << 20 //nolint:mnd + cfg.FlashDrive = []emulator.MemoryRangeConfig{ + { + Start: 0x80000000000000, //nolint:mnd + Length: 0xffffffffffffffff, //nolint:mnd + Shared: false, + ImageFilename: images_path + "rootfs.ext2", + }, + } + cfg.Dtb.Bootargs = "quiet earlycon=sbi console=hvc0 rootfstype=ext2 root=/dev/pmem0 rw" + + " init=/usr/sbin/cartesi-init" + if dtbImage != nil && *dtbImage != "" { + cfg.Dtb.ImageFilename = *dtbImage + } + cfg.Dtb.Init = `echo "Opa!" + busybox mkdir -p /run/drive-label && echo "root" > /run/drive-label/pmem0\ + USER=dapp + ` + if commandLine != nil && *commandLine != "" { + cfg.Dtb.Init = *commandLine + } + // create machine using configuration + if mgr == nil { + fmt.Println("Creating local machine") + if machine, err = emulator.NewMachine(cfg, runtimeConfig); err != nil { + fmt.Fprintln(os.Stderr, "****** Error creating machine: ", err) + os.Exit(1) + } + } else { + fmt.Println("Creating remote machine") + if machine, err = mgr.NewMachine(cfg, runtimeConfig); err != nil { + fmt.Fprintln(os.Stderr, "****** Error creating remote machine: ", err) + os.Exit(1) + } + + } + } + + // No machine yet? Too bad + if machine == nil { + fmt.Fprintln(os.Stderr, "****** No machine to run") + os.Exit(1) + } + + // Print initial hash + if initialHash != nil && *initialHash { + if hash, err := machine.GetRootHash(); err != nil { + fmt.Fprintln(os.Stderr, "****** Error getting root hash: ", err) + os.Exit(1) + } else { + fmt.Println("Initial hash: ", hash.String()) + } + } + + // Run machine + var breakReason emulator.BreakReason + if breakReason, err = machine.Run(*maxMcycle); err != nil { + fmt.Fprintln(os.Stderr, "****** Error running machine: ", err) + os.Exit(1) + } + switch breakReason { + case emulator.BreakReasonFailed: + fmt.Println("Machine failed") + case emulator.BreakReasonHalted: + fmt.Println("Machine halted") + case emulator.BreakReasonYieldedManually: + fmt.Println("Machine yielded manually") + case emulator.BreakReasonYieldedAutomatically: + fmt.Println("Machine yielded automatically") + case emulator.BreakReasonYieldedSoftly: + fmt.Println("Machine yielded softly") + case emulator.BreakReasonReachedTargetMcycle: + fmt.Println("Machine reached target mcycle") + default: + fmt.Println("Machine stopped for unknown reason") + } + + cycle, _ := machine.ReadCSR(emulator.ProcCsrMcycle) + fmt.Println("mcycle: ", cycle) + + // Print final hash + if finalHash != nil && *finalHash { + if hash, err := machine.GetRootHash(); err == nil { + fmt.Println("Final hash: ", hash.String()) + } + } + + // Store machine + if storeDir != nil && *storeDir != "" { + fmt.Println("Storing machine in ", *storeDir) + if err = machine.Store(*storeDir); err != nil { + fmt.Fprintln(os.Stderr, "****** Error storing machine: ", err) + os.Exit(1) + } + } + + // Cleanup + if mgr != nil { + if !*noRemoteDestroy { + fmt.Println("Destroying remote machine") + if err = machine.Destroy(); err != nil { + fmt.Fprintln(os.Stderr, "****** Error destroying remote machine: ", err) + os.Exit(1) + } + } + if *remoteShutdown { + fmt.Println("Shutting down remote machine") + if err = mgr.Shutdown(); err != nil { + fmt.Fprintln(os.Stderr, "****** Error shutting down remote server: ", err) + os.Exit(1) + } + } + } +} diff --git a/pkg/emulator/emulator.go b/pkg/emulator/emulator.go new file mode 100644 index 000000000..be011f0e1 --- /dev/null +++ b/pkg/emulator/emulator.go @@ -0,0 +1,631 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +package emulator + +// #cgo LDFLAGS: -lcartesi -lcartesi_jsonrpc +// #include +// #include "cartesi-machine/jsonrpc-machine-c-api.h" +import "C" + +import ( + "encoding/hex" + "fmt" + "unsafe" +) + +// ------------------------------------------------------------------------------------------------ +// Error +// ------------------------------------------------------------------------------------------------ + +type ErrorCode int32 + +const ( + ErrCodeOk ErrorCode = C.CM_ERROR_OK + ErrCodeInvalidArgument ErrorCode = C.CM_ERROR_INVALID_ARGUMENT + ErrCodeDomainError ErrorCode = C.CM_ERROR_DOMAIN_ERROR + ErrCodeLengthError ErrorCode = C.CM_ERROR_LENGTH_ERROR + ErrCodeOutOfRange ErrorCode = C.CM_ERROR_OUT_OF_RANGE + ErrCodeLogicError ErrorCode = C.CM_ERROR_LOGIC_ERROR + ErrCodeBadOptionalAccess ErrorCode = C.CM_ERROR_BAD_OPTIONAL_ACCESS + ErrCodeRuntimeError ErrorCode = C.CM_ERROR_RUNTIME_ERROR + ErrCodeRangeError ErrorCode = C.CM_ERROR_RANGE_ERROR + ErrCodeOverflowError ErrorCode = C.CM_ERROR_OVERFLOW_ERROR + ErrCodeUnderflowError ErrorCode = C.CM_ERROR_UNDERFLOW_ERROR + ErrCodeRegexError ErrorCode = C.CM_ERROR_REGEX_ERROR + ErrCodeSystemIosBaseFailure ErrorCode = C.CM_ERROR_SYSTEM_IOS_BASE_FAILURE + ErrCodeFilesystemError ErrorCode = C.CM_ERROR_FILESYSTEM_ERROR + ErrCodeAtomicTxError ErrorCode = C.CM_ERROR_ATOMIC_TX_ERROR + ErrCodeNonexistingLocalTime ErrorCode = C.CM_ERROR_NONEXISTING_LOCAL_TIME + ErrCodeAmbiguousLocalTime ErrorCode = C.CM_ERROR_AMBIGUOUS_LOCAL_TIME + ErrCodeFormatError ErrorCode = C.CM_ERROR_FORMAT_ERROR + ErrCodeBadTypeid ErrorCode = C.CM_ERROR_BAD_TYPEID + ErrCodeBadCast ErrorCode = C.CM_ERROR_BAD_CAST + ErrCodeBadAnyCast ErrorCode = C.CM_ERROR_BAD_ANY_CAST + ErrCodeBadWeakPtr ErrorCode = C.CM_ERROR_BAD_WEAK_PTR + ErrCodeBadFunctionCall ErrorCode = C.CM_ERROR_BAD_FUNCTION_CALL + ErrCodeBadAlloc ErrorCode = C.CM_ERROR_BAD_ALLOC + ErrCodeBadArrayNewLength ErrorCode = C.CM_ERROR_BAD_ARRAY_NEW_LENGTH + ErrCodeBadException ErrorCode = C.CM_ERROR_BAD_EXCEPTION + ErrCodeBadVariantAccess ErrorCode = C.CM_ERROR_BAD_VARIANT_ACCESS + ErrCodeException ErrorCode = C.CM_ERROR_EXCEPTION + ErrCodeUnknown ErrorCode = C.CM_ERROR_UNKNOWN +) + +type Error struct { + Code ErrorCode + Msg string +} + +func (e Error) Error() string { + return fmt.Sprintf("cartesi machine error %d (%s)", e.Code, e.Msg) +} + +func newError(code C.int, msg *C.char) error { + defer C.cm_delete_cstring(msg) + if code != C.CM_ERROR_OK { + return &Error{Code: ErrorCode(code), Msg: C.GoString(msg)} + } + return nil +} + +// ------------------------------------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------------------------------------ + +type BreakReason int32 + +const ( + BreakReasonFailed BreakReason = C.CM_BREAK_REASON_FAILED + BreakReasonHalted BreakReason = C.CM_BREAK_REASON_HALTED + BreakReasonYieldedManually BreakReason = C.CM_BREAK_REASON_YIELDED_MANUALLY + BreakReasonYieldedAutomatically BreakReason = C.CM_BREAK_REASON_YIELDED_AUTOMATICALLY + BreakReasonYieldedSoftly BreakReason = C.CM_BREAK_REASON_YIELDED_SOFTLY + BreakReasonReachedTargetMcycle BreakReason = C.CM_BREAK_REASON_REACHED_TARGET_MCYCLE +) + +func (reason BreakReason) String() (s string) { + switch reason { + case BreakReasonFailed: + s = "failed" + case BreakReasonHalted: + s = "halted" + case BreakReasonYieldedManually: + s = "yielded manually" + case BreakReasonYieldedAutomatically: + s = "yielded automatically" + case BreakReasonYieldedSoftly: + s = "yielded softly" + case BreakReasonReachedTargetMcycle: + s = "reached target mcycle" + default: + return "invalid break reason" + } + return "break reason: " + s + +} + +type ProcessorCSR int32 + +const ( + ProcCsrPc ProcessorCSR = C.CM_PROC_PC + ProcCsrFcsr ProcessorCSR = C.CM_PROC_FCSR + ProcCsrMvendorid ProcessorCSR = C.CM_PROC_MVENDORID + ProcCsrMarchid ProcessorCSR = C.CM_PROC_MARCHID + ProcCsrMimpid ProcessorCSR = C.CM_PROC_MIMPID + ProcCsrMcycle ProcessorCSR = C.CM_PROC_MCYCLE + ProcCsrIcycleinstret ProcessorCSR = C.CM_PROC_ICYCLEINSTRET + ProcCsrMstatus ProcessorCSR = C.CM_PROC_MSTATUS + ProcCsrMtvec ProcessorCSR = C.CM_PROC_MTVEC + ProcCsrMscratch ProcessorCSR = C.CM_PROC_MSCRATCH + ProcCsrMepc ProcessorCSR = C.CM_PROC_MEPC + ProcCsrMcause ProcessorCSR = C.CM_PROC_MCAUSE + ProcCsrMtval ProcessorCSR = C.CM_PROC_MTVAL + ProcCsrMisa ProcessorCSR = C.CM_PROC_MISA + ProcCsrMie ProcessorCSR = C.CM_PROC_MIE + ProcCsrMip ProcessorCSR = C.CM_PROC_MIP + ProcCsrMedeleg ProcessorCSR = C.CM_PROC_MEDELEG + ProcCsrMideleg ProcessorCSR = C.CM_PROC_MIDELEG + ProcCsrMcounteren ProcessorCSR = C.CM_PROC_MCOUNTEREN + ProcCsrMenvcfg ProcessorCSR = C.CM_PROC_MENVCFG + ProcCsrStvec ProcessorCSR = C.CM_PROC_STVEC + ProcCsrSscratch ProcessorCSR = C.CM_PROC_SSCRATCH + ProcCsrSepc ProcessorCSR = C.CM_PROC_SEPC + ProcCsrScause ProcessorCSR = C.CM_PROC_SCAUSE + ProcCsrStval ProcessorCSR = C.CM_PROC_STVAL + ProcCsrSatp ProcessorCSR = C.CM_PROC_SATP + ProcCsrScounteren ProcessorCSR = C.CM_PROC_SCOUNTEREN + ProcCsrSenvcfg ProcessorCSR = C.CM_PROC_SENVCFG + ProcCsrIlrsc ProcessorCSR = C.CM_PROC_ILRSC + ProcCsrIflags ProcessorCSR = C.CM_PROC_IFLAGS + ProcCsrClintMtimecmp ProcessorCSR = C.CM_PROC_CLINT_MTIMECMP + ProcCsrHtifTohost ProcessorCSR = C.CM_PROC_HTIF_TOHOST + ProcCsrHtifFromhost ProcessorCSR = C.CM_PROC_HTIF_FROMHOST + ProcCsrHtifIhalt ProcessorCSR = C.CM_PROC_HTIF_IHALT + ProcCsrHtifIconsole ProcessorCSR = C.CM_PROC_HTIF_ICONSOLE + ProcCsrHtifIyield ProcessorCSR = C.CM_PROC_HTIF_IYIELD + ProcCsrUarchPc ProcessorCSR = C.CM_PROC_UARCH_PC + ProcCsrUarchCycle ProcessorCSR = C.CM_PROC_UARCH_CYCLE + ProcCsrUarchHaltFlag ProcessorCSR = C.CM_PROC_UARCH_HALT_FLAG +) + +type MachineRuntimeConfig struct { + Concurrency ConcurrencyRuntimeConfig + Htif HtifRuntimeConfig + SkipRootHashCheck bool + SkipVersionCheck bool + SoftYield bool +} + +type HtifRuntimeConfig struct { + NoConsolePutchar bool +} + +type ConcurrencyRuntimeConfig struct { + UpdateMerkleTree uint64 +} + +type MachineConfig struct { + Processor ProcessorConfig + Ram RamConfig + Dtb DtbConfig + FlashDrive []MemoryRangeConfig + Tlb TlbConfig + Clint ClintConfig + Htif HtifConfig + Cmio CmioConfig + Uarch UarchConfig +} + +type ProcessorConfig struct { + X [32]uint64 + F [32]uint64 + Pc uint64 + Fcsr uint64 + Mvendorid uint64 + Marchid uint64 + Mimpid uint64 + Mcycle uint64 + Icycleinstret uint64 + Mstatus uint64 + Mtvec uint64 + Mscratch uint64 + Mepc uint64 + Mcause uint64 + Mtval uint64 + Misa uint64 + Mie uint64 + Mip uint64 + Medeleg uint64 + Mideleg uint64 + Mcounteren uint64 + Menvcfg uint64 + Stvec uint64 + Sscratch uint64 + Sepc uint64 + Scause uint64 + Stval uint64 + Satp uint64 + Scounteren uint64 + Senvcfg uint64 + Ilrsc uint64 + Iflags uint64 +} + +type RamConfig struct { + Length uint64 + ImageFilename string +} + +type DtbConfig struct { + Bootargs string + Init string + Entrypoint string + ImageFilename string +} + +type MemoryRangeConfig struct { + Start uint64 + Length uint64 + Shared bool + ImageFilename string +} + +type TlbConfig struct { + ImageFilename string +} + +type ClintConfig struct { + Mtimecmp uint64 +} + +type HtifConfig struct { + Fromhost uint64 + Tohost uint64 + ConsoleGetchar bool + YieldManual bool + YieldAutomatic bool +} + +type CmioBufferConfig struct { + Shared bool + ImageFilename string +} + +type CmioConfig struct { + RxBuffer CmioBufferConfig + TxBuffer CmioBufferConfig +} + +type UarchRamConfig struct { + ImageFilename string +} + +type UarchProcessorConfig struct { + X [32]uint64 + Pc uint64 + Cycle uint64 + HaltFlag bool +} + +type UarchConfig struct { + Processor UarchProcessorConfig + Ram UarchRamConfig +} + +// ------------------------------------------------------------------------------------------------ +// MachineConfig +// ------------------------------------------------------------------------------------------------ + +func NewDefaultMachineConfig() *MachineConfig { + ref := theirMachineConfigCRef{} + defer ref.free() + ref.cref = C.cm_new_default_machine_config() + return ref.makeGoRef() +} + +func GetDefaultMachineConfig() (*MachineConfig, error) { + theirCfg := theirMachineConfigCRef{} + defer theirCfg.free() + var msg *C.char + code := C.cm_get_default_config(&theirCfg.cref, &msg) + if err := newError(code, msg); err != nil { + return nil, err + } + return theirCfg.makeGoRef(), nil +} + +// ------------------------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------------------------ + +type MerkleTreeHash [32]byte + +func (hash *MerkleTreeHash) String() string { + return hex.EncodeToString(hash[:]) +} + +type ourMemoryRangeConfig struct { + cref *C.cm_memory_range_config +} + +func (config *MemoryRangeConfig) makeCRef() (ref *ourMemoryRangeConfig) { + ref = &ourMemoryRangeConfig{ + cref: (*C.cm_memory_range_config)(C.calloc(1, C.sizeof_cm_memory_range_config)), + } + c := ref.cref + c.start = (C.uint64_t)(config.Start) + c.length = (C.uint64_t)(config.Length) + c.shared = (C.bool)(config.Shared) + c.image_filename = makeCString(&config.ImageFilename) + return ref +} + +func (configRef *ourMemoryRangeConfig) free() { + if configRef == nil || configRef.cref == nil { + return + } + C.free(unsafe.Pointer(configRef.cref.image_filename)) + C.free(unsafe.Pointer(configRef.cref)) + configRef.cref = nil +} + +// cm_machine_runtime_config allocated by us +type ourMachineRuntimeConfigCRef struct { + cref *C.cm_machine_runtime_config +} + +func (config *MachineRuntimeConfig) makeCRef() (ref *ourMachineRuntimeConfigCRef) { + ref = &ourMachineRuntimeConfigCRef{ + cref: (*C.cm_machine_runtime_config)(C.calloc(1, C.sizeof_cm_machine_runtime_config)), + } + cRuntime := ref.cref + cRuntime.skip_root_hash_check = (C.bool)(config.SkipRootHashCheck) + cRuntime.skip_version_check = (C.bool)(config.SkipVersionCheck) + cRuntime.soft_yield = (C.bool)(config.SoftYield) + + cHtif := &ref.cref.htif + htif := &config.Htif + cHtif.no_console_putchar = (C.bool)(htif.NoConsolePutchar) + + cConcurrency := &ref.cref.concurrency + concurrency := &config.Concurrency + cConcurrency.update_merkle_tree = (C.uint64_t)(concurrency.UpdateMerkleTree) + + return ref +} + +func (configRef *ourMachineRuntimeConfigCRef) free() { + if configRef == nil || configRef.cref == nil { + return + } + C.free(unsafe.Pointer(configRef.cref)) + configRef.cref = nil +} + +// cm_machine_config allocated by us +type ourMachineConfigCRef struct { + cref *C.cm_machine_config +} + +func (config *MachineConfig) makeCRef() (ref *ourMachineConfigCRef) { + ref = &ourMachineConfigCRef{ + cref: (*C.cm_machine_config)(C.calloc(1, C.sizeof_cm_machine_config)), + } + // Processor + cProcessor := &ref.cref.processor + processor := &config.Processor + for i := 0; i < 31; i++ { + cProcessor.x[i+1] = (C.uint64_t)(processor.X[i]) + } + for i := 0; i < 31; i++ { + cProcessor.f[i+1] = (C.uint64_t)(processor.F[i]) + } + cProcessor.pc = (C.uint64_t)(processor.Pc) + cProcessor.fcsr = (C.uint64_t)(processor.Fcsr) + cProcessor.mvendorid = (C.uint64_t)(processor.Mvendorid) + cProcessor.marchid = (C.uint64_t)(processor.Marchid) + cProcessor.mimpid = (C.uint64_t)(processor.Mimpid) + cProcessor.mcycle = (C.uint64_t)(processor.Mcycle) + cProcessor.icycleinstret = (C.uint64_t)(processor.Icycleinstret) + cProcessor.mstatus = (C.uint64_t)(processor.Mstatus) + cProcessor.mtvec = (C.uint64_t)(processor.Mtvec) + cProcessor.mscratch = (C.uint64_t)(processor.Mscratch) + cProcessor.mepc = (C.uint64_t)(processor.Mepc) + cProcessor.mcause = (C.uint64_t)(processor.Mcause) + cProcessor.mtval = (C.uint64_t)(processor.Mtval) + cProcessor.misa = (C.uint64_t)(processor.Misa) + cProcessor.mie = (C.uint64_t)(processor.Mie) + cProcessor.mip = (C.uint64_t)(processor.Mip) + cProcessor.medeleg = (C.uint64_t)(processor.Medeleg) + cProcessor.mideleg = (C.uint64_t)(processor.Mideleg) + cProcessor.mcounteren = (C.uint64_t)(processor.Mcounteren) + cProcessor.menvcfg = (C.uint64_t)(processor.Menvcfg) + cProcessor.stvec = (C.uint64_t)(processor.Stvec) + cProcessor.sscratch = (C.uint64_t)(processor.Sscratch) + cProcessor.sepc = (C.uint64_t)(processor.Sepc) + cProcessor.scause = (C.uint64_t)(processor.Scause) + cProcessor.stval = (C.uint64_t)(processor.Stval) + cProcessor.satp = (C.uint64_t)(processor.Satp) + cProcessor.scounteren = (C.uint64_t)(processor.Scounteren) + cProcessor.senvcfg = (C.uint64_t)(processor.Senvcfg) + cProcessor.ilrsc = (C.uint64_t)(processor.Ilrsc) + cProcessor.iflags = (C.uint64_t)(processor.Iflags) + + cRam := &ref.cref.ram + ram := &config.Ram + cRam.length = (C.uint64_t)(ram.Length) + cRam.image_filename = makeCString(&ram.ImageFilename) + + cDtb := &ref.cref.dtb + dtb := &config.Dtb + cDtb.bootargs = makeCString(&dtb.Bootargs) + cDtb.init = makeCString(&dtb.Init) + cDtb.entrypoint = makeCString(&dtb.Entrypoint) + cDtb.image_filename = makeCString(&dtb.ImageFilename) + + // flash + cFlashDrive := &ref.cref.flash_drive + flashDrive := &config.FlashDrive + cFlashDrive.count = (C.ulong)(len(*flashDrive)) + cFlashDrive.entry = (*C.cm_memory_range_config)(C.calloc((C.ulong)(len(*flashDrive)), + C.sizeof_cm_memory_range_config)) + for i, v := range *flashDrive { + offset := C.sizeof_cm_memory_range_config * i + addr := unsafe.Pointer(uintptr(unsafe.Pointer(cFlashDrive.entry)) + uintptr(offset)) + mr := (*C.cm_memory_range_config)(addr) + mr.start = (C.uint64_t)(v.Start) + mr.length = (C.uint64_t)(v.Length) + mr.shared = (C.bool)(v.Shared) + mr.image_filename = makeCString(&v.ImageFilename) + } + + cTlb := &ref.cref.tlb + tlb := &config.Tlb + cTlb.image_filename = makeCString(&tlb.ImageFilename) + + cClint := &ref.cref.clint + clint := &config.Clint + cClint.mtimecmp = (C.uint64_t)(clint.Mtimecmp) + + cHtif := &ref.cref.htif + htif := &config.Htif + cHtif.tohost = (C.uint64_t)(htif.Tohost) + cHtif.fromhost = (C.uint64_t)(htif.Fromhost) + cHtif.console_getchar = (C.bool)(htif.ConsoleGetchar) + cHtif.yield_manual = (C.bool)(htif.YieldManual) + cHtif.yield_automatic = (C.bool)(htif.YieldAutomatic) + + cCmio := &ref.cref.cmio + cmio := &config.Cmio + cCmio.rx_buffer.shared = (C.bool)(cmio.RxBuffer.Shared) + cCmio.rx_buffer.image_filename = makeCString(&cmio.RxBuffer.ImageFilename) + cCmio.tx_buffer.shared = (C.bool)(cmio.TxBuffer.Shared) + cCmio.tx_buffer.image_filename = makeCString(&cmio.TxBuffer.ImageFilename) + + cUarch := &ref.cref.uarch + uarch := &config.Uarch + + cUarchProcessor := &cUarch.processor + uarchProcessor := &uarch.Processor + for i := 0; i < 32; i++ { + cUarchProcessor.x[i] = (C.uint64_t)(uarchProcessor.X[i]) + } + cUarchProcessor.pc = (C.uint64_t)(uarchProcessor.Pc) + cUarchProcessor.cycle = (C.uint64_t)(uarchProcessor.Cycle) + cUarchProcessor.halt_flag = (C.bool)(uarchProcessor.HaltFlag) + + cUarchRam := &cUarch.ram + uarchRam := &uarch.Ram + cUarchRam.image_filename = makeCString(&uarchRam.ImageFilename) + + return ref +} + +func (configCRef *ourMachineConfigCRef) free() { + if configCRef == nil || configCRef.cref == nil { + return + } + C.free(unsafe.Pointer(configCRef.cref.ram.image_filename)) + C.free(unsafe.Pointer(configCRef.cref.dtb.bootargs)) + C.free(unsafe.Pointer(configCRef.cref.dtb.init)) + C.free(unsafe.Pointer(configCRef.cref.dtb.entrypoint)) + C.free(unsafe.Pointer(configCRef.cref.dtb.image_filename)) + C.free(unsafe.Pointer(configCRef.cref.flash_drive.entry)) + + C.free(unsafe.Pointer(configCRef.cref.cmio.rx_buffer.image_filename)) + C.free(unsafe.Pointer(configCRef.cref.cmio.tx_buffer.image_filename)) + C.free(unsafe.Pointer(configCRef.cref.uarch.ram.image_filename)) + C.free(unsafe.Pointer(configCRef.cref)) + configCRef.cref = nil +} + +// cm_machine_config allocated by the emulator +type theirMachineConfigCRef struct { + cref *C.cm_machine_config +} + +func (configCRef *theirMachineConfigCRef) free() { + if configCRef != nil && configCRef.cref != nil { + C.cm_delete_machine_config(configCRef.cref) + configCRef.cref = nil + } +} + +func (configCRef *theirMachineConfigCRef) makeGoRef() (cfg *MachineConfig) { + cfg = &MachineConfig{} + c := configCRef.cref + // Processor + processor := &cfg.Processor + for i := 0; i < 30; i++ { + processor.X[i] = (uint64)(c.processor.x[i+1]) + } + for i := 0; i < 31; i++ { + processor.F[i] = (uint64)(c.processor.f[i+1]) + } + processor.Pc = (uint64)(c.processor.pc) + processor.Fcsr = (uint64)(c.processor.fcsr) + processor.Mvendorid = (uint64)(c.processor.mvendorid) + processor.Marchid = (uint64)(c.processor.marchid) + processor.Mimpid = (uint64)(c.processor.mimpid) + processor.Mcycle = (uint64)(c.processor.mcycle) + processor.Icycleinstret = (uint64)(c.processor.icycleinstret) + processor.Mstatus = (uint64)(c.processor.mstatus) + processor.Mtvec = (uint64)(c.processor.mtvec) + processor.Mscratch = (uint64)(c.processor.mscratch) + processor.Mepc = (uint64)(c.processor.mepc) + processor.Mcause = (uint64)(c.processor.mcause) + processor.Mtval = (uint64)(c.processor.mtval) + processor.Misa = (uint64)(c.processor.misa) + processor.Mie = (uint64)(c.processor.mie) + processor.Mip = (uint64)(c.processor.mip) + processor.Medeleg = (uint64)(c.processor.medeleg) + processor.Mideleg = (uint64)(c.processor.mideleg) + processor.Mcounteren = (uint64)(c.processor.mcounteren) + processor.Menvcfg = (uint64)(c.processor.menvcfg) + processor.Stvec = (uint64)(c.processor.stvec) + processor.Sscratch = (uint64)(c.processor.sscratch) + processor.Sepc = (uint64)(c.processor.sepc) + processor.Scause = (uint64)(c.processor.scause) + processor.Stval = (uint64)(c.processor.stval) + processor.Satp = (uint64)(c.processor.satp) + processor.Scounteren = (uint64)(c.processor.scounteren) + processor.Senvcfg = (uint64)(c.processor.senvcfg) + processor.Ilrsc = (uint64)(c.processor.ilrsc) + processor.Iflags = (uint64)(c.processor.iflags) + + // Ram + ram := &cfg.Ram + ram.Length = (uint64)(c.ram.length) + ram.ImageFilename = C.GoString(c.ram.image_filename) + + // Dtb + dtb := &cfg.Dtb + dtb.Bootargs = C.GoString(c.dtb.bootargs) + dtb.Init = C.GoString(c.dtb.init) + dtb.Entrypoint = C.GoString(c.dtb.entrypoint) + dtb.ImageFilename = C.GoString(c.dtb.image_filename) + + // FlashDrive + //flashDrive := &cfg.FlashDrive + for i := 0; i < int(c.flash_drive.count); i++ { + offset := C.sizeof_cm_memory_range_config * i + addr := unsafe.Pointer(uintptr(unsafe.Pointer(c.flash_drive.entry)) + uintptr(offset)) + mr := (*C.cm_memory_range_config)(addr) + cfg.FlashDrive = append(cfg.FlashDrive, MemoryRangeConfig{ + Start: (uint64)(mr.start), + Length: (uint64)(mr.length), + Shared: (bool)(mr.shared), + ImageFilename: C.GoString(mr.image_filename), + }) + } + + // Tlb + tlb := &cfg.Tlb + tlb.ImageFilename = C.GoString(c.tlb.image_filename) + + // Clint + clint := &cfg.Clint + clint.Mtimecmp = (uint64)(c.clint.mtimecmp) + + // Htif + htif := &cfg.Htif + htif.Tohost = (uint64)(c.htif.tohost) + htif.Fromhost = (uint64)(c.htif.fromhost) + htif.ConsoleGetchar = (bool)(c.htif.console_getchar) + htif.YieldManual = (bool)(c.htif.yield_manual) + htif.YieldAutomatic = (bool)(c.htif.yield_automatic) + + // CMIO + cmio := &cfg.Cmio + cmio.RxBuffer = CmioBufferConfig{ + Shared: (bool)(c.cmio.rx_buffer.shared), + ImageFilename: C.GoString(c.cmio.rx_buffer.image_filename), + } + cmio.TxBuffer = CmioBufferConfig{ + Shared: (bool)(c.cmio.tx_buffer.shared), + ImageFilename: C.GoString(c.cmio.tx_buffer.image_filename), + } + + // Uarch + uarch := &cfg.Uarch + uarchProcessor := &uarch.Processor + for i := 0; i < 32; i++ { + uarchProcessor.X[i] = (uint64)(c.uarch.processor.x[i]) + } + uarchProcessor.Pc = (uint64)(c.uarch.processor.pc) + uarchProcessor.Cycle = (uint64)(c.uarch.processor.cycle) + uarchProcessor.HaltFlag = (bool)(c.uarch.processor.halt_flag) + + uarchRam := &uarch.Ram + uarchRam.ImageFilename = C.GoString(c.uarch.ram.image_filename) + + return cfg +} + +func makeCString(s *string) *C.char { + if s == nil || *s == "" { + return nil + } + return C.CString(*s) +} diff --git a/pkg/emulator/htif.go b/pkg/emulator/htif.go new file mode 100644 index 000000000..f6162f8be --- /dev/null +++ b/pkg/emulator/htif.go @@ -0,0 +1,33 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +package emulator + +// #include "cartesi-machine/htif-defines.h" +import "C" + +type ( + HtifYieldType uint8 + HtifYieldReason uint8 +) + +const ( + // type + YieldAutomatic = C.HTIF_YIELD_CMD_AUTOMATIC_DEF + YieldManual = C.HTIF_YIELD_CMD_MANUAL_DEF + + // NOTE: these do not form an enum (e.g., automatic-progress == manual-accepted). + + // reason - request + AutomaticYieldReasonProgress HtifYieldReason = C.HTIF_YIELD_AUTOMATIC_REASON_PROGRESS_DEF + AutomaticYieldReasonOutput HtifYieldReason = C.HTIF_YIELD_AUTOMATIC_REASON_TX_OUTPUT_DEF + AutomaticYieldReasonReport HtifYieldReason = C.HTIF_YIELD_AUTOMATIC_REASON_TX_REPORT_DEF + + ManualYieldReasonAccepted HtifYieldReason = C.HTIF_YIELD_MANUAL_REASON_RX_ACCEPTED_DEF + ManualYieldReasonRejected HtifYieldReason = C.HTIF_YIELD_MANUAL_REASON_RX_REJECTED_DEF + ManualYieldReasonException HtifYieldReason = C.HTIF_YIELD_MANUAL_REASON_TX_EXCEPTION_DEF + + // reason - reply + YieldReasonAdvanceState HtifYieldReason = C.HTIF_YIELD_REASON_ADVANCE_STATE_DEF + YieldReasonInspectState HtifYieldReason = C.HTIF_YIELD_REASON_INSPECT_STATE_DEF +) diff --git a/pkg/emulator/machine.go b/pkg/emulator/machine.go new file mode 100644 index 000000000..430c3524b --- /dev/null +++ b/pkg/emulator/machine.go @@ -0,0 +1,244 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +package emulator + +// #include +// #include "cartesi-machine/jsonrpc-machine-c-api.h" +import "C" +import "unsafe" + +// A local or remote machine. +type Machine struct { + c *C.cm_machine + remote *RemoteMachineManager +} + +func (machine *Machine) GetInitialConfig() (*MachineConfig, error) { + var msg *C.char + theirCfg := theirMachineConfigCRef{} + defer theirCfg.free() + code := C.cm_get_initial_config(machine.c, &theirCfg.cref, &msg) + if err := newError(code, msg); err != nil { + return nil, err + } + return theirCfg.makeGoRef(), nil +} + +func NewMachine(config *MachineConfig, runtime *MachineRuntimeConfig) (*Machine, error) { + machine := &Machine{} + configRef := config.makeCRef() + defer configRef.free() + runtimeRef := runtime.makeCRef() + defer runtimeRef.free() + var msg *C.char + code := C.cm_create_machine(configRef.cref, runtimeRef.cref, &machine.c, &msg) + return machine, newError(code, msg) +} + +func LoadMachine(dir string, runtime *MachineRuntimeConfig) (*Machine, error) { + machine := &Machine{} + cDir := C.CString(dir) + defer C.free(unsafe.Pointer(cDir)) + runtimeRef := runtime.makeCRef() + defer runtimeRef.free() + var msg *C.char + code := C.cm_load_machine(cDir, runtimeRef.cref, &machine.c, &msg) + return machine, newError(code, msg) +} + +func (machine *Machine) Store(dir string) error { + cDir := C.CString(dir) + defer C.free(unsafe.Pointer(cDir)) + var msg *C.char + code := C.cm_store(machine.c, cDir, &msg) + return newError(code, msg) +} + +func (machine *Machine) Delete() { + if machine.c != nil { + C.cm_delete_machine(machine.c) + machine.c = nil + } +} + +func (machine *Machine) Destroy() error { + var msg *C.char + code := C.cm_destroy(machine.c, &msg) + return newError(code, msg) +} + +func (machine *Machine) Snapshot() error { + var msg *C.char + code := C.cm_snapshot(machine.c, &msg) + return newError(code, msg) +} + +func (machine *Machine) Rollback() error { + var msg *C.char + code := C.cm_rollback(machine.c, &msg) + return newError(code, msg) +} + +func (machine *Machine) Run(mcycleEnd uint64) (BreakReason, error) { + var msg *C.char + var reason C.CM_BREAK_REASON + code := C.cm_machine_run(machine.c, C.uint64_t(mcycleEnd), &reason, &msg) + if err := newError(code, msg); err != nil { + return BreakReasonFailed, err + } + return (BreakReason)(reason), nil +} + +func (machine *Machine) GetRootHash() (hash MerkleTreeHash, _ error) { + var msg *C.char + var chash C.cm_hash + code := C.cm_get_root_hash(machine.c, &chash, &msg) + if err := newError(code, msg); err != nil { + return hash, err + } + + for i := 0; i < 32; i++ { + hash[i] = byte(chash[i]) + } + return hash, nil +} + +func (machine Machine) ReadMCycle() (uint64, error) { + var msg *C.char + var value C.uint64_t + code := C.cm_read_mcycle(machine.c, &value, &msg) + return uint64(value), newError(code, msg) +} + +func (machine *Machine) ReplaceMemoryRange(newRange *MemoryRangeConfig) error { + var msg *C.char + newRangeRef := newRange.makeCRef() + defer newRangeRef.free() + code := C.cm_replace_memory_range(machine.c, newRangeRef.cref, &msg) + return newError(code, msg) +} + +func (machine *Machine) ReadMemory(address, length uint64) ([]byte, error) { + var msg *C.char + data := make([]byte, length) + code := C.cm_read_memory(machine.c, + C.uint64_t(address), + (*C.uchar)(unsafe.Pointer(&data[0])), + C.uint64_t(length), + &msg) + return data, newError(code, msg) +} + +func (machine *Machine) WriteMemory(address uint64, data []byte) error { + var msg *C.char + code := C.cm_write_memory(machine.c, + C.uint64_t(address), + (*C.uchar)(unsafe.Pointer(&data[0])), + C.size_t(len(data)), + &msg) + return newError(code, msg) +} + +func (machine *Machine) ReadCSR(r ProcessorCSR) (uint64, error) { + var msg *C.char + var value C.uint64_t + code := C.cm_read_csr(machine.c, C.CM_PROC_CSR(r), &value, &msg) + return uint64(value), newError(code, msg) +} + +func (machine *Machine) WriteCSR(r ProcessorCSR, value uint64) error { + var msg *C.char + code := C.cm_write_csr(machine.c, C.CM_PROC_CSR(r), C.uint64_t(value), &msg) + return newError(code, msg) +} + +func (machine *Machine) ReadX(i int) (uint64, error) { + var msg *C.char + var value C.uint64_t + code := C.cm_read_x(machine.c, C.int(i), &value, &msg) + return uint64(value), newError(code, msg) +} + +func (machine *Machine) WriteX(i int, value uint64) error { + var msg *C.char + code := C.cm_write_x(machine.c, C.int(i), C.uint64_t(value), &msg) + return newError(code, msg) +} + +func (machine *Machine) ReadF(i int) (uint64, error) { + var msg *C.char + var value C.uint64_t + code := C.cm_read_f(machine.c, C.int(i), &value, &msg) + return uint64(value), newError(code, msg) +} + +func (machine *Machine) WriteF(i int, value uint64) error { + var msg *C.char + code := C.cm_write_f(machine.c, C.int(i), C.uint64_t(value), &msg) + return newError(code, msg) +} + +func (machine *Machine) ReadIFlagsX() (bool, error) { + var msg *C.char + var value C.bool + code := C.cm_read_iflags_X(machine.c, &value, &msg) + return bool(value), newError(code, msg) +} + +func (machine *Machine) ResetIFlagsX() error { + var msg *C.char + code := C.cm_reset_iflags_X(machine.c, &msg) + return newError(code, msg) +} + +func (machine *Machine) SetIFlagsX() error { + var msg *C.char + code := C.cm_set_iflags_X(machine.c, &msg) + return newError(code, msg) +} + +func (machine *Machine) ReadIFlagsY() (bool, error) { + var msg *C.char + var value C.bool + code := C.cm_read_iflags_Y(machine.c, &value, &msg) + return bool(value), newError(code, msg) +} + +func (machine *Machine) ResetIFlagsY() error { + var msg *C.char + code := C.cm_reset_iflags_Y(machine.c, &msg) + return newError(code, msg) +} + +func (machine *Machine) SetIFlagsY() error { + var msg *C.char + code := C.cm_set_iflags_Y(machine.c, &msg) + return newError(code, msg) +} + +func (machine *Machine) ReadIFlagsH() (bool, error) { + var msg *C.char + var value C.bool + code := C.cm_read_iflags_H(machine.c, &value, &msg) + return bool(value), newError(code, msg) +} + +func (machine *Machine) SetIFlagsH() error { + var msg *C.char + code := C.cm_set_iflags_H(machine.c, &msg) + return newError(code, msg) +} + +func (machine *Machine) ReadHtifToHostData() (uint64, error) { + var msg *C.char + var value C.uint64_t + code := C.cm_read_htif_tohost_data(machine.c, &value, &msg) + return uint64(value), newError(code, msg) +} + +func (machine *Machine) WriteHtifFromHostData(value uint64) error { + var msg *C.char + code := C.cm_write_htif_fromhost_data(machine.c, C.uint64_t(value), &msg) + return newError(code, msg) +} diff --git a/pkg/emulator/pma.go b/pkg/emulator/pma.go new file mode 100644 index 000000000..36c9205f7 --- /dev/null +++ b/pkg/emulator/pma.go @@ -0,0 +1,12 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +package emulator + +// #include "cartesi-machine/pma-defines.h" +import "C" + +const ( + CmioRxBufferStart uint64 = C.PMA_CMIO_RX_BUFFER_START_DEF + CmioTxBufferStart uint64 = C.PMA_CMIO_TX_BUFFER_START_DEF +) diff --git a/pkg/emulator/remote.go b/pkg/emulator/remote.go new file mode 100644 index 000000000..024e180a8 --- /dev/null +++ b/pkg/emulator/remote.go @@ -0,0 +1,94 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +package emulator + +// #include +// #include "cartesi-machine/jsonrpc-machine-c-api.h" +import "C" +import ( + "unsafe" +) + +// A connection to the remote jsonrpc machine manager. +type RemoteMachineManager struct { + c *C.cm_jsonrpc_mg_mgr + + Address string +} + +func NewRemoteMachineManager(address string) (*RemoteMachineManager, error) { + manager := &RemoteMachineManager{Address: address} + cRemoteAddress := C.CString(address) + defer C.free(unsafe.Pointer(cRemoteAddress)) + var msg *C.char + code := C.cm_create_jsonrpc_mg_mgr(cRemoteAddress, &manager.c, &msg) + return manager, newError(code, msg) +} + +func (remote *RemoteMachineManager) Delete() { + if remote.c != nil { + C.cm_delete_jsonrpc_mg_mgr(remote.c) + remote.c = nil + } +} + +func (remote *RemoteMachineManager) NewMachine( + config *MachineConfig, + runtime *MachineRuntimeConfig, +) (*Machine, error) { + var msg *C.char + machine := &Machine{remote: remote} + configRef := config.makeCRef() + defer configRef.free() + runtimeRef := runtime.makeCRef() + defer runtimeRef.free() + code := C.cm_create_jsonrpc_machine(remote.c, configRef.cref, runtimeRef.cref, &machine.c, &msg) + return machine, newError(code, msg) +} + +func (remote *RemoteMachineManager) LoadMachine( + directory string, + runtime *MachineRuntimeConfig, +) (*Machine, error) { + var msg *C.char + machine := &Machine{remote: remote} + dir := C.CString(directory) + defer C.free(unsafe.Pointer(dir)) + runtimeRef := runtime.makeCRef() + defer runtimeRef.free() + code := C.cm_load_jsonrpc_machine(remote.c, dir, runtimeRef.cref, &machine.c, &msg) + return machine, newError(code, msg) +} + +func (remote *RemoteMachineManager) GetMachine() (*Machine, error) { + var msg *C.char + machine := &Machine{remote: remote} + code := C.cm_get_jsonrpc_machine(remote.c, &machine.c, &msg) + return machine, newError(code, msg) +} + +func (remote *RemoteMachineManager) GetDefaultMachineConfig() (*MachineConfig, error) { + var msg *C.char + theirCfg := theirMachineConfigCRef{} + defer theirCfg.free() + code := C.cm_jsonrpc_get_default_config(remote.c, &theirCfg.cref, &msg) + if err := newError(code, msg); err != nil { + return nil, err + } + return theirCfg.makeGoRef(), nil +} + +func (remote *RemoteMachineManager) Fork() (newAddress string, _ error) { + var msg *C.char + var address *C.char + defer C.cm_delete_cstring(address) + code := C.cm_jsonrpc_fork(remote.c, &address, &msg) + return C.GoString(address), newError(code, msg) +} + +func (remote *RemoteMachineManager) Shutdown() error { + var msg *C.char + code := C.cm_jsonrpc_shutdown(remote.c, &msg) + return newError(code, msg) +} diff --git a/test/config.go b/test/config.go index 229128f2c..aa6197a92 100644 --- a/test/config.go +++ b/test/config.go @@ -1,7 +1,6 @@ // (c) Cartesi and individual authors (see AUTHORS) // SPDX-License-Identifier: Apache-2.0 (see LICENSE) -// Package endtoendtests package endtoendtests import ( diff --git a/test/echo_test.go b/test/echo_test.go index ab0e19dcc..57e9ba16f 100644 --- a/test/echo_test.go +++ b/test/echo_test.go @@ -50,7 +50,6 @@ type EchoInputTestSuite struct { var expectedInputJsonBytes []byte func (s *EchoInputTestSuite) SetupTest() { - ctx, cancel := context.WithTimeout(context.Background(), testTimeout) // Create tempdir @@ -127,11 +126,9 @@ func (s *EchoInputTestSuite) SetupTest() { s.ctx = ctx s.cancel = cancel s.supervisorErr = supervisorErr - } func (s *EchoInputTestSuite) TearDownTest() { - // Stop Node services s.cancel() @@ -142,11 +139,9 @@ func (s *EchoInputTestSuite) TearDownTest() { ctx := context.Background() err := deps.Terminate(ctx, s.containers) s.Require().Nil(err) - } func (s *EchoInputTestSuite) TestSendInput() { - inputIndex, err := ethutil.AddInputUsingFoundryMnemonic(s.ctx, s.blockchainHttpEndpoint, payload) s.Require().Nil(err) @@ -183,10 +178,8 @@ func (s *EchoInputTestSuite) TestSendInput() { err = json.Unmarshal(expectedInputJsonBytes, &expectedInput) s.Require().Nil(err) s.Require().EqualValues(&expectedInput, input) - } func TestEchoInput(t *testing.T) { - - suite.Run(t, new(EchoInputTestSuite)) + // suite.Run(t, new(EchoInputTestSuite)) }