Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ TEST_DIR := test
DOWNLOADDIR := downloads
SRC_DIR := src

EMULATOR_VERSION ?= v0.19.0
EMULATOR_VERSION ?= v0.20.0
EMULATOR_TAG ?=

TESTS_DATA_FILE ?= machine-emulator-tests-data.deb
Expand Down
33 changes: 22 additions & 11 deletions helper_scripts/generate_EmulatorConstants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,45 @@ end

local out = io.stdout



out:write(' bytes32 constant UARCH_PRISTINE_STATE_HASH = 0x' .. hexstring(cartesi.UARCH_PRISTINE_STATE_HASH) .. ';\n')
out:write(' uint64 constant UARCH_CYCLE_ADDRESS = 0x' .. hex(cartesi.machine:get_reg_address("uarch_cycle")) .. ';\n')
out:write(' uint64 constant UARCH_CYCLE_MAX = 0x' .. hex(cartesi.UARCH_CYCLE_MAX) .. ';\n')
out:write(' uint64 constant UARCH_HALT_FLAG_ADDRESS = 0x' ..
hex(cartesi.machine:get_reg_address("uarch_halt_flag")) .. ';\n')
out:write(' uint64 constant UARCH_PC_ADDRESS = 0x' .. hex(cartesi.machine:get_reg_address("uarch_pc")) .. ';\n')
out:write(' uint64 constant UARCH_X0_ADDRESS = 0x' .. hex(cartesi.machine:get_reg_address("uarch_x0")) .. ';\n')
out:write(' uint64 constant UARCH_SHADOW_START_ADDRESS = 0x' .. hex(cartesi.UARCH_SHADOW_START_ADDRESS) .. ';\n')
out:write(' uint64 constant UARCH_SHADOW_LENGTH = 0x' .. hex(cartesi.UARCH_SHADOW_LENGTH) .. ';\n')
out:write(' uint64 constant AR_SHADOW_TLB_START = 0x' .. hex(cartesi.AR_SHADOW_TLB_START) .. ';\n')
out:write(' uint64 constant AR_SHADOW_TLB_LENGTH = 0x' .. hex(cartesi.AR_SHADOW_TLB_LENGTH) .. ';\n')
out:write(' uint64 constant UARCH_RAM_START_ADDRESS = 0x' .. hex(cartesi.UARCH_RAM_START_ADDRESS) .. ';\n')
out:write(' uint64 constant UARCH_RAM_LENGTH = 0x' .. hex(cartesi.UARCH_RAM_LENGTH) .. ';\n')
out:write(' uint64 constant UARCH_STATE_START_ADDRESS = 0x' .. hex(cartesi.UARCH_STATE_START_ADDRESS) .. ';\n')
out:write(' uint8 constant UARCH_STATE_LOG2_SIZE = ' .. cartesi.UARCH_STATE_LOG2_SIZE .. ';\n')
out:write(' bytes32 constant UARCH_PRISTINE_STATE_HASH = 0x' .. hexstring(cartesi.UARCH_PRISTINE_STATE_HASH) .. ';\n')
out:write(' uint64 constant UARCH_ECALL_FN_HALT = ' .. cartesi.UARCH_ECALL_FN_HALT .. ';\n')
out:write(' uint64 constant UARCH_ECALL_FN_PUTCHAR = ' .. cartesi.UARCH_ECALL_FN_PUTCHAR .. ';\n')
out:write(' uint64 constant UARCH_ECALL_FN_MARK_DIRTY_PAGE = ' .. cartesi.UARCH_ECALL_FN_MARK_DIRTY_PAGE .. ';\n')
out:write(' uint64 constant UARCH_ECALL_FN_WRITE_TLB = ' .. cartesi.UARCH_ECALL_FN_WRITE_TLB .. ';\n')
out:write(' uint64 constant HTIF_YIELD = 0x' .. hex(cartesi.machine:get_reg_address("htif_iyield")) .. ';\n')
out:write(' uint64 constant IFLAGS_Y_ADDRESS = 0x' .. hex(cartesi.machine:get_reg_address("iflags_Y")) .. ';\n')
out:write(' uint64 constant HTIF_FROMHOST_ADDRESS = 0x' ..
hex(cartesi.machine:get_reg_address("htif_fromhost")) .. ';\n')
out:write(' uint64 constant HTIF_TOHOST_ADDRESS = 0x' ..
hex(cartesi.machine:get_reg_address("htif_tohost")) .. ';\n')
out:write(' uint8 constant CMIO_YIELD_REASON_ADVANCE_STATE = 0x' ..
hex(cartesi.CMIO_YIELD_REASON_ADVANCE_STATE) .. ';\n')
out:write(' uint32 constant TREE_LOG2_WORD_SIZE = 0x' .. hex(cartesi.TREE_LOG2_WORD_SIZE) .. ';\n')
out:write(' uint32 constant TREE_WORD_SIZE = uint32(1) << TREE_LOG2_WORD_SIZE;\n')
out:write(' uint64 constant PMA_CMIO_RX_BUFFER_START = 0x' .. hex(cartesi.PMA_CMIO_RX_BUFFER_START) .. ';\n')
out:write(' uint8 constant PMA_CMIO_RX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.PMA_CMIO_RX_BUFFER_LOG2_SIZE) .. ';\n')
out:write(' uint64 constant PMA_CMIO_TX_BUFFER_START = 0x' .. hex(cartesi.PMA_CMIO_TX_BUFFER_START) .. ';\n')
out:write(' uint8 constant PMA_CMIO_TX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.PMA_CMIO_TX_BUFFER_LOG2_SIZE) .. ';\n')
out:close()
out:write(' uint32 constant HASH_TREE_LOG2_WORD_SIZE = 0x' .. hex(cartesi.HASH_TREE_LOG2_WORD_SIZE) .. ';\n')
out:write(' uint32 constant HASH_TREE_WORD_SIZE = uint32(1) << HASH_TREE_LOG2_WORD_SIZE;\n')
out:write(' uint16 constant CMIO_YIELD_MANUAL_REASON_RX_ACCEPTED = 0x' ..
hex(cartesi.CMIO_YIELD_MANUAL_REASON_RX_ACCEPTED) .. ';\n')
out:write(' uint16 constant CMIO_YIELD_MANUAL_REASON_RX_REJECTED = 0x' ..
hex(cartesi.CMIO_YIELD_MANUAL_REASON_RX_REJECTED) .. ';\n')
out:write(' uint16 constant CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION = 0x' ..
hex(cartesi.CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION) .. ';\n')
out:write(' uint8 constant UARCH_STATE_LOG2_SIZE = ' .. cartesi.UARCH_STATE_LOG2_SIZE .. ';\n')
out:write(' uint64 constant AR_CMIO_RX_BUFFER_START = 0x' .. hex(cartesi.AR_CMIO_RX_BUFFER_START) .. ';\n')
out:write(' uint8 constant AR_CMIO_RX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.AR_CMIO_RX_BUFFER_LOG2_SIZE) .. ';\n')
out:write(' uint64 constant AR_CMIO_TX_BUFFER_START = 0x' .. hex(cartesi.AR_CMIO_TX_BUFFER_START) .. ';\n')
out:write(' uint8 constant AR_CMIO_TX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.AR_CMIO_TX_BUFFER_LOG2_SIZE) .. ';\n')
out:write(' uint64 constant REVERT_ROOT_HASH_ADDRESS = 0x' .. hex(cartesi.AR_SHADOW_REVERT_ROOT_HASH_START) .. ';\n')

out:close()
2 changes: 1 addition & 1 deletion helper_scripts/generate_SendCmioResponse.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ cpp_src=`echo "${BASH_REMATCH[1]}" \
| $SED "/ uint32 length);/d" \
| $SED "s/machine_merkle_tree::get_log2_word_size()/TREE_LOG2_WORD_SIZE/g" \
| $SED -E "s/($COMPAT_FNS)/EmulatorCompat.\1/g" \
| $SED "s/writeMemoryWithPadding(a, PMA_CMIO_RX_BUFFER_START, data, dataLength, writeLengthLog2Size);/a.writeRegion(Memory.regionFromPhysicalAddress(PMA_CMIO_RX_BUFFER_START.toPhysicalAddress(),Memory.alignedSizeFromLog2(uint8(writeLengthLog2Size - TREE_LOG2_WORD_SIZE))),dataHash);"/g \
| $SED "s/writeMemoryWithPadding(a, AR_CMIO_RX_BUFFER_START, data, dataLength, writeLengthLog2Size);/a.writeRegion(Memory.regionFromPhysicalAddress(AR_CMIO_RX_BUFFER_START.toPhysicalAddress(),Memory.alignedSizeFromLog2(uint8(writeLengthLog2Size - HASH_TREE_LOG2_WORD_SIZE))),dataHash);"/g \
| $SED -E "s/($CONSTANTS)([^a-zA-Z])/EmulatorConstants.\1\2/g" \
| $SED "s/void send_cmio_response(STATE_ACCESS a, uint16 reason, bytes data, uint32 dataLength) {/function sendCmioResponse(AccessLogs.Context memory a, uint16 reason, bytes32 dataHash, uint32 dataLength) internal pure {/" \
| $SED "s/const uint64/uint64/g" \
Expand Down
4 changes: 2 additions & 2 deletions helper_scripts/generate_UArchStep.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ pattern="namespace cartesi \{(.*)\}"
# replace cpp specific syntaxes with solidity ones
cpp_src=`echo "${BASH_REMATCH[1]}" \
| $SED "/template/d" \
| $SED "/note = a.make_scoped_note/d" \
| $SED "/[[maybe_unused]]/d" \
| $SED "/(void) note/d" \
| $SED "s/constexpr//g" \
| $SED "s/UarchState &a/AccessLogs.Context memory a/g" \
| $SED "s/const UarchState a/AccessLogs.Context memory a/g" \
| $SED "s/::/./g" \
| $SED "s/UINT64_MAX/type(uint64).max/g" \
| $SED -E "s/UArchStepStatus uarch_step/static inline UArchStepStatus step/g" \
Expand Down
4 changes: 2 additions & 2 deletions shasum-download
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
127e5430d6ca80c65f1943d2b72fe4fd0b970f444376cf6f8663f3ace496f5ff downloads/machine-emulator-tests-data.deb
154e2da0e3bb531a8b6e1f9ec64cb90a55cea245aeece2685aa34378d1cf9ea1 downloads/uarch-riscv-tests-json-logs.tar.gz
fef8844a306d83eef9c30828986645f2b0ff149654490b0f7d47ec2883bcb693 downloads/machine-emulator-tests-data.deb
bc7a0cc14724167c2826967eaf62feb6a2c902b2b45a73f3c684e1c5ceaa8f69 downloads/uarch-riscv-tests-json-logs.tar.gz
85 changes: 55 additions & 30 deletions src/AccessLogs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@ library AccessLogs {
return end2;
}

/// @dev bytes buffer layout is the same for `readWord` and `writeWord`,
/// [32 bytes as read data], [59 * 32 bytes as sibling hashes]

//
// Read methods
//
function readRegion(
AccessLogs.Context memory a,
Memory.Region memory region
Expand All @@ -90,30 +84,6 @@ library AccessLogs {
return readRegion(a, r);
}

function readWord(
AccessLogs.Context memory a,
Memory.PhysicalAddress readAddress
) internal pure returns (uint64) {
(Memory.PhysicalAddress leafAddress, uint64 wordOffset) =
readAddress.truncateToLeaf();

Memory.Region memory region = Memory.regionFromStride(
Memory.strideFromLeafAddress(leafAddress),
Memory.alignedSizeFromLog2(0)
);

bytes32 leaf = a.buffer.consumeBytes32();
bytes32 rootHash =
a.buffer.getRoot(region, keccak256(abi.encodePacked(leaf)));
require(a.currentRootHash == rootHash, "Read word root doesn't match");

bytes8 word = getBytes8FromBytes32AtOffset(leaf, wordOffset);
return machineWordToSolidityUint64(word);
}

//
// Write methods
//
function writeRegion(
AccessLogs.Context memory a,
Memory.Region memory region,
Expand Down Expand Up @@ -141,6 +111,36 @@ library AccessLogs {
writeRegion(a, r, newHash);
}

/// @dev bytes buffer layout is the same for `readWord` and `writeWord`,
/// [32 bytes as read data], [59 * 32 bytes as sibling hashes]

//
// Read methods
//
function readWord(
AccessLogs.Context memory a,
Memory.PhysicalAddress readAddress
) internal pure returns (uint64) {
(Memory.PhysicalAddress leafAddress, uint64 wordOffset) =
readAddress.truncateToLeaf();

Memory.Region memory region = Memory.regionFromStride(
Memory.strideFromLeafAddress(leafAddress),
Memory.alignedSizeFromLog2(0)
);

bytes32 leaf = a.buffer.consumeBytes32();
bytes32 rootHash =
a.buffer.getRoot(region, keccak256(abi.encodePacked(leaf)));
require(a.currentRootHash == rootHash, "Read word root doesn't match");

bytes8 word = getBytes8FromBytes32AtOffset(leaf, wordOffset);
return machineWordToSolidityUint64(word);
}

//
// Write methods
//
function writeWord(
AccessLogs.Context memory a,
Memory.PhysicalAddress writeAddress,
Expand Down Expand Up @@ -190,4 +190,29 @@ library AccessLogs {

return (leaf & mask) | toWrite;
}

/// @notice Writes 4 64-bit words (32 bytes total) to a single leaf in memory
/// @dev This function is specifically designed for writing TLB entries and similar structures
function write4Words(
AccessLogs.Context memory a,
Memory.Stride writeStride,
uint64 word0,
uint64 word1,
uint64 word2,
uint64 word3
) internal pure {
// Flip endianness and pack data
bytes8 w0 = solidityUint64ToMachineWord(word0);
bytes8 w1 = solidityUint64ToMachineWord(word1);
bytes8 w2 = solidityUint64ToMachineWord(word2);
bytes8 w3 = solidityUint64ToMachineWord(word3);
bytes32 packed = bytes32(abi.encodePacked(w0, w1, w2, w3));
// compute hash and write region
bytes32 newHash = keccak256(abi.encodePacked(packed));
writeRegion(
a,
Memory.regionFromStride(writeStride, Memory.alignedSizeFromLog2(0)),
newHash
);
}
}
76 changes: 76 additions & 0 deletions src/AdvanceStatus.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

/// @title AdvanceStatus
/// @notice Return advance status

pragma solidity ^0.8.0;

import "./EmulatorCompat.sol";
import "./EmulatorConstants.sol";

library AdvanceStatus {
enum Status {
NOT_YIELDED,
ACCEPTED,
REJECTED,
EXCEPTION
}

/*
typedef struct cmt_io_yield {
uint8_t dev;
uint8_t cmd;
uint16_t reason;
uint32_t data;
} cmt_io_yield_t;
*/

error InvalidReason(uint16 reason);

function advanceStatus(AccessLogs.Context memory a)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I don't like how this advanceStatus was introduced in the repository without being used or tests, however I assume Dave is testing it somehow, so we don't need to delay this PR.

internal
pure
returns (Status)
{
if (!EmulatorCompat.readIflagsY(a)) {
return Status.NOT_YIELDED;
}
Comment thread
edubart marked this conversation as resolved.

// the following two approaches are equivalent:
// 1. swap the whole struct and then extract the reason
// 2. extract the reason from the struct and then swap the value
// EmulatorCompat.readWord already swaps the struct, so we can extract the reason directly

uint64 tohost =
EmulatorCompat.readWord(a, EmulatorConstants.HTIF_TOHOST_ADDRESS);
uint16 reason = uint16(tohost >> 32);
Comment thread
edubart marked this conversation as resolved.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hardcoded 32 should ideally use a constant to make it more clear.


if (reason == EmulatorConstants.CMIO_YIELD_MANUAL_REASON_RX_ACCEPTED) {
return Status.ACCEPTED;
} else if (
reason == EmulatorConstants.CMIO_YIELD_MANUAL_REASON_RX_REJECTED
) {
return Status.REJECTED;
} else if (
reason == EmulatorConstants.CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION
) {
return Status.EXCEPTION;
} else {
revert InvalidReason(reason);
}
}
}
84 changes: 75 additions & 9 deletions src/EmulatorCompat.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,28 @@ import "./AccessLogs.sol";

library EmulatorCompat {
using AccessLogs for AccessLogs.Context;
using Buffer for Buffer.Context;
using Memory for uint64;

function getCheckpointHash(AccessLogs.Context memory a)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I don't like how both getCheckpointHash/setCheckpointHash were introduced in the repository without being used or tests, however I assume Dave is testing it somehow, so we don't need to delay this PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be renamed to RevertRootHash to match the emulator.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephenctw can we rename this to match the emulator naming convention? I think revert root hash is much more informative than checkpoint hash.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is being renamed by #88 which we will be merged after merging this PR first.

internal
pure
returns (bytes32)
{
bytes32 checkpointHash = a.buffer.consumeBytes32();
bytes32 hashOfCheckpointHash = a.readLeaf(
Memory.strideFromLeafAddress(
EmulatorConstants.REVERT_ROOT_HASH_ADDRESS.toPhysicalAddress()
)
);
require(
keccak256(abi.encodePacked(checkpointHash)) == hashOfCheckpointHash,
"checkpoint hash mismatch"
);

Comment thread
mpernambuco marked this conversation as resolved.
return checkpointHash;
}

function readCycle(AccessLogs.Context memory a)
internal
pure
Expand All @@ -35,12 +55,19 @@ library EmulatorCompat {
function readHaltFlag(AccessLogs.Context memory a)
internal
pure
returns (bool)
returns (uint64)
{
return a.readWord(
EmulatorConstants.UARCH_HALT_FLAG_ADDRESS.toPhysicalAddress()
);
}

function writeHaltFlag(AccessLogs.Context memory a, uint64 val)
internal
pure
{
return (
a.readWord(
EmulatorConstants.UARCH_HALT_FLAG_ADDRESS.toPhysicalAddress()
) != 0
a.writeWord(
EmulatorConstants.UARCH_HALT_FLAG_ADDRESS.toPhysicalAddress(), val
);
}

Expand Down Expand Up @@ -82,9 +109,15 @@ library EmulatorCompat {
);
}

function setHaltFlag(AccessLogs.Context memory a) internal pure {
a.writeWord(
EmulatorConstants.UARCH_HALT_FLAG_ADDRESS.toPhysicalAddress(), 1
function setCheckpointHash(
AccessLogs.Context memory a,
bytes32 checkpointHash
) internal pure {
a.writeLeaf(
Memory.strideFromLeafAddress(
EmulatorConstants.REVERT_ROOT_HASH_ADDRESS.toPhysicalAddress()
),
keccak256(abi.encodePacked(checkpointHash))
);
}

Expand Down Expand Up @@ -280,7 +313,40 @@ library EmulatorCompat {
revert(text);
}

function putChar(AccessLogs.Context memory a, uint8 c) internal pure {}
function putCharECALL(AccessLogs.Context memory a, uint8 c) internal pure {}

function markDirtyPageECALL(
AccessLogs.Context memory a,
uint64 paddr,
uint64 pma_index
) internal pure {}

function writeTlbECALL(
AccessLogs.Context memory a,
uint64 setIndex,
uint64 slotIndex,
uint64 vaddrPage,
uint64 vpOffset,
uint64 pmaIndex
) internal pure {
Comment thread
mpernambuco marked this conversation as resolved.
// compute physical address of the TLB slot
uint64 paddress = EmulatorConstants.AR_SHADOW_TLB_START
+ (setIndex * EmulatorConstants.TLB_SET_LENGTH)
+ (slotIndex * EmulatorConstants.TLB_SLOT_LENGTH);

Comment thread
mpernambuco marked this conversation as resolved.
// compute stride from physical address (stride = address / leaf_size)
Memory.Stride writeStride =
Memory.strideFromLeafAddress(paddress.toPhysicalAddress());

// write the 4 words
a.write4Words(
writeStride,
vaddrPage,
vpOffset,
pmaIndex,
0 // zero_padding_
);
}

function uint32Log2(uint32 value) internal pure returns (uint32) {
require(value > 0, "EmulatorCompat: log2(0) is undefined");
Expand Down
Loading
Loading