diff --git a/helper_scripts/generate_EmulatorConstants.lua b/helper_scripts/generate_EmulatorConstants.lua index eff52ba..9f4a6f7 100755 --- a/helper_scripts/generate_EmulatorConstants.lua +++ b/helper_scripts/generate_EmulatorConstants.lua @@ -15,8 +15,7 @@ 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_HALT_FLAG_ADDRESS = 0x' .. hex(cartesi.machine:get_reg_address("uarch_halt_flag")) .. ';\n') @@ -27,22 +26,28 @@ out:write(' uint64 constant UARCH_SHADOW_LENGTH = 0x' .. hex(cartesi.UARCH_SH 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 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(' uint8 constant CMIO_YIELD_REASON_ADVANCE_STATE = 0x' .. - hex(cartesi.CMIO_YIELD_REASON_ADVANCE_STATE) .. ';\n') +out:write(' uint64 constant HTIF_TOHOST_ADDRESS = 0x' .. + hex(cartesi.machine:get_reg_address("htif_tohost")) .. ';\n') +out:write(' uint64 constant PMA_CMIO_TX_BUFFER_START = 0x' .. hex(cartesi.PMA_CMIO_TX_BUFFER_START) .. ';\n') +out:write(' uint64 constant PMA_CMIO_RX_BUFFER_START = 0x' .. hex(cartesi.PMA_CMIO_RX_BUFFER_START) .. ';\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(' 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(' uint8 constant PMA_CMIO_TX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.PMA_CMIO_TX_BUFFER_LOG2_SIZE) .. ';\n') -out:close() +out:write(' uint8 constant PMA_CMIO_RX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.PMA_CMIO_RX_BUFFER_LOG2_SIZE) .. ';\n') +out:write(' uint8 constant CMIO_YIELD_REASON_ADVANCE_STATE = 0x' .. + hex(cartesi.CMIO_YIELD_REASON_ADVANCE_STATE) .. ';\n') out:close() diff --git a/src/AccessLogs.sol b/src/AccessLogs.sol index 719c362..457a6ba 100644 --- a/src/AccessLogs.sol +++ b/src/AccessLogs.sol @@ -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 @@ -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, @@ -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, @@ -169,20 +169,20 @@ library AccessLogs { a.currentRootHash = newRootHash; } - function getBytes8FromBytes32AtOffset(bytes32 source, uint64 offset) + function getBytes8FromBytes32AtOffset(bytes32 source, uint64 offsetInBytes) internal pure returns (bytes8) { - return bytes8(source << (offset << Memory.LOG2_WORD)); + return bytes8(source << (offsetInBytes << Memory.LOG2_WORD)); } function setBytes8ToBytes32AtOffset( bytes8 word, bytes32 leaf, - uint64 offset + uint64 offsetInBytes ) internal pure returns (bytes32) { - uint256 wordOffset = offset << Memory.LOG2_WORD; + uint256 wordOffset = offsetInBytes << Memory.LOG2_WORD; bytes32 toWrite = bytes32(word) >> wordOffset; bytes32 wordMask = bytes32(~bytes8(0)); diff --git a/src/AdvanceStatus.sol b/src/AdvanceStatus.sol new file mode 100644 index 0000000..3df320b --- /dev/null +++ b/src/AdvanceStatus.sol @@ -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) + internal + pure + returns (Status) + { + if (!EmulatorCompat.readIflagsY(a)) { + return Status.NOT_YIELDED; + } + + // 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); + + 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); + } + } +} diff --git a/src/EmulatorCompat.sol b/src/EmulatorCompat.sol index 1e08456..ed3a1a6 100644 --- a/src/EmulatorCompat.sol +++ b/src/EmulatorCompat.sol @@ -20,8 +20,27 @@ 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) + internal + pure + returns (bytes32) + { + bytes32 checkpointHash = a.buffer.consumeBytes32(); + bytes32 hashOfCheckpointHash = a.readLeaf( + Memory.strideFromLeafAddress( + EmulatorConstants.CHECKPOINT_ADDRESS.toPhysicalAddress() + ) + ); + require( + keccak256(abi.encodePacked(checkpointHash)) == hashOfCheckpointHash + ); + + return checkpointHash; + } + function readCycle(AccessLogs.Context memory a) internal pure @@ -88,6 +107,18 @@ library EmulatorCompat { ); } + function setCheckpointHash( + AccessLogs.Context memory a, + bytes32 checkpointHash + ) internal pure { + a.writeLeaf( + Memory.strideFromLeafAddress( + EmulatorConstants.CHECKPOINT_ADDRESS.toPhysicalAddress() + ), + keccak256(abi.encodePacked(checkpointHash)) + ); + } + function writePc(AccessLogs.Context memory a, uint64 val) internal pure { a.writeWord(EmulatorConstants.UARCH_PC_ADDRESS.toPhysicalAddress(), val); } diff --git a/src/EmulatorConstants.sol b/src/EmulatorConstants.sol index f38a9f4..ef0013d 100644 --- a/src/EmulatorConstants.sol +++ b/src/EmulatorConstants.sol @@ -24,6 +24,8 @@ pragma solidity ^0.8.0; library EmulatorConstants { // START OF AUTO-GENERATED CODE + bytes32 constant UARCH_PRISTINE_STATE_HASH = + 0x1bbf39b2c4324c9c8862b8e5550fe35b06ebccb0dcd2c3f114cc1411813ca5fc; uint64 constant UARCH_CYCLE_ADDRESS = 0x400008; uint64 constant UARCH_HALT_FLAG_ADDRESS = 0x400000; uint64 constant UARCH_PC_ADDRESS = 0x400010; @@ -33,23 +35,26 @@ library EmulatorConstants { uint64 constant UARCH_RAM_START_ADDRESS = 0x600000; uint64 constant UARCH_RAM_LENGTH = 0x200000; uint64 constant UARCH_STATE_START_ADDRESS = 0x400000; - uint8 constant UARCH_STATE_LOG2_SIZE = 22; - bytes32 constant UARCH_PRISTINE_STATE_HASH = - 0x1bbf39b2c4324c9c8862b8e5550fe35b06ebccb0dcd2c3f114cc1411813ca5fc; uint64 constant UARCH_ECALL_FN_HALT = 1; uint64 constant UARCH_ECALL_FN_PUTCHAR = 2; uint64 constant HTIF_YIELD = 0x348; uint64 constant IFLAGS_Y_ADDRESS = 0x2f8; uint64 constant HTIF_FROMHOST_ADDRESS = 0x330; - uint8 constant CMIO_YIELD_REASON_ADVANCE_STATE = 0x0; + uint64 constant HTIF_TOHOST_ADDRESS = 0x328; + uint64 constant PMA_CMIO_TX_BUFFER_START = 0x60800000; + uint64 constant PMA_CMIO_RX_BUFFER_START = 0x60000000; uint32 constant TREE_LOG2_WORD_SIZE = 0x5; uint32 constant TREE_WORD_SIZE = uint32(1) << TREE_LOG2_WORD_SIZE; - uint64 constant PMA_CMIO_RX_BUFFER_START = 0x60000000; - uint8 constant PMA_CMIO_RX_BUFFER_LOG2_SIZE = 0x15; - uint64 constant PMA_CMIO_TX_BUFFER_START = 0x60800000; + uint16 constant CMIO_YIELD_MANUAL_REASON_RX_ACCEPTED = 0x1; + uint16 constant CMIO_YIELD_MANUAL_REASON_RX_REJECTED = 0x2; + uint16 constant CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION = 0x4; + uint8 constant UARCH_STATE_LOG2_SIZE = 22; uint8 constant PMA_CMIO_TX_BUFFER_LOG2_SIZE = 0x15; + uint8 constant PMA_CMIO_RX_BUFFER_LOG2_SIZE = 0x15; + uint8 constant CMIO_YIELD_REASON_ADVANCE_STATE = 0x0; // END OF AUTO-GENERATED CODE uint32 constant IFLAGS_Y_SHIFT = 1; uint64 constant LOG2_CYCLES_TO_RESET = 10; + uint64 constant CHECKPOINT_ADDRESS = 0x7ffff000; } diff --git a/templates/AccessLogs.sol.template b/templates/AccessLogs.sol.template index 1247327..736454b 100644 --- a/templates/AccessLogs.sol.template +++ b/templates/AccessLogs.sol.template @@ -72,15 +72,7 @@ library AccessLogs { return end2; } - - //:#ifndef test - - /// @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 @@ -102,32 +94,7 @@ library AccessLogs { Memory.regionFromStride(readStride, Memory.alignedSizeFromLog2(0)); 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, @@ -155,6 +122,41 @@ library AccessLogs { writeRegion(a, r, newHash); } + //:#ifndef test + + /// @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, @@ -242,12 +244,6 @@ library AccessLogs { } } - function writeRegion( - AccessLogs.Context memory a, - Memory.Region memory region, - bytes32 newHash - ) internal pure {} - function accessWord( AccessLogs.Context memory a, Memory.PhysicalAddress paddr diff --git a/templates/EmulatorConstants.sol.template b/templates/EmulatorConstants.sol.template index b238fe0..e68fcfa 100644 --- a/templates/EmulatorConstants.sol.template +++ b/templates/EmulatorConstants.sol.template @@ -27,4 +27,5 @@ library EmulatorConstants { uint32 constant IFLAGS_Y_SHIFT = 1; uint64 constant LOG2_CYCLES_TO_RESET = 10; + uint64 constant CHECKPOINT_ADDRESS = 0x7ffff000; }