Commit a2653a38 authored by Inphi's avatar Inphi Committed by GitHub

cannon: 64-bit Refactor (#12029)

* cannon: 64-bit Refactor

Refactor Cannon codebase to support both 32-bit and 64-bit MIPS emulation
while reusing code as much as possible.

* fix 64-bit test compilation errors

* review comments

* more review comments

* fcntl syscall err for 64-bit

* simplify pad check

* cannon: sc must store lsb 32 on 64-bits

* lint

* fix sc 64-bit logic

* add TODO state test
parent fed6f354
...@@ -162,6 +162,9 @@ jobs: ...@@ -162,6 +162,9 @@ jobs:
description: Whether to notify on failure description: Whether to notify on failure
type: boolean type: boolean
default: false default: false
mips64:
type: boolean
default: false
resource_class: xlarge resource_class: xlarge
steps: steps:
- checkout - checkout
...@@ -184,14 +187,29 @@ jobs: ...@@ -184,14 +187,29 @@ jobs:
command: | command: |
make lint make lint
working_directory: cannon working_directory: cannon
- run: - when:
name: Cannon Go tests condition:
command: | not: <<parameters.mips64>>
export SKIP_SLOW_TESTS=<<parameters.skip_slow_tests>> steps:
mkdir -p /testlogs - run:
gotestsum --format=testname --junitfile=/tmp/test-results/cannon.xml --jsonfile=/testlogs/log.json \ name: Cannon Go 32-bit tests
-- -parallel=8 -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage.out ./... command: |
working_directory: cannon export SKIP_SLOW_TESTS=<<parameters.skip_slow_tests>>
mkdir -p /testlogs
gotestsum --format=testname --junitfile=/tmp/test-results/cannon.xml --jsonfile=/testlogs/log.json \
-- -parallel=8 -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage.out ./...
working_directory: cannon
- when:
condition: <<parameters.mips64>>
steps:
- run:
name: Cannon Go 64-bit tests
command: |
export SKIP_SLOW_TESTS=<<parameters.skip_slow_tests>>
mkdir -p /testlogs
gotestsum --format=testname --junitfile=/tmp/test-results/cannon.xml --jsonfile=/testlogs/log.json \
-- --tags=cannon64 -parallel=8 -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage.out ./...
working_directory: cannon
- run: - run:
name: upload Cannon coverage name: upload Cannon coverage
command: codecov --verbose --clean --flags cannon-go-tests command: codecov --verbose --clean --flags cannon-go-tests
......
...@@ -15,18 +15,25 @@ endif ...@@ -15,18 +15,25 @@ endif
.DEFAULT_GOAL := cannon .DEFAULT_GOAL := cannon
cannon-impl: cannon32-impl:
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon-impl . env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build --tags=cannon32 -v $(LDFLAGS) -o ./bin/cannon32-impl .
cannon-embeds: cannon-impl cannon64-impl:
@cp bin/cannon-impl ./multicannon/embeds/cannon-2 env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build --tags=cannon64 -v $(LDFLAGS) -o ./bin/cannon64-impl .
@cp bin/cannon-impl ./multicannon/embeds/cannon-1
cannon-embeds: cannon32-impl cannon64-impl
# singlethreaded-v2
@cp bin/cannon32-impl ./multicannon/embeds/cannon-2
# multithreaded
@cp bin/cannon32-impl ./multicannon/embeds/cannon-1
# 64-bit multithreaded
@cp bin/cannon64-impl ./multicannon/embeds/cannon-3
cannon: cannon-embeds cannon: cannon-embeds
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon ./multicannon/ env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon ./multicannon/
clean: clean:
rm -rf bin rm -rf bin multicannon/embeds/cannon*
elf: elf:
make -C ./testdata/example elf make -C ./testdata/example elf
...@@ -84,9 +91,10 @@ fuzz: ...@@ -84,9 +91,10 @@ fuzz:
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests
.PHONY: \ .PHONY: \
cannon \ cannon32-impl \
cannon-impl \ cannon64-impl \
cannon-embeds \ cannon-embeds \
cannon \
clean \ clean \
test \ test \
lint \ lint \
......
...@@ -25,7 +25,7 @@ var ( ...@@ -25,7 +25,7 @@ var (
} }
LoadELFPathFlag = &cli.PathFlag{ LoadELFPathFlag = &cli.PathFlag{
Name: "path", Name: "path",
Usage: "Path to 32-bit big-endian MIPS ELF file", Usage: "Path to 32/64-bit big-endian MIPS ELF file",
TakesFile: true, TakesFile: true,
Required: true, Required: true,
} }
...@@ -80,7 +80,7 @@ func LoadELF(ctx *cli.Context) error { ...@@ -80,7 +80,7 @@ func LoadELF(ctx *cli.Context) error {
} }
return program.PatchStack(state) return program.PatchStack(state)
} }
case versions.VersionMultiThreaded: case versions.VersionMultiThreaded, versions.VersionMultiThreaded64:
createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) { createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) {
return program.LoadELF(f, multithreaded.CreateInitialState) return program.LoadELF(f, multithreaded.CreateInitialState)
} }
......
...@@ -11,19 +11,19 @@ import ( ...@@ -11,19 +11,19 @@ import (
"strings" "strings"
"time" "time"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/versions" "github.com/ethereum-optimism/optimism/cannon/mipsevm/versions"
"github.com/ethereum-optimism/optimism/cannon/serialize" "github.com/ethereum-optimism/optimism/cannon/serialize"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/pkg/profile" "github.com/pkg/profile"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
) )
var ( var (
...@@ -128,7 +128,7 @@ type Proof struct { ...@@ -128,7 +128,7 @@ type Proof struct {
OracleKey hexutil.Bytes `json:"oracle-key,omitempty"` OracleKey hexutil.Bytes `json:"oracle-key,omitempty"`
OracleValue hexutil.Bytes `json:"oracle-value,omitempty"` OracleValue hexutil.Bytes `json:"oracle-value,omitempty"`
OracleOffset uint32 `json:"oracle-offset,omitempty"` OracleOffset arch.Word `json:"oracle-offset,omitempty"`
} }
type rawHint string type rawHint string
...@@ -288,7 +288,7 @@ func Run(ctx *cli.Context) error { ...@@ -288,7 +288,7 @@ func Run(ctx *cli.Context) error {
stopAtAnyPreimage := false stopAtAnyPreimage := false
var stopAtPreimageKeyPrefix []byte var stopAtPreimageKeyPrefix []byte
stopAtPreimageOffset := uint32(0) stopAtPreimageOffset := arch.Word(0)
if ctx.IsSet(RunStopAtPreimageFlag.Name) { if ctx.IsSet(RunStopAtPreimageFlag.Name) {
val := ctx.String(RunStopAtPreimageFlag.Name) val := ctx.String(RunStopAtPreimageFlag.Name)
parts := strings.Split(val, "@") parts := strings.Split(val, "@")
...@@ -297,11 +297,11 @@ func Run(ctx *cli.Context) error { ...@@ -297,11 +297,11 @@ func Run(ctx *cli.Context) error {
} }
stopAtPreimageKeyPrefix = common.FromHex(parts[0]) stopAtPreimageKeyPrefix = common.FromHex(parts[0])
if len(parts) == 2 { if len(parts) == 2 {
x, err := strconv.ParseUint(parts[1], 10, 32) x, err := strconv.ParseUint(parts[1], 10, arch.WordSizeBytes)
if err != nil { if err != nil {
return fmt.Errorf("invalid preimage offset: %w", err) return fmt.Errorf("invalid preimage offset: %w", err)
} }
stopAtPreimageOffset = uint32(x) stopAtPreimageOffset = arch.Word(x)
} }
} else { } else {
switch ctx.String(RunStopAtPreimageTypeFlag.Name) { switch ctx.String(RunStopAtPreimageTypeFlag.Name) {
...@@ -463,7 +463,7 @@ func Run(ctx *cli.Context) error { ...@@ -463,7 +463,7 @@ func Run(ctx *cli.Context) error {
} }
lastPreimageKey, lastPreimageValue, lastPreimageOffset := vm.LastPreimage() lastPreimageKey, lastPreimageValue, lastPreimageOffset := vm.LastPreimage()
if lastPreimageOffset != ^uint32(0) { if lastPreimageOffset != ^arch.Word(0) {
if stopAtAnyPreimage { if stopAtAnyPreimage {
l.Info("Stopping at preimage read") l.Info("Stopping at preimage read")
break break
......
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
app.Name = "cannon" app.Name = os.Args[0]
app.Usage = "MIPS Fault Proof tool" app.Usage = "MIPS Fault Proof tool"
app.Description = "MIPS Fault Proof tool" app.Description = "MIPS Fault Proof tool"
app.Commands = []*cli.Command{ app.Commands = []*cli.Command{
......
//go:build !cannon64
// +build !cannon64
package arch
import "encoding/binary"
type (
// Word differs from the tradditional meaning in MIPS. The type represents the *maximum* architecture specific access length and value sizes.
Word = uint32
// SignedInteger specifies the maximum signed integer type used for arithmetic.
SignedInteger = int32
)
const (
IsMips32 = true
WordSize = 32
WordSizeBytes = WordSize >> 3
PageAddrSize = 12
PageKeySize = WordSize - PageAddrSize
MemProofLeafCount = 28
MemProofSize = MemProofLeafCount * 32
AddressMask = 0xFFffFFfc
ExtMask = 0x3
HeapStart = 0x05_00_00_00
HeapEnd = 0x60_00_00_00
ProgramBreak = 0x40_00_00_00
HighMemoryStart = 0x7f_ff_d0_00
)
var ByteOrderWord = byteOrder32{}
type byteOrder32 struct{}
func (bo byteOrder32) Word(b []byte) Word {
return binary.BigEndian.Uint32(b)
}
func (bo byteOrder32) AppendWord(b []byte, v uint32) []byte {
return binary.BigEndian.AppendUint32(b, v)
}
func (bo byteOrder32) PutWord(b []byte, v uint32) {
binary.BigEndian.PutUint32(b, v)
}
//go:build cannon64
// +build cannon64
package arch
import "encoding/binary"
type (
// Word differs from the tradditional meaning in MIPS. The type represents the *maximum* architecture specific access length and value sizes
Word = uint64
// SignedInteger specifies the maximum signed integer type used for arithmetic.
SignedInteger = int64
)
const (
IsMips32 = false
WordSize = 64
WordSizeBytes = WordSize >> 3
PageAddrSize = 12
PageKeySize = WordSize - PageAddrSize
MemProofLeafCount = 60
MemProofSize = MemProofLeafCount * 32
AddressMask = 0xFFFFFFFFFFFFFFF8
ExtMask = 0x7
HeapStart = 0x10_00_00_00_00_00_00_00
HeapEnd = 0x60_00_00_00_00_00_00_00
ProgramBreak = 0x40_00_00_00_00_00_00_00
HighMemoryStart = 0x7F_FF_FF_FF_D0_00_00_00
)
var ByteOrderWord = byteOrder64{}
type byteOrder64 struct{}
func (bo byteOrder64) Word(b []byte) Word {
return binary.BigEndian.Uint64(b)
}
func (bo byteOrder64) AppendWord(b []byte, v uint64) []byte {
return binary.BigEndian.AppendUint64(b, v)
}
func (bo byteOrder64) PutWord(b []byte, v uint64) {
binary.BigEndian.PutUint64(b, v)
}
package arch
type ByteOrder interface {
Word([]byte) Word
AppendWord([]byte, Word) []byte
PutWord([]byte, Word)
}
...@@ -7,12 +7,12 @@ import ( ...@@ -7,12 +7,12 @@ import (
) )
type MemTracker interface { type MemTracker interface {
TrackMemAccess(addr uint32) TrackMemAccess(addr Word)
} }
type MemoryTrackerImpl struct { type MemoryTrackerImpl struct {
memory *memory.Memory memory *memory.Memory
lastMemAccess uint32 lastMemAccess Word
memProofEnabled bool memProofEnabled bool
// proof of first unique memory access // proof of first unique memory access
memProof [memory.MEM_PROOF_SIZE]byte memProof [memory.MEM_PROOF_SIZE]byte
...@@ -24,9 +24,9 @@ func NewMemoryTracker(memory *memory.Memory) *MemoryTrackerImpl { ...@@ -24,9 +24,9 @@ func NewMemoryTracker(memory *memory.Memory) *MemoryTrackerImpl {
return &MemoryTrackerImpl{memory: memory} return &MemoryTrackerImpl{memory: memory}
} }
func (m *MemoryTrackerImpl) TrackMemAccess(effAddr uint32) { func (m *MemoryTrackerImpl) TrackMemAccess(effAddr Word) {
if m.memProofEnabled && m.lastMemAccess != effAddr { if m.memProofEnabled && m.lastMemAccess != effAddr {
if m.lastMemAccess != ^uint32(0) { if m.lastMemAccess != ^Word(0) {
panic(fmt.Errorf("unexpected different mem access at %08x, already have access at %08x buffered", effAddr, m.lastMemAccess)) panic(fmt.Errorf("unexpected different mem access at %08x, already have access at %08x buffered", effAddr, m.lastMemAccess))
} }
m.lastMemAccess = effAddr m.lastMemAccess = effAddr
...@@ -36,7 +36,7 @@ func (m *MemoryTrackerImpl) TrackMemAccess(effAddr uint32) { ...@@ -36,7 +36,7 @@ func (m *MemoryTrackerImpl) TrackMemAccess(effAddr uint32) {
// TrackMemAccess2 creates a proof for a memory access following a call to TrackMemAccess // TrackMemAccess2 creates a proof for a memory access following a call to TrackMemAccess
// This is used to generate proofs for contiguous memory accesses within the same step // This is used to generate proofs for contiguous memory accesses within the same step
func (m *MemoryTrackerImpl) TrackMemAccess2(effAddr uint32) { func (m *MemoryTrackerImpl) TrackMemAccess2(effAddr Word) {
if m.memProofEnabled && m.lastMemAccess+4 != effAddr { if m.memProofEnabled && m.lastMemAccess+4 != effAddr {
panic(fmt.Errorf("unexpected disjointed mem access at %08x, last memory access is at %08x buffered", effAddr, m.lastMemAccess)) panic(fmt.Errorf("unexpected disjointed mem access at %08x, last memory access is at %08x buffered", effAddr, m.lastMemAccess))
} }
...@@ -46,7 +46,7 @@ func (m *MemoryTrackerImpl) TrackMemAccess2(effAddr uint32) { ...@@ -46,7 +46,7 @@ func (m *MemoryTrackerImpl) TrackMemAccess2(effAddr uint32) {
func (m *MemoryTrackerImpl) Reset(enableProof bool) { func (m *MemoryTrackerImpl) Reset(enableProof bool) {
m.memProofEnabled = enableProof m.memProofEnabled = enableProof
m.lastMemAccess = ^uint32(0) m.lastMemAccess = ^Word(0)
} }
func (m *MemoryTrackerImpl) MemProof() [memory.MEM_PROOF_SIZE]byte { func (m *MemoryTrackerImpl) MemProof() [memory.MEM_PROOF_SIZE]byte {
......
package exec package exec
import ( import (
"fmt"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
// TODO(#12205): MIPS64 port. Replace with a custom library
u128 "lukechampine.com/uint128"
) )
const ( const (
OpLoadLinked = 0x30 OpLoadLinked = 0x30
OpStoreConditional = 0x38 OpStoreConditional = 0x38
OpLoadLinked64 = 0x34
OpStoreConditional64 = 0x3c
) )
func GetInstructionDetails(pc uint32, memory *memory.Memory) (insn, opcode, fun uint32) { func GetInstructionDetails(pc Word, memory *memory.Memory) (insn, opcode, fun uint32) {
insn = memory.GetMemory(pc) insn = memory.GetMemory(pc)
opcode = insn >> 26 // First 6-bits opcode = insn >> 26 // First 6-bits
fun = insn & 0x3f // Last 6-bits fun = insn & 0x3f // Last 6-bits
...@@ -18,47 +26,53 @@ func GetInstructionDetails(pc uint32, memory *memory.Memory) (insn, opcode, fun ...@@ -18,47 +26,53 @@ func GetInstructionDetails(pc uint32, memory *memory.Memory) (insn, opcode, fun
return insn, opcode, fun return insn, opcode, fun
} }
func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memory *memory.Memory, insn, opcode, fun uint32, memTracker MemTracker, stackTracker StackTracker) (memUpdated bool, memAddr uint32, err error) { func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory *memory.Memory, insn, opcode, fun uint32, memTracker MemTracker, stackTracker StackTracker) (memUpdated bool, memAddr Word, err error) {
// j-type j/jal // j-type j/jal
if opcode == 2 || opcode == 3 { if opcode == 2 || opcode == 3 {
linkReg := uint32(0) linkReg := Word(0)
if opcode == 3 { if opcode == 3 {
linkReg = 31 linkReg = 31
} }
// Take top 4 bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset // Take the top bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset
target := (cpu.NextPC & 0xF0000000) | ((insn & 0x03FFFFFF) << 2) target := (cpu.NextPC & SignExtend(0xF0000000, 32)) | Word((insn&0x03FFFFFF)<<2)
stackTracker.PushStack(cpu.PC, target) stackTracker.PushStack(cpu.PC, target)
err = HandleJump(cpu, registers, linkReg, target) err = HandleJump(cpu, registers, linkReg, target)
return return
} }
// register fetch // register fetch
rs := uint32(0) // source register 1 value rs := Word(0) // source register 1 value
rt := uint32(0) // source register 2 / temp value rt := Word(0) // source register 2 / temp value
rtReg := (insn >> 16) & 0x1F rtReg := Word((insn >> 16) & 0x1F)
// R-type or I-type (stores rt) // R-type or I-type (stores rt)
rs = registers[(insn>>21)&0x1F] rs = registers[(insn>>21)&0x1F]
rdReg := rtReg rdReg := rtReg
if opcode == 0 || opcode == 0x1c { if opcode == 0x27 || opcode == 0x1A || opcode == 0x1B { // 64-bit opcodes lwu, ldl, ldr
assertMips64(insn)
// store rt value with store
rt = registers[rtReg]
// store actual rt with lwu, ldl and ldr
rdReg = rtReg
} else if opcode == 0 || opcode == 0x1c {
// R-type (stores rd) // R-type (stores rd)
rt = registers[rtReg] rt = registers[rtReg]
rdReg = (insn >> 11) & 0x1F rdReg = Word((insn >> 11) & 0x1F)
} else if opcode < 0x20 { } else if opcode < 0x20 {
// rt is SignExtImm // rt is SignExtImm
// don't sign extend for andi, ori, xori // don't sign extend for andi, ori, xori
if opcode == 0xC || opcode == 0xD || opcode == 0xe { if opcode == 0xC || opcode == 0xD || opcode == 0xe {
// ZeroExtImm // ZeroExtImm
rt = insn & 0xFFFF rt = Word(insn & 0xFFFF)
} else { } else {
// SignExtImm // SignExtImm
rt = SignExtend(insn&0xFFFF, 16) rt = SignExtendImmediate(insn)
} }
} else if opcode >= 0x28 || opcode == 0x22 || opcode == 0x26 { } else if opcode >= 0x28 || opcode == 0x22 || opcode == 0x26 {
// store rt value with store // store rt value with store
rt = registers[rtReg] rt = registers[rtReg]
// store actual rt with lwl and lwr // store actual rt with lwl, ldl, and lwr
rdReg = rtReg rdReg = rtReg
} }
...@@ -67,30 +81,39 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memor ...@@ -67,30 +81,39 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memor
return return
} }
storeAddr := uint32(0xFF_FF_FF_FF) storeAddr := ^Word(0)
// memory fetch (all I-type) // memory fetch (all I-type)
// we do the load for stores also // we do the load for stores also
mem := uint32(0) mem := Word(0)
if opcode >= 0x20 { if opcode >= 0x20 {
// M[R[rs]+SignExtImm] // M[R[rs]+SignExtImm]
rs += SignExtend(insn&0xFFFF, 16) rs += SignExtendImmediate(insn)
addr := rs & 0xFFFFFFFC addr := rs & arch.AddressMask
memTracker.TrackMemAccess(addr) memTracker.TrackMemAccess(addr)
mem = memory.GetMemory(addr) mem = memory.GetWord(addr)
if opcode >= 0x28 { if opcode >= 0x28 {
// store // store for 32-bit
storeAddr = addr // for 64-bit: ld (0x37) is the only non-store opcode >= 0x28
// store opcodes don't write back to a register // SAFETY: On 32-bit mode, 0x37 will be considered an invalid opcode by ExecuteMipsInstruction
rdReg = 0 if arch.IsMips32 || opcode != 0x37 {
// store
storeAddr = addr
// store opcodes don't write back to a register
rdReg = 0
}
} }
} }
// ALU // ALU
val := ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem) val := ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem)
if opcode == 0 && fun >= 8 && fun < 0x1c { funSel := uint32(0x1c)
if !arch.IsMips32 {
funSel = 0x20
}
if opcode == 0 && fun >= 8 && fun < funSel {
if fun == 8 || fun == 9 { // jr/jalr if fun == 8 || fun == 9 { // jr/jalr
linkReg := uint32(0) linkReg := Word(0)
if fun == 9 { if fun == 9 {
linkReg = rdReg linkReg = rdReg
stackTracker.PushStack(cpu.PC, rs) stackTracker.PushStack(cpu.PC, rs)
...@@ -112,16 +135,16 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memor ...@@ -112,16 +135,16 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memor
// lo and hi registers // lo and hi registers
// can write back // can write back
if fun >= 0x10 && fun < 0x1c { if fun >= 0x10 && fun < funSel {
err = HandleHiLo(cpu, registers, fun, rs, rt, rdReg) err = HandleHiLo(cpu, registers, fun, rs, rt, rdReg)
return return
} }
} }
// write memory // write memory
if storeAddr != 0xFF_FF_FF_FF { if storeAddr != ^Word(0) {
memTracker.TrackMemAccess(storeAddr) memTracker.TrackMemAccess(storeAddr)
memory.SetMemory(storeAddr, val) memory.SetWord(storeAddr, val)
memUpdated = true memUpdated = true
memAddr = storeAddr memAddr = storeAddr
} }
...@@ -131,12 +154,24 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memor ...@@ -131,12 +154,24 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memor
return return
} }
func SignExtendImmediate(insn uint32) uint32 { func SignExtendImmediate(insn uint32) Word {
return SignExtend(insn&0xFFFF, 16) return SignExtend(Word(insn&0xFFFF), 16)
}
func assertMips64(insn uint32) {
if arch.IsMips32 {
panic(fmt.Sprintf("invalid instruction: %x", insn))
}
}
func assertMips64Fun(fun uint32) {
if arch.IsMips32 {
panic(fmt.Sprintf("invalid instruction func: %x", fun))
}
} }
func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem Word) Word {
if opcode == 0 || (opcode >= 8 && opcode < 0xF) { if opcode == 0 || (opcode >= 8 && opcode < 0xF) || (!arch.IsMips32 && (opcode == 0x18 || opcode == 0x19)) {
// transform ArithLogI to SPECIAL // transform ArithLogI to SPECIAL
switch opcode { switch opcode {
case 8: case 8:
...@@ -153,24 +188,28 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { ...@@ -153,24 +188,28 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 {
fun = 0x25 // ori fun = 0x25 // ori
case 0xE: case 0xE:
fun = 0x26 // xori fun = 0x26 // xori
case 0x18:
fun = 0x2c // daddi
case 0x19:
fun = 0x2d // daddiu
} }
switch fun { switch fun {
case 0x00: // sll case 0x00: // sll
return rt << ((insn >> 6) & 0x1F) return SignExtend((rt&0xFFFFFFFF)<<((insn>>6)&0x1F), 32)
case 0x02: // srl case 0x02: // srl
return rt >> ((insn >> 6) & 0x1F) return SignExtend((rt&0xFFFFFFFF)>>((insn>>6)&0x1F), 32)
case 0x03: // sra case 0x03: // sra
shamt := (insn >> 6) & 0x1F shamt := Word((insn >> 6) & 0x1F)
return SignExtend(rt>>shamt, 32-shamt) return SignExtend((rt&0xFFFFFFFF)>>shamt, 32-shamt)
case 0x04: // sllv case 0x04: // sllv
return rt << (rs & 0x1F) return SignExtend((rt&0xFFFFFFFF)<<(rs&0x1F), 32)
case 0x06: // srlv case 0x06: // srlv
return rt >> (rs & 0x1F) return SignExtend((rt&0xFFFFFFFF)>>(rs&0x1F), 32)
case 0x07: // srav case 0x07: // srav
shamt := rs & 0x1F shamt := Word(rs & 0x1F)
return SignExtend(rt>>shamt, 32-shamt) return SignExtend((rt&0xFFFFFFFF)>>shamt, 32-shamt)
// functs in range [0x8, 0x1b] are handled specially by other functions // functs in range [0x8, 0x1b] for 32-bit and [0x8, 0x1f] for 64-bit are handled specially by other functions
case 0x08: // jr case 0x08: // jr
return rs return rs
case 0x09: // jalr case 0x09: // jalr
...@@ -192,6 +231,15 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { ...@@ -192,6 +231,15 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 {
return rs return rs
case 0x13: // mtlo case 0x13: // mtlo
return rs return rs
case 0x14: // dsllv
assertMips64(insn)
return rt
case 0x16: // dsrlv
assertMips64(insn)
return rt
case 0x17: // dsrav
assertMips64(insn)
return rt
case 0x18: // mult case 0x18: // mult
return rs return rs
case 0x19: // multu case 0x19: // multu
...@@ -200,15 +248,27 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { ...@@ -200,15 +248,27 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 {
return rs return rs
case 0x1b: // divu case 0x1b: // divu
return rs return rs
case 0x1C: // dmult
assertMips64(insn)
return rs
case 0x1D: // dmultu
assertMips64(insn)
return rs
case 0x1E: // ddiv
assertMips64(insn)
return rs
case 0x1F: // ddivu
assertMips64(insn)
return rs
// The rest includes transformed R-type arith imm instructions // The rest includes transformed R-type arith imm instructions
case 0x20: // add case 0x20: // add
return rs + rt return SignExtend(Word(int32(rs)+int32(rt)), 32)
case 0x21: // addu case 0x21: // addu
return rs + rt return SignExtend(Word(uint32(rs)+uint32(rt)), 32)
case 0x22: // sub case 0x22: // sub
return rs - rt return SignExtend(Word(int32(rs)-int32(rt)), 32)
case 0x23: // subu case 0x23: // subu
return rs - rt return SignExtend(Word(uint32(rs)-uint32(rt)), 32)
case 0x24: // and case 0x24: // and
return rs & rt return rs & rt
case 0x25: // or case 0x25: // or
...@@ -218,7 +278,7 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { ...@@ -218,7 +278,7 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 {
case 0x27: // nor case 0x27: // nor
return ^(rs | rt) return ^(rs | rt)
case 0x2a: // slti case 0x2a: // slti
if int32(rs) < int32(rt) { if arch.SignedInteger(rs) < arch.SignedInteger(rt) {
return 1 return 1
} }
return 0 return 0
...@@ -227,8 +287,38 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { ...@@ -227,8 +287,38 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 {
return 1 return 1
} }
return 0 return 0
case 0x2c: // dadd
assertMips64(insn)
return rs + rt
case 0x2d: // daddu
assertMips64(insn)
return rs + rt
case 0x2e: // dsub
assertMips64(insn)
return rs - rt
case 0x2f: // dsubu
assertMips64(insn)
return rs - rt
case 0x38: // dsll
assertMips64(insn)
return rt << ((insn >> 6) & 0x1f)
case 0x3A: // dsrl
assertMips64(insn)
return rt >> ((insn >> 6) & 0x1f)
case 0x3B: // dsra
assertMips64(insn)
return Word(int64(rt) >> ((insn >> 6) & 0x1f))
case 0x3C: // dsll32
assertMips64(insn)
return rt << (((insn >> 6) & 0x1f) + 32)
case 0x3E: // dsll32
assertMips64(insn)
return rt >> (((insn >> 6) & 0x1f) + 32)
case 0x3F: // dsll32
assertMips64(insn)
return Word(int64(rt) >> (((insn >> 6) & 0x1f) + 32))
default: default:
panic("invalid instruction") panic(fmt.Sprintf("invalid instruction: %x", insn))
} }
} else { } else {
switch opcode { switch opcode {
...@@ -236,7 +326,7 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { ...@@ -236,7 +326,7 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 {
case 0x1C: case 0x1C:
switch fun { switch fun {
case 0x2: // mul case 0x2: // mul
return uint32(int32(rs) * int32(rt)) return SignExtend(Word(int32(rs)*int32(rt)), 32)
case 0x20, 0x21: // clz, clo case 0x20, 0x21: // clz, clo
if fun == 0x20 { if fun == 0x20 {
rs = ^rs rs = ^rs
...@@ -245,45 +335,98 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { ...@@ -245,45 +335,98 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 {
for ; rs&0x80000000 != 0; i++ { for ; rs&0x80000000 != 0; i++ {
rs <<= 1 rs <<= 1
} }
return i return Word(i)
} }
case 0x0F: // lui case 0x0F: // lui
return rt << 16 return SignExtend(rt<<16, 32)
case 0x20: // lb case 0x20: // lb
return SignExtend((mem>>(24-(rs&3)*8))&0xFF, 8) msb := uint32(arch.WordSize - 8) // 24 for 32-bit and 56 for 64-bit
return SignExtend((mem>>(msb-uint32(rs&arch.ExtMask)*8))&0xFF, 8)
case 0x21: // lh case 0x21: // lh
return SignExtend((mem>>(16-(rs&2)*8))&0xFFFF, 16) msb := uint32(arch.WordSize - 16) // 16 for 32-bit and 48 for 64-bit
mask := Word(arch.ExtMask - 1)
return SignExtend((mem>>(msb-uint32(rs&mask)*8))&0xFFFF, 16)
case 0x22: // lwl case 0x22: // lwl
val := mem << ((rs & 3) * 8) val := mem << ((rs & 3) * 8)
mask := uint32(0xFFFFFFFF) << ((rs & 3) * 8) mask := Word(uint32(0xFFFFFFFF) << ((rs & 3) * 8))
return (rt & ^mask) | val return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32)
case 0x23: // lw case 0x23: // lw
// TODO(#12205): port to MIPS64
return mem return mem
//return SignExtend((mem>>(32-((rs&0x4)<<3)))&0xFFFFFFFF, 32)
case 0x24: // lbu case 0x24: // lbu
return (mem >> (24 - (rs&3)*8)) & 0xFF msb := uint32(arch.WordSize - 8) // 24 for 32-bit and 56 for 64-bit
return (mem >> (msb - uint32(rs&arch.ExtMask)*8)) & 0xFF
case 0x25: // lhu case 0x25: // lhu
return (mem >> (16 - (rs&2)*8)) & 0xFFFF msb := uint32(arch.WordSize - 16) // 16 for 32-bit and 48 for 64-bit
mask := Word(arch.ExtMask - 1)
return (mem >> (msb - uint32(rs&mask)*8)) & 0xFFFF
case 0x26: // lwr case 0x26: // lwr
val := mem >> (24 - (rs&3)*8) val := mem >> (24 - (rs&3)*8)
mask := uint32(0xFFFFFFFF) >> (24 - (rs&3)*8) mask := Word(uint32(0xFFFFFFFF) >> (24 - (rs&3)*8))
return (rt & ^mask) | val return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32)
case 0x28: // sb case 0x28: // sb
val := (rt & 0xFF) << (24 - (rs&3)*8) msb := uint32(arch.WordSize - 8) // 24 for 32-bit and 56 for 64-bit
mask := 0xFFFFFFFF ^ uint32(0xFF<<(24-(rs&3)*8)) val := (rt & 0xFF) << (msb - uint32(rs&arch.ExtMask)*8)
mask := ^Word(0) ^ Word(0xFF<<(msb-uint32(rs&arch.ExtMask)*8))
return (mem & mask) | val return (mem & mask) | val
case 0x29: // sh case 0x29: // sh
val := (rt & 0xFFFF) << (16 - (rs&2)*8) msb := uint32(arch.WordSize - 16) // 16 for 32-bit and 48 for 64-bit
mask := 0xFFFFFFFF ^ uint32(0xFFFF<<(16-(rs&2)*8)) rsMask := Word(arch.ExtMask - 1) // 2 for 32-bit and 6 for 64-bit
sl := msb - uint32(rs&rsMask)*8
val := (rt & 0xFFFF) << sl
mask := ^Word(0) ^ Word(0xFFFF<<sl)
return (mem & mask) | val return (mem & mask) | val
case 0x2a: // swl case 0x2a: // swl
// TODO(#12205): port to MIPS64
val := rt >> ((rs & 3) * 8) val := rt >> ((rs & 3) * 8)
mask := uint32(0xFFFFFFFF) >> ((rs & 3) * 8) mask := uint32(0xFFFFFFFF) >> ((rs & 3) * 8)
return (mem & ^mask) | val return (mem & Word(^mask)) | val
case 0x2b: // sw case 0x2b: // sw
// TODO(#12205): port to MIPS64
return rt return rt
case 0x2e: // swr case 0x2e: // swr
// TODO(#12205): port to MIPS64
val := rt << (24 - (rs&3)*8) val := rt << (24 - (rs&3)*8)
mask := uint32(0xFFFFFFFF) << (24 - (rs&3)*8) mask := uint32(0xFFFFFFFF) << (24 - (rs&3)*8)
return (mem & Word(^mask)) | val
// MIPS64
case 0x1A: // ldl
assertMips64(insn)
sl := (rs & 0x7) << 3
val := mem << sl
mask := ^Word(0) << sl
return val | (rt & ^mask)
case 0x1B: // ldr
assertMips64(insn)
sr := 56 - ((rs & 0x7) << 3)
val := mem >> sr
mask := ^Word(0) << (64 - sr)
return val | (rt & mask)
case 0x27: // lwu
assertMips64(insn)
return (mem >> (32 - ((rs & 0x4) << 3))) & 0xFFFFFFFF
case 0x2C: // sdl
assertMips64(insn)
sr := (rs & 0x7) << 3
val := rt >> sr
mask := ^Word(0) >> sr
return val | (mem & ^mask)
case 0x2D: // sdr
assertMips64(insn)
sl := 56 - ((rs & 0x7) << 3)
val := rt << sl
mask := ^Word(0) << sl
return val | (mem & ^mask)
case 0x37: // ld
assertMips64(insn)
return mem
case 0x3F: // sd
assertMips64(insn)
sl := (rs & 0x7) << 3
val := rt << sl
mask := ^Word(0) << sl
return (mem & ^mask) | val return (mem & ^mask) | val
default: default:
panic("invalid instruction") panic("invalid instruction")
...@@ -292,10 +435,10 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { ...@@ -292,10 +435,10 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 {
panic("invalid instruction") panic("invalid instruction")
} }
func SignExtend(dat uint32, idx uint32) uint32 { func SignExtend(dat Word, idx Word) Word {
isSigned := (dat >> (idx - 1)) != 0 isSigned := (dat >> (idx - 1)) != 0
signed := ((uint32(1) << (32 - idx)) - 1) << idx signed := ((Word(1) << (arch.WordSize - idx)) - 1) << idx
mask := (uint32(1) << idx) - 1 mask := (Word(1) << idx) - 1
if isSigned { if isSigned {
return dat&mask | signed return dat&mask | signed
} else { } else {
...@@ -303,7 +446,7 @@ func SignExtend(dat uint32, idx uint32) uint32 { ...@@ -303,7 +446,7 @@ func SignExtend(dat uint32, idx uint32) uint32 {
} }
} }
func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]uint32, opcode uint32, insn uint32, rtReg uint32, rs uint32) error { func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]Word, opcode uint32, insn uint32, rtReg Word, rs Word) error {
if cpu.NextPC != cpu.PC+4 { if cpu.NextPC != cpu.PC+4 {
panic("branch in delay slot") panic("branch in delay slot")
} }
...@@ -313,9 +456,9 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]uint32, opcode uint32, ...@@ -313,9 +456,9 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]uint32, opcode uint32,
rt := registers[rtReg] rt := registers[rtReg]
shouldBranch = (rs == rt && opcode == 4) || (rs != rt && opcode == 5) shouldBranch = (rs == rt && opcode == 4) || (rs != rt && opcode == 5)
} else if opcode == 6 { } else if opcode == 6 {
shouldBranch = int32(rs) <= 0 // blez shouldBranch = arch.SignedInteger(rs) <= 0 // blez
} else if opcode == 7 { } else if opcode == 7 {
shouldBranch = int32(rs) > 0 // bgtz shouldBranch = arch.SignedInteger(rs) > 0 // bgtz
} else if opcode == 1 { } else if opcode == 1 {
// regimm // regimm
rtv := (insn >> 16) & 0x1F rtv := (insn >> 16) & 0x1F
...@@ -330,15 +473,15 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]uint32, opcode uint32, ...@@ -330,15 +473,15 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]uint32, opcode uint32,
prevPC := cpu.PC prevPC := cpu.PC
cpu.PC = cpu.NextPC // execute the delay slot first cpu.PC = cpu.NextPC // execute the delay slot first
if shouldBranch { if shouldBranch {
cpu.NextPC = prevPC + 4 + (SignExtend(insn&0xFFFF, 16) << 2) // then continue with the instruction the branch jumps to. cpu.NextPC = prevPC + 4 + (SignExtend(Word(insn&0xFFFF), 16) << 2) // then continue with the instruction the branch jumps to.
} else { } else {
cpu.NextPC = cpu.NextPC + 4 // branch not taken cpu.NextPC = cpu.NextPC + 4 // branch not taken
} }
return nil return nil
} }
func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]uint32, fun uint32, rs uint32, rt uint32, storeReg uint32) error { func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]Word, fun uint32, rs Word, rt Word, storeReg Word) error {
val := uint32(0) val := Word(0)
switch fun { switch fun {
case 0x10: // mfhi case 0x10: // mfhi
val = cpu.HI val = cpu.HI
...@@ -350,16 +493,44 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]uint32, fun uint32, rs u ...@@ -350,16 +493,44 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]uint32, fun uint32, rs u
cpu.LO = rs cpu.LO = rs
case 0x18: // mult case 0x18: // mult
acc := uint64(int64(int32(rs)) * int64(int32(rt))) acc := uint64(int64(int32(rs)) * int64(int32(rt)))
cpu.HI = uint32(acc >> 32) cpu.HI = SignExtend(Word(acc>>32), 32)
cpu.LO = uint32(acc) cpu.LO = SignExtend(Word(uint32(acc)), 32)
case 0x19: // multu case 0x19: // multu
acc := uint64(uint64(rs) * uint64(rt)) acc := uint64(uint32(rs)) * uint64(uint32(rt))
cpu.HI = uint32(acc >> 32) cpu.HI = SignExtend(Word(acc>>32), 32)
cpu.LO = uint32(acc) cpu.LO = SignExtend(Word(uint32(acc)), 32)
case 0x1a: // div case 0x1a: // div
cpu.HI = uint32(int32(rs) % int32(rt)) cpu.HI = SignExtend(Word(int32(rs)%int32(rt)), 32)
cpu.LO = uint32(int32(rs) / int32(rt)) cpu.LO = SignExtend(Word(int32(rs)/int32(rt)), 32)
case 0x1b: // divu case 0x1b: // divu
cpu.HI = SignExtend(Word(uint32(rs)%uint32(rt)), 32)
cpu.LO = SignExtend(Word(uint32(rs)/uint32(rt)), 32)
case 0x14: // dsllv
assertMips64Fun(fun)
val = rt << (rs & 0x3F)
case 0x16: // dsrlv
assertMips64Fun(fun)
val = rt >> (rs & 0x3F)
case 0x17: // dsrav
assertMips64Fun(fun)
val = Word(int64(rt) >> (rs & 0x3F))
case 0x1c: // dmult
// TODO(#12205): port to MIPS64. Is signed multiply needed for dmult
assertMips64Fun(fun)
acc := u128.From64(uint64(rs)).Mul(u128.From64(uint64(rt)))
cpu.HI = Word(acc.Hi)
cpu.LO = Word(acc.Lo)
case 0x1d: // dmultu
assertMips64Fun(fun)
acc := u128.From64(uint64(rs)).Mul(u128.From64(uint64(rt)))
cpu.HI = Word(acc.Hi)
cpu.LO = Word(acc.Lo)
case 0x1e: // ddiv
assertMips64Fun(fun)
cpu.HI = Word(int64(rs) % int64(rt))
cpu.LO = Word(int64(rs) / int64(rt))
case 0x1f: // ddivu
assertMips64Fun(fun)
cpu.HI = rs % rt cpu.HI = rs % rt
cpu.LO = rs / rt cpu.LO = rs / rt
} }
...@@ -373,7 +544,7 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]uint32, fun uint32, rs u ...@@ -373,7 +544,7 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]uint32, fun uint32, rs u
return nil return nil
} }
func HandleJump(cpu *mipsevm.CpuScalars, registers *[32]uint32, linkReg uint32, dest uint32) error { func HandleJump(cpu *mipsevm.CpuScalars, registers *[32]Word, linkReg Word, dest Word) error {
if cpu.NextPC != cpu.PC+4 { if cpu.NextPC != cpu.PC+4 {
panic("jump in delay slot") panic("jump in delay slot")
} }
...@@ -386,7 +557,7 @@ func HandleJump(cpu *mipsevm.CpuScalars, registers *[32]uint32, linkReg uint32, ...@@ -386,7 +557,7 @@ func HandleJump(cpu *mipsevm.CpuScalars, registers *[32]uint32, linkReg uint32,
return nil return nil
} }
func HandleRd(cpu *mipsevm.CpuScalars, registers *[32]uint32, storeReg uint32, val uint32, conditional bool) error { func HandleRd(cpu *mipsevm.CpuScalars, registers *[32]Word, storeReg Word, val Word, conditional bool) error {
if storeReg >= 32 { if storeReg >= 32 {
panic("invalid register") panic("invalid register")
} }
......
...@@ -8,10 +8,18 @@ import ( ...@@ -8,10 +8,18 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
type Word = arch.Word
const (
AddressMask = arch.AddressMask
)
// TODO(#12205): redefine syscalls for MIPS64
// Syscall codes // Syscall codes
const ( const (
SysMmap = 4090 SysMmap = 4090
...@@ -79,7 +87,7 @@ const ( ...@@ -79,7 +87,7 @@ const (
// Errors // Errors
const ( const (
SysErrorSignal = ^uint32(0) SysErrorSignal = ^Word(0)
MipsEBADF = 0x9 MipsEBADF = 0x9
MipsEINVAL = 0x16 MipsEINVAL = 0x16
MipsEAGAIN = 0xb MipsEAGAIN = 0xb
...@@ -92,7 +100,7 @@ const ( ...@@ -92,7 +100,7 @@ const (
FutexWakePrivate = 129 FutexWakePrivate = 129
FutexTimeoutSteps = 10_000 FutexTimeoutSteps = 10_000
FutexNoTimeout = ^uint64(0) FutexNoTimeout = ^uint64(0)
FutexEmptyAddr = ^uint32(0) FutexEmptyAddr = ^Word(0)
) )
// SysClone flags // SysClone flags
...@@ -145,7 +153,7 @@ const ( ...@@ -145,7 +153,7 @@ const (
ClockGettimeMonotonicFlag = 1 ClockGettimeMonotonicFlag = 1
) )
func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) { func GetSyscallArgs(registers *[32]Word) (syscallNum, a0, a1, a2, a3 Word) {
syscallNum = registers[2] // v0 syscallNum = registers[2] // v0
a0 = registers[4] a0 = registers[4]
...@@ -156,8 +164,8 @@ func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) { ...@@ -156,8 +164,8 @@ func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) {
return syscallNum, a0, a1, a2, a3 return syscallNum, a0, a1, a2, a3
} }
func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) { func HandleSysMmap(a0, a1, heap Word) (v0, v1, newHeap Word) {
v1 = uint32(0) v1 = Word(0)
newHeap = heap newHeap = heap
sz := a1 sz := a1
...@@ -182,34 +190,41 @@ func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) { ...@@ -182,34 +190,41 @@ func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) {
return v0, v1, newHeap return v0, v1, newHeap
} }
func HandleSysRead(a0, a1, a2 uint32, preimageKey [32]byte, preimageOffset uint32, preimageReader PreimageReader, memory *memory.Memory, memTracker MemTracker) (v0, v1, newPreimageOffset uint32, memUpdated bool, memAddr uint32) { func HandleSysRead(
a0, a1, a2 Word,
preimageKey [32]byte,
preimageOffset Word,
preimageReader PreimageReader,
memory *memory.Memory,
memTracker MemTracker,
) (v0, v1, newPreimageOffset Word, memUpdated bool, memAddr Word) {
// args: a0 = fd, a1 = addr, a2 = count // args: a0 = fd, a1 = addr, a2 = count
// returns: v0 = read, v1 = err code // returns: v0 = read, v1 = err code
v0 = uint32(0) v0 = Word(0)
v1 = uint32(0) v1 = Word(0)
newPreimageOffset = preimageOffset newPreimageOffset = preimageOffset
switch a0 { switch a0 {
case FdStdin: case FdStdin:
// leave v0 and v1 zero: read nothing, no error // leave v0 and v1 zero: read nothing, no error
case FdPreimageRead: // pre-image oracle case FdPreimageRead: // pre-image oracle
effAddr := a1 & 0xFFffFFfc effAddr := a1 & AddressMask
memTracker.TrackMemAccess(effAddr) memTracker.TrackMemAccess(effAddr)
mem := memory.GetMemory(effAddr) mem := memory.GetWord(effAddr)
dat, datLen := preimageReader.ReadPreimage(preimageKey, preimageOffset) dat, datLen := preimageReader.ReadPreimage(preimageKey, preimageOffset)
//fmt.Printf("reading pre-image data: addr: %08x, offset: %d, datLen: %d, data: %x, key: %s count: %d\n", a1, preimageOffset, datLen, dat[:datLen], preimageKey, a2) //fmt.Printf("reading pre-image data: addr: %08x, offset: %d, datLen: %d, data: %x, key: %s count: %d\n", a1, preimageOffset, datLen, dat[:datLen], preimageKey, a2)
alignment := a1 & 3 alignment := a1 & arch.ExtMask
space := 4 - alignment space := arch.WordSizeBytes - alignment
if space < datLen { if space < datLen {
datLen = space datLen = space
} }
if a2 < datLen { if a2 < datLen {
datLen = a2 datLen = a2
} }
var outMem [4]byte var outMem [arch.WordSizeBytes]byte
binary.BigEndian.PutUint32(outMem[:], mem) arch.ByteOrderWord.PutWord(outMem[:], mem)
copy(outMem[alignment:], dat[:datLen]) copy(outMem[alignment:], dat[:datLen])
memory.SetMemory(effAddr, binary.BigEndian.Uint32(outMem[:])) memory.SetWord(effAddr, arch.ByteOrderWord.Word(outMem[:]))
memUpdated = true memUpdated = true
memAddr = effAddr memAddr = effAddr
newPreimageOffset += datLen newPreimageOffset += datLen
...@@ -219,17 +234,25 @@ func HandleSysRead(a0, a1, a2 uint32, preimageKey [32]byte, preimageOffset uint3 ...@@ -219,17 +234,25 @@ func HandleSysRead(a0, a1, a2 uint32, preimageKey [32]byte, preimageOffset uint3
// don't actually read into memory, just say we read it all, we ignore the result anyway // don't actually read into memory, just say we read it all, we ignore the result anyway
v0 = a2 v0 = a2
default: default:
v0 = 0xFFffFFff v0 = ^Word(0)
v1 = MipsEBADF v1 = MipsEBADF
} }
return v0, v1, newPreimageOffset, memUpdated, memAddr return v0, v1, newPreimageOffset, memUpdated, memAddr
} }
func HandleSysWrite(a0, a1, a2 uint32, lastHint hexutil.Bytes, preimageKey [32]byte, preimageOffset uint32, oracle mipsevm.PreimageOracle, memory *memory.Memory, memTracker MemTracker, stdOut, stdErr io.Writer) (v0, v1 uint32, newLastHint hexutil.Bytes, newPreimageKey common.Hash, newPreimageOffset uint32) { func HandleSysWrite(a0, a1, a2 Word,
lastHint hexutil.Bytes,
preimageKey [32]byte,
preimageOffset Word,
oracle mipsevm.PreimageOracle,
memory *memory.Memory,
memTracker MemTracker,
stdOut, stdErr io.Writer,
) (v0, v1 Word, newLastHint hexutil.Bytes, newPreimageKey common.Hash, newPreimageOffset Word) {
// args: a0 = fd, a1 = addr, a2 = count // args: a0 = fd, a1 = addr, a2 = count
// returns: v0 = written, v1 = err code // returns: v0 = written, v1 = err code
v1 = uint32(0) v1 = Word(0)
newLastHint = lastHint newLastHint = lastHint
newPreimageKey = preimageKey newPreimageKey = preimageKey
newPreimageOffset = preimageOffset newPreimageOffset = preimageOffset
...@@ -257,41 +280,41 @@ func HandleSysWrite(a0, a1, a2 uint32, lastHint hexutil.Bytes, preimageKey [32]b ...@@ -257,41 +280,41 @@ func HandleSysWrite(a0, a1, a2 uint32, lastHint hexutil.Bytes, preimageKey [32]b
newLastHint = lastHint newLastHint = lastHint
v0 = a2 v0 = a2
case FdPreimageWrite: case FdPreimageWrite:
effAddr := a1 & 0xFFffFFfc effAddr := a1 & arch.AddressMask
memTracker.TrackMemAccess(effAddr) memTracker.TrackMemAccess(effAddr)
mem := memory.GetMemory(effAddr) mem := memory.GetWord(effAddr)
key := preimageKey key := preimageKey
alignment := a1 & 3 alignment := a1 & arch.ExtMask
space := 4 - alignment space := arch.WordSizeBytes - alignment
if space < a2 { if space < a2 {
a2 = space a2 = space
} }
copy(key[:], key[a2:]) copy(key[:], key[a2:])
var tmp [4]byte var tmp [arch.WordSizeBytes]byte
binary.BigEndian.PutUint32(tmp[:], mem) arch.ByteOrderWord.PutWord(tmp[:], mem)
copy(key[32-a2:], tmp[alignment:]) copy(key[32-a2:], tmp[alignment:])
newPreimageKey = key newPreimageKey = key
newPreimageOffset = 0 newPreimageOffset = 0
//fmt.Printf("updating pre-image key: %s\n", m.state.PreimageKey) //fmt.Printf("updating pre-image key: %s\n", m.state.PreimageKey)
v0 = a2 v0 = a2
default: default:
v0 = 0xFFffFFff v0 = ^Word(0)
v1 = MipsEBADF v1 = MipsEBADF
} }
return v0, v1, newLastHint, newPreimageKey, newPreimageOffset return v0, v1, newLastHint, newPreimageKey, newPreimageOffset
} }
func HandleSysFcntl(a0, a1 uint32) (v0, v1 uint32) { func HandleSysFcntl(a0, a1 Word) (v0, v1 Word) {
// args: a0 = fd, a1 = cmd // args: a0 = fd, a1 = cmd
v1 = uint32(0) v1 = Word(0)
if a1 == 1 { // F_GETFD: get file descriptor flags if a1 == 1 { // F_GETFD: get file descriptor flags
switch a0 { switch a0 {
case FdStdin, FdStdout, FdStderr, FdPreimageRead, FdHintRead, FdPreimageWrite, FdHintWrite: case FdStdin, FdStdout, FdStderr, FdPreimageRead, FdHintRead, FdPreimageWrite, FdHintWrite:
v0 = 0 // No flags set v0 = 0 // No flags set
default: default:
v0 = 0xFFffFFff v0 = ^Word(0)
v1 = MipsEBADF v1 = MipsEBADF
} }
} else if a1 == 3 { // F_GETFL: get file status flags } else if a1 == 3 { // F_GETFL: get file status flags
...@@ -301,18 +324,18 @@ func HandleSysFcntl(a0, a1 uint32) (v0, v1 uint32) { ...@@ -301,18 +324,18 @@ func HandleSysFcntl(a0, a1 uint32) (v0, v1 uint32) {
case FdStdout, FdStderr, FdPreimageWrite, FdHintWrite: case FdStdout, FdStderr, FdPreimageWrite, FdHintWrite:
v0 = 1 // O_WRONLY v0 = 1 // O_WRONLY
default: default:
v0 = 0xFFffFFff v0 = ^Word(0)
v1 = MipsEBADF v1 = MipsEBADF
} }
} else { } else {
v0 = 0xFFffFFff v0 = ^Word(0)
v1 = MipsEINVAL // cmd not recognized by this kernel v1 = MipsEINVAL // cmd not recognized by this kernel
} }
return v0, v1 return v0, v1
} }
func HandleSyscallUpdates(cpu *mipsevm.CpuScalars, registers *[32]uint32, v0, v1 uint32) { func HandleSyscallUpdates(cpu *mipsevm.CpuScalars, registers *[32]Word, v0, v1 Word) {
registers[2] = v0 registers[2] = v0
registers[7] = v1 registers[7] = v1
......
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
) )
type PreimageReader interface { type PreimageReader interface {
ReadPreimage(key [32]byte, offset uint32) (dat [32]byte, datLen uint32) ReadPreimage(key [32]byte, offset Word) (dat [32]byte, datLen Word)
} }
// TrackingPreimageOracleReader wraps around a PreimageOracle, implements the PreimageOracle interface, and adds tracking functionality. // TrackingPreimageOracleReader wraps around a PreimageOracle, implements the PreimageOracle interface, and adds tracking functionality.
...@@ -22,8 +22,8 @@ type TrackingPreimageOracleReader struct { ...@@ -22,8 +22,8 @@ type TrackingPreimageOracleReader struct {
lastPreimage []byte lastPreimage []byte
// key for above preimage // key for above preimage
lastPreimageKey [32]byte lastPreimageKey [32]byte
// offset we last read from, or max uint32 if nothing is read this step // offset we last read from, or max Word if nothing is read this step
lastPreimageOffset uint32 lastPreimageOffset Word
} }
func NewTrackingPreimageOracleReader(po mipsevm.PreimageOracle) *TrackingPreimageOracleReader { func NewTrackingPreimageOracleReader(po mipsevm.PreimageOracle) *TrackingPreimageOracleReader {
...@@ -31,7 +31,7 @@ func NewTrackingPreimageOracleReader(po mipsevm.PreimageOracle) *TrackingPreimag ...@@ -31,7 +31,7 @@ func NewTrackingPreimageOracleReader(po mipsevm.PreimageOracle) *TrackingPreimag
} }
func (p *TrackingPreimageOracleReader) Reset() { func (p *TrackingPreimageOracleReader) Reset() {
p.lastPreimageOffset = ^uint32(0) p.lastPreimageOffset = ^Word(0)
} }
func (p *TrackingPreimageOracleReader) Hint(v []byte) { func (p *TrackingPreimageOracleReader) Hint(v []byte) {
...@@ -45,7 +45,7 @@ func (p *TrackingPreimageOracleReader) GetPreimage(k [32]byte) []byte { ...@@ -45,7 +45,7 @@ func (p *TrackingPreimageOracleReader) GetPreimage(k [32]byte) []byte {
return preimage return preimage
} }
func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset uint32) (dat [32]byte, datLen uint32) { func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset Word) (dat [32]byte, datLen Word) {
preimage := p.lastPreimage preimage := p.lastPreimage
if key != p.lastPreimageKey { if key != p.lastPreimageKey {
p.lastPreimageKey = key p.lastPreimageKey = key
...@@ -57,14 +57,14 @@ func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset uint32) ...@@ -57,14 +57,14 @@ func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset uint32)
p.lastPreimage = preimage p.lastPreimage = preimage
} }
p.lastPreimageOffset = offset p.lastPreimageOffset = offset
if offset >= uint32(len(preimage)) { if offset >= Word(len(preimage)) {
panic("Preimage offset out-of-bounds") panic("Preimage offset out-of-bounds")
} }
datLen = uint32(copy(dat[:], preimage[offset:])) datLen = Word(copy(dat[:], preimage[offset:]))
return return
} }
func (p *TrackingPreimageOracleReader) LastPreimage() ([32]byte, []byte, uint32) { func (p *TrackingPreimageOracleReader) LastPreimage() ([32]byte, []byte, Word) {
return p.lastPreimageKey, p.lastPreimage, p.lastPreimageOffset return p.lastPreimageKey, p.lastPreimage, p.lastPreimageOffset
} }
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
) )
type StackTracker interface { type StackTracker interface {
PushStack(caller uint32, target uint32) PushStack(caller Word, target Word)
PopStack() PopStack()
} }
...@@ -19,7 +19,7 @@ type TraceableStackTracker interface { ...@@ -19,7 +19,7 @@ type TraceableStackTracker interface {
type NoopStackTracker struct{} type NoopStackTracker struct{}
func (n *NoopStackTracker) PushStack(caller uint32, target uint32) {} func (n *NoopStackTracker) PushStack(caller Word, target Word) {}
func (n *NoopStackTracker) PopStack() {} func (n *NoopStackTracker) PopStack() {}
...@@ -28,8 +28,8 @@ func (n *NoopStackTracker) Traceback() {} ...@@ -28,8 +28,8 @@ func (n *NoopStackTracker) Traceback() {}
type StackTrackerImpl struct { type StackTrackerImpl struct {
state mipsevm.FPVMState state mipsevm.FPVMState
stack []uint32 stack []Word
caller []uint32 caller []Word
meta mipsevm.Metadata meta mipsevm.Metadata
} }
...@@ -45,7 +45,7 @@ func NewStackTrackerUnsafe(state mipsevm.FPVMState, meta mipsevm.Metadata) *Stac ...@@ -45,7 +45,7 @@ func NewStackTrackerUnsafe(state mipsevm.FPVMState, meta mipsevm.Metadata) *Stac
return &StackTrackerImpl{state: state, meta: meta} return &StackTrackerImpl{state: state, meta: meta}
} }
func (s *StackTrackerImpl) PushStack(caller uint32, target uint32) { func (s *StackTrackerImpl) PushStack(caller Word, target Word) {
s.caller = append(s.caller, caller) s.caller = append(s.caller, caller)
s.stack = append(s.stack, target) s.stack = append(s.stack, target)
} }
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
) )
...@@ -17,22 +18,22 @@ type FPVMState interface { ...@@ -17,22 +18,22 @@ type FPVMState interface {
GetMemory() *memory.Memory GetMemory() *memory.Memory
// GetHeap returns the current memory address at the top of the heap // GetHeap returns the current memory address at the top of the heap
GetHeap() uint32 GetHeap() arch.Word
// GetPreimageKey returns the most recently accessed preimage key // GetPreimageKey returns the most recently accessed preimage key
GetPreimageKey() common.Hash GetPreimageKey() common.Hash
// GetPreimageOffset returns the current offset into the current preimage // GetPreimageOffset returns the current offset into the current preimage
GetPreimageOffset() uint32 GetPreimageOffset() arch.Word
// GetPC returns the currently executing program counter // GetPC returns the currently executing program counter
GetPC() uint32 GetPC() arch.Word
// GetCpu returns the currently active cpu scalars, including the program counter // GetCpu returns the currently active cpu scalars, including the program counter
GetCpu() CpuScalars GetCpu() CpuScalars
// GetRegistersRef returns a pointer to the currently active registers // GetRegistersRef returns a pointer to the currently active registers
GetRegistersRef() *[32]uint32 GetRegistersRef() *[32]arch.Word
// GetStep returns the current VM step // GetStep returns the current VM step
GetStep() uint64 GetStep() uint64
...@@ -48,9 +49,9 @@ type FPVMState interface { ...@@ -48,9 +49,9 @@ type FPVMState interface {
// so a VM can start from any state without fetching prior pre-images, // so a VM can start from any state without fetching prior pre-images,
// and instead just repeat the last hint on setup, // and instead just repeat the last hint on setup,
// to make sure pre-image requests can be served. // to make sure pre-image requests can be served.
// The first 4 bytes are a uint32 length prefix. // The first 4 bytes are a Word length prefix.
// Warning: the hint MAY NOT BE COMPLETE. I.e. this is buffered, // Warning: the hint MAY NOT BE COMPLETE. I.e. this is buffered,
// and should only be read when len(LastHint) > 4 && uint32(LastHint[:4]) <= len(LastHint[4:]) // and should only be read when len(LastHint) > 4 && Word(LastHint[:4]) <= len(LastHint[4:])
GetLastHint() hexutil.Bytes GetLastHint() hexutil.Bytes
// EncodeWitness returns the witness for the current state and the state hash // EncodeWitness returns the witness for the current state and the state hash
...@@ -60,10 +61,10 @@ type FPVMState interface { ...@@ -60,10 +61,10 @@ type FPVMState interface {
CreateVM(logger log.Logger, po PreimageOracle, stdOut, stdErr io.Writer, meta Metadata) FPVM CreateVM(logger log.Logger, po PreimageOracle, stdOut, stdErr io.Writer, meta Metadata) FPVM
} }
type SymbolMatcher func(addr uint32) bool type SymbolMatcher func(addr arch.Word) bool
type Metadata interface { type Metadata interface {
LookupSymbol(addr uint32) string LookupSymbol(addr arch.Word) string
CreateSymbolMatcher(name string) SymbolMatcher CreateSymbolMatcher(name string) SymbolMatcher
} }
...@@ -78,7 +79,7 @@ type FPVM interface { ...@@ -78,7 +79,7 @@ type FPVM interface {
CheckInfiniteLoop() bool CheckInfiniteLoop() bool
// LastPreimage returns the last preimage accessed by the VM // LastPreimage returns the last preimage accessed by the VM
LastPreimage() (preimageKey [32]byte, preimage []byte, preimageOffset uint32) LastPreimage() (preimageKey [32]byte, preimage []byte, preimageOffset arch.Word)
// Traceback prints a traceback of the program to the console // Traceback prints a traceback of the program to the console
Traceback() Traceback()
...@@ -91,5 +92,5 @@ type FPVM interface { ...@@ -91,5 +92,5 @@ type FPVM interface {
// LookupSymbol returns the symbol located at the specified address. // LookupSymbol returns the symbol located at the specified address.
// May return an empty string if there's no symbol table available. // May return an empty string if there's no symbol table available.
LookupSymbol(addr uint32) string LookupSymbol(addr arch.Word) string
} }
...@@ -9,21 +9,25 @@ import ( ...@@ -9,21 +9,25 @@ import (
"slices" "slices"
"sort" "sort"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
) )
// Note: 2**12 = 4 KiB, the min phys page size in the Go runtime. // Note: 2**12 = 4 KiB, the min phys page size in the Go runtime.
const ( const (
PageAddrSize = 12 PageAddrSize = arch.PageAddrSize
PageKeySize = 32 - PageAddrSize PageKeySize = arch.PageKeySize
PageSize = 1 << PageAddrSize PageSize = 1 << PageAddrSize
PageAddrMask = PageSize - 1 PageAddrMask = PageSize - 1
MaxPageCount = 1 << PageKeySize MaxPageCount = 1 << PageKeySize
PageKeyMask = MaxPageCount - 1 PageKeyMask = MaxPageCount - 1
MemProofLeafCount = arch.MemProofLeafCount
) )
const MEM_PROOF_SIZE = 28 * 32 const MEM_PROOF_SIZE = arch.MemProofSize
type Word = arch.Word
func HashPair(left, right [32]byte) [32]byte { func HashPair(left, right [32]byte) [32]byte {
out := crypto.Keccak256Hash(left[:], right[:]) out := crypto.Keccak256Hash(left[:], right[:])
...@@ -45,22 +49,22 @@ type Memory struct { ...@@ -45,22 +49,22 @@ type Memory struct {
nodes map[uint64]*[32]byte nodes map[uint64]*[32]byte
// pageIndex -> cached page // pageIndex -> cached page
pages map[uint32]*CachedPage pages map[Word]*CachedPage
// Note: since we don't de-alloc pages, we don't do ref-counting. // Note: since we don't de-alloc pages, we don't do ref-counting.
// Once a page exists, it doesn't leave memory // Once a page exists, it doesn't leave memory
// two caches: we often read instructions from one page, and do memory things with another page. // two caches: we often read instructions from one page, and do memory things with another page.
// this prevents map lookups each instruction // this prevents map lookups each instruction
lastPageKeys [2]uint32 lastPageKeys [2]Word
lastPage [2]*CachedPage lastPage [2]*CachedPage
} }
func NewMemory() *Memory { func NewMemory() *Memory {
return &Memory{ return &Memory{
nodes: make(map[uint64]*[32]byte), nodes: make(map[uint64]*[32]byte),
pages: make(map[uint32]*CachedPage), pages: make(map[Word]*CachedPage),
lastPageKeys: [2]uint32{^uint32(0), ^uint32(0)}, // default to invalid keys, to not match any pages lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages
} }
} }
...@@ -68,7 +72,7 @@ func (m *Memory) PageCount() int { ...@@ -68,7 +72,7 @@ func (m *Memory) PageCount() int {
return len(m.pages) return len(m.pages)
} }
func (m *Memory) ForEachPage(fn func(pageIndex uint32, page *Page) error) error { func (m *Memory) ForEachPage(fn func(pageIndex Word, page *Page) error) error {
for pageIndex, cachedPage := range m.pages { for pageIndex, cachedPage := range m.pages {
if err := fn(pageIndex, cachedPage.Data); err != nil { if err := fn(pageIndex, cachedPage.Data); err != nil {
return err return err
...@@ -77,16 +81,16 @@ func (m *Memory) ForEachPage(fn func(pageIndex uint32, page *Page) error) error ...@@ -77,16 +81,16 @@ func (m *Memory) ForEachPage(fn func(pageIndex uint32, page *Page) error) error
return nil return nil
} }
func (m *Memory) Invalidate(addr uint32) { func (m *Memory) invalidate(addr Word) {
// addr must be aligned to 4 bytes // addr must be aligned
if addr&0x3 != 0 { if addr&arch.ExtMask != 0 {
panic(fmt.Errorf("unaligned memory access: %x", addr)) panic(fmt.Errorf("unaligned memory access: %x", addr))
} }
// find page, and invalidate addr within it // find page, and invalidate addr within it
if p, ok := m.pageLookup(addr >> PageAddrSize); ok { if p, ok := m.pageLookup(addr >> PageAddrSize); ok {
prevValid := p.Ok[1] prevValid := p.Ok[1]
p.Invalidate(addr & PageAddrMask) p.invalidate(addr & PageAddrMask)
if !prevValid { // if the page was already invalid before, then nodes to mem-root will also still be. if !prevValid { // if the page was already invalid before, then nodes to mem-root will also still be.
return return
} }
...@@ -105,23 +109,23 @@ func (m *Memory) Invalidate(addr uint32) { ...@@ -105,23 +109,23 @@ func (m *Memory) Invalidate(addr uint32) {
func (m *Memory) MerkleizeSubtree(gindex uint64) [32]byte { func (m *Memory) MerkleizeSubtree(gindex uint64) [32]byte {
l := uint64(bits.Len64(gindex)) l := uint64(bits.Len64(gindex))
if l > 28 { if l > MemProofLeafCount {
panic("gindex too deep") panic("gindex too deep")
} }
if l > PageKeySize { if l > PageKeySize {
depthIntoPage := l - 1 - PageKeySize depthIntoPage := l - 1 - PageKeySize
pageIndex := (gindex >> depthIntoPage) & PageKeyMask pageIndex := (gindex >> depthIntoPage) & PageKeyMask
if p, ok := m.pages[uint32(pageIndex)]; ok { if p, ok := m.pages[Word(pageIndex)]; ok {
pageGindex := (1 << depthIntoPage) | (gindex & ((1 << depthIntoPage) - 1)) pageGindex := (1 << depthIntoPage) | (gindex & ((1 << depthIntoPage) - 1))
return p.MerkleizeSubtree(pageGindex) return p.MerkleizeSubtree(pageGindex)
} else { } else {
return zeroHashes[28-l] // page does not exist return zeroHashes[MemProofLeafCount-l] // page does not exist
} }
} }
n, ok := m.nodes[gindex] n, ok := m.nodes[gindex]
if !ok { if !ok {
// if the node doesn't exist, the whole sub-tree is zeroed // if the node doesn't exist, the whole sub-tree is zeroed
return zeroHashes[28-l] return zeroHashes[MemProofLeafCount-l]
} }
if n != nil { if n != nil {
return *n return *n
...@@ -133,16 +137,16 @@ func (m *Memory) MerkleizeSubtree(gindex uint64) [32]byte { ...@@ -133,16 +137,16 @@ func (m *Memory) MerkleizeSubtree(gindex uint64) [32]byte {
return r return r
} }
func (m *Memory) MerkleProof(addr uint32) (out [MEM_PROOF_SIZE]byte) { func (m *Memory) MerkleProof(addr Word) (out [MEM_PROOF_SIZE]byte) {
proof := m.traverseBranch(1, addr, 0) proof := m.traverseBranch(1, addr, 0)
// encode the proof // encode the proof
for i := 0; i < 28; i++ { for i := 0; i < MemProofLeafCount; i++ {
copy(out[i*32:(i+1)*32], proof[i][:]) copy(out[i*32:(i+1)*32], proof[i][:])
} }
return out return out
} }
func (m *Memory) traverseBranch(parent uint64, addr uint32, depth uint8) (proof [][32]byte) { func (m *Memory) traverseBranch(parent uint64, addr Word, depth uint8) (proof [][32]byte) {
if depth == 32-5 { if depth == 32-5 {
proof = make([][32]byte, 0, 32-5+1) proof = make([][32]byte, 0, 32-5+1)
proof = append(proof, m.MerkleizeSubtree(parent)) proof = append(proof, m.MerkleizeSubtree(parent))
...@@ -166,7 +170,7 @@ func (m *Memory) MerkleRoot() [32]byte { ...@@ -166,7 +170,7 @@ func (m *Memory) MerkleRoot() [32]byte {
return m.MerkleizeSubtree(1) return m.MerkleizeSubtree(1)
} }
func (m *Memory) pageLookup(pageIndex uint32) (*CachedPage, bool) { func (m *Memory) pageLookup(pageIndex Word) (*CachedPage, bool) {
// hit caches // hit caches
if pageIndex == m.lastPageKeys[0] { if pageIndex == m.lastPageKeys[0] {
return m.lastPage[0], true return m.lastPage[0], true
...@@ -187,9 +191,9 @@ func (m *Memory) pageLookup(pageIndex uint32) (*CachedPage, bool) { ...@@ -187,9 +191,9 @@ func (m *Memory) pageLookup(pageIndex uint32) (*CachedPage, bool) {
return p, ok return p, ok
} }
func (m *Memory) SetMemory(addr uint32, v uint32) { func (m *Memory) SetMemory(addr Word, v uint32) {
// addr must be aligned to 4 bytes // addr must be aligned to 4 bytes
if addr&0x3 != 0 { if addr&arch.ExtMask != 0 {
panic(fmt.Errorf("unaligned memory access: %x", addr)) panic(fmt.Errorf("unaligned memory access: %x", addr))
} }
...@@ -201,14 +205,35 @@ func (m *Memory) SetMemory(addr uint32, v uint32) { ...@@ -201,14 +205,35 @@ func (m *Memory) SetMemory(addr uint32, v uint32) {
// Go may mmap relatively large ranges, but we only allocate the pages just in time. // Go may mmap relatively large ranges, but we only allocate the pages just in time.
p = m.AllocPage(pageIndex) p = m.AllocPage(pageIndex)
} else { } else {
m.Invalidate(addr) // invalidate this branch of memory, now that the value changed m.invalidate(addr) // invalidate this branch of memory, now that the value changed
} }
binary.BigEndian.PutUint32(p.Data[pageAddr:pageAddr+4], v) binary.BigEndian.PutUint32(p.Data[pageAddr:pageAddr+4], v)
} }
func (m *Memory) GetMemory(addr uint32) uint32 { // SetWord stores [arch.Word] sized values at the specified address
func (m *Memory) SetWord(addr Word, v Word) {
// addr must be aligned to WordSizeBytes bytes
if addr&arch.ExtMask != 0 {
panic(fmt.Errorf("unaligned memory access: %x", addr))
}
pageIndex := addr >> PageAddrSize
pageAddr := addr & PageAddrMask
p, ok := m.pageLookup(pageIndex)
if !ok {
// allocate the page if we have not already.
// Go may mmap relatively large ranges, but we only allocate the pages just in time.
p = m.AllocPage(pageIndex)
} else {
m.invalidate(addr) // invalidate this branch of memory, now that the value changed
}
arch.ByteOrderWord.PutWord(p.Data[pageAddr:pageAddr+arch.WordSizeBytes], v)
}
// GetMemory reads the 32-bit value located at the specified address.
func (m *Memory) GetMemory(addr Word) uint32 {
// addr must be aligned to 4 bytes // addr must be aligned to 4 bytes
if addr&0x3 != 0 { if addr&arch.ExtMask != 0 {
panic(fmt.Errorf("unaligned memory access: %x", addr)) panic(fmt.Errorf("unaligned memory access: %x", addr))
} }
p, ok := m.pageLookup(addr >> PageAddrSize) p, ok := m.pageLookup(addr >> PageAddrSize)
...@@ -219,7 +244,22 @@ func (m *Memory) GetMemory(addr uint32) uint32 { ...@@ -219,7 +244,22 @@ func (m *Memory) GetMemory(addr uint32) uint32 {
return binary.BigEndian.Uint32(p.Data[pageAddr : pageAddr+4]) return binary.BigEndian.Uint32(p.Data[pageAddr : pageAddr+4])
} }
func (m *Memory) AllocPage(pageIndex uint32) *CachedPage { // GetWord reads the maximum sized value, [arch.Word], located at the specified address.
// Note: Also known by the MIPS64 specification as a "double-word" memory access.
func (m *Memory) GetWord(addr Word) Word {
// addr must be word aligned
if addr&arch.ExtMask != 0 {
panic(fmt.Errorf("unaligned memory access: %x", addr))
}
p, ok := m.pageLookup(addr >> PageAddrSize)
if !ok {
return 0
}
pageAddr := addr & PageAddrMask
return arch.ByteOrderWord.Word(p.Data[pageAddr : pageAddr+arch.WordSizeBytes])
}
func (m *Memory) AllocPage(pageIndex Word) *CachedPage {
p := &CachedPage{Data: new(Page)} p := &CachedPage{Data: new(Page)}
m.pages[pageIndex] = p m.pages[pageIndex] = p
// make nodes to root // make nodes to root
...@@ -232,8 +272,8 @@ func (m *Memory) AllocPage(pageIndex uint32) *CachedPage { ...@@ -232,8 +272,8 @@ func (m *Memory) AllocPage(pageIndex uint32) *CachedPage {
} }
type pageEntry struct { type pageEntry struct {
Index uint32 `json:"index"` Index Word `json:"index"`
Data *Page `json:"data"` Data *Page `json:"data"`
} }
func (m *Memory) MarshalJSON() ([]byte, error) { // nosemgrep func (m *Memory) MarshalJSON() ([]byte, error) { // nosemgrep
...@@ -256,8 +296,8 @@ func (m *Memory) UnmarshalJSON(data []byte) error { ...@@ -256,8 +296,8 @@ func (m *Memory) UnmarshalJSON(data []byte) error {
return err return err
} }
m.nodes = make(map[uint64]*[32]byte) m.nodes = make(map[uint64]*[32]byte)
m.pages = make(map[uint32]*CachedPage) m.pages = make(map[Word]*CachedPage)
m.lastPageKeys = [2]uint32{^uint32(0), ^uint32(0)} m.lastPageKeys = [2]Word{^Word(0), ^Word(0)}
m.lastPage = [2]*CachedPage{nil, nil} m.lastPage = [2]*CachedPage{nil, nil}
for i, p := range pages { for i, p := range pages {
if _, ok := m.pages[p.Index]; ok { if _, ok := m.pages[p.Index]; ok {
...@@ -268,7 +308,7 @@ func (m *Memory) UnmarshalJSON(data []byte) error { ...@@ -268,7 +308,7 @@ func (m *Memory) UnmarshalJSON(data []byte) error {
return nil return nil
} }
func (m *Memory) SetMemoryRange(addr uint32, r io.Reader) error { func (m *Memory) SetMemoryRange(addr Word, r io.Reader) error {
for { for {
pageIndex := addr >> PageAddrSize pageIndex := addr >> PageAddrSize
pageAddr := addr & PageAddrMask pageAddr := addr & PageAddrMask
...@@ -284,7 +324,7 @@ func (m *Memory) SetMemoryRange(addr uint32, r io.Reader) error { ...@@ -284,7 +324,7 @@ func (m *Memory) SetMemoryRange(addr uint32, r io.Reader) error {
} }
return err return err
} }
addr += uint32(n) addr += Word(n)
} }
} }
...@@ -292,13 +332,13 @@ func (m *Memory) SetMemoryRange(addr uint32, r io.Reader) error { ...@@ -292,13 +332,13 @@ func (m *Memory) SetMemoryRange(addr uint32, r io.Reader) error {
// The format is a simple concatenation of fields, with prefixed item count for repeating items and using big endian // The format is a simple concatenation of fields, with prefixed item count for repeating items and using big endian
// encoding for numbers. // encoding for numbers.
// //
// len(PageCount) uint32 // len(PageCount) Word
// For each page (order is arbitrary): // For each page (order is arbitrary):
// //
// page index uint32 // page index Word
// page Data [PageSize]byte // page Data [PageSize]byte
func (m *Memory) Serialize(out io.Writer) error { func (m *Memory) Serialize(out io.Writer) error {
if err := binary.Write(out, binary.BigEndian, uint32(m.PageCount())); err != nil { if err := binary.Write(out, binary.BigEndian, Word(m.PageCount())); err != nil {
return err return err
} }
indexes := maps.Keys(m.pages) indexes := maps.Keys(m.pages)
...@@ -317,12 +357,12 @@ func (m *Memory) Serialize(out io.Writer) error { ...@@ -317,12 +357,12 @@ func (m *Memory) Serialize(out io.Writer) error {
} }
func (m *Memory) Deserialize(in io.Reader) error { func (m *Memory) Deserialize(in io.Reader) error {
var pageCount uint32 var pageCount Word
if err := binary.Read(in, binary.BigEndian, &pageCount); err != nil { if err := binary.Read(in, binary.BigEndian, &pageCount); err != nil {
return err return err
} }
for i := uint32(0); i < pageCount; i++ { for i := Word(0); i < pageCount; i++ {
var pageIndex uint32 var pageIndex Word
if err := binary.Read(in, binary.BigEndian, &pageIndex); err != nil { if err := binary.Read(in, binary.BigEndian, &pageIndex); err != nil {
return err return err
} }
...@@ -337,8 +377,8 @@ func (m *Memory) Deserialize(in io.Reader) error { ...@@ -337,8 +377,8 @@ func (m *Memory) Deserialize(in io.Reader) error {
func (m *Memory) Copy() *Memory { func (m *Memory) Copy() *Memory {
out := NewMemory() out := NewMemory()
out.nodes = make(map[uint64]*[32]byte) out.nodes = make(map[uint64]*[32]byte)
out.pages = make(map[uint32]*CachedPage) out.pages = make(map[Word]*CachedPage)
out.lastPageKeys = [2]uint32{^uint32(0), ^uint32(0)} out.lastPageKeys = [2]Word{^Word(0), ^Word(0)}
out.lastPage = [2]*CachedPage{nil, nil} out.lastPage = [2]*CachedPage{nil, nil}
for k, page := range m.pages { for k, page := range m.pages {
data := new(Page) data := new(Page)
...@@ -350,8 +390,8 @@ func (m *Memory) Copy() *Memory { ...@@ -350,8 +390,8 @@ func (m *Memory) Copy() *Memory {
type memReader struct { type memReader struct {
m *Memory m *Memory
addr uint32 addr Word
count uint32 count Word
} }
func (r *memReader) Read(dest []byte) (n int, err error) { func (r *memReader) Read(dest []byte) (n int, err error) {
...@@ -365,7 +405,7 @@ func (r *memReader) Read(dest []byte) (n int, err error) { ...@@ -365,7 +405,7 @@ func (r *memReader) Read(dest []byte) (n int, err error) {
pageIndex := r.addr >> PageAddrSize pageIndex := r.addr >> PageAddrSize
start := r.addr & PageAddrMask start := r.addr & PageAddrMask
end := uint32(PageSize) end := Word(PageSize)
if pageIndex == (endAddr >> PageAddrSize) { if pageIndex == (endAddr >> PageAddrSize) {
end = endAddr & PageAddrMask end = endAddr & PageAddrMask
...@@ -376,12 +416,12 @@ func (r *memReader) Read(dest []byte) (n int, err error) { ...@@ -376,12 +416,12 @@ func (r *memReader) Read(dest []byte) (n int, err error) {
} else { } else {
n = copy(dest, make([]byte, end-start)) // default to zeroes n = copy(dest, make([]byte, end-start)) // default to zeroes
} }
r.addr += uint32(n) r.addr += Word(n)
r.count -= uint32(n) r.count -= Word(n)
return n, nil return n, nil
} }
func (m *Memory) ReadMemoryRange(addr uint32, count uint32) io.Reader { func (m *Memory) ReadMemoryRange(addr Word, count Word) io.Reader {
return &memReader{m: m, addr: addr, count: count} return &memReader{m: m, addr: addr, count: count}
} }
......
...@@ -118,7 +118,7 @@ func TestMemoryReadWrite(t *testing.T) { ...@@ -118,7 +118,7 @@ func TestMemoryReadWrite(t *testing.T) {
_, err := rand.Read(data[:]) _, err := rand.Read(data[:])
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data))) require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data)))
for _, i := range []uint32{0, 4, 1000, 20_000 - 4} { for _, i := range []Word{0, 4, 1000, 20_000 - 4} {
v := m.GetMemory(i) v := m.GetMemory(i)
expected := binary.BigEndian.Uint32(data[i : i+4]) expected := binary.BigEndian.Uint32(data[i : i+4])
require.Equalf(t, expected, v, "read at %d", i) require.Equalf(t, expected, v, "read at %d", i)
...@@ -129,7 +129,7 @@ func TestMemoryReadWrite(t *testing.T) { ...@@ -129,7 +129,7 @@ func TestMemoryReadWrite(t *testing.T) {
m := NewMemory() m := NewMemory()
data := []byte(strings.Repeat("under the big bright yellow sun ", 40)) data := []byte(strings.Repeat("under the big bright yellow sun ", 40))
require.NoError(t, m.SetMemoryRange(0x1337, bytes.NewReader(data))) require.NoError(t, m.SetMemoryRange(0x1337, bytes.NewReader(data)))
res, err := io.ReadAll(m.ReadMemoryRange(0x1337-10, uint32(len(data)+20))) res, err := io.ReadAll(m.ReadMemoryRange(0x1337-10, Word(len(data)+20)))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, make([]byte, 10), res[:10], "empty start") require.Equal(t, make([]byte, 10), res[:10], "empty start")
require.Equal(t, data, res[10:len(res)-10], "result") require.Equal(t, data, res[10:len(res)-10], "result")
......
...@@ -70,7 +70,7 @@ type CachedPage struct { ...@@ -70,7 +70,7 @@ type CachedPage struct {
Ok [PageSize / 32]bool Ok [PageSize / 32]bool
} }
func (p *CachedPage) Invalidate(pageAddr uint32) { func (p *CachedPage) invalidate(pageAddr Word) {
if pageAddr >= PageSize { if pageAddr >= PageSize {
panic("invalid page addr") panic("invalid page addr")
} }
......
...@@ -29,16 +29,16 @@ func TestCachedPage(t *testing.T) { ...@@ -29,16 +29,16 @@ func TestCachedPage(t *testing.T) {
post := p.MerkleRoot() post := p.MerkleRoot()
require.Equal(t, pre, post, "no change expected until cache is invalidated") require.Equal(t, pre, post, "no change expected until cache is invalidated")
p.Invalidate(42) p.invalidate(42)
post2 := p.MerkleRoot() post2 := p.MerkleRoot()
require.NotEqual(t, post, post2, "change after cache invalidation") require.NotEqual(t, post, post2, "change after cache invalidation")
p.Data[2000] = 0xef p.Data[2000] = 0xef
p.Invalidate(42) p.invalidate(42)
post3 := p.MerkleRoot() post3 := p.MerkleRoot()
require.Equal(t, post2, post3, "local invalidation is not global invalidation") require.Equal(t, post2, post3, "local invalidation is not global invalidation")
p.Invalidate(2000) p.invalidate(2000)
post4 := p.MerkleRoot() post4 := p.MerkleRoot()
require.NotEqual(t, post3, post4, "can see the change now") require.NotEqual(t, post3, post4, "can see the change now")
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
) )
...@@ -77,7 +78,7 @@ func (m *InstrumentedState) Step(proof bool) (wit *mipsevm.StepWitness, err erro ...@@ -77,7 +78,7 @@ func (m *InstrumentedState) Step(proof bool) (wit *mipsevm.StepWitness, err erro
wit.ProofData = append(wit.ProofData, memProof[:]...) wit.ProofData = append(wit.ProofData, memProof[:]...)
wit.ProofData = append(wit.ProofData, memProof2[:]...) wit.ProofData = append(wit.ProofData, memProof2[:]...)
lastPreimageKey, lastPreimage, lastPreimageOffset := m.preimageOracle.LastPreimage() lastPreimageKey, lastPreimage, lastPreimageOffset := m.preimageOracle.LastPreimage()
if lastPreimageOffset != ^uint32(0) { if lastPreimageOffset != ^arch.Word(0) {
wit.PreimageOffset = lastPreimageOffset wit.PreimageOffset = lastPreimageOffset
wit.PreimageKey = lastPreimageKey wit.PreimageKey = lastPreimageKey
wit.PreimageValue = lastPreimage wit.PreimageValue = lastPreimage
...@@ -90,7 +91,7 @@ func (m *InstrumentedState) CheckInfiniteLoop() bool { ...@@ -90,7 +91,7 @@ func (m *InstrumentedState) CheckInfiniteLoop() bool {
return false return false
} }
func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, uint32) { func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, arch.Word) {
return m.preimageOracle.LastPreimage() return m.preimageOracle.LastPreimage()
} }
...@@ -111,7 +112,7 @@ func (m *InstrumentedState) Traceback() { ...@@ -111,7 +112,7 @@ func (m *InstrumentedState) Traceback() {
m.stackTracker.Traceback() m.stackTracker.Traceback()
} }
func (m *InstrumentedState) LookupSymbol(addr uint32) string { func (m *InstrumentedState) LookupSymbol(addr arch.Word) string {
if m.meta == nil { if m.meta == nil {
return "" return ""
} }
......
...@@ -20,7 +20,6 @@ func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer ...@@ -20,7 +20,6 @@ func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer
func TestInstrumentedState_OpenMips(t *testing.T) { func TestInstrumentedState_OpenMips(t *testing.T) {
t.Parallel() t.Parallel()
// TODO: Add mt-specific tests here
testutil.RunVMTests_OpenMips(t, CreateEmptyState, vmFactory, "clone.bin") testutil.RunVMTests_OpenMips(t, CreateEmptyState, vmFactory, "clone.bin")
} }
......
...@@ -9,21 +9,24 @@ import ( ...@@ -9,21 +9,24 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
type Word = arch.Word
func (m *InstrumentedState) handleSyscall() error { func (m *InstrumentedState) handleSyscall() error {
thread := m.state.GetCurrentThread() thread := m.state.GetCurrentThread()
syscallNum, a0, a1, a2, a3 := exec.GetSyscallArgs(m.state.GetRegistersRef()) syscallNum, a0, a1, a2, a3 := exec.GetSyscallArgs(m.state.GetRegistersRef())
v0 := uint32(0) v0 := Word(0)
v1 := uint32(0) v1 := Word(0)
//fmt.Printf("syscall: %d\n", syscallNum) //fmt.Printf("syscall: %d\n", syscallNum)
switch syscallNum { switch syscallNum {
case exec.SysMmap: case exec.SysMmap:
var newHeap uint32 var newHeap Word
v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap)
m.state.Heap = newHeap m.state.Heap = newHeap
case exec.SysBrk: case exec.SysBrk:
...@@ -74,9 +77,9 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -74,9 +77,9 @@ func (m *InstrumentedState) handleSyscall() error {
m.state.ExitCode = uint8(a0) m.state.ExitCode = uint8(a0)
return nil return nil
case exec.SysRead: case exec.SysRead:
var newPreimageOffset uint32 var newPreimageOffset Word
var memUpdated bool var memUpdated bool
var memAddr uint32 var memAddr Word
v0, v1, newPreimageOffset, memUpdated, memAddr = exec.HandleSysRead(a0, a1, a2, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker) v0, v1, newPreimageOffset, memUpdated, memAddr = exec.HandleSysRead(a0, a1, a2, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker)
m.state.PreimageOffset = newPreimageOffset m.state.PreimageOffset = newPreimageOffset
if memUpdated { if memUpdated {
...@@ -85,7 +88,7 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -85,7 +88,7 @@ func (m *InstrumentedState) handleSyscall() error {
case exec.SysWrite: case exec.SysWrite:
var newLastHint hexutil.Bytes var newLastHint hexutil.Bytes
var newPreimageKey common.Hash var newPreimageKey common.Hash
var newPreimageOffset uint32 var newPreimageOffset Word
v0, v1, newLastHint, newPreimageKey, newPreimageOffset = exec.HandleSysWrite(a0, a1, a2, m.state.LastHint, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker, m.stdOut, m.stdErr) v0, v1, newLastHint, newPreimageKey, newPreimageOffset = exec.HandleSysWrite(a0, a1, a2, m.state.LastHint, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker, m.stdOut, m.stdErr)
m.state.LastHint = newLastHint m.state.LastHint = newLastHint
m.state.PreimageKey = newPreimageKey m.state.PreimageKey = newPreimageKey
...@@ -105,11 +108,11 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -105,11 +108,11 @@ func (m *InstrumentedState) handleSyscall() error {
return nil return nil
case exec.SysFutex: case exec.SysFutex:
// args: a0 = addr, a1 = op, a2 = val, a3 = timeout // args: a0 = addr, a1 = op, a2 = val, a3 = timeout
effAddr := a0 & 0xFFffFFfc effAddr := a0 & arch.AddressMask
switch a1 { switch a1 {
case exec.FutexWaitPrivate: case exec.FutexWaitPrivate:
m.memoryTracker.TrackMemAccess(effAddr) m.memoryTracker.TrackMemAccess(effAddr)
mem := m.state.Memory.GetMemory(effAddr) mem := m.state.Memory.GetWord(effAddr)
if mem != a2 { if mem != a2 {
v0 = exec.SysErrorSignal v0 = exec.SysErrorSignal
v1 = exec.MipsEAGAIN v1 = exec.MipsEAGAIN
...@@ -153,20 +156,20 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -153,20 +156,20 @@ func (m *InstrumentedState) handleSyscall() error {
switch a0 { switch a0 {
case exec.ClockGettimeRealtimeFlag, exec.ClockGettimeMonotonicFlag: case exec.ClockGettimeRealtimeFlag, exec.ClockGettimeMonotonicFlag:
v0, v1 = 0, 0 v0, v1 = 0, 0
var secs, nsecs uint32 var secs, nsecs Word
if a0 == exec.ClockGettimeMonotonicFlag { if a0 == exec.ClockGettimeMonotonicFlag {
// monotonic clock_gettime is used by Go guest programs for goroutine scheduling and to implement // monotonic clock_gettime is used by Go guest programs for goroutine scheduling and to implement
// `time.Sleep` (and other sleep related operations). // `time.Sleep` (and other sleep related operations).
secs = uint32(m.state.Step / exec.HZ) secs = Word(m.state.Step / exec.HZ)
nsecs = uint32((m.state.Step % exec.HZ) * (1_000_000_000 / exec.HZ)) nsecs = Word((m.state.Step % exec.HZ) * (1_000_000_000 / exec.HZ))
} // else realtime set to Unix Epoch } // else realtime set to Unix Epoch
effAddr := a1 & 0xFFffFFfc effAddr := a1 & arch.AddressMask
m.memoryTracker.TrackMemAccess(effAddr) m.memoryTracker.TrackMemAccess(effAddr)
m.state.Memory.SetMemory(effAddr, secs) m.state.Memory.SetWord(effAddr, secs)
m.handleMemoryUpdate(effAddr) m.handleMemoryUpdate(effAddr)
m.memoryTracker.TrackMemAccess2(effAddr + 4) m.memoryTracker.TrackMemAccess2(effAddr + 4)
m.state.Memory.SetMemory(effAddr+4, nsecs) m.state.Memory.SetWord(effAddr+4, nsecs)
m.handleMemoryUpdate(effAddr + 4) m.handleMemoryUpdate(effAddr + 4)
default: default:
v0 = exec.SysErrorSignal v0 = exec.SysErrorSignal
...@@ -182,6 +185,8 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -182,6 +185,8 @@ func (m *InstrumentedState) handleSyscall() error {
case exec.SysSigaltstack: case exec.SysSigaltstack:
case exec.SysRtSigaction: case exec.SysRtSigaction:
case exec.SysPrlimit64: case exec.SysPrlimit64:
// TODO(#12205): may be needed for 64-bit Cannon
// case exec.SysGetRtLimit:
case exec.SysClose: case exec.SysClose:
case exec.SysPread64: case exec.SysPread64:
case exec.SysFstat64: case exec.SysFstat64:
...@@ -256,9 +261,9 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -256,9 +261,9 @@ func (m *InstrumentedState) mipsStep() error {
m.onWaitComplete(thread, true) m.onWaitComplete(thread, true)
return nil return nil
} else { } else {
effAddr := thread.FutexAddr & 0xFFffFFfc effAddr := thread.FutexAddr & arch.AddressMask
m.memoryTracker.TrackMemAccess(effAddr) m.memoryTracker.TrackMemAccess(effAddr)
mem := m.state.Memory.GetMemory(effAddr) mem := m.state.Memory.GetWord(effAddr)
if thread.FutexVal == mem { if thread.FutexVal == mem {
// still got expected value, continue sleeping, try next thread. // still got expected value, continue sleeping, try next thread.
m.preemptThread(thread) m.preemptThread(thread)
...@@ -299,6 +304,12 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -299,6 +304,12 @@ func (m *InstrumentedState) mipsStep() error {
if opcode == exec.OpLoadLinked || opcode == exec.OpStoreConditional { if opcode == exec.OpLoadLinked || opcode == exec.OpStoreConditional {
return m.handleRMWOps(insn, opcode) return m.handleRMWOps(insn, opcode)
} }
if opcode == exec.OpLoadLinked64 || opcode == exec.OpStoreConditional64 {
if arch.IsMips32 {
panic(fmt.Sprintf("invalid instruction: %x", insn))
}
return m.handleRMWOps(insn, opcode)
}
// Exec the rest of the step logic // Exec the rest of the step logic
memUpdated, memAddr, err := exec.ExecMipsCoreStepLogic(m.state.getCpuRef(), m.state.GetRegistersRef(), m.state.Memory, insn, opcode, fun, m.memoryTracker, m.stackTracker) memUpdated, memAddr, err := exec.ExecMipsCoreStepLogic(m.state.getCpuRef(), m.state.GetRegistersRef(), m.state.Memory, insn, opcode, fun, m.memoryTracker, m.stackTracker)
...@@ -312,7 +323,7 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -312,7 +323,7 @@ func (m *InstrumentedState) mipsStep() error {
return nil return nil
} }
func (m *InstrumentedState) handleMemoryUpdate(memAddr uint32) { func (m *InstrumentedState) handleMemoryUpdate(memAddr Word) {
if memAddr == m.state.LLAddress { if memAddr == m.state.LLAddress {
// Reserved address was modified, clear the reservation // Reserved address was modified, clear the reservation
m.clearLLMemoryReservation() m.clearLLMemoryReservation()
...@@ -329,27 +340,32 @@ func (m *InstrumentedState) clearLLMemoryReservation() { ...@@ -329,27 +340,32 @@ func (m *InstrumentedState) clearLLMemoryReservation() {
func (m *InstrumentedState) handleRMWOps(insn, opcode uint32) error { func (m *InstrumentedState) handleRMWOps(insn, opcode uint32) error {
baseReg := (insn >> 21) & 0x1F baseReg := (insn >> 21) & 0x1F
base := m.state.GetRegistersRef()[baseReg] base := m.state.GetRegistersRef()[baseReg]
rtReg := (insn >> 16) & 0x1F rtReg := Word((insn >> 16) & 0x1F)
offset := exec.SignExtendImmediate(insn) offset := exec.SignExtendImmediate(insn)
effAddr := (base + offset) & 0xFFFFFFFC effAddr := (base + offset) & arch.AddressMask
m.memoryTracker.TrackMemAccess(effAddr) m.memoryTracker.TrackMemAccess(effAddr)
mem := m.state.Memory.GetMemory(effAddr) mem := m.state.Memory.GetWord(effAddr)
var retVal uint32 var retVal Word
threadId := m.state.GetCurrentThread().ThreadId threadId := m.state.GetCurrentThread().ThreadId
if opcode == exec.OpLoadLinked { if opcode == exec.OpLoadLinked || opcode == exec.OpLoadLinked64 {
retVal = mem retVal = mem
m.state.LLReservationActive = true m.state.LLReservationActive = true
m.state.LLAddress = effAddr m.state.LLAddress = effAddr
m.state.LLOwnerThread = threadId m.state.LLOwnerThread = threadId
} else if opcode == exec.OpStoreConditional { } else if opcode == exec.OpStoreConditional || opcode == exec.OpStoreConditional64 {
// TODO(#12205): Determine bits affected by coherence stores on 64-bits
// Check if our memory reservation is still intact // Check if our memory reservation is still intact
if m.state.LLReservationActive && m.state.LLOwnerThread == threadId && m.state.LLAddress == effAddr { if m.state.LLReservationActive && m.state.LLOwnerThread == threadId && m.state.LLAddress == effAddr {
// Complete atomic update: set memory and return 1 for success // Complete atomic update: set memory and return 1 for success
m.clearLLMemoryReservation() m.clearLLMemoryReservation()
rt := m.state.GetRegistersRef()[rtReg] rt := m.state.GetRegistersRef()[rtReg]
m.state.Memory.SetMemory(effAddr, rt) if opcode == exec.OpStoreConditional {
m.state.Memory.SetMemory(effAddr, uint32(rt))
} else {
m.state.Memory.SetWord(effAddr, rt)
}
retVal = 1 retVal = 1
} else { } else {
// Atomic update failed, return 0 for failure // Atomic update failed, return 0 for failure
...@@ -370,8 +386,8 @@ func (m *InstrumentedState) onWaitComplete(thread *ThreadState, isTimedOut bool) ...@@ -370,8 +386,8 @@ func (m *InstrumentedState) onWaitComplete(thread *ThreadState, isTimedOut bool)
thread.FutexTimeoutStep = 0 thread.FutexTimeoutStep = 0
// Complete the FUTEX_WAIT syscall // Complete the FUTEX_WAIT syscall
v0 := uint32(0) v0 := Word(0)
v1 := uint32(0) v1 := Word(0)
if isTimedOut { if isTimedOut {
v0 = exec.SysErrorSignal v0 = exec.SysErrorSignal
v1 = exec.MipsETIMEDOUT v1 = exec.MipsETIMEDOUT
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
type ThreadedStackTracker interface { type ThreadedStackTracker interface {
exec.TraceableStackTracker exec.TraceableStackTracker
DropThread(threadId uint32) DropThread(threadId Word)
} }
type NoopThreadedStackTracker struct { type NoopThreadedStackTracker struct {
...@@ -18,12 +18,12 @@ type NoopThreadedStackTracker struct { ...@@ -18,12 +18,12 @@ type NoopThreadedStackTracker struct {
var _ ThreadedStackTracker = (*ThreadedStackTrackerImpl)(nil) var _ ThreadedStackTracker = (*ThreadedStackTrackerImpl)(nil)
func (n *NoopThreadedStackTracker) DropThread(threadId uint32) {} func (n *NoopThreadedStackTracker) DropThread(threadId Word) {}
type ThreadedStackTrackerImpl struct { type ThreadedStackTrackerImpl struct {
meta mipsevm.Metadata meta mipsevm.Metadata
state *State state *State
trackersByThreadId map[uint32]exec.TraceableStackTracker trackersByThreadId map[Word]exec.TraceableStackTracker
} }
var _ ThreadedStackTracker = (*ThreadedStackTrackerImpl)(nil) var _ ThreadedStackTracker = (*ThreadedStackTrackerImpl)(nil)
...@@ -36,11 +36,11 @@ func NewThreadedStackTracker(state *State, meta mipsevm.Metadata) (*ThreadedStac ...@@ -36,11 +36,11 @@ func NewThreadedStackTracker(state *State, meta mipsevm.Metadata) (*ThreadedStac
return &ThreadedStackTrackerImpl{ return &ThreadedStackTrackerImpl{
state: state, state: state,
meta: meta, meta: meta,
trackersByThreadId: make(map[uint32]exec.TraceableStackTracker), trackersByThreadId: make(map[Word]exec.TraceableStackTracker),
}, nil }, nil
} }
func (t *ThreadedStackTrackerImpl) PushStack(caller uint32, target uint32) { func (t *ThreadedStackTrackerImpl) PushStack(caller Word, target Word) {
t.getCurrentTracker().PushStack(caller, target) t.getCurrentTracker().PushStack(caller, target)
} }
...@@ -62,6 +62,6 @@ func (t *ThreadedStackTrackerImpl) getCurrentTracker() exec.TraceableStackTracke ...@@ -62,6 +62,6 @@ func (t *ThreadedStackTrackerImpl) getCurrentTracker() exec.TraceableStackTracke
return tracker return tracker
} }
func (t *ThreadedStackTrackerImpl) DropThread(threadId uint32) { func (t *ThreadedStackTrackerImpl) DropThread(threadId Word) {
delete(t.trackersByThreadId, threadId) delete(t.trackersByThreadId, threadId)
} }
...@@ -11,54 +11,57 @@ import ( ...@@ -11,54 +11,57 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/serialize" "github.com/ethereum-optimism/optimism/cannon/serialize"
) )
// STATE_WITNESS_SIZE is the size of the state witness encoding in bytes. // STATE_WITNESS_SIZE is the size of the state witness encoding in bytes.
const STATE_WITNESS_SIZE = 172
const ( const (
MEMROOT_WITNESS_OFFSET = 0 MEMROOT_WITNESS_OFFSET = 0
PREIMAGE_KEY_WITNESS_OFFSET = MEMROOT_WITNESS_OFFSET + 32 PREIMAGE_KEY_WITNESS_OFFSET = MEMROOT_WITNESS_OFFSET + 32
PREIMAGE_OFFSET_WITNESS_OFFSET = PREIMAGE_KEY_WITNESS_OFFSET + 32 PREIMAGE_OFFSET_WITNESS_OFFSET = PREIMAGE_KEY_WITNESS_OFFSET + 32
HEAP_WITNESS_OFFSET = PREIMAGE_OFFSET_WITNESS_OFFSET + 4 HEAP_WITNESS_OFFSET = PREIMAGE_OFFSET_WITNESS_OFFSET + arch.WordSizeBytes
LL_RESERVATION_ACTIVE_OFFSET = HEAP_WITNESS_OFFSET + 4 LL_RESERVATION_ACTIVE_OFFSET = HEAP_WITNESS_OFFSET + arch.WordSizeBytes
LL_ADDRESS_OFFSET = LL_RESERVATION_ACTIVE_OFFSET + 1 LL_ADDRESS_OFFSET = LL_RESERVATION_ACTIVE_OFFSET + 1
LL_OWNER_THREAD_OFFSET = LL_ADDRESS_OFFSET + 4 LL_OWNER_THREAD_OFFSET = LL_ADDRESS_OFFSET + arch.WordSizeBytes
EXITCODE_WITNESS_OFFSET = LL_OWNER_THREAD_OFFSET + 4 EXITCODE_WITNESS_OFFSET = LL_OWNER_THREAD_OFFSET + arch.WordSizeBytes
EXITED_WITNESS_OFFSET = EXITCODE_WITNESS_OFFSET + 1 EXITED_WITNESS_OFFSET = EXITCODE_WITNESS_OFFSET + 1
STEP_WITNESS_OFFSET = EXITED_WITNESS_OFFSET + 1 STEP_WITNESS_OFFSET = EXITED_WITNESS_OFFSET + 1
STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET = STEP_WITNESS_OFFSET + 8 STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET = STEP_WITNESS_OFFSET + 8
WAKEUP_WITNESS_OFFSET = STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET + 8 WAKEUP_WITNESS_OFFSET = STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET + 8
TRAVERSE_RIGHT_WITNESS_OFFSET = WAKEUP_WITNESS_OFFSET + 4 TRAVERSE_RIGHT_WITNESS_OFFSET = WAKEUP_WITNESS_OFFSET + arch.WordSizeBytes
LEFT_THREADS_ROOT_WITNESS_OFFSET = TRAVERSE_RIGHT_WITNESS_OFFSET + 1 LEFT_THREADS_ROOT_WITNESS_OFFSET = TRAVERSE_RIGHT_WITNESS_OFFSET + 1
RIGHT_THREADS_ROOT_WITNESS_OFFSET = LEFT_THREADS_ROOT_WITNESS_OFFSET + 32 RIGHT_THREADS_ROOT_WITNESS_OFFSET = LEFT_THREADS_ROOT_WITNESS_OFFSET + 32
THREAD_ID_WITNESS_OFFSET = RIGHT_THREADS_ROOT_WITNESS_OFFSET + 32 THREAD_ID_WITNESS_OFFSET = RIGHT_THREADS_ROOT_WITNESS_OFFSET + 32
// 172 and 196 bytes for 32 and 64-bit respectively
STATE_WITNESS_SIZE = THREAD_ID_WITNESS_OFFSET + arch.WordSizeBytes
) )
type State struct { type State struct {
Memory *memory.Memory Memory *memory.Memory
PreimageKey common.Hash PreimageKey common.Hash
PreimageOffset uint32 // note that the offset includes the 8-byte length prefix PreimageOffset Word // note that the offset includes the 8-byte length prefix
Heap uint32 // to handle mmap growth Heap Word // to handle mmap growth
LLReservationActive bool // Whether there is an active memory reservation initiated via the LL (load linked) op LLReservationActive bool // Whether there is an active memory reservation initiated via the LL (load linked) op
LLAddress uint32 // The "linked" memory address reserved via the LL (load linked) op LLAddress Word // The "linked" memory address reserved via the LL (load linked) op
LLOwnerThread uint32 // The id of the thread that holds the reservation on LLAddress LLOwnerThread Word // The id of the thread that holds the reservation on LLAddress
ExitCode uint8 ExitCode uint8
Exited bool Exited bool
Step uint64 Step uint64
StepsSinceLastContextSwitch uint64 StepsSinceLastContextSwitch uint64
Wakeup uint32 Wakeup Word
TraverseRight bool TraverseRight bool
LeftThreadStack []*ThreadState LeftThreadStack []*ThreadState
RightThreadStack []*ThreadState RightThreadStack []*ThreadState
NextThreadId uint32 NextThreadId Word
// LastHint is optional metadata, and not part of the VM state itself. // LastHint is optional metadata, and not part of the VM state itself.
LastHint hexutil.Bytes LastHint hexutil.Bytes
...@@ -86,7 +89,7 @@ func CreateEmptyState() *State { ...@@ -86,7 +89,7 @@ func CreateEmptyState() *State {
} }
} }
func CreateInitialState(pc, heapStart uint32) *State { func CreateInitialState(pc, heapStart Word) *State {
state := CreateEmptyState() state := CreateEmptyState()
currentThread := state.GetCurrentThread() currentThread := state.GetCurrentThread()
currentThread.Cpu.PC = pc currentThread.Cpu.PC = pc
...@@ -97,6 +100,7 @@ func CreateInitialState(pc, heapStart uint32) *State { ...@@ -97,6 +100,7 @@ func CreateInitialState(pc, heapStart uint32) *State {
} }
func (s *State) CreateVM(logger log.Logger, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta mipsevm.Metadata) mipsevm.FPVM { func (s *State) CreateVM(logger log.Logger, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta mipsevm.Metadata) mipsevm.FPVM {
logger.Info("Using cannon multithreaded VM", "is32", arch.IsMips32)
return NewInstrumentedState(s, po, stdOut, stdErr, logger, meta) return NewInstrumentedState(s, po, stdOut, stdErr, logger, meta)
} }
...@@ -139,7 +143,7 @@ func (s *State) calculateThreadStackRoot(stack []*ThreadState) common.Hash { ...@@ -139,7 +143,7 @@ func (s *State) calculateThreadStackRoot(stack []*ThreadState) common.Hash {
return curRoot return curRoot
} }
func (s *State) GetPC() uint32 { func (s *State) GetPC() Word {
activeThread := s.GetCurrentThread() activeThread := s.GetCurrentThread()
return activeThread.Cpu.PC return activeThread.Cpu.PC
} }
...@@ -153,7 +157,7 @@ func (s *State) getCpuRef() *mipsevm.CpuScalars { ...@@ -153,7 +157,7 @@ func (s *State) getCpuRef() *mipsevm.CpuScalars {
return &s.GetCurrentThread().Cpu return &s.GetCurrentThread().Cpu
} }
func (s *State) GetRegistersRef() *[32]uint32 { func (s *State) GetRegistersRef() *[32]Word {
activeThread := s.GetCurrentThread() activeThread := s.GetCurrentThread()
return &activeThread.Registers return &activeThread.Registers
} }
...@@ -176,7 +180,7 @@ func (s *State) GetMemory() *memory.Memory { ...@@ -176,7 +180,7 @@ func (s *State) GetMemory() *memory.Memory {
return s.Memory return s.Memory
} }
func (s *State) GetHeap() uint32 { func (s *State) GetHeap() Word {
return s.Heap return s.Heap
} }
...@@ -184,7 +188,7 @@ func (s *State) GetPreimageKey() common.Hash { ...@@ -184,7 +188,7 @@ func (s *State) GetPreimageKey() common.Hash {
return s.PreimageKey return s.PreimageKey
} }
func (s *State) GetPreimageOffset() uint32 { func (s *State) GetPreimageOffset() Word {
return s.PreimageOffset return s.PreimageOffset
} }
...@@ -193,24 +197,24 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) { ...@@ -193,24 +197,24 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) {
memRoot := s.Memory.MerkleRoot() memRoot := s.Memory.MerkleRoot()
out = append(out, memRoot[:]...) out = append(out, memRoot[:]...)
out = append(out, s.PreimageKey[:]...) out = append(out, s.PreimageKey[:]...)
out = binary.BigEndian.AppendUint32(out, s.PreimageOffset) out = arch.ByteOrderWord.AppendWord(out, s.PreimageOffset)
out = binary.BigEndian.AppendUint32(out, s.Heap) out = arch.ByteOrderWord.AppendWord(out, s.Heap)
out = mipsevm.AppendBoolToWitness(out, s.LLReservationActive) out = mipsevm.AppendBoolToWitness(out, s.LLReservationActive)
out = binary.BigEndian.AppendUint32(out, s.LLAddress) out = arch.ByteOrderWord.AppendWord(out, s.LLAddress)
out = binary.BigEndian.AppendUint32(out, s.LLOwnerThread) out = arch.ByteOrderWord.AppendWord(out, s.LLOwnerThread)
out = append(out, s.ExitCode) out = append(out, s.ExitCode)
out = mipsevm.AppendBoolToWitness(out, s.Exited) out = mipsevm.AppendBoolToWitness(out, s.Exited)
out = binary.BigEndian.AppendUint64(out, s.Step) out = binary.BigEndian.AppendUint64(out, s.Step)
out = binary.BigEndian.AppendUint64(out, s.StepsSinceLastContextSwitch) out = binary.BigEndian.AppendUint64(out, s.StepsSinceLastContextSwitch)
out = binary.BigEndian.AppendUint32(out, s.Wakeup) out = arch.ByteOrderWord.AppendWord(out, s.Wakeup)
leftStackRoot := s.getLeftThreadStackRoot() leftStackRoot := s.getLeftThreadStackRoot()
rightStackRoot := s.getRightThreadStackRoot() rightStackRoot := s.getRightThreadStackRoot()
out = mipsevm.AppendBoolToWitness(out, s.TraverseRight) out = mipsevm.AppendBoolToWitness(out, s.TraverseRight)
out = append(out, (leftStackRoot)[:]...) out = append(out, (leftStackRoot)[:]...)
out = append(out, (rightStackRoot)[:]...) out = append(out, (rightStackRoot)[:]...)
out = binary.BigEndian.AppendUint32(out, s.NextThreadId) out = arch.ByteOrderWord.AppendWord(out, s.NextThreadId)
return out, stateHashFromWitness(out) return out, stateHashFromWitness(out)
} }
...@@ -245,20 +249,20 @@ func (s *State) ThreadCount() int { ...@@ -245,20 +249,20 @@ func (s *State) ThreadCount() int {
// StateVersion uint8(1) // StateVersion uint8(1)
// Memory As per Memory.Serialize // Memory As per Memory.Serialize
// PreimageKey [32]byte // PreimageKey [32]byte
// PreimageOffset uint32 // PreimageOffset Word
// Heap uint32 // Heap Word
// ExitCode uint8 // ExitCode uint8
// Exited uint8 - 0 for false, 1 for true // Exited uint8 - 0 for false, 1 for true
// Step uint64 // Step uint64
// StepsSinceLastContextSwitch uint64 // StepsSinceLastContextSwitch uint64
// Wakeup uint32 // Wakeup Word
// TraverseRight uint8 - 0 for false, 1 for true // TraverseRight uint8 - 0 for false, 1 for true
// NextThreadId uint32 // NextThreadId Word
// len(LeftThreadStack) uint32 // len(LeftThreadStack) Word
// LeftThreadStack entries as per ThreadState.Serialize // LeftThreadStack entries as per ThreadState.Serialize
// len(RightThreadStack) uint32 // len(RightThreadStack) Word
// RightThreadStack entries as per ThreadState.Serialize // RightThreadStack entries as per ThreadState.Serialize
// len(LastHint) uint32 (0 when LastHint is nil) // len(LastHint) Word (0 when LastHint is nil)
// LastHint []byte // LastHint []byte
func (s *State) Serialize(out io.Writer) error { func (s *State) Serialize(out io.Writer) error {
bout := serialize.NewBinaryWriter(out) bout := serialize.NewBinaryWriter(out)
...@@ -306,7 +310,7 @@ func (s *State) Serialize(out io.Writer) error { ...@@ -306,7 +310,7 @@ func (s *State) Serialize(out io.Writer) error {
return err return err
} }
if err := bout.WriteUInt(uint32(len(s.LeftThreadStack))); err != nil { if err := bout.WriteUInt(Word(len(s.LeftThreadStack))); err != nil {
return err return err
} }
for _, stack := range s.LeftThreadStack { for _, stack := range s.LeftThreadStack {
...@@ -314,7 +318,7 @@ func (s *State) Serialize(out io.Writer) error { ...@@ -314,7 +318,7 @@ func (s *State) Serialize(out io.Writer) error {
return err return err
} }
} }
if err := bout.WriteUInt(uint32(len(s.RightThreadStack))); err != nil { if err := bout.WriteUInt(Word(len(s.RightThreadStack))); err != nil {
return err return err
} }
for _, stack := range s.RightThreadStack { for _, stack := range s.RightThreadStack {
...@@ -376,7 +380,7 @@ func (s *State) Deserialize(in io.Reader) error { ...@@ -376,7 +380,7 @@ func (s *State) Deserialize(in io.Reader) error {
return err return err
} }
var leftThreadStackSize uint32 var leftThreadStackSize Word
if err := bin.ReadUInt(&leftThreadStackSize); err != nil { if err := bin.ReadUInt(&leftThreadStackSize); err != nil {
return err return err
} }
...@@ -388,7 +392,7 @@ func (s *State) Deserialize(in io.Reader) error { ...@@ -388,7 +392,7 @@ func (s *State) Deserialize(in io.Reader) error {
} }
} }
var rightThreadStackSize uint32 var rightThreadStackSize Word
if err := bin.ReadUInt(&rightThreadStackSize); err != nil { if err := bin.ReadUInt(&rightThreadStackSize); err != nil {
return err return err
} }
...@@ -423,7 +427,7 @@ func GetStateHashFn() mipsevm.HashFn { ...@@ -423,7 +427,7 @@ func GetStateHashFn() mipsevm.HashFn {
func stateHashFromWitness(sw []byte) common.Hash { func stateHashFromWitness(sw []byte) common.Hash {
if len(sw) != STATE_WITNESS_SIZE { if len(sw) != STATE_WITNESS_SIZE {
panic("Invalid witness length") panic(fmt.Sprintf("Invalid witness length. Got %d, expected %d", len(sw), STATE_WITNESS_SIZE))
} }
hash := crypto.Keccak256Hash(sw) hash := crypto.Keccak256Hash(sw)
exitCode := sw[EXITCODE_WITNESS_OFFSET] exitCode := sw[EXITCODE_WITNESS_OFFSET]
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
...@@ -41,11 +42,11 @@ func TestState_EncodeWitness(t *testing.T) { ...@@ -41,11 +42,11 @@ func TestState_EncodeWitness(t *testing.T) {
{exited: true, exitCode: 3}, {exited: true, exitCode: 3},
} }
heap := uint32(12) heap := Word(12)
llAddress := uint32(55) llAddress := Word(55)
llThreadOwner := uint32(99) llThreadOwner := Word(99)
preimageKey := crypto.Keccak256Hash([]byte{1, 2, 3, 4}) preimageKey := crypto.Keccak256Hash([]byte{1, 2, 3, 4})
preimageOffset := uint32(24) preimageOffset := Word(24)
step := uint64(33) step := uint64(33)
stepsSinceContextSwitch := uint64(123) stepsSinceContextSwitch := uint64(123)
for _, c := range cases { for _, c := range cases {
...@@ -207,7 +208,7 @@ func TestSerializeStateRoundTrip(t *testing.T) { ...@@ -207,7 +208,7 @@ func TestSerializeStateRoundTrip(t *testing.T) {
LO: 0xbeef, LO: 0xbeef,
HI: 0xbabe, HI: 0xbabe,
}, },
Registers: [32]uint32{ Registers: [32]Word{
0xdeadbeef, 0xdeadbeef,
0xdeadbeef, 0xdeadbeef,
0xc0ffee, 0xc0ffee,
...@@ -230,7 +231,7 @@ func TestSerializeStateRoundTrip(t *testing.T) { ...@@ -230,7 +231,7 @@ func TestSerializeStateRoundTrip(t *testing.T) {
LO: 0xeeef, LO: 0xeeef,
HI: 0xeabe, HI: 0xeabe,
}, },
Registers: [32]uint32{ Registers: [32]Word{
0xabcdef, 0xabcdef,
0x123456, 0x123456,
}, },
...@@ -250,7 +251,7 @@ func TestSerializeStateRoundTrip(t *testing.T) { ...@@ -250,7 +251,7 @@ func TestSerializeStateRoundTrip(t *testing.T) {
LO: 0xdeef, LO: 0xdeef,
HI: 0xdabe, HI: 0xdabe,
}, },
Registers: [32]uint32{ Registers: [32]Word{
0x654321, 0x654321,
}, },
}, },
...@@ -267,7 +268,7 @@ func TestSerializeStateRoundTrip(t *testing.T) { ...@@ -267,7 +268,7 @@ func TestSerializeStateRoundTrip(t *testing.T) {
LO: 0xceef, LO: 0xceef,
HI: 0xcabe, HI: 0xcabe,
}, },
Registers: [32]uint32{ Registers: [32]Word{
0x987653, 0x987653,
0xfedbca, 0xfedbca,
}, },
...@@ -302,7 +303,7 @@ func TestState_EncodeThreadProof_SingleThread(t *testing.T) { ...@@ -302,7 +303,7 @@ func TestState_EncodeThreadProof_SingleThread(t *testing.T) {
activeThread.Cpu.HI = 11 activeThread.Cpu.HI = 11
activeThread.Cpu.LO = 22 activeThread.Cpu.LO = 22
for i := 0; i < 32; i++ { for i := 0; i < 32; i++ {
activeThread.Registers[i] = uint32(i) activeThread.Registers[i] = Word(i)
} }
expectedProof := append([]byte{}, activeThread.serializeThread()[:]...) expectedProof := append([]byte{}, activeThread.serializeThread()[:]...)
...@@ -324,12 +325,12 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) { ...@@ -324,12 +325,12 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) {
// Set some fields on our threads // Set some fields on our threads
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
curThread := state.LeftThreadStack[i] curThread := state.LeftThreadStack[i]
curThread.Cpu.PC = uint32(4 * i) curThread.Cpu.PC = Word(4 * i)
curThread.Cpu.NextPC = curThread.Cpu.PC + 4 curThread.Cpu.NextPC = curThread.Cpu.PC + 4
curThread.Cpu.HI = uint32(11 + i) curThread.Cpu.HI = Word(11 + i)
curThread.Cpu.LO = uint32(22 + i) curThread.Cpu.LO = Word(22 + i)
for j := 0; j < 32; j++ { for j := 0; j < 32; j++ {
curThread.Registers[j] = uint32(j + i) curThread.Registers[j] = Word(j + i)
} }
} }
...@@ -355,12 +356,12 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) { ...@@ -355,12 +356,12 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) {
func TestState_EncodeThreadProof_EmptyThreadStackPanic(t *testing.T) { func TestState_EncodeThreadProof_EmptyThreadStackPanic(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
wakeupAddr uint32 wakeupAddr Word
traverseRight bool traverseRight bool
}{ }{
{"traverse left during wakeup traversal", uint32(99), false}, {"traverse left during wakeup traversal", Word(99), false},
{"traverse left during normal traversal", exec.FutexEmptyAddr, false}, {"traverse left during normal traversal", exec.FutexEmptyAddr, false},
{"traverse right during wakeup traversal", uint32(99), true}, {"traverse right during wakeup traversal", Word(99), true},
{"traverse right during normal traversal", exec.FutexEmptyAddr, true}, {"traverse right during normal traversal", exec.FutexEmptyAddr, true},
} }
...@@ -382,3 +383,19 @@ func TestState_EncodeThreadProof_EmptyThreadStackPanic(t *testing.T) { ...@@ -382,3 +383,19 @@ func TestState_EncodeThreadProof_EmptyThreadStackPanic(t *testing.T) {
}) })
} }
} }
func TestStateWitnessSize(t *testing.T) {
expectedWitnessSize := 172
if !arch.IsMips32 {
expectedWitnessSize = 196
}
require.Equal(t, expectedWitnessSize, STATE_WITNESS_SIZE)
}
func TestThreadStateWitnessSize(t *testing.T) {
expectedWitnessSize := 166
if !arch.IsMips32 {
expectedWitnessSize = 322
}
require.Equal(t, expectedWitnessSize, SERIALIZED_THREAD_SIZE)
}
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
) )
...@@ -15,11 +16,11 @@ import ( ...@@ -15,11 +16,11 @@ import (
// to define an expected post-state. The post-state is then validated with ExpectedMTState.Validate(t, postState) // to define an expected post-state. The post-state is then validated with ExpectedMTState.Validate(t, postState)
type ExpectedMTState struct { type ExpectedMTState struct {
PreimageKey common.Hash PreimageKey common.Hash
PreimageOffset uint32 PreimageOffset arch.Word
Heap uint32 Heap arch.Word
LLReservationActive bool LLReservationActive bool
LLAddress uint32 LLAddress arch.Word
LLOwnerThread uint32 LLOwnerThread arch.Word
ExitCode uint8 ExitCode uint8
Exited bool Exited bool
Step uint64 Step uint64
...@@ -28,37 +29,37 @@ type ExpectedMTState struct { ...@@ -28,37 +29,37 @@ type ExpectedMTState struct {
expectedMemory *memory.Memory expectedMemory *memory.Memory
// Threading-related expectations // Threading-related expectations
StepsSinceLastContextSwitch uint64 StepsSinceLastContextSwitch uint64
Wakeup uint32 Wakeup arch.Word
TraverseRight bool TraverseRight bool
NextThreadId uint32 NextThreadId arch.Word
ThreadCount int ThreadCount int
RightStackSize int RightStackSize int
LeftStackSize int LeftStackSize int
prestateActiveThreadId uint32 prestateActiveThreadId arch.Word
prestateActiveThreadOrig ExpectedThreadState // Cached for internal use prestateActiveThreadOrig ExpectedThreadState // Cached for internal use
ActiveThreadId uint32 ActiveThreadId arch.Word
threadExpectations map[uint32]*ExpectedThreadState threadExpectations map[arch.Word]*ExpectedThreadState
} }
type ExpectedThreadState struct { type ExpectedThreadState struct {
ThreadId uint32 ThreadId arch.Word
ExitCode uint8 ExitCode uint8
Exited bool Exited bool
FutexAddr uint32 FutexAddr arch.Word
FutexVal uint32 FutexVal arch.Word
FutexTimeoutStep uint64 FutexTimeoutStep uint64
PC uint32 PC arch.Word
NextPC uint32 NextPC arch.Word
HI uint32 HI arch.Word
LO uint32 LO arch.Word
Registers [32]uint32 Registers [32]arch.Word
Dropped bool Dropped bool
} }
func NewExpectedMTState(fromState *multithreaded.State) *ExpectedMTState { func NewExpectedMTState(fromState *multithreaded.State) *ExpectedMTState {
currentThread := fromState.GetCurrentThread() currentThread := fromState.GetCurrentThread()
expectedThreads := make(map[uint32]*ExpectedThreadState) expectedThreads := make(map[arch.Word]*ExpectedThreadState)
for _, t := range GetAllThreads(fromState) { for _, t := range GetAllThreads(fromState) {
expectedThreads[t.ThreadId] = newExpectedThreadState(t) expectedThreads[t.ThreadId] = newExpectedThreadState(t)
} }
...@@ -118,12 +119,17 @@ func (e *ExpectedMTState) ExpectStep() { ...@@ -118,12 +119,17 @@ func (e *ExpectedMTState) ExpectStep() {
e.StepsSinceLastContextSwitch += 1 e.StepsSinceLastContextSwitch += 1
} }
func (e *ExpectedMTState) ExpectMemoryWrite(addr uint32, val uint32) { func (e *ExpectedMTState) ExpectMemoryWrite(addr arch.Word, val uint32) {
e.expectedMemory.SetMemory(addr, val) e.expectedMemory.SetMemory(addr, val)
e.MemoryRoot = e.expectedMemory.MerkleRoot() e.MemoryRoot = e.expectedMemory.MerkleRoot()
} }
func (e *ExpectedMTState) ExpectMemoryWriteMultiple(addr uint32, val uint32, addr2 uint32, val2 uint32) { func (e *ExpectedMTState) ExpectMemoryWordWrite(addr arch.Word, val arch.Word) {
e.expectedMemory.SetWord(addr, val)
e.MemoryRoot = e.expectedMemory.MerkleRoot()
}
func (e *ExpectedMTState) ExpectMemoryWriteMultiple(addr arch.Word, val uint32, addr2 arch.Word, val2 uint32) {
e.expectedMemory.SetMemory(addr, val) e.expectedMemory.SetMemory(addr, val)
e.expectedMemory.SetMemory(addr2, val2) e.expectedMemory.SetMemory(addr2, val2)
e.MemoryRoot = e.expectedMemory.MerkleRoot() e.MemoryRoot = e.expectedMemory.MerkleRoot()
...@@ -166,7 +172,7 @@ func (e *ExpectedMTState) PrestateActiveThread() *ExpectedThreadState { ...@@ -166,7 +172,7 @@ func (e *ExpectedMTState) PrestateActiveThread() *ExpectedThreadState {
return e.threadExpectations[e.prestateActiveThreadId] return e.threadExpectations[e.prestateActiveThreadId]
} }
func (e *ExpectedMTState) Thread(threadId uint32) *ExpectedThreadState { func (e *ExpectedMTState) Thread(threadId arch.Word) *ExpectedThreadState {
return e.threadExpectations[threadId] return e.threadExpectations[threadId]
} }
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
//"github.com/ethereum/go-ethereum/common" //"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
) )
...@@ -45,10 +46,10 @@ func TestValidate_shouldCatchMutations(t *testing.T) { ...@@ -45,10 +46,10 @@ func TestValidate_shouldCatchMutations(t *testing.T) {
{name: "LeftStackSize", mut: func(e *ExpectedMTState, st *multithreaded.State) { e.LeftStackSize += 1 }}, {name: "LeftStackSize", mut: func(e *ExpectedMTState, st *multithreaded.State) { e.LeftStackSize += 1 }},
{name: "ActiveThreadId", mut: func(e *ExpectedMTState, st *multithreaded.State) { e.ActiveThreadId += 1 }}, {name: "ActiveThreadId", mut: func(e *ExpectedMTState, st *multithreaded.State) { e.ActiveThreadId += 1 }},
{name: "Empty thread expectations", mut: func(e *ExpectedMTState, st *multithreaded.State) { {name: "Empty thread expectations", mut: func(e *ExpectedMTState, st *multithreaded.State) {
e.threadExpectations = map[uint32]*ExpectedThreadState{} e.threadExpectations = map[arch.Word]*ExpectedThreadState{}
}}, }},
{name: "Mismatched thread expectations", mut: func(e *ExpectedMTState, st *multithreaded.State) { {name: "Mismatched thread expectations", mut: func(e *ExpectedMTState, st *multithreaded.State) {
e.threadExpectations = map[uint32]*ExpectedThreadState{someThread.ThreadId: newExpectedThreadState(someThread)} e.threadExpectations = map[arch.Word]*ExpectedThreadState{someThread.ThreadId: newExpectedThreadState(someThread)}
}}, }},
{name: "Active threadId", mut: func(e *ExpectedMTState, st *multithreaded.State) { {name: "Active threadId", mut: func(e *ExpectedMTState, st *multithreaded.State) {
e.threadExpectations[st.GetCurrentThread().ThreadId].ThreadId += 1 e.threadExpectations[st.GetCurrentThread().ThreadId].ThreadId += 1
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
...@@ -27,18 +28,18 @@ func (m *StateMutatorMultiThreaded) Randomize(randSeed int64) { ...@@ -27,18 +28,18 @@ func (m *StateMutatorMultiThreaded) Randomize(randSeed int64) {
step := r.RandStep() step := r.RandStep()
m.state.PreimageKey = r.RandHash() m.state.PreimageKey = r.RandHash()
m.state.PreimageOffset = r.Uint32() m.state.PreimageOffset = r.Word()
m.state.Step = step m.state.Step = step
m.state.LastHint = r.RandHint() m.state.LastHint = r.RandHint()
m.state.StepsSinceLastContextSwitch = uint64(r.Intn(exec.SchedQuantum)) m.state.StepsSinceLastContextSwitch = uint64(r.Intn(exec.SchedQuantum))
// Randomize memory-related fields // Randomize memory-related fields
halfMemory := math.MaxUint32 / 2 halfMemory := math.MaxUint32 / 2
m.state.Heap = uint32(r.Intn(halfMemory) + halfMemory) m.state.Heap = arch.Word(r.Intn(halfMemory) + halfMemory)
m.state.LLReservationActive = r.Intn(2) == 1 m.state.LLReservationActive = r.Intn(2) == 1
if m.state.LLReservationActive { if m.state.LLReservationActive {
m.state.LLAddress = uint32(r.Intn(halfMemory)) m.state.LLAddress = arch.Word(r.Intn(halfMemory))
m.state.LLOwnerThread = uint32(r.Intn(10)) m.state.LLOwnerThread = arch.Word(r.Intn(10))
} }
// Randomize threads // Randomize threads
...@@ -48,11 +49,11 @@ func (m *StateMutatorMultiThreaded) Randomize(randSeed int64) { ...@@ -48,11 +49,11 @@ func (m *StateMutatorMultiThreaded) Randomize(randSeed int64) {
SetupThreads(randSeed+1, m.state, traverseRight, activeStackThreads, inactiveStackThreads) SetupThreads(randSeed+1, m.state, traverseRight, activeStackThreads, inactiveStackThreads)
} }
func (m *StateMutatorMultiThreaded) SetHI(val uint32) { func (m *StateMutatorMultiThreaded) SetHI(val arch.Word) {
m.state.GetCurrentThread().Cpu.HI = val m.state.GetCurrentThread().Cpu.HI = val
} }
func (m *StateMutatorMultiThreaded) SetLO(val uint32) { func (m *StateMutatorMultiThreaded) SetLO(val arch.Word) {
m.state.GetCurrentThread().Cpu.LO = val m.state.GetCurrentThread().Cpu.LO = val
} }
...@@ -64,16 +65,16 @@ func (m *StateMutatorMultiThreaded) SetExited(val bool) { ...@@ -64,16 +65,16 @@ func (m *StateMutatorMultiThreaded) SetExited(val bool) {
m.state.Exited = val m.state.Exited = val
} }
func (m *StateMutatorMultiThreaded) SetPC(val uint32) { func (m *StateMutatorMultiThreaded) SetPC(val arch.Word) {
thread := m.state.GetCurrentThread() thread := m.state.GetCurrentThread()
thread.Cpu.PC = val thread.Cpu.PC = val
} }
func (m *StateMutatorMultiThreaded) SetHeap(val uint32) { func (m *StateMutatorMultiThreaded) SetHeap(val arch.Word) {
m.state.Heap = val m.state.Heap = val
} }
func (m *StateMutatorMultiThreaded) SetNextPC(val uint32) { func (m *StateMutatorMultiThreaded) SetNextPC(val arch.Word) {
thread := m.state.GetCurrentThread() thread := m.state.GetCurrentThread()
thread.Cpu.NextPC = val thread.Cpu.NextPC = val
} }
...@@ -86,7 +87,7 @@ func (m *StateMutatorMultiThreaded) SetPreimageKey(val common.Hash) { ...@@ -86,7 +87,7 @@ func (m *StateMutatorMultiThreaded) SetPreimageKey(val common.Hash) {
m.state.PreimageKey = val m.state.PreimageKey = val
} }
func (m *StateMutatorMultiThreaded) SetPreimageOffset(val uint32) { func (m *StateMutatorMultiThreaded) SetPreimageOffset(val arch.Word) {
m.state.PreimageOffset = val m.state.PreimageOffset = val
} }
......
package testutil package testutil
import ( import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
) )
...@@ -14,8 +15,8 @@ func RandomThread(randSeed int64) *multithreaded.ThreadState { ...@@ -14,8 +15,8 @@ func RandomThread(randSeed int64) *multithreaded.ThreadState {
thread.Registers = *r.RandRegisters() thread.Registers = *r.RandRegisters()
thread.Cpu.PC = pc thread.Cpu.PC = pc
thread.Cpu.NextPC = pc + 4 thread.Cpu.NextPC = pc + 4
thread.Cpu.HI = r.Uint32() thread.Cpu.HI = r.Word()
thread.Cpu.LO = r.Uint32() thread.Cpu.LO = r.Word()
return thread return thread
} }
...@@ -37,7 +38,7 @@ func InitializeSingleThread(randSeed int, state *multithreaded.State, traverseRi ...@@ -37,7 +38,7 @@ func InitializeSingleThread(randSeed int, state *multithreaded.State, traverseRi
func SetupThreads(randomSeed int64, state *multithreaded.State, traverseRight bool, activeStackSize, otherStackSize int) { func SetupThreads(randomSeed int64, state *multithreaded.State, traverseRight bool, activeStackSize, otherStackSize int) {
var activeStack, otherStack []*multithreaded.ThreadState var activeStack, otherStack []*multithreaded.ThreadState
tid := uint32(0) tid := arch.Word(0)
for i := 0; i < activeStackSize; i++ { for i := 0; i < activeStackSize; i++ {
thread := RandomThread(randomSeed + int64(i)) thread := RandomThread(randomSeed + int64(i))
thread.ThreadId = tid thread.ThreadId = tid
...@@ -129,13 +130,13 @@ func FindNextThreadFiltered(state *multithreaded.State, filter ThreadFilter) *mu ...@@ -129,13 +130,13 @@ func FindNextThreadFiltered(state *multithreaded.State, filter ThreadFilter) *mu
return nil return nil
} }
func FindNextThreadExcluding(state *multithreaded.State, threadId uint32) *multithreaded.ThreadState { func FindNextThreadExcluding(state *multithreaded.State, threadId arch.Word) *multithreaded.ThreadState {
return FindNextThreadFiltered(state, func(t *multithreaded.ThreadState) bool { return FindNextThreadFiltered(state, func(t *multithreaded.ThreadState) bool {
return t.ThreadId != threadId return t.ThreadId != threadId
}) })
} }
func FindThread(state *multithreaded.State, threadId uint32) *multithreaded.ThreadState { func FindThread(state *multithreaded.State, threadId arch.Word) *multithreaded.ThreadState {
for _, t := range GetAllThreads(state) { for _, t := range GetAllThreads(state) {
if t.ThreadId == threadId { if t.ThreadId == threadId {
return t return t
......
...@@ -8,34 +8,47 @@ import ( ...@@ -8,34 +8,47 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
) )
// SERIALIZED_THREAD_SIZE is the size of a serialized ThreadState object const (
const SERIALIZED_THREAD_SIZE = 166 THREAD_ID_STATE_WITNESS_OFFSET = 0
THREAD_EXIT_CODE_WITNESS_OFFSET = THREAD_ID_STATE_WITNESS_OFFSET + arch.WordSizeBytes
// THREAD_WITNESS_SIZE is the size of a thread witness encoded in bytes. THREAD_EXITED_WITNESS_OFFSET = THREAD_EXIT_CODE_WITNESS_OFFSET + 1
// THREAD_FUTEX_ADDR_WITNESS_OFFSET = THREAD_EXITED_WITNESS_OFFSET + 1
// It consists of the active thread serialized and concatenated with the THREAD_FUTEX_VAL_WITNESS_OFFSET = THREAD_FUTEX_ADDR_WITNESS_OFFSET + arch.WordSizeBytes
// 32 byte hash onion of the active thread stack without the active thread THREAD_FUTEX_TIMEOUT_STEP_WITNESS_OFFSET = THREAD_FUTEX_VAL_WITNESS_OFFSET + arch.WordSizeBytes
const THREAD_WITNESS_SIZE = SERIALIZED_THREAD_SIZE + 32 THREAD_FUTEX_CPU_WITNESS_OFFSET = THREAD_FUTEX_TIMEOUT_STEP_WITNESS_OFFSET + 8
THREAD_REGISTERS_WITNESS_OFFSET = THREAD_FUTEX_CPU_WITNESS_OFFSET + (4 * arch.WordSizeBytes)
// SERIALIZED_THREAD_SIZE is the size of a serialized ThreadState object
// 166 and 322 bytes for 32 and 64-bit respectively
SERIALIZED_THREAD_SIZE = THREAD_REGISTERS_WITNESS_OFFSET + (32 * arch.WordSizeBytes)
// THREAD_WITNESS_SIZE is the size of a thread witness encoded in bytes.
//
// It consists of the active thread serialized and concatenated with the
// 32 byte hash onion of the active thread stack without the active thread
THREAD_WITNESS_SIZE = SERIALIZED_THREAD_SIZE + 32
)
// The empty thread root - keccak256(bytes32(0) ++ bytes32(0)) // The empty thread root - keccak256(bytes32(0) ++ bytes32(0))
var EmptyThreadsRoot common.Hash = common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5") var EmptyThreadsRoot common.Hash = common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
type ThreadState struct { type ThreadState struct {
ThreadId uint32 `json:"threadId"` ThreadId Word `json:"threadId"`
ExitCode uint8 `json:"exit"` ExitCode uint8 `json:"exit"`
Exited bool `json:"exited"` Exited bool `json:"exited"`
FutexAddr uint32 `json:"futexAddr"` FutexAddr Word `json:"futexAddr"`
FutexVal uint32 `json:"futexVal"` FutexVal Word `json:"futexVal"`
FutexTimeoutStep uint64 `json:"futexTimeoutStep"` FutexTimeoutStep uint64 `json:"futexTimeoutStep"`
Cpu mipsevm.CpuScalars `json:"cpu"` Cpu mipsevm.CpuScalars `json:"cpu"`
Registers [32]uint32 `json:"registers"` Registers [32]Word `json:"registers"`
} }
func CreateEmptyThread() *ThreadState { func CreateEmptyThread() *ThreadState {
initThreadId := uint32(0) initThreadId := Word(0)
return &ThreadState{ return &ThreadState{
ThreadId: initThreadId, ThreadId: initThreadId,
ExitCode: 0, ExitCode: 0,
...@@ -49,27 +62,27 @@ func CreateEmptyThread() *ThreadState { ...@@ -49,27 +62,27 @@ func CreateEmptyThread() *ThreadState {
FutexAddr: exec.FutexEmptyAddr, FutexAddr: exec.FutexEmptyAddr,
FutexVal: 0, FutexVal: 0,
FutexTimeoutStep: 0, FutexTimeoutStep: 0,
Registers: [32]uint32{}, Registers: [32]Word{},
} }
} }
func (t *ThreadState) serializeThread() []byte { func (t *ThreadState) serializeThread() []byte {
out := make([]byte, 0, SERIALIZED_THREAD_SIZE) out := make([]byte, 0, SERIALIZED_THREAD_SIZE)
out = binary.BigEndian.AppendUint32(out, t.ThreadId) out = arch.ByteOrderWord.AppendWord(out, t.ThreadId)
out = append(out, t.ExitCode) out = append(out, t.ExitCode)
out = mipsevm.AppendBoolToWitness(out, t.Exited) out = mipsevm.AppendBoolToWitness(out, t.Exited)
out = binary.BigEndian.AppendUint32(out, t.FutexAddr) out = arch.ByteOrderWord.AppendWord(out, t.FutexAddr)
out = binary.BigEndian.AppendUint32(out, t.FutexVal) out = arch.ByteOrderWord.AppendWord(out, t.FutexVal)
out = binary.BigEndian.AppendUint64(out, t.FutexTimeoutStep) out = binary.BigEndian.AppendUint64(out, t.FutexTimeoutStep)
out = binary.BigEndian.AppendUint32(out, t.Cpu.PC) out = arch.ByteOrderWord.AppendWord(out, t.Cpu.PC)
out = binary.BigEndian.AppendUint32(out, t.Cpu.NextPC) out = arch.ByteOrderWord.AppendWord(out, t.Cpu.NextPC)
out = binary.BigEndian.AppendUint32(out, t.Cpu.LO) out = arch.ByteOrderWord.AppendWord(out, t.Cpu.LO)
out = binary.BigEndian.AppendUint32(out, t.Cpu.HI) out = arch.ByteOrderWord.AppendWord(out, t.Cpu.HI)
for _, r := range t.Registers { for _, r := range t.Registers {
out = binary.BigEndian.AppendUint32(out, r) out = arch.ByteOrderWord.AppendWord(out, r)
} }
return out return out
...@@ -115,7 +128,7 @@ func (t *ThreadState) Deserialize(in io.Reader) error { ...@@ -115,7 +128,7 @@ func (t *ThreadState) Deserialize(in io.Reader) error {
if err := binary.Read(in, binary.BigEndian, &t.Cpu.HI); err != nil { if err := binary.Read(in, binary.BigEndian, &t.Cpu.HI); err != nil {
return err return err
} }
// Read the registers as big endian uint32s // Read the registers as big endian Words
for i := range t.Registers { for i := range t.Registers {
if err := binary.Read(in, binary.BigEndian, &t.Registers[i]); err != nil { if err := binary.Read(in, binary.BigEndian, &t.Registers[i]); err != nil {
return err return err
......
...@@ -7,19 +7,22 @@ import ( ...@@ -7,19 +7,22 @@ import (
"io" "io"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
) )
const ( const (
HEAP_START = 0x05_00_00_00 HEAP_START = arch.HeapStart
HEAP_END = 0x60_00_00_00 HEAP_END = arch.HeapEnd
PROGRAM_BREAK = 0x40_00_00_00 PROGRAM_BREAK = arch.ProgramBreak
) )
type CreateInitialFPVMState[T mipsevm.FPVMState] func(pc, heapStart uint32) T type Word = arch.Word
type CreateInitialFPVMState[T mipsevm.FPVMState] func(pc, heapStart Word) T
func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[T]) (T, error) { func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[T]) (T, error) {
var empty T var empty T
s := initState(uint32(f.Entry), HEAP_START) s := initState(Word(f.Entry), HEAP_START)
for i, prog := range f.Progs { for i, prog := range f.Progs {
if prog.Type == 0x70000003 { // MIPS_ABIFLAGS if prog.Type == 0x70000003 { // MIPS_ABIFLAGS
...@@ -39,13 +42,14 @@ func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[ ...@@ -39,13 +42,14 @@ func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[
} }
} }
// TODO(#12205)
if prog.Vaddr+prog.Memsz >= uint64(1<<32) { if prog.Vaddr+prog.Memsz >= uint64(1<<32) {
return empty, fmt.Errorf("program %d out of 32-bit mem range: %x - %x (size: %x)", i, prog.Vaddr, prog.Vaddr+prog.Memsz, prog.Memsz) return empty, fmt.Errorf("program %d out of 32-bit mem range: %x - %x (size: %x)", i, prog.Vaddr, prog.Vaddr+prog.Memsz, prog.Memsz)
} }
if prog.Vaddr+prog.Memsz >= HEAP_START { if prog.Vaddr+prog.Memsz >= HEAP_START {
return empty, fmt.Errorf("program %d overlaps with heap: %x - %x (size: %x). The heap start offset must be reconfigured", i, prog.Vaddr, prog.Vaddr+prog.Memsz, prog.Memsz) return empty, fmt.Errorf("program %d overlaps with heap: %x - %x (size: %x). The heap start offset must be reconfigured", i, prog.Vaddr, prog.Vaddr+prog.Memsz, prog.Memsz)
} }
if err := s.GetMemory().SetMemoryRange(uint32(prog.Vaddr), r); err != nil { if err := s.GetMemory().SetMemoryRange(Word(prog.Vaddr), r); err != nil {
return empty, fmt.Errorf("failed to read program segment %d: %w", i, err) return empty, fmt.Errorf("failed to read program segment %d: %w", i, err)
} }
} }
......
...@@ -10,8 +10,8 @@ import ( ...@@ -10,8 +10,8 @@ import (
type Symbol struct { type Symbol struct {
Name string `json:"name"` Name string `json:"name"`
Start uint32 `json:"start"` Start Word `json:"start"`
Size uint32 `json:"size"` Size Word `json:"size"`
} }
type Metadata struct { type Metadata struct {
...@@ -31,12 +31,12 @@ func MakeMetadata(elfProgram *elf.File) (*Metadata, error) { ...@@ -31,12 +31,12 @@ func MakeMetadata(elfProgram *elf.File) (*Metadata, error) {
}) })
out := &Metadata{Symbols: make([]Symbol, len(syms))} out := &Metadata{Symbols: make([]Symbol, len(syms))}
for i, s := range syms { for i, s := range syms {
out.Symbols[i] = Symbol{Name: s.Name, Start: uint32(s.Value), Size: uint32(s.Size)} out.Symbols[i] = Symbol{Name: s.Name, Start: Word(s.Value), Size: Word(s.Size)}
} }
return out, nil return out, nil
} }
func (m *Metadata) LookupSymbol(addr uint32) string { func (m *Metadata) LookupSymbol(addr Word) string {
if len(m.Symbols) == 0 { if len(m.Symbols) == 0 {
return "!unknown" return "!unknown"
} }
...@@ -59,12 +59,12 @@ func (m *Metadata) CreateSymbolMatcher(name string) mipsevm.SymbolMatcher { ...@@ -59,12 +59,12 @@ func (m *Metadata) CreateSymbolMatcher(name string) mipsevm.SymbolMatcher {
if s.Name == name { if s.Name == name {
start := s.Start start := s.Start
end := s.Start + s.Size end := s.Start + s.Size
return func(addr uint32) bool { return func(addr Word) bool {
return addr >= start && addr < end return addr >= start && addr < end
} }
} }
} }
return func(addr uint32) bool { return func(addr Word) bool {
return false return false
} }
} }
...@@ -3,14 +3,16 @@ package program ...@@ -3,14 +3,16 @@ package program
import ( import (
"bytes" "bytes"
"debug/elf" "debug/elf"
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
) )
const WordSizeBytes = arch.WordSizeBytes
// PatchGoGC patches out garbage-collection-related symbols to disable garbage collection // PatchGoGC patches out garbage-collection-related symbols to disable garbage collection
// and improves performance by patching out floating-point-related symbols // and improves performance by patching out floating-point-related symbols
func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error { func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error {
...@@ -39,10 +41,10 @@ func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error { ...@@ -39,10 +41,10 @@ func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error {
"flag.init", "flag.init",
// We need to patch this out, we don't pass float64nan because we don't support floats // We need to patch this out, we don't pass float64nan because we don't support floats
"runtime.check": "runtime.check":
// MIPS32 patch: ret (pseudo instruction) // MIPSx patch: ret (pseudo instruction)
// 03e00008 = jr $ra = ret (pseudo instruction) // 03e00008 = jr $ra = ret (pseudo instruction)
// 00000000 = nop (executes with delay-slot, but does nothing) // 00000000 = nop (executes with delay-slot, but does nothing)
if err := st.GetMemory().SetMemoryRange(uint32(s.Value), bytes.NewReader([]byte{ if err := st.GetMemory().SetMemoryRange(Word(s.Value), bytes.NewReader([]byte{
0x03, 0xe0, 0x00, 0x08, 0x03, 0xe0, 0x00, 0x08,
0, 0, 0, 0, 0, 0, 0, 0,
})); err != nil { })); err != nil {
...@@ -56,41 +58,54 @@ func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error { ...@@ -56,41 +58,54 @@ func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error {
// PatchStack sets up the program's initial stack frame and stack pointer // PatchStack sets up the program's initial stack frame and stack pointer
func PatchStack(st mipsevm.FPVMState) error { func PatchStack(st mipsevm.FPVMState) error {
// setup stack pointer // setup stack pointer
sp := uint32(0x7f_ff_d0_00) sp := Word(arch.HighMemoryStart)
// allocate 1 page for the initial stack data, and 16KB = 4 pages for the stack to grow // allocate 1 page for the initial stack data, and 16KB = 4 pages for the stack to grow
if err := st.GetMemory().SetMemoryRange(sp-4*memory.PageSize, bytes.NewReader(make([]byte, 5*memory.PageSize))); err != nil { if err := st.GetMemory().SetMemoryRange(sp-4*memory.PageSize, bytes.NewReader(make([]byte, 5*memory.PageSize))); err != nil {
return errors.New("failed to allocate page for stack content") return errors.New("failed to allocate page for stack content")
} }
st.GetRegistersRef()[29] = sp st.GetRegistersRef()[29] = sp
storeMem := func(addr uint32, v uint32) { storeMem := func(addr Word, v Word) {
var dat [4]byte var dat [WordSizeBytes]byte
binary.BigEndian.PutUint32(dat[:], v) arch.ByteOrderWord.PutWord(dat[:], v)
_ = st.GetMemory().SetMemoryRange(addr, bytes.NewReader(dat[:])) _ = st.GetMemory().SetMemoryRange(addr, bytes.NewReader(dat[:]))
} }
// init argc, argv, aux on stack auxv3Offset := sp + WordSizeBytes*10
storeMem(sp+4*0, 1) // argc = 1 (argument count) randomness := []byte("4;byfairdiceroll")
storeMem(sp+4*1, sp+4*21) // argv[0] randomness = pad(randomness)
storeMem(sp+4*2, 0) // argv[1] = terminating _ = st.GetMemory().SetMemoryRange(auxv3Offset, bytes.NewReader(randomness))
storeMem(sp+4*3, sp+4*14) // envp[0] = x (offset to first env var)
storeMem(sp+4*4, 0) // envp[1] = terminating
storeMem(sp+4*5, 6) // auxv[0] = _AT_PAGESZ = 6 (key)
storeMem(sp+4*6, 4096) // auxv[1] = page size of 4 KiB (value) - (== minPhysPageSize)
storeMem(sp+4*7, 25) // auxv[2] = AT_RANDOM
storeMem(sp+4*8, sp+4*10) // auxv[3] = address of 16 bytes containing random value
storeMem(sp+4*9, 0) // auxv[term] = 0
_ = st.GetMemory().SetMemoryRange(sp+4*10, bytes.NewReader([]byte("4;byfairdiceroll"))) // 16 bytes of "randomness" envp0Offset := auxv3Offset + Word(len(randomness))
envar := append([]byte("GODEBUG=memprofilerate=0"), 0x0)
envar = pad(envar)
_ = st.GetMemory().SetMemoryRange(envp0Offset, bytes.NewReader(envar))
// append 4 extra zero bytes to end at 4-byte alignment argv0Offset := envp0Offset + Word(len(envar))
envar := append([]byte("GODEBUG=memprofilerate=0"), 0x0, 0x0, 0x0, 0x0) programName := append([]byte("op-program"), 0x0)
_ = st.GetMemory().SetMemoryRange(sp+4*14, bytes.NewReader(envar)) programName = pad(programName)
_ = st.GetMemory().SetMemoryRange(argv0Offset, bytes.NewReader(programName))
// 24 bytes for GODEBUG=memprofilerate=0 + 4 null bytes // init argc, argv, aux on stack
// Then append program name + 2 null bytes for 4-byte alignment storeMem(sp+WordSizeBytes*0, 1) // argc = 1 (argument count)
programName := append([]byte("op-program"), 0x0, 0x0) storeMem(sp+WordSizeBytes*1, argv0Offset) // argv[0]
_ = st.GetMemory().SetMemoryRange(sp+4*21, bytes.NewReader(programName)) storeMem(sp+WordSizeBytes*2, 0) // argv[1] = terminating
storeMem(sp+WordSizeBytes*3, envp0Offset) // envp[0] = x (offset to first env var)
storeMem(sp+WordSizeBytes*4, 0) // envp[1] = terminating
storeMem(sp+WordSizeBytes*5, 6) // auxv[0] = _AT_PAGESZ = 6 (key)
storeMem(sp+WordSizeBytes*6, 4096) // auxv[1] = page size of 4 KiB (value) - (== minPhysPageSize)
storeMem(sp+WordSizeBytes*7, 25) // auxv[2] = AT_RANDOM
storeMem(sp+WordSizeBytes*8, auxv3Offset) // auxv[3] = address of 16 bytes containing random value
storeMem(sp+WordSizeBytes*9, 0) // auxv[term] = 0
return nil return nil
} }
// pad adds appropriate padding to buf to end at Word alignment
func pad(buf []byte) []byte {
if len(buf)%WordSizeBytes == 0 {
return buf
}
bytesToAlignment := WordSizeBytes - len(buf)%WordSizeBytes
return append(buf, make([]byte, bytesToAlignment)...)
}
...@@ -28,7 +28,7 @@ var _ mipsevm.FPVM = (*InstrumentedState)(nil) ...@@ -28,7 +28,7 @@ var _ mipsevm.FPVM = (*InstrumentedState)(nil)
func NewInstrumentedState(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta mipsevm.Metadata) *InstrumentedState { func NewInstrumentedState(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta mipsevm.Metadata) *InstrumentedState {
var sleepCheck mipsevm.SymbolMatcher var sleepCheck mipsevm.SymbolMatcher
if meta == nil { if meta == nil {
sleepCheck = func(addr uint32) bool { return false } sleepCheck = func(addr Word) bool { return false }
} else { } else {
sleepCheck = meta.CreateSymbolMatcher("runtime.notesleep") sleepCheck = meta.CreateSymbolMatcher("runtime.notesleep")
} }
...@@ -75,7 +75,7 @@ func (m *InstrumentedState) Step(proof bool) (wit *mipsevm.StepWitness, err erro ...@@ -75,7 +75,7 @@ func (m *InstrumentedState) Step(proof bool) (wit *mipsevm.StepWitness, err erro
memProof := m.memoryTracker.MemProof() memProof := m.memoryTracker.MemProof()
wit.ProofData = append(wit.ProofData, memProof[:]...) wit.ProofData = append(wit.ProofData, memProof[:]...)
lastPreimageKey, lastPreimage, lastPreimageOffset := m.preimageOracle.LastPreimage() lastPreimageKey, lastPreimage, lastPreimageOffset := m.preimageOracle.LastPreimage()
if lastPreimageOffset != ^uint32(0) { if lastPreimageOffset != ^Word(0) {
wit.PreimageOffset = lastPreimageOffset wit.PreimageOffset = lastPreimageOffset
wit.PreimageKey = lastPreimageKey wit.PreimageKey = lastPreimageKey
wit.PreimageValue = lastPreimage wit.PreimageValue = lastPreimage
...@@ -88,7 +88,7 @@ func (m *InstrumentedState) CheckInfiniteLoop() bool { ...@@ -88,7 +88,7 @@ func (m *InstrumentedState) CheckInfiniteLoop() bool {
return m.sleepCheck(m.state.GetPC()) return m.sleepCheck(m.state.GetPC())
} }
func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, uint32) { func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, Word) {
return m.preimageOracle.LastPreimage() return m.preimageOracle.LastPreimage()
} }
...@@ -109,7 +109,7 @@ func (m *InstrumentedState) Traceback() { ...@@ -109,7 +109,7 @@ func (m *InstrumentedState) Traceback() {
m.stackTracker.Traceback() m.stackTracker.Traceback()
} }
func (m *InstrumentedState) LookupSymbol(addr uint32) string { func (m *InstrumentedState) LookupSymbol(addr Word) string {
if m.meta == nil { if m.meta == nil {
return "" return ""
} }
......
...@@ -6,24 +6,26 @@ import ( ...@@ -6,24 +6,26 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
type Word = arch.Word
func (m *InstrumentedState) handleSyscall() error { func (m *InstrumentedState) handleSyscall() error {
syscallNum, a0, a1, a2, _ := exec.GetSyscallArgs(&m.state.Registers) syscallNum, a0, a1, a2, _ := exec.GetSyscallArgs(&m.state.Registers)
v0 := uint32(0) v0 := Word(0)
v1 := uint32(0) v1 := Word(0)
//fmt.Printf("syscall: %d\n", syscallNum) //fmt.Printf("syscall: %d\n", syscallNum)
switch syscallNum { switch syscallNum {
case exec.SysMmap: case exec.SysMmap:
var newHeap uint32 var newHeap Word
v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap)
m.state.Heap = newHeap m.state.Heap = newHeap
case exec.SysBrk: case exec.SysBrk:
v0 = program.PROGRAM_BREAK v0 = arch.ProgramBreak
case exec.SysClone: // clone (not supported) case exec.SysClone: // clone (not supported)
v0 = 1 v0 = 1
case exec.SysExitGroup: case exec.SysExitGroup:
...@@ -31,13 +33,13 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -31,13 +33,13 @@ func (m *InstrumentedState) handleSyscall() error {
m.state.ExitCode = uint8(a0) m.state.ExitCode = uint8(a0)
return nil return nil
case exec.SysRead: case exec.SysRead:
var newPreimageOffset uint32 var newPreimageOffset Word
v0, v1, newPreimageOffset, _, _ = exec.HandleSysRead(a0, a1, a2, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker) v0, v1, newPreimageOffset, _, _ = exec.HandleSysRead(a0, a1, a2, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker)
m.state.PreimageOffset = newPreimageOffset m.state.PreimageOffset = newPreimageOffset
case exec.SysWrite: case exec.SysWrite:
var newLastHint hexutil.Bytes var newLastHint hexutil.Bytes
var newPreimageKey common.Hash var newPreimageKey common.Hash
var newPreimageOffset uint32 var newPreimageOffset Word
v0, v1, newLastHint, newPreimageKey, newPreimageOffset = exec.HandleSysWrite(a0, a1, a2, m.state.LastHint, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker, m.stdOut, m.stdErr) v0, v1, newLastHint, newPreimageKey, newPreimageOffset = exec.HandleSysWrite(a0, a1, a2, m.state.LastHint, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker, m.stdOut, m.stdErr)
m.state.LastHint = newLastHint m.state.LastHint = newLastHint
m.state.PreimageKey = newPreimageKey m.state.PreimageKey = newPreimageKey
...@@ -78,19 +80,19 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -78,19 +80,19 @@ func (m *InstrumentedState) mipsStep() error {
func (m *InstrumentedState) handleRMWOps(insn, opcode uint32) error { func (m *InstrumentedState) handleRMWOps(insn, opcode uint32) error {
baseReg := (insn >> 21) & 0x1F baseReg := (insn >> 21) & 0x1F
base := m.state.Registers[baseReg] base := m.state.Registers[baseReg]
rtReg := (insn >> 16) & 0x1F rtReg := Word((insn >> 16) & 0x1F)
offset := exec.SignExtendImmediate(insn) offset := exec.SignExtendImmediate(insn)
effAddr := (base + offset) & 0xFFFFFFFC effAddr := (base + offset) & arch.AddressMask
m.memoryTracker.TrackMemAccess(effAddr) m.memoryTracker.TrackMemAccess(effAddr)
mem := m.state.Memory.GetMemory(effAddr) mem := m.state.Memory.GetWord(effAddr)
var retVal uint32 var retVal Word
if opcode == exec.OpLoadLinked { if opcode == exec.OpLoadLinked {
retVal = mem retVal = mem
} else if opcode == exec.OpStoreConditional { } else if opcode == exec.OpStoreConditional {
rt := m.state.Registers[rtReg] rt := m.state.Registers[rtReg]
m.state.Memory.SetMemory(effAddr, rt) m.state.Memory.SetWord(effAddr, rt)
retVal = 1 // 1 for success retVal = 1 // 1 for success
} else { } else {
panic(fmt.Sprintf("Invalid instruction passed to handleRMWOps (opcode %08x)", opcode)) panic(fmt.Sprintf("Invalid instruction passed to handleRMWOps (opcode %08x)", opcode))
......
...@@ -13,28 +13,30 @@ import ( ...@@ -13,28 +13,30 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
) )
// STATE_WITNESS_SIZE is the size of the state witness encoding in bytes. // STATE_WITNESS_SIZE is the size of the state witness encoding in bytes.
// ignoring 64-bit STATE_WITNESS_SIZE as it's not supported for singlethreaded
const STATE_WITNESS_SIZE = 226 const STATE_WITNESS_SIZE = 226
type State struct { type State struct {
Memory *memory.Memory `json:"memory"` Memory *memory.Memory `json:"memory"`
PreimageKey common.Hash `json:"preimageKey"` PreimageKey common.Hash `json:"preimageKey"`
PreimageOffset uint32 `json:"preimageOffset"` // note that the offset includes the 8-byte length prefix PreimageOffset Word `json:"preimageOffset"` // note that the offset includes the 8-byte length prefix
Cpu mipsevm.CpuScalars `json:"cpu"` Cpu mipsevm.CpuScalars `json:"cpu"`
Heap uint32 `json:"heap"` // to handle mmap growth Heap Word `json:"heap"` // to handle mmap growth
ExitCode uint8 `json:"exit"` ExitCode uint8 `json:"exit"`
Exited bool `json:"exited"` Exited bool `json:"exited"`
Step uint64 `json:"step"` Step uint64 `json:"step"`
Registers [32]uint32 `json:"registers"` Registers [32]Word `json:"registers"`
// LastHint is optional metadata, and not part of the VM state itself. // LastHint is optional metadata, and not part of the VM state itself.
LastHint hexutil.Bytes `json:"lastHint,omitempty"` LastHint hexutil.Bytes `json:"lastHint,omitempty"`
...@@ -51,7 +53,7 @@ func CreateEmptyState() *State { ...@@ -51,7 +53,7 @@ func CreateEmptyState() *State {
HI: 0, HI: 0,
}, },
Heap: 0, Heap: 0,
Registers: [32]uint32{}, Registers: [32]Word{},
Memory: memory.NewMemory(), Memory: memory.NewMemory(),
ExitCode: 0, ExitCode: 0,
Exited: false, Exited: false,
...@@ -59,7 +61,7 @@ func CreateEmptyState() *State { ...@@ -59,7 +61,7 @@ func CreateEmptyState() *State {
} }
} }
func CreateInitialState(pc, heapStart uint32) *State { func CreateInitialState(pc, heapStart Word) *State {
state := CreateEmptyState() state := CreateEmptyState()
state.Cpu.PC = pc state.Cpu.PC = pc
state.Cpu.NextPC = pc + 4 state.Cpu.NextPC = pc + 4
...@@ -75,16 +77,16 @@ func (s *State) CreateVM(logger log.Logger, po mipsevm.PreimageOracle, stdOut, s ...@@ -75,16 +77,16 @@ func (s *State) CreateVM(logger log.Logger, po mipsevm.PreimageOracle, stdOut, s
type stateMarshaling struct { type stateMarshaling struct {
Memory *memory.Memory `json:"memory"` Memory *memory.Memory `json:"memory"`
PreimageKey common.Hash `json:"preimageKey"` PreimageKey common.Hash `json:"preimageKey"`
PreimageOffset uint32 `json:"preimageOffset"` PreimageOffset Word `json:"preimageOffset"`
PC uint32 `json:"pc"` PC Word `json:"pc"`
NextPC uint32 `json:"nextPC"` NextPC Word `json:"nextPC"`
LO uint32 `json:"lo"` LO Word `json:"lo"`
HI uint32 `json:"hi"` HI Word `json:"hi"`
Heap uint32 `json:"heap"` Heap Word `json:"heap"`
ExitCode uint8 `json:"exit"` ExitCode uint8 `json:"exit"`
Exited bool `json:"exited"` Exited bool `json:"exited"`
Step uint64 `json:"step"` Step uint64 `json:"step"`
Registers [32]uint32 `json:"registers"` Registers [32]Word `json:"registers"`
LastHint hexutil.Bytes `json:"lastHint,omitempty"` LastHint hexutil.Bytes `json:"lastHint,omitempty"`
} }
...@@ -128,11 +130,11 @@ func (s *State) UnmarshalJSON(data []byte) error { ...@@ -128,11 +130,11 @@ func (s *State) UnmarshalJSON(data []byte) error {
return nil return nil
} }
func (s *State) GetPC() uint32 { return s.Cpu.PC } func (s *State) GetPC() Word { return s.Cpu.PC }
func (s *State) GetCpu() mipsevm.CpuScalars { return s.Cpu } func (s *State) GetCpu() mipsevm.CpuScalars { return s.Cpu }
func (s *State) GetRegistersRef() *[32]uint32 { return &s.Registers } func (s *State) GetRegistersRef() *[32]Word { return &s.Registers }
func (s *State) GetExitCode() uint8 { return s.ExitCode } func (s *State) GetExitCode() uint8 { return s.ExitCode }
...@@ -152,7 +154,7 @@ func (s *State) GetMemory() *memory.Memory { ...@@ -152,7 +154,7 @@ func (s *State) GetMemory() *memory.Memory {
return s.Memory return s.Memory
} }
func (s *State) GetHeap() uint32 { func (s *State) GetHeap() Word {
return s.Heap return s.Heap
} }
...@@ -160,7 +162,7 @@ func (s *State) GetPreimageKey() common.Hash { ...@@ -160,7 +162,7 @@ func (s *State) GetPreimageKey() common.Hash {
return s.PreimageKey return s.PreimageKey
} }
func (s *State) GetPreimageOffset() uint32 { func (s *State) GetPreimageOffset() Word {
return s.PreimageOffset return s.PreimageOffset
} }
...@@ -169,17 +171,17 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) { ...@@ -169,17 +171,17 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) {
memRoot := s.Memory.MerkleRoot() memRoot := s.Memory.MerkleRoot()
out = append(out, memRoot[:]...) out = append(out, memRoot[:]...)
out = append(out, s.PreimageKey[:]...) out = append(out, s.PreimageKey[:]...)
out = binary.BigEndian.AppendUint32(out, s.PreimageOffset) out = arch.ByteOrderWord.AppendWord(out, s.PreimageOffset)
out = binary.BigEndian.AppendUint32(out, s.Cpu.PC) out = arch.ByteOrderWord.AppendWord(out, s.Cpu.PC)
out = binary.BigEndian.AppendUint32(out, s.Cpu.NextPC) out = arch.ByteOrderWord.AppendWord(out, s.Cpu.NextPC)
out = binary.BigEndian.AppendUint32(out, s.Cpu.LO) out = arch.ByteOrderWord.AppendWord(out, s.Cpu.LO)
out = binary.BigEndian.AppendUint32(out, s.Cpu.HI) out = arch.ByteOrderWord.AppendWord(out, s.Cpu.HI)
out = binary.BigEndian.AppendUint32(out, s.Heap) out = arch.ByteOrderWord.AppendWord(out, s.Heap)
out = append(out, s.ExitCode) out = append(out, s.ExitCode)
out = mipsevm.AppendBoolToWitness(out, s.Exited) out = mipsevm.AppendBoolToWitness(out, s.Exited)
out = binary.BigEndian.AppendUint64(out, s.Step) out = binary.BigEndian.AppendUint64(out, s.Step)
for _, r := range s.Registers { for _, r := range s.Registers {
out = binary.BigEndian.AppendUint32(out, r) out = arch.ByteOrderWord.AppendWord(out, r)
} }
return out, stateHashFromWitness(out) return out, stateHashFromWitness(out)
} }
...@@ -191,17 +193,17 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) { ...@@ -191,17 +193,17 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) {
// StateVersion uint8(0) // StateVersion uint8(0)
// Memory As per Memory.Serialize // Memory As per Memory.Serialize
// PreimageKey [32]byte // PreimageKey [32]byte
// PreimageOffset uint32 // PreimageOffset Word
// Cpu.PC uint32 // Cpu.PC Word
// Cpu.NextPC uint32 // Cpu.NextPC Word
// Cpu.LO uint32 // Cpu.LO Word
// Cpu.HI uint32 // Cpu.HI Word
// Heap uint32 // Heap Word
// ExitCode uint8 // ExitCode uint8
// Exited uint8 - 0 for false, 1 for true // Exited uint8 - 0 for false, 1 for true
// Step uint64 // Step uint64
// Registers [32]uint32 // Registers [32]Word
// len(LastHint) uint32 (0 when LastHint is nil) // len(LastHint) Word (0 when LastHint is nil)
// LastHint []byte // LastHint []byte
func (s *State) Serialize(out io.Writer) error { func (s *State) Serialize(out io.Writer) error {
bout := serialize.NewBinaryWriter(out) bout := serialize.NewBinaryWriter(out)
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
...@@ -128,7 +129,7 @@ func TestSerializeStateRoundTrip(t *testing.T) { ...@@ -128,7 +129,7 @@ func TestSerializeStateRoundTrip(t *testing.T) {
ExitCode: 1, ExitCode: 1,
Exited: true, Exited: true,
Step: 0xdeadbeef, Step: 0xdeadbeef,
Registers: [32]uint32{ Registers: [32]arch.Word{
0xdeadbeef, 0xdeadbeef,
0xdeadbeef, 0xdeadbeef,
0xc0ffee, 0xc0ffee,
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
) )
...@@ -19,12 +20,12 @@ func (m *StateMutatorSingleThreaded) Randomize(randSeed int64) { ...@@ -19,12 +20,12 @@ func (m *StateMutatorSingleThreaded) Randomize(randSeed int64) {
step := r.RandStep() step := r.RandStep()
m.state.PreimageKey = r.RandHash() m.state.PreimageKey = r.RandHash()
m.state.PreimageOffset = r.Uint32() m.state.PreimageOffset = r.Word()
m.state.Cpu.PC = pc m.state.Cpu.PC = pc
m.state.Cpu.NextPC = pc + 4 m.state.Cpu.NextPC = pc + 4
m.state.Cpu.HI = r.Uint32() m.state.Cpu.HI = r.Word()
m.state.Cpu.LO = r.Uint32() m.state.Cpu.LO = r.Word()
m.state.Heap = r.Uint32() m.state.Heap = r.Word()
m.state.Step = step m.state.Step = step
m.state.LastHint = r.RandHint() m.state.LastHint = r.RandHint()
m.state.Registers = *r.RandRegisters() m.state.Registers = *r.RandRegisters()
...@@ -36,23 +37,23 @@ func NewStateMutatorSingleThreaded(state *singlethreaded.State) testutil.StateMu ...@@ -36,23 +37,23 @@ func NewStateMutatorSingleThreaded(state *singlethreaded.State) testutil.StateMu
return &StateMutatorSingleThreaded{state: state} return &StateMutatorSingleThreaded{state: state}
} }
func (m *StateMutatorSingleThreaded) SetPC(val uint32) { func (m *StateMutatorSingleThreaded) SetPC(val arch.Word) {
m.state.Cpu.PC = val m.state.Cpu.PC = val
} }
func (m *StateMutatorSingleThreaded) SetNextPC(val uint32) { func (m *StateMutatorSingleThreaded) SetNextPC(val arch.Word) {
m.state.Cpu.NextPC = val m.state.Cpu.NextPC = val
} }
func (m *StateMutatorSingleThreaded) SetHI(val uint32) { func (m *StateMutatorSingleThreaded) SetHI(val arch.Word) {
m.state.Cpu.HI = val m.state.Cpu.HI = val
} }
func (m *StateMutatorSingleThreaded) SetLO(val uint32) { func (m *StateMutatorSingleThreaded) SetLO(val arch.Word) {
m.state.Cpu.LO = val m.state.Cpu.LO = val
} }
func (m *StateMutatorSingleThreaded) SetHeap(val uint32) { func (m *StateMutatorSingleThreaded) SetHeap(val arch.Word) {
m.state.Heap = val m.state.Heap = val
} }
...@@ -72,7 +73,7 @@ func (m *StateMutatorSingleThreaded) SetPreimageKey(val common.Hash) { ...@@ -72,7 +73,7 @@ func (m *StateMutatorSingleThreaded) SetPreimageKey(val common.Hash) {
m.state.PreimageKey = val m.state.PreimageKey = val
} }
func (m *StateMutatorSingleThreaded) SetPreimageOffset(val uint32) { func (m *StateMutatorSingleThreaded) SetPreimageOffset(val arch.Word) {
m.state.PreimageOffset = val m.state.PreimageOffset = val
} }
......
package mipsevm package mipsevm
import "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
type CpuScalars struct { type CpuScalars struct {
PC uint32 `json:"pc"` PC arch.Word `json:"pc"`
NextPC uint32 `json:"nextPC"` NextPC arch.Word `json:"nextPC"`
LO uint32 `json:"lo"` LO arch.Word `json:"lo"`
HI uint32 `json:"hi"` HI arch.Word `json:"hi"`
} }
const ( const (
......
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
...@@ -98,13 +99,13 @@ func TestEVM(t *testing.T) { ...@@ -98,13 +99,13 @@ func TestEVM(t *testing.T) {
"mipsevm produced different state than EVM at step %d", state.GetStep()) "mipsevm produced different state than EVM at step %d", state.GetStep())
} }
if exitGroup { if exitGroup {
require.NotEqual(t, uint32(testutil.EndAddr), goVm.GetState().GetPC(), "must not reach end") require.NotEqual(t, arch.Word(testutil.EndAddr), goVm.GetState().GetPC(), "must not reach end")
require.True(t, goVm.GetState().GetExited(), "must set exited state") require.True(t, goVm.GetState().GetExited(), "must set exited state")
require.Equal(t, uint8(1), goVm.GetState().GetExitCode(), "must exit with 1") require.Equal(t, uint8(1), goVm.GetState().GetExitCode(), "must exit with 1")
} else if expectPanic { } else if expectPanic {
require.NotEqual(t, uint32(testutil.EndAddr), state.GetPC(), "must not reach end") require.NotEqual(t, arch.Word(testutil.EndAddr), state.GetPC(), "must not reach end")
} else { } else {
require.Equal(t, uint32(testutil.EndAddr), state.GetPC(), "must reach end") require.Equal(t, arch.Word(testutil.EndAddr), state.GetPC(), "must reach end")
// inspect test result // inspect test result
done, result := state.GetMemory().GetMemory(testutil.BaseAddrEnd+4), state.GetMemory().GetMemory(testutil.BaseAddrEnd+8) done, result := state.GetMemory().GetMemory(testutil.BaseAddrEnd+4), state.GetMemory().GetMemory(testutil.BaseAddrEnd+8)
require.Equal(t, done, uint32(1), "must be done") require.Equal(t, done, uint32(1), "must be done")
...@@ -121,10 +122,10 @@ func TestEVMSingleStep_Jump(t *testing.T) { ...@@ -121,10 +122,10 @@ func TestEVMSingleStep_Jump(t *testing.T) {
versions := GetMipsVersionTestCases(t) versions := GetMipsVersionTestCases(t)
cases := []struct { cases := []struct {
name string name string
pc uint32 pc arch.Word
nextPC uint32 nextPC arch.Word
insn uint32 insn uint32
expectNextPC uint32 expectNextPC arch.Word
expectLink bool expectLink bool
}{ }{
{name: "j MSB set target", pc: 0, nextPC: 4, insn: 0x0A_00_00_02, expectNextPC: 0x08_00_00_08}, // j 0x02_00_00_02 {name: "j MSB set target", pc: 0, nextPC: 4, insn: 0x0A_00_00_02, expectNextPC: 0x08_00_00_08}, // j 0x02_00_00_02
...@@ -169,29 +170,29 @@ func TestEVMSingleStep_Operators(t *testing.T) { ...@@ -169,29 +170,29 @@ func TestEVMSingleStep_Operators(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
isImm bool isImm bool
rs uint32 rs Word
rt uint32 rt Word
imm uint16 imm uint16
funct uint32 funct uint32
opcode uint32 opcode uint32
expectRes uint32 expectRes Word
}{ }{
{name: "add", funct: 0x20, isImm: false, rs: uint32(12), rt: uint32(20), expectRes: uint32(32)}, // add t0, s1, s2 {name: "add", funct: 0x20, isImm: false, rs: Word(12), rt: Word(20), expectRes: Word(32)}, // add t0, s1, s2
{name: "addu", funct: 0x21, isImm: false, rs: uint32(12), rt: uint32(20), expectRes: uint32(32)}, // addu t0, s1, s2 {name: "addu", funct: 0x21, isImm: false, rs: Word(12), rt: Word(20), expectRes: Word(32)}, // addu t0, s1, s2
{name: "addi", opcode: 0x8, isImm: true, rs: uint32(4), rt: uint32(1), imm: uint16(40), expectRes: uint32(44)}, // addi t0, s1, 40 {name: "addi", opcode: 0x8, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // addi t0, s1, 40
{name: "addi sign", opcode: 0x8, isImm: true, rs: uint32(2), rt: uint32(1), imm: uint16(0xfffe), expectRes: uint32(0)}, // addi t0, s1, -2 {name: "addi sign", opcode: 0x8, isImm: true, rs: Word(2), rt: Word(1), imm: uint16(0xfffe), expectRes: Word(0)}, // addi t0, s1, -2
{name: "addiu", opcode: 0x9, isImm: true, rs: uint32(4), rt: uint32(1), imm: uint16(40), expectRes: uint32(44)}, // addiu t0, s1, 40 {name: "addiu", opcode: 0x9, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // addiu t0, s1, 40
{name: "sub", funct: 0x22, isImm: false, rs: uint32(20), rt: uint32(12), expectRes: uint32(8)}, // sub t0, s1, s2 {name: "sub", funct: 0x22, isImm: false, rs: Word(20), rt: Word(12), expectRes: Word(8)}, // sub t0, s1, s2
{name: "subu", funct: 0x23, isImm: false, rs: uint32(20), rt: uint32(12), expectRes: uint32(8)}, // subu t0, s1, s2 {name: "subu", funct: 0x23, isImm: false, rs: Word(20), rt: Word(12), expectRes: Word(8)}, // subu t0, s1, s2
{name: "and", funct: 0x24, isImm: false, rs: uint32(1200), rt: uint32(490), expectRes: uint32(160)}, // and t0, s1, s2 {name: "and", funct: 0x24, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(160)}, // and t0, s1, s2
{name: "andi", opcode: 0xc, isImm: true, rs: uint32(4), rt: uint32(1), imm: uint16(40), expectRes: uint32(0)}, // andi t0, s1, 40 {name: "andi", opcode: 0xc, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(0)}, // andi t0, s1, 40
{name: "or", funct: 0x25, isImm: false, rs: uint32(1200), rt: uint32(490), expectRes: uint32(1530)}, // or t0, s1, s2 {name: "or", funct: 0x25, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1530)}, // or t0, s1, s2
{name: "ori", opcode: 0xd, isImm: true, rs: uint32(4), rt: uint32(1), imm: uint16(40), expectRes: uint32(44)}, // ori t0, s1, 40 {name: "ori", opcode: 0xd, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // ori t0, s1, 40
{name: "xor", funct: 0x26, isImm: false, rs: uint32(1200), rt: uint32(490), expectRes: uint32(1370)}, // xor t0, s1, s2 {name: "xor", funct: 0x26, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1370)}, // xor t0, s1, s2
{name: "xori", opcode: 0xe, isImm: true, rs: uint32(4), rt: uint32(1), imm: uint16(40), expectRes: uint32(44)}, // xori t0, s1, 40 {name: "xori", opcode: 0xe, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // xori t0, s1, 40
{name: "nor", funct: 0x27, isImm: false, rs: uint32(1200), rt: uint32(490), expectRes: uint32(4294965765)}, // nor t0, s1, s2 {name: "nor", funct: 0x27, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(4294965765)}, // nor t0, s1, s2
{name: "slt", funct: 0x2a, isImm: false, rs: 0xFF_FF_FF_FE, rt: uint32(5), expectRes: uint32(1)}, // slt t0, s1, s2 {name: "slt", funct: 0x2a, isImm: false, rs: 0xFF_FF_FF_FE, rt: Word(5), expectRes: Word(1)}, // slt t0, s1, s2
{name: "sltu", funct: 0x2b, isImm: false, rs: uint32(1200), rt: uint32(490), expectRes: uint32(0)}, // sltu t0, s1, s2 {name: "sltu", funct: 0x2b, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(0)}, // sltu t0, s1, s2
} }
for _, v := range versions { for _, v := range versions {
...@@ -237,17 +238,17 @@ func TestEVM_MMap(t *testing.T) { ...@@ -237,17 +238,17 @@ func TestEVM_MMap(t *testing.T) {
versions := GetMipsVersionTestCases(t) versions := GetMipsVersionTestCases(t)
cases := []struct { cases := []struct {
name string name string
heap uint32 heap arch.Word
address uint32 address arch.Word
size uint32 size arch.Word
shouldFail bool shouldFail bool
expectedHeap uint32 expectedHeap arch.Word
}{ }{
{name: "Increment heap by max value", heap: program.HEAP_START, address: 0, size: ^uint32(0), shouldFail: true}, {name: "Increment heap by max value", heap: program.HEAP_START, address: 0, size: ^arch.Word(0), shouldFail: true},
{name: "Increment heap to 0", heap: program.HEAP_START, address: 0, size: ^uint32(0) - program.HEAP_START + 1, shouldFail: true}, {name: "Increment heap to 0", heap: program.HEAP_START, address: 0, size: ^arch.Word(0) - program.HEAP_START + 1, shouldFail: true},
{name: "Increment heap to previous page", heap: program.HEAP_START, address: 0, size: ^uint32(0) - program.HEAP_START - memory.PageSize + 1, shouldFail: true}, {name: "Increment heap to previous page", heap: program.HEAP_START, address: 0, size: ^arch.Word(0) - program.HEAP_START - memory.PageSize + 1, shouldFail: true},
{name: "Increment max page size", heap: program.HEAP_START, address: 0, size: ^uint32(0) & ^uint32(memory.PageAddrMask), shouldFail: true}, {name: "Increment max page size", heap: program.HEAP_START, address: 0, size: ^arch.Word(0) & ^arch.Word(memory.PageAddrMask), shouldFail: true},
{name: "Increment max page size from 0", heap: 0, address: 0, size: ^uint32(0) & ^uint32(memory.PageAddrMask), shouldFail: true}, {name: "Increment max page size from 0", heap: 0, address: 0, size: ^arch.Word(0) & ^arch.Word(memory.PageAddrMask), shouldFail: true},
{name: "Increment heap at limit", heap: program.HEAP_END, address: 0, size: 1, shouldFail: true}, {name: "Increment heap at limit", heap: program.HEAP_END, address: 0, size: 1, shouldFail: true},
{name: "Increment heap to limit", heap: program.HEAP_END - memory.PageSize, address: 0, size: 1, shouldFail: false, expectedHeap: program.HEAP_END}, {name: "Increment heap to limit", heap: program.HEAP_END - memory.PageSize, address: 0, size: 1, shouldFail: false, expectedHeap: program.HEAP_END},
{name: "Increment heap within limit", heap: program.HEAP_END - 2*memory.PageSize, address: 0, size: 1, shouldFail: false, expectedHeap: program.HEAP_END - memory.PageSize}, {name: "Increment heap within limit", heap: program.HEAP_END - 2*memory.PageSize, address: 0, size: 1, shouldFail: false, expectedHeap: program.HEAP_END - memory.PageSize},
...@@ -464,10 +465,10 @@ func TestEVMSysWriteHint(t *testing.T) { ...@@ -464,10 +465,10 @@ func TestEVMSysWriteHint(t *testing.T) {
state := goVm.GetState() state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysWrite state.GetRegistersRef()[2] = exec.SysWrite
state.GetRegistersRef()[4] = exec.FdHintWrite state.GetRegistersRef()[4] = exec.FdHintWrite
state.GetRegistersRef()[5] = uint32(tt.memOffset) state.GetRegistersRef()[5] = arch.Word(tt.memOffset)
state.GetRegistersRef()[6] = uint32(tt.bytesToWrite) state.GetRegistersRef()[6] = arch.Word(tt.bytesToWrite)
err := state.GetMemory().SetMemoryRange(uint32(tt.memOffset), bytes.NewReader(tt.hintData)) err := state.GetMemory().SetMemoryRange(arch.Word(tt.memOffset), bytes.NewReader(tt.hintData))
require.NoError(t, err) require.NoError(t, err)
state.GetMemory().SetMemory(state.GetPC(), insn) state.GetMemory().SetMemory(state.GetPC(), insn)
step := state.GetStep() step := state.GetStep()
...@@ -477,8 +478,8 @@ func TestEVMSysWriteHint(t *testing.T) { ...@@ -477,8 +478,8 @@ func TestEVMSysWriteHint(t *testing.T) {
expected.PC = state.GetCpu().NextPC expected.PC = state.GetCpu().NextPC
expected.NextPC = state.GetCpu().NextPC + 4 expected.NextPC = state.GetCpu().NextPC + 4
expected.LastHint = tt.expectedLastHint expected.LastHint = tt.expectedLastHint
expected.Registers[2] = uint32(tt.bytesToWrite) // Return count of bytes written expected.Registers[2] = arch.Word(tt.bytesToWrite) // Return count of bytes written
expected.Registers[7] = 0 // no Error expected.Registers[7] = 0 // no Error
stepWitness, err := goVm.Step(true) stepWitness, err := goVm.Step(true)
require.NoError(t, err) require.NoError(t, err)
...@@ -497,7 +498,7 @@ func TestEVMFault(t *testing.T) { ...@@ -497,7 +498,7 @@ func TestEVMFault(t *testing.T) {
versions := GetMipsVersionTestCases(t) versions := GetMipsVersionTestCases(t)
cases := []struct { cases := []struct {
name string name string
nextPC uint32 nextPC arch.Word
insn uint32 insn uint32
}{ }{
{"illegal instruction", 0, 0xFF_FF_FF_FF}, {"illegal instruction", 0, 0xFF_FF_FF_FF},
......
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
mttestutil "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded/testutil" mttestutil "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded/testutil"
...@@ -21,15 +22,17 @@ import ( ...@@ -21,15 +22,17 @@ import (
preimage "github.com/ethereum-optimism/optimism/op-preimage" preimage "github.com/ethereum-optimism/optimism/op-preimage"
) )
type Word = arch.Word
func TestEVM_MT_LL(t *testing.T) { func TestEVM_MT_LL(t *testing.T) {
var tracer *tracing.Hooks var tracer *tracing.Hooks
cases := []struct { cases := []struct {
name string name string
base uint32 base Word
offset int offset int
value uint32 value Word
effAddr uint32 effAddr Word
rtReg int rtReg int
}{ }{
{name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5}, {name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5},
...@@ -44,7 +47,7 @@ func TestEVM_MT_LL(t *testing.T) { ...@@ -44,7 +47,7 @@ func TestEVM_MT_LL(t *testing.T) {
t.Run(tName, func(t *testing.T) { t.Run(tName, func(t *testing.T) {
rtReg := c.rtReg rtReg := c.rtReg
baseReg := 6 baseReg := 6
pc := uint32(0x44) pc := Word(0x44)
insn := uint32((0b11_0000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) insn := uint32((0b11_0000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset))
goVm, state, contracts := setup(t, i, nil) goVm, state, contracts := setup(t, i, nil)
step := state.GetStep() step := state.GetStep()
...@@ -53,11 +56,11 @@ func TestEVM_MT_LL(t *testing.T) { ...@@ -53,11 +56,11 @@ func TestEVM_MT_LL(t *testing.T) {
state.GetCurrentThread().Cpu.PC = pc state.GetCurrentThread().Cpu.PC = pc
state.GetCurrentThread().Cpu.NextPC = pc + 4 state.GetCurrentThread().Cpu.NextPC = pc + 4
state.GetMemory().SetMemory(pc, insn) state.GetMemory().SetMemory(pc, insn)
state.GetMemory().SetMemory(c.effAddr, c.value) state.GetMemory().SetWord(c.effAddr, c.value)
state.GetRegistersRef()[baseReg] = c.base state.GetRegistersRef()[baseReg] = c.base
if withExistingReservation { if withExistingReservation {
state.LLReservationActive = true state.LLReservationActive = true
state.LLAddress = c.effAddr + uint32(4) state.LLAddress = c.effAddr + Word(4)
state.LLOwnerThread = 123 state.LLOwnerThread = 123
} else { } else {
state.LLReservationActive = false state.LLReservationActive = false
...@@ -105,12 +108,12 @@ func TestEVM_MT_SC(t *testing.T) { ...@@ -105,12 +108,12 @@ func TestEVM_MT_SC(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
base uint32 base Word
offset int offset int
value uint32 value Word
effAddr uint32 effAddr Word
rtReg int rtReg int
threadId uint32 threadId Word
}{ }{
{name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5, threadId: 4}, {name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5, threadId: 4},
{name: "Aligned effAddr, signed extended", base: 0x00_00_00_01, offset: 0xFF33, value: 0xABCD, effAddr: 0xFF_FF_FF_34, rtReg: 5, threadId: 4}, {name: "Aligned effAddr, signed extended", base: 0x00_00_00_01, offset: 0xFF33, value: 0xABCD, effAddr: 0xFF_FF_FF_34, rtReg: 5, threadId: 4},
...@@ -125,14 +128,14 @@ func TestEVM_MT_SC(t *testing.T) { ...@@ -125,14 +128,14 @@ func TestEVM_MT_SC(t *testing.T) {
t.Run(tName, func(t *testing.T) { t.Run(tName, func(t *testing.T) {
rtReg := c.rtReg rtReg := c.rtReg
baseReg := 6 baseReg := 6
pc := uint32(0x44) pc := Word(0x44)
insn := uint32((0b11_1000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) insn := uint32((0b11_1000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset))
goVm, state, contracts := setup(t, i, nil) goVm, state, contracts := setup(t, i, nil)
mttestutil.InitializeSingleThread(i*23456, state, i%2 == 1) mttestutil.InitializeSingleThread(i*23456, state, i%2 == 1)
step := state.GetStep() step := state.GetStep()
// Define LL-related params // Define LL-related params
var llAddress, llOwnerThread uint32 var llAddress, llOwnerThread Word
if v.matchEffAddr { if v.matchEffAddr {
llAddress = c.effAddr llAddress = c.effAddr
} else { } else {
...@@ -158,10 +161,10 @@ func TestEVM_MT_SC(t *testing.T) { ...@@ -158,10 +161,10 @@ func TestEVM_MT_SC(t *testing.T) {
// Setup expectations // Setup expectations
expected := mttestutil.NewExpectedMTState(state) expected := mttestutil.NewExpectedMTState(state)
expected.ExpectStep() expected.ExpectStep()
var retVal uint32 var retVal Word
if v.shouldSucceed { if v.shouldSucceed {
retVal = 1 retVal = 1
expected.ExpectMemoryWrite(c.effAddr, c.value) expected.ExpectMemoryWordWrite(c.effAddr, c.value)
expected.LLReservationActive = false expected.LLReservationActive = false
expected.LLAddress = 0 expected.LLAddress = 0
expected.LLOwnerThread = 0 expected.LLOwnerThread = 0
...@@ -207,10 +210,10 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) { ...@@ -207,10 +210,10 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
addr uint32 addr Word
count uint32 count Word
writeLen uint32 writeLen Word
preimageOffset uint32 preimageOffset Word
prestateMem uint32 prestateMem uint32
postateMem uint32 postateMem uint32
shouldPanic bool shouldPanic bool
...@@ -236,14 +239,14 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) { ...@@ -236,14 +239,14 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) {
for _, v := range llVariations { for _, v := range llVariations {
tName := fmt.Sprintf("%v (%v)", c.name, v.name) tName := fmt.Sprintf("%v (%v)", c.name, v.name)
t.Run(tName, func(t *testing.T) { t.Run(tName, func(t *testing.T) {
effAddr := 0xFFffFFfc & c.addr effAddr := arch.AddressMask & c.addr
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey() preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageValue) oracle := testutil.StaticOracle(t, preimageValue)
goVm, state, contracts := setup(t, i, oracle) goVm, state, contracts := setup(t, i, oracle)
step := state.GetStep() step := state.GetStep()
// Define LL-related params // Define LL-related params
var llAddress, llOwnerThread uint32 var llAddress, llOwnerThread Word
if v.matchEffAddr { if v.matchEffAddr {
llAddress = effAddr llAddress = effAddr
} else { } else {
...@@ -315,16 +318,16 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { ...@@ -315,16 +318,16 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) {
{name: "no reservation, mismatched addr", llReservationActive: false, matchThreadId: true, matchEffAddr: false, shouldClearReservation: false}, {name: "no reservation, mismatched addr", llReservationActive: false, matchThreadId: true, matchEffAddr: false, shouldClearReservation: false},
} }
pc := uint32(0x04) pc := Word(0x04)
rt := uint32(0x12_34_56_78) rt := Word(0x12_34_56_78)
baseReg := 5 baseReg := 5
rtReg := 6 rtReg := 6
cases := []struct { cases := []struct {
name string name string
opcode int opcode int
offset int offset int
base uint32 base Word
effAddr uint32 effAddr Word
preMem uint32 preMem uint32
postMem uint32 postMem uint32
}{ }{
...@@ -343,7 +346,7 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { ...@@ -343,7 +346,7 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) {
step := state.GetStep() step := state.GetStep()
// Define LL-related params // Define LL-related params
var llAddress, llOwnerThread uint32 var llAddress, llOwnerThread Word
if v.matchEffAddr { if v.matchEffAddr {
llAddress = c.effAddr llAddress = c.effAddr
} else { } else {
...@@ -393,13 +396,13 @@ func TestEVM_SysClone_FlagHandling(t *testing.T) { ...@@ -393,13 +396,13 @@ func TestEVM_SysClone_FlagHandling(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
flags uint32 flags Word
valid bool valid bool
}{ }{
{"the supported flags bitmask", exec.ValidCloneFlags, true}, {"the supported flags bitmask", exec.ValidCloneFlags, true},
{"no flags", 0, false}, {"no flags", 0, false},
{"all flags", ^uint32(0), false}, {"all flags", ^Word(0), false},
{"all unsupported flags", ^uint32(exec.ValidCloneFlags), false}, {"all unsupported flags", ^Word(exec.ValidCloneFlags), false},
{"a few supported flags", exec.CloneFs | exec.CloneSysvsem, false}, {"a few supported flags", exec.CloneFs | exec.CloneSysvsem, false},
{"one supported flag", exec.CloneFs, false}, {"one supported flag", exec.CloneFs, false},
{"mixed supported and unsupported flags", exec.CloneFs | exec.CloneParentSettid, false}, {"mixed supported and unsupported flags", exec.CloneFs | exec.CloneParentSettid, false},
...@@ -459,7 +462,7 @@ func TestEVM_SysClone_Successful(t *testing.T) { ...@@ -459,7 +462,7 @@ func TestEVM_SysClone_Successful(t *testing.T) {
for i, c := range cases { for i, c := range cases {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
stackPtr := uint32(100) stackPtr := Word(100)
goVm, state, contracts := setup(t, i, nil) goVm, state, contracts := setup(t, i, nil)
mttestutil.InitializeSingleThread(i*333, state, c.traverseRight) mttestutil.InitializeSingleThread(i*333, state, c.traverseRight)
...@@ -470,7 +473,7 @@ func TestEVM_SysClone_Successful(t *testing.T) { ...@@ -470,7 +473,7 @@ func TestEVM_SysClone_Successful(t *testing.T) {
step := state.GetStep() step := state.GetStep()
// Sanity-check assumptions // Sanity-check assumptions
require.Equal(t, uint32(1), state.NextThreadId) require.Equal(t, Word(1), state.NextThreadId)
// Setup expectations // Setup expectations
expected := mttestutil.NewExpectedMTState(state) expected := mttestutil.NewExpectedMTState(state)
...@@ -514,7 +517,7 @@ func TestEVM_SysGetTID(t *testing.T) { ...@@ -514,7 +517,7 @@ func TestEVM_SysGetTID(t *testing.T) {
var tracer *tracing.Hooks var tracer *tracing.Hooks
cases := []struct { cases := []struct {
name string name string
threadId uint32 threadId Word
}{ }{
{"zero", 0}, {"zero", 0},
{"non-zero", 11}, {"non-zero", 11},
...@@ -570,8 +573,8 @@ func TestEVM_SysExit(t *testing.T) { ...@@ -570,8 +573,8 @@ func TestEVM_SysExit(t *testing.T) {
mttestutil.SetupThreads(int64(i*1111), state, i%2 == 0, c.threadCount, 0) mttestutil.SetupThreads(int64(i*1111), state, i%2 == 0, c.threadCount, 0)
state.Memory.SetMemory(state.GetPC(), syscallInsn) state.Memory.SetMemory(state.GetPC(), syscallInsn)
state.GetRegistersRef()[2] = exec.SysExit // Set syscall number state.GetRegistersRef()[2] = exec.SysExit // Set syscall number
state.GetRegistersRef()[4] = uint32(exitCode) // The first argument (exit code) state.GetRegistersRef()[4] = Word(exitCode) // The first argument (exit code)
step := state.Step step := state.Step
// Set up expectations // Set up expectations
...@@ -654,11 +657,11 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { ...@@ -654,11 +657,11 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) {
var tracer *tracing.Hooks var tracer *tracing.Hooks
cases := []struct { cases := []struct {
name string name string
addressParam uint32 addressParam Word
effAddr uint32 effAddr Word
targetValue uint32 targetValue Word
actualValue uint32 actualValue Word
timeout uint32 timeout Word
shouldFail bool shouldFail bool
shouldSetTimeout bool shouldSetTimeout bool
}{ }{
...@@ -678,7 +681,7 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { ...@@ -678,7 +681,7 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) {
step := state.GetStep() step := state.GetStep()
state.Memory.SetMemory(state.GetPC(), syscallInsn) state.Memory.SetMemory(state.GetPC(), syscallInsn)
state.Memory.SetMemory(c.effAddr, c.actualValue) state.Memory.SetWord(c.effAddr, c.actualValue)
state.GetRegistersRef()[2] = exec.SysFutex // Set syscall number state.GetRegistersRef()[2] = exec.SysFutex // Set syscall number
state.GetRegistersRef()[4] = c.addressParam state.GetRegistersRef()[4] = c.addressParam
state.GetRegistersRef()[5] = exec.FutexWaitPrivate state.GetRegistersRef()[5] = exec.FutexWaitPrivate
...@@ -721,8 +724,8 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) { ...@@ -721,8 +724,8 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) {
var tracer *tracing.Hooks var tracer *tracing.Hooks
cases := []struct { cases := []struct {
name string name string
addressParam uint32 addressParam Word
effAddr uint32 effAddr Word
activeThreadCount int activeThreadCount int
inactiveThreadCount int inactiveThreadCount int
traverseRight bool traverseRight bool
...@@ -800,7 +803,7 @@ func TestEVM_SysFutex_UnsupportedOp(t *testing.T) { ...@@ -800,7 +803,7 @@ func TestEVM_SysFutex_UnsupportedOp(t *testing.T) {
const FUTEX_CMP_REQUEUE_PI = 12 const FUTEX_CMP_REQUEUE_PI = 12
const FUTEX_LOCK_PI2 = 13 const FUTEX_LOCK_PI2 = 13
unsupportedFutexOps := map[string]uint32{ unsupportedFutexOps := map[string]Word{
"FUTEX_WAIT": FUTEX_WAIT, "FUTEX_WAIT": FUTEX_WAIT,
"FUTEX_WAKE": FUTEX_WAKE, "FUTEX_WAKE": FUTEX_WAKE,
"FUTEX_FD": FUTEX_FD, "FUTEX_FD": FUTEX_FD,
...@@ -889,7 +892,7 @@ func runPreemptSyscall(t *testing.T, syscallName string, syscallNum uint32) { ...@@ -889,7 +892,7 @@ func runPreemptSyscall(t *testing.T, syscallName string, syscallNum uint32) {
mttestutil.SetupThreads(int64(i*3259), state, traverseRight, c.activeThreads, c.inactiveThreads) mttestutil.SetupThreads(int64(i*3259), state, traverseRight, c.activeThreads, c.inactiveThreads)
state.Memory.SetMemory(state.GetPC(), syscallInsn) state.Memory.SetMemory(state.GetPC(), syscallInsn)
state.GetRegistersRef()[2] = syscallNum // Set syscall number state.GetRegistersRef()[2] = Word(syscallNum) // Set syscall number
step := state.Step step := state.Step
// Set up post-state expectations // Set up post-state expectations
...@@ -972,7 +975,7 @@ func TestEVM_SysClockGettimeRealtime(t *testing.T) { ...@@ -972,7 +975,7 @@ func TestEVM_SysClockGettimeRealtime(t *testing.T) {
testEVM_SysClockGettime(t, exec.ClockGettimeRealtimeFlag) testEVM_SysClockGettime(t, exec.ClockGettimeRealtimeFlag)
} }
func testEVM_SysClockGettime(t *testing.T, clkid uint32) { func testEVM_SysClockGettime(t *testing.T, clkid Word) {
var tracer *tracing.Hooks var tracer *tracing.Hooks
llVariations := []struct { llVariations := []struct {
...@@ -996,7 +999,7 @@ func testEVM_SysClockGettime(t *testing.T, clkid uint32) { ...@@ -996,7 +999,7 @@ func testEVM_SysClockGettime(t *testing.T, clkid uint32) {
cases := []struct { cases := []struct {
name string name string
timespecAddr uint32 timespecAddr Word
}{ }{
{"aligned timespec address", 0x1000}, {"aligned timespec address", 0x1000},
{"unaligned timespec address", 0x1003}, {"unaligned timespec address", 0x1003},
...@@ -1007,12 +1010,12 @@ func testEVM_SysClockGettime(t *testing.T, clkid uint32) { ...@@ -1007,12 +1010,12 @@ func testEVM_SysClockGettime(t *testing.T, clkid uint32) {
t.Run(tName, func(t *testing.T) { t.Run(tName, func(t *testing.T) {
goVm, state, contracts := setup(t, 2101, nil) goVm, state, contracts := setup(t, 2101, nil)
mttestutil.InitializeSingleThread(2101+i, state, i%2 == 1) mttestutil.InitializeSingleThread(2101+i, state, i%2 == 1)
effAddr := c.timespecAddr & 0xFFffFFfc effAddr := c.timespecAddr & arch.AddressMask
effAddr2 := effAddr + 4 effAddr2 := effAddr + 4
step := state.Step step := state.Step
// Define LL-related params // Define LL-related params
var llAddress, llOwnerThread uint32 var llAddress, llOwnerThread Word
if v.matchEffAddr { if v.matchEffAddr {
llAddress = effAddr llAddress = effAddr
} else if v.matchEffAddr2 { } else if v.matchEffAddr2 {
...@@ -1039,13 +1042,13 @@ func testEVM_SysClockGettime(t *testing.T, clkid uint32) { ...@@ -1039,13 +1042,13 @@ func testEVM_SysClockGettime(t *testing.T, clkid uint32) {
expected.ActiveThread().Registers[2] = 0 expected.ActiveThread().Registers[2] = 0
expected.ActiveThread().Registers[7] = 0 expected.ActiveThread().Registers[7] = 0
next := state.Step + 1 next := state.Step + 1
var secs, nsecs uint32 var secs, nsecs Word
if clkid == exec.ClockGettimeMonotonicFlag { if clkid == exec.ClockGettimeMonotonicFlag {
secs = uint32(next / exec.HZ) secs = Word(next / exec.HZ)
nsecs = uint32((next % exec.HZ) * (1_000_000_000 / exec.HZ)) nsecs = Word((next % exec.HZ) * (1_000_000_000 / exec.HZ))
} }
expected.ExpectMemoryWrite(effAddr, secs) expected.ExpectMemoryWordWrite(effAddr, secs)
expected.ExpectMemoryWrite(effAddr2, nsecs) expected.ExpectMemoryWordWrite(effAddr2, nsecs)
if v.shouldClearReservation { if v.shouldClearReservation {
expected.LLReservationActive = false expected.LLReservationActive = false
expected.LLAddress = 0 expected.LLAddress = 0
...@@ -1069,7 +1072,7 @@ func TestEVM_SysClockGettimeNonMonotonic(t *testing.T) { ...@@ -1069,7 +1072,7 @@ func TestEVM_SysClockGettimeNonMonotonic(t *testing.T) {
var tracer *tracing.Hooks var tracer *tracing.Hooks
goVm, state, contracts := setup(t, 2101, nil) goVm, state, contracts := setup(t, 2101, nil)
timespecAddr := uint32(0x1000) timespecAddr := Word(0x1000)
state.Memory.SetMemory(state.GetPC(), syscallInsn) state.Memory.SetMemory(state.GetPC(), syscallInsn)
state.GetRegistersRef()[2] = exec.SysClockGetTime // Set syscall number state.GetRegistersRef()[2] = exec.SysClockGetTime // Set syscall number
state.GetRegistersRef()[4] = 0xDEAD // a0 - invalid clockid state.GetRegistersRef()[4] = 0xDEAD // a0 - invalid clockid
...@@ -1131,7 +1134,7 @@ func TestEVM_NoopSyscall(t *testing.T) { ...@@ -1131,7 +1134,7 @@ func TestEVM_NoopSyscall(t *testing.T) {
goVm, state, contracts := setup(t, int(noopVal), nil) goVm, state, contracts := setup(t, int(noopVal), nil)
state.Memory.SetMemory(state.GetPC(), syscallInsn) state.Memory.SetMemory(state.GetPC(), syscallInsn)
state.GetRegistersRef()[2] = noopVal // Set syscall number state.GetRegistersRef()[2] = Word(noopVal) // Set syscall number
step := state.Step step := state.Step
// Set up post-state expectations // Set up post-state expectations
...@@ -1178,7 +1181,7 @@ func TestEVM_UnsupportedSyscall(t *testing.T) { ...@@ -1178,7 +1181,7 @@ func TestEVM_UnsupportedSyscall(t *testing.T) {
goVm, state, contracts := setup(t, i*3434, nil) goVm, state, contracts := setup(t, i*3434, nil)
// Setup basic getThreadId syscall instruction // Setup basic getThreadId syscall instruction
state.Memory.SetMemory(state.GetPC(), syscallInsn) state.Memory.SetMemory(state.GetPC(), syscallInsn)
state.GetRegistersRef()[2] = syscallNum state.GetRegistersRef()[2] = Word(syscallNum)
// Set up post-state expectations // Set up post-state expectations
require.Panics(t, func() { _, _ = goVm.Step(true) }) require.Panics(t, func() { _, _ = goVm.Step(true) })
...@@ -1194,9 +1197,9 @@ func TestEVM_NormalTraversalStep_HandleWaitingThread(t *testing.T) { ...@@ -1194,9 +1197,9 @@ func TestEVM_NormalTraversalStep_HandleWaitingThread(t *testing.T) {
step uint64 step uint64
activeStackSize int activeStackSize int
otherStackSize int otherStackSize int
futexAddr uint32 futexAddr Word
targetValue uint32 targetValue Word
actualValue uint32 actualValue Word
timeoutStep uint64 timeoutStep uint64
shouldWakeup bool shouldWakeup bool
shouldTimeout bool shouldTimeout bool
...@@ -1225,7 +1228,7 @@ func TestEVM_NormalTraversalStep_HandleWaitingThread(t *testing.T) { ...@@ -1225,7 +1228,7 @@ func TestEVM_NormalTraversalStep_HandleWaitingThread(t *testing.T) {
if !c.shouldWakeup && c.shouldTimeout { if !c.shouldWakeup && c.shouldTimeout {
require.Fail(t, "Invalid test case - cannot expect a timeout with no wakeup") require.Fail(t, "Invalid test case - cannot expect a timeout with no wakeup")
} }
effAddr := c.futexAddr & 0xFF_FF_FF_Fc effAddr := c.futexAddr & arch.AddressMask
goVm, state, contracts := setup(t, i, nil) goVm, state, contracts := setup(t, i, nil)
mttestutil.SetupThreads(int64(i*101), state, traverseRight, c.activeStackSize, c.otherStackSize) mttestutil.SetupThreads(int64(i*101), state, traverseRight, c.activeStackSize, c.otherStackSize)
state.Step = c.step state.Step = c.step
...@@ -1234,7 +1237,7 @@ func TestEVM_NormalTraversalStep_HandleWaitingThread(t *testing.T) { ...@@ -1234,7 +1237,7 @@ func TestEVM_NormalTraversalStep_HandleWaitingThread(t *testing.T) {
activeThread.FutexAddr = c.futexAddr activeThread.FutexAddr = c.futexAddr
activeThread.FutexVal = c.targetValue activeThread.FutexVal = c.targetValue
activeThread.FutexTimeoutStep = c.timeoutStep activeThread.FutexTimeoutStep = c.timeoutStep
state.GetMemory().SetMemory(effAddr, c.actualValue) state.GetMemory().SetWord(effAddr, c.actualValue)
// Set up post-state expectations // Set up post-state expectations
expected := mttestutil.NewExpectedMTState(state) expected := mttestutil.NewExpectedMTState(state)
...@@ -1328,14 +1331,14 @@ func TestEVM_NormalTraversal_Full(t *testing.T) { ...@@ -1328,14 +1331,14 @@ func TestEVM_NormalTraversal_Full(t *testing.T) {
} }
func TestEVM_WakeupTraversalStep(t *testing.T) { func TestEVM_WakeupTraversalStep(t *testing.T) {
addr := uint32(0x1234) addr := Word(0x1234)
wakeupVal := uint32(0x999) wakeupVal := Word(0x999)
var tracer *tracing.Hooks var tracer *tracing.Hooks
cases := []struct { cases := []struct {
name string name string
wakeupAddr uint32 wakeupAddr Word
futexAddr uint32 futexAddr Word
targetVal uint32 targetVal Word
traverseRight bool traverseRight bool
activeStackSize int activeStackSize int
otherStackSize int otherStackSize int
...@@ -1373,7 +1376,7 @@ func TestEVM_WakeupTraversalStep(t *testing.T) { ...@@ -1373,7 +1376,7 @@ func TestEVM_WakeupTraversalStep(t *testing.T) {
step := state.Step step := state.Step
state.Wakeup = c.wakeupAddr state.Wakeup = c.wakeupAddr
state.GetMemory().SetMemory(c.wakeupAddr&0xFF_FF_FF_FC, wakeupVal) state.GetMemory().SetWord(c.wakeupAddr&arch.AddressMask, wakeupVal)
activeThread := state.GetCurrentThread() activeThread := state.GetCurrentThread()
activeThread.FutexAddr = c.futexAddr activeThread.FutexAddr = c.futexAddr
activeThread.FutexVal = c.targetVal activeThread.FutexVal = c.targetVal
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
...@@ -20,10 +21,10 @@ func TestEVM_LL(t *testing.T) { ...@@ -20,10 +21,10 @@ func TestEVM_LL(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
base uint32 base Word
offset int offset int
value uint32 value Word
effAddr uint32 effAddr Word
rtReg int rtReg int
}{ }{
{name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5}, {name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5},
...@@ -37,12 +38,12 @@ func TestEVM_LL(t *testing.T) { ...@@ -37,12 +38,12 @@ func TestEVM_LL(t *testing.T) {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
rtReg := c.rtReg rtReg := c.rtReg
baseReg := 6 baseReg := 6
pc := uint32(0x44) pc := Word(0x44)
insn := uint32((0b11_0000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) insn := uint32((0b11_0000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset))
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(pc), testutil.WithNextPC(pc+4)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(pc), testutil.WithNextPC(pc+4))
state := goVm.GetState() state := goVm.GetState()
state.GetMemory().SetMemory(pc, insn) state.GetMemory().SetMemory(pc, insn)
state.GetMemory().SetMemory(c.effAddr, c.value) state.GetMemory().SetWord(c.effAddr, c.value)
state.GetRegistersRef()[baseReg] = c.base state.GetRegistersRef()[baseReg] = c.base
step := state.GetStep() step := state.GetStep()
...@@ -70,10 +71,10 @@ func TestEVM_SC(t *testing.T) { ...@@ -70,10 +71,10 @@ func TestEVM_SC(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
base uint32 base Word
offset int offset int
value uint32 value Word
effAddr uint32 effAddr Word
rtReg int rtReg int
}{ }{
{name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5}, {name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5},
...@@ -87,7 +88,7 @@ func TestEVM_SC(t *testing.T) { ...@@ -87,7 +88,7 @@ func TestEVM_SC(t *testing.T) {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
rtReg := c.rtReg rtReg := c.rtReg
baseReg := 6 baseReg := 6
pc := uint32(0x44) pc := Word(0x44)
insn := uint32((0b11_1000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) insn := uint32((0b11_1000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset))
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(pc), testutil.WithNextPC(pc+4)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(pc), testutil.WithNextPC(pc+4))
state := goVm.GetState() state := goVm.GetState()
...@@ -103,7 +104,7 @@ func TestEVM_SC(t *testing.T) { ...@@ -103,7 +104,7 @@ func TestEVM_SC(t *testing.T) {
expected.NextPC = pc + 8 expected.NextPC = pc + 8
expectedMemory := memory.NewMemory() expectedMemory := memory.NewMemory()
expectedMemory.SetMemory(pc, insn) expectedMemory.SetMemory(pc, insn)
expectedMemory.SetMemory(c.effAddr, c.value) expectedMemory.SetWord(c.effAddr, c.value)
expected.MemoryRoot = expectedMemory.MerkleRoot() expected.MemoryRoot = expectedMemory.MerkleRoot()
if rtReg != 0 { if rtReg != 0 {
expected.Registers[rtReg] = 1 // 1 for success expected.Registers[rtReg] = 1 // 1 for success
...@@ -130,10 +131,10 @@ func TestEVM_SysRead_Preimage(t *testing.T) { ...@@ -130,10 +131,10 @@ func TestEVM_SysRead_Preimage(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
addr uint32 addr Word
count uint32 count Word
writeLen uint32 writeLen Word
preimageOffset uint32 preimageOffset Word
prestateMem uint32 prestateMem uint32
postateMem uint32 postateMem uint32
shouldPanic bool shouldPanic bool
...@@ -157,7 +158,7 @@ func TestEVM_SysRead_Preimage(t *testing.T) { ...@@ -157,7 +158,7 @@ func TestEVM_SysRead_Preimage(t *testing.T) {
} }
for i, c := range cases { for i, c := range cases {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
effAddr := 0xFFffFFfc & c.addr effAddr := arch.AddressMask & c.addr
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey() preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageValue) oracle := testutil.StaticOracle(t, preimageValue)
goVm := v.VMFactory(oracle, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPreimageKey(preimageKey), testutil.WithPreimageOffset(c.preimageOffset)) goVm := v.VMFactory(oracle, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPreimageKey(preimageKey), testutil.WithPreimageOffset(c.preimageOffset))
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
...@@ -50,13 +51,13 @@ func FuzzStateSyscallBrk(f *testing.F) { ...@@ -50,13 +51,13 @@ func FuzzStateSyscallBrk(f *testing.F) {
func FuzzStateSyscallMmap(f *testing.F) { func FuzzStateSyscallMmap(f *testing.F) {
// Add special cases for large memory allocation // Add special cases for large memory allocation
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END), int64(1)) f.Add(Word(0), Word(0x1000), Word(program.HEAP_END), int64(1))
f.Add(uint32(0), uint32(1<<31), uint32(program.HEAP_START), int64(2)) f.Add(Word(0), Word(1<<31), Word(program.HEAP_START), int64(2))
// Check edge case - just within bounds // Check edge case - just within bounds
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END-4096), int64(3)) f.Add(Word(0), Word(0x1000), Word(program.HEAP_END-4096), int64(3))
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, siz uint32, heap uint32, seed int64) { f.Fuzz(func(t *testing.T, addr Word, siz Word, heap Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
...@@ -112,7 +113,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) { ...@@ -112,7 +113,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) {
testutil.WithRandomization(seed)) testutil.WithRandomization(seed))
state := goVm.GetState() state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysExitGroup state.GetRegistersRef()[2] = exec.SysExitGroup
state.GetRegistersRef()[4] = uint32(exitCode) state.GetRegistersRef()[4] = Word(exitCode)
state.GetMemory().SetMemory(state.GetPC(), syscallInsn) state.GetMemory().SetMemory(state.GetPC(), syscallInsn)
step := state.GetStep() step := state.GetStep()
...@@ -134,7 +135,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) { ...@@ -134,7 +135,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) {
func FuzzStateSyscallFcntl(f *testing.F) { func FuzzStateSyscallFcntl(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, fd uint32, cmd uint32, seed int64) { f.Fuzz(func(t *testing.T, fd Word, cmd Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
...@@ -190,7 +191,7 @@ func FuzzStateSyscallFcntl(f *testing.F) { ...@@ -190,7 +191,7 @@ func FuzzStateSyscallFcntl(f *testing.F) {
func FuzzStateHintRead(f *testing.F) { func FuzzStateHintRead(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32, seed int64) { f.Fuzz(func(t *testing.T, addr Word, count Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
preimageData := []byte("hello world") preimageData := []byte("hello world")
...@@ -227,15 +228,15 @@ func FuzzStateHintRead(f *testing.F) { ...@@ -227,15 +228,15 @@ func FuzzStateHintRead(f *testing.F) {
func FuzzStatePreimageRead(f *testing.F) { func FuzzStatePreimageRead(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, pc uint32, count uint32, preimageOffset uint32, seed int64) { f.Fuzz(func(t *testing.T, addr arch.Word, pc arch.Word, count arch.Word, preimageOffset arch.Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
effAddr := addr & 0xFF_FF_FF_FC effAddr := addr & arch.AddressMask
pc = pc & 0xFF_FF_FF_FC pc = pc & arch.AddressMask
preexistingMemoryVal := [4]byte{0xFF, 0xFF, 0xFF, 0xFF} preexistingMemoryVal := [4]byte{0xFF, 0xFF, 0xFF, 0xFF}
preimageValue := []byte("hello world") preimageValue := []byte("hello world")
preimageData := testutil.AddPreimageLengthPrefix(preimageValue) preimageData := testutil.AddPreimageLengthPrefix(preimageValue)
if preimageOffset >= uint32(len(preimageData)) || pc == effAddr { if preimageOffset >= Word(len(preimageData)) || pc == effAddr {
t.SkipNow() t.SkipNow()
} }
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey() preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey()
...@@ -252,13 +253,13 @@ func FuzzStatePreimageRead(f *testing.F) { ...@@ -252,13 +253,13 @@ func FuzzStatePreimageRead(f *testing.F) {
state.GetMemory().SetMemory(effAddr, binary.BigEndian.Uint32(preexistingMemoryVal[:])) state.GetMemory().SetMemory(effAddr, binary.BigEndian.Uint32(preexistingMemoryVal[:]))
step := state.GetStep() step := state.GetStep()
alignment := addr & 3 alignment := addr & arch.ExtMask
writeLen := 4 - alignment writeLen := 4 - alignment
if count < writeLen { if count < writeLen {
writeLen = count writeLen = count
} }
// Cap write length to remaining bytes of the preimage // Cap write length to remaining bytes of the preimage
preimageDataLen := uint32(len(preimageData)) preimageDataLen := Word(len(preimageData))
if preimageOffset+writeLen > preimageDataLen { if preimageOffset+writeLen > preimageDataLen {
writeLen = preimageDataLen - preimageOffset writeLen = preimageDataLen - preimageOffset
} }
...@@ -290,11 +291,11 @@ func FuzzStatePreimageRead(f *testing.F) { ...@@ -290,11 +291,11 @@ func FuzzStatePreimageRead(f *testing.F) {
func FuzzStateHintWrite(f *testing.F) { func FuzzStateHintWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32, hint1, hint2, hint3 []byte, randSeed int64) { f.Fuzz(func(t *testing.T, addr Word, count Word, hint1, hint2, hint3 []byte, randSeed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
// Make sure pc does not overlap with hint data in memory // Make sure pc does not overlap with hint data in memory
pc := uint32(0) pc := Word(0)
if addr <= 8 { if addr <= 8 {
addr += 8 addr += 8
} }
...@@ -372,15 +373,15 @@ func FuzzStateHintWrite(f *testing.F) { ...@@ -372,15 +373,15 @@ func FuzzStateHintWrite(f *testing.F) {
func FuzzStatePreimageWrite(f *testing.F) { func FuzzStatePreimageWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32, seed int64) { f.Fuzz(func(t *testing.T, addr arch.Word, count arch.Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
// Make sure pc does not overlap with preimage data in memory // Make sure pc does not overlap with preimage data in memory
pc := uint32(0) pc := Word(0)
if addr <= 8 { if addr <= 8 {
addr += 8 addr += 8
} }
effAddr := addr & 0xFF_FF_FF_FC effAddr := addr & arch.AddressMask
preexistingMemoryVal := [4]byte{0x12, 0x34, 0x56, 0x78} preexistingMemoryVal := [4]byte{0x12, 0x34, 0x56, 0x78}
preimageData := []byte("hello world") preimageData := []byte("hello world")
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey() preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey()
...@@ -398,7 +399,7 @@ func FuzzStatePreimageWrite(f *testing.F) { ...@@ -398,7 +399,7 @@ func FuzzStatePreimageWrite(f *testing.F) {
step := state.GetStep() step := state.GetStep()
expectBytesWritten := count expectBytesWritten := count
alignment := addr & 0x3 alignment := addr & arch.ExtMask
sz := 4 - alignment sz := 4 - alignment
if sz < expectBytesWritten { if sz < expectBytesWritten {
expectBytesWritten = sz expectBytesWritten = sz
......
...@@ -14,13 +14,13 @@ import ( ...@@ -14,13 +14,13 @@ import (
func FuzzStateSyscallCloneMT(f *testing.F) { func FuzzStateSyscallCloneMT(f *testing.F) {
v := GetMultiThreadedTestCase(f) v := GetMultiThreadedTestCase(f)
f.Fuzz(func(t *testing.T, nextThreadId, stackPtr uint32, seed int64) { f.Fuzz(func(t *testing.T, nextThreadId, stackPtr Word, seed int64) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed))
state := mttestutil.GetMtState(t, goVm) state := mttestutil.GetMtState(t, goVm)
// Update existing threads to avoid collision with nextThreadId // Update existing threads to avoid collision with nextThreadId
if mttestutil.FindThread(state, nextThreadId) != nil { if mttestutil.FindThread(state, nextThreadId) != nil {
for i, t := range mttestutil.GetAllThreads(state) { for i, t := range mttestutil.GetAllThreads(state) {
t.ThreadId = nextThreadId - uint32(i+1) t.ThreadId = nextThreadId - Word(i+1)
} }
} }
......
...@@ -16,6 +16,7 @@ import ( ...@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
preimage "github.com/ethereum-optimism/optimism/op-preimage" preimage "github.com/ethereum-optimism/optimism/op-preimage"
) )
...@@ -97,7 +98,7 @@ func EncodeStepInput(t *testing.T, wit *mipsevm.StepWitness, localContext mipsev ...@@ -97,7 +98,7 @@ func EncodeStepInput(t *testing.T, wit *mipsevm.StepWitness, localContext mipsev
return input return input
} }
func (m *MIPSEVM) encodePreimageOracleInput(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset uint32, localContext mipsevm.LocalContext) ([]byte, error) { func (m *MIPSEVM) encodePreimageOracleInput(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset arch.Word, localContext mipsevm.LocalContext) ([]byte, error) {
if preimageKey == ([32]byte{}) { if preimageKey == ([32]byte{}) {
return nil, errors.New("cannot encode pre-image oracle input, witness has no pre-image to proof") return nil, errors.New("cannot encode pre-image oracle input, witness has no pre-image to proof")
} }
...@@ -151,7 +152,7 @@ func (m *MIPSEVM) encodePreimageOracleInput(t *testing.T, preimageKey [32]byte, ...@@ -151,7 +152,7 @@ func (m *MIPSEVM) encodePreimageOracleInput(t *testing.T, preimageKey [32]byte,
} }
} }
func (m *MIPSEVM) assertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset uint32) { func (m *MIPSEVM) assertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset arch.Word) {
poInput, err := m.encodePreimageOracleInput(t, preimageKey, preimageValue, preimageOffset, mipsevm.LocalContext{}) poInput, err := m.encodePreimageOracleInput(t, preimageKey, preimageValue, preimageOffset, mipsevm.LocalContext{})
require.NoError(t, err, "encode preimage oracle input") require.NoError(t, err, "encode preimage oracle input")
_, _, evmErr := m.env.Call(m.sender, m.addrs.Oracle, poInput, m.startingGas, common.U2560) _, _, evmErr := m.env.Call(m.sender, m.addrs.Oracle, poInput, m.startingGas, common.U2560)
...@@ -200,7 +201,7 @@ func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *Contract ...@@ -200,7 +201,7 @@ func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *Contract
require.Equal(t, 0, len(logs)) require.Equal(t, 0, len(logs))
} }
func AssertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset uint32, contracts *ContractMetadata, tracer *tracing.Hooks) { func AssertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset arch.Word, contracts *ContractMetadata, tracer *tracing.Hooks) {
evm := NewMIPSEVM(contracts) evm := NewMIPSEVM(contracts)
evm.SetTracer(tracer) evm.SetTracer(tracer)
LogStepFailureAtCleanup(t, evm) LogStepFailureAtCleanup(t, evm)
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"encoding/binary" "encoding/binary"
"math/rand" "math/rand"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -21,6 +22,14 @@ func (h *RandHelper) Uint32() uint32 { ...@@ -21,6 +22,14 @@ func (h *RandHelper) Uint32() uint32 {
return h.r.Uint32() return h.r.Uint32()
} }
func (h *RandHelper) Word() arch.Word {
if arch.IsMips32 {
return arch.Word(h.r.Uint32())
} else {
return arch.Word(h.r.Uint64())
}
}
func (h *RandHelper) Fraction() float64 { func (h *RandHelper) Fraction() float64 {
return h.r.Float64() return h.r.Float64()
} }
...@@ -57,10 +66,10 @@ func (h *RandHelper) RandHint() []byte { ...@@ -57,10 +66,10 @@ func (h *RandHelper) RandHint() []byte {
return bytes return bytes
} }
func (h *RandHelper) RandRegisters() *[32]uint32 { func (h *RandHelper) RandRegisters() *[32]arch.Word {
registers := new([32]uint32) registers := new([32]arch.Word)
for i := 0; i < 32; i++ { for i := 0; i < 32; i++ {
registers[i] = h.r.Uint32() registers[i] = h.Word()
} }
return registers return registers
} }
...@@ -73,8 +82,8 @@ func (h *RandHelper) RandomBytes(t require.TestingT, length int) []byte { ...@@ -73,8 +82,8 @@ func (h *RandHelper) RandomBytes(t require.TestingT, length int) []byte {
return randBytes return randBytes
} }
func (h *RandHelper) RandPC() uint32 { func (h *RandHelper) RandPC() arch.Word {
return AlignPC(h.r.Uint32()) return AlignPC(h.Word())
} }
func (h *RandHelper) RandStep() uint64 { func (h *RandHelper) RandStep() uint64 {
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
) )
...@@ -33,12 +34,12 @@ func AddPreimageLengthPrefix(data []byte) []byte { ...@@ -33,12 +34,12 @@ func AddPreimageLengthPrefix(data []byte) []byte {
type StateMutator interface { type StateMutator interface {
SetPreimageKey(val common.Hash) SetPreimageKey(val common.Hash)
SetPreimageOffset(val uint32) SetPreimageOffset(val arch.Word)
SetPC(val uint32) SetPC(val arch.Word)
SetNextPC(val uint32) SetNextPC(val arch.Word)
SetHI(val uint32) SetHI(val arch.Word)
SetLO(val uint32) SetLO(val arch.Word)
SetHeap(addr uint32) SetHeap(addr arch.Word)
SetExitCode(val uint8) SetExitCode(val uint8)
SetExited(val bool) SetExited(val bool)
SetStep(val uint64) SetStep(val uint64)
...@@ -48,26 +49,26 @@ type StateMutator interface { ...@@ -48,26 +49,26 @@ type StateMutator interface {
type StateOption func(state StateMutator) type StateOption func(state StateMutator)
func WithPC(pc uint32) StateOption { func WithPC(pc arch.Word) StateOption {
return func(state StateMutator) { return func(state StateMutator) {
state.SetPC(pc) state.SetPC(pc)
} }
} }
func WithNextPC(nextPC uint32) StateOption { func WithNextPC(nextPC arch.Word) StateOption {
return func(state StateMutator) { return func(state StateMutator) {
state.SetNextPC(nextPC) state.SetNextPC(nextPC)
} }
} }
func WithPCAndNextPC(pc uint32) StateOption { func WithPCAndNextPC(pc arch.Word) StateOption {
return func(state StateMutator) { return func(state StateMutator) {
state.SetPC(pc) state.SetPC(pc)
state.SetNextPC(pc + 4) state.SetNextPC(pc + 4)
} }
} }
func WithHeap(addr uint32) StateOption { func WithHeap(addr arch.Word) StateOption {
return func(state StateMutator) { return func(state StateMutator) {
state.SetHeap(addr) state.SetHeap(addr)
} }
...@@ -85,7 +86,7 @@ func WithPreimageKey(key common.Hash) StateOption { ...@@ -85,7 +86,7 @@ func WithPreimageKey(key common.Hash) StateOption {
} }
} }
func WithPreimageOffset(offset uint32) StateOption { func WithPreimageOffset(offset arch.Word) StateOption {
return func(state StateMutator) { return func(state StateMutator) {
state.SetPreimageOffset(offset) state.SetPreimageOffset(offset)
} }
...@@ -103,12 +104,12 @@ func WithRandomization(seed int64) StateOption { ...@@ -103,12 +104,12 @@ func WithRandomization(seed int64) StateOption {
} }
} }
func AlignPC(pc uint32) uint32 { func AlignPC(pc arch.Word) arch.Word {
// Memory-align random pc and leave room for nextPC // Memory-align random pc and leave room for nextPC
pc = pc & 0xFF_FF_FF_FC // Align address pc = pc & arch.AddressMask // Align address
if pc >= 0xFF_FF_FF_FC { if pc >= arch.AddressMask && arch.IsMips32 {
// Leave room to set and then increment nextPC // Leave room to set and then increment nextPC
pc = 0xFF_FF_FF_FC - 8 pc = arch.AddressMask - 8
} }
return pc return pc
} }
...@@ -123,17 +124,17 @@ func BoundStep(step uint64) uint64 { ...@@ -123,17 +124,17 @@ func BoundStep(step uint64) uint64 {
type ExpectedState struct { type ExpectedState struct {
PreimageKey common.Hash PreimageKey common.Hash
PreimageOffset uint32 PreimageOffset arch.Word
PC uint32 PC arch.Word
NextPC uint32 NextPC arch.Word
HI uint32 HI arch.Word
LO uint32 LO arch.Word
Heap uint32 Heap arch.Word
ExitCode uint8 ExitCode uint8
Exited bool Exited bool
Step uint64 Step uint64
LastHint hexutil.Bytes LastHint hexutil.Bytes
Registers [32]uint32 Registers [32]arch.Word
MemoryRoot common.Hash MemoryRoot common.Hash
expectedMemory *memory.Memory expectedMemory *memory.Memory
} }
...@@ -164,7 +165,7 @@ func (e *ExpectedState) ExpectStep() { ...@@ -164,7 +165,7 @@ func (e *ExpectedState) ExpectStep() {
e.NextPC += 4 e.NextPC += 4
} }
func (e *ExpectedState) ExpectMemoryWrite(addr uint32, val uint32) { func (e *ExpectedState) ExpectMemoryWrite(addr arch.Word, val uint32) {
e.expectedMemory.SetMemory(addr, val) e.expectedMemory.SetMemory(addr, val)
e.MemoryRoot = e.expectedMemory.MerkleRoot() e.MemoryRoot = e.expectedMemory.MerkleRoot()
} }
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
...@@ -76,13 +77,13 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa ...@@ -76,13 +77,13 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa
} }
if exitGroup { if exitGroup {
require.NotEqual(t, uint32(EndAddr), us.GetState().GetPC(), "must not reach end") require.NotEqual(t, arch.Word(EndAddr), us.GetState().GetPC(), "must not reach end")
require.True(t, us.GetState().GetExited(), "must set exited state") require.True(t, us.GetState().GetExited(), "must set exited state")
require.Equal(t, uint8(1), us.GetState().GetExitCode(), "must exit with 1") require.Equal(t, uint8(1), us.GetState().GetExitCode(), "must exit with 1")
} else if expectPanic { } else if expectPanic {
require.NotEqual(t, uint32(EndAddr), us.GetState().GetPC(), "must not reach end") require.NotEqual(t, arch.Word(EndAddr), us.GetState().GetPC(), "must not reach end")
} else { } else {
require.Equal(t, uint32(EndAddr), us.GetState().GetPC(), "must reach end") require.Equal(t, arch.Word(EndAddr), us.GetState().GetPC(), "must reach end")
done, result := state.GetMemory().GetMemory(BaseAddrEnd+4), state.GetMemory().GetMemory(BaseAddrEnd+8) done, result := state.GetMemory().GetMemory(BaseAddrEnd+4), state.GetMemory().GetMemory(BaseAddrEnd+8)
// inspect test result // inspect test result
require.Equal(t, done, uint32(1), "must be done") require.Equal(t, done, uint32(1), "must be done")
......
...@@ -27,7 +27,7 @@ func DetectVersion(path string) (StateVersion, error) { ...@@ -27,7 +27,7 @@ func DetectVersion(path string) (StateVersion, error) {
} }
switch ver { switch ver {
case VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2: case VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2, VersionMultiThreaded64:
return ver, nil return ver, nil
default: default:
return 0, fmt.Errorf("%w: %d", ErrUnknownVersion, ver) return 0, fmt.Errorf("%w: %d", ErrUnknownVersion, ver)
......
...@@ -34,6 +34,9 @@ func TestDetectVersion(t *testing.T) { ...@@ -34,6 +34,9 @@ func TestDetectVersion(t *testing.T) {
// Iterate all known versions to ensure we have a test case to detect every state version // Iterate all known versions to ensure we have a test case to detect every state version
for _, version := range StateVersionTypes { for _, version := range StateVersionTypes {
version := version version := version
if version == VersionMultiThreaded64 {
t.Skip("TODO(#12205)")
}
t.Run(version.String(), func(t *testing.T) { t.Run(version.String(), func(t *testing.T) {
testDetection(t, version, ".bin.gz") testDetection(t, version, ".bin.gz")
}) })
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"io" "io"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/serialize" "github.com/ethereum-optimism/optimism/cannon/serialize"
...@@ -21,14 +22,16 @@ const ( ...@@ -21,14 +22,16 @@ const (
VersionMultiThreaded VersionMultiThreaded
// VersionSingleThreaded2 is based on VersionSingleThreaded with the addition of support for fcntl(F_GETFD) syscall // VersionSingleThreaded2 is based on VersionSingleThreaded with the addition of support for fcntl(F_GETFD) syscall
VersionSingleThreaded2 VersionSingleThreaded2
VersionMultiThreaded64
) )
var ( var (
ErrUnknownVersion = errors.New("unknown version") ErrUnknownVersion = errors.New("unknown version")
ErrJsonNotSupported = errors.New("json not supported") ErrJsonNotSupported = errors.New("json not supported")
ErrUnsupportedMipsArch = errors.New("mips architecture is not supported")
) )
var StateVersionTypes = []StateVersion{VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2} var StateVersionTypes = []StateVersion{VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2, VersionMultiThreaded64}
func LoadStateFromFile(path string) (*VersionedState, error) { func LoadStateFromFile(path string) (*VersionedState, error) {
if !serialize.IsBinaryFile(path) { if !serialize.IsBinaryFile(path) {
...@@ -45,15 +48,25 @@ func LoadStateFromFile(path string) (*VersionedState, error) { ...@@ -45,15 +48,25 @@ func LoadStateFromFile(path string) (*VersionedState, error) {
func NewFromState(state mipsevm.FPVMState) (*VersionedState, error) { func NewFromState(state mipsevm.FPVMState) (*VersionedState, error) {
switch state := state.(type) { switch state := state.(type) {
case *singlethreaded.State: case *singlethreaded.State:
if !arch.IsMips32 {
return nil, ErrUnsupportedMipsArch
}
return &VersionedState{ return &VersionedState{
Version: VersionSingleThreaded2, Version: VersionSingleThreaded2,
FPVMState: state, FPVMState: state,
}, nil }, nil
case *multithreaded.State: case *multithreaded.State:
return &VersionedState{ if arch.IsMips32 {
Version: VersionMultiThreaded, return &VersionedState{
FPVMState: state, Version: VersionMultiThreaded,
}, nil FPVMState: state,
}, nil
} else {
return &VersionedState{
Version: VersionMultiThreaded64,
FPVMState: state,
}, nil
}
default: default:
return nil, fmt.Errorf("%w: %T", ErrUnknownVersion, state) return nil, fmt.Errorf("%w: %T", ErrUnknownVersion, state)
} }
...@@ -82,6 +95,9 @@ func (s *VersionedState) Deserialize(in io.Reader) error { ...@@ -82,6 +95,9 @@ func (s *VersionedState) Deserialize(in io.Reader) error {
switch s.Version { switch s.Version {
case VersionSingleThreaded2: case VersionSingleThreaded2:
if !arch.IsMips32 {
return ErrUnsupportedMipsArch
}
state := &singlethreaded.State{} state := &singlethreaded.State{}
if err := state.Deserialize(in); err != nil { if err := state.Deserialize(in); err != nil {
return err return err
...@@ -89,6 +105,19 @@ func (s *VersionedState) Deserialize(in io.Reader) error { ...@@ -89,6 +105,19 @@ func (s *VersionedState) Deserialize(in io.Reader) error {
s.FPVMState = state s.FPVMState = state
return nil return nil
case VersionMultiThreaded: case VersionMultiThreaded:
if !arch.IsMips32 {
return ErrUnsupportedMipsArch
}
state := &multithreaded.State{}
if err := state.Deserialize(in); err != nil {
return err
}
s.FPVMState = state
return nil
case VersionMultiThreaded64:
if arch.IsMips32 {
return ErrUnsupportedMipsArch
}
state := &multithreaded.State{} state := &multithreaded.State{}
if err := state.Deserialize(in); err != nil { if err := state.Deserialize(in); err != nil {
return err return err
...@@ -106,6 +135,9 @@ func (s *VersionedState) MarshalJSON() ([]byte, error) { ...@@ -106,6 +135,9 @@ func (s *VersionedState) MarshalJSON() ([]byte, error) {
if s.Version != VersionSingleThreaded { if s.Version != VersionSingleThreaded {
return nil, fmt.Errorf("%w for type %T", ErrJsonNotSupported, s.FPVMState) return nil, fmt.Errorf("%w for type %T", ErrJsonNotSupported, s.FPVMState)
} }
if !arch.IsMips32 {
return nil, ErrUnsupportedMipsArch
}
return json.Marshal(s.FPVMState) return json.Marshal(s.FPVMState)
} }
...@@ -117,6 +149,8 @@ func (s StateVersion) String() string { ...@@ -117,6 +149,8 @@ func (s StateVersion) String() string {
return "multithreaded" return "multithreaded"
case VersionSingleThreaded2: case VersionSingleThreaded2:
return "singlethreaded-2" return "singlethreaded-2"
case VersionMultiThreaded64:
return "multithreaded64"
default: default:
return "unknown" return "unknown"
} }
...@@ -130,6 +164,8 @@ func ParseStateVersion(ver string) (StateVersion, error) { ...@@ -130,6 +164,8 @@ func ParseStateVersion(ver string) (StateVersion, error) {
return VersionMultiThreaded, nil return VersionMultiThreaded, nil
case "singlethreaded-2": case "singlethreaded-2":
return VersionSingleThreaded2, nil return VersionSingleThreaded2, nil
case "multithreaded64":
return VersionMultiThreaded64, nil
default: default:
return StateVersion(0), errors.New("unknown state version") return StateVersion(0), errors.New("unknown state version")
} }
......
...@@ -49,6 +49,10 @@ func TestLoadStateFromFile(t *testing.T) { ...@@ -49,6 +49,10 @@ func TestLoadStateFromFile(t *testing.T) {
}) })
} }
func TestLoadStateFromFile64(t *testing.T) {
t.Skip("TODO(#12205): Test asserting that cannon64 fails to decode a 32-bit state")
}
func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) { func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) {
tests := []struct { tests := []struct {
version StateVersion version StateVersion
......
package mipsevm package mipsevm
import "github.com/ethereum/go-ethereum/common" import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum/go-ethereum/common"
)
type LocalContext common.Hash type LocalContext common.Hash
...@@ -13,7 +16,7 @@ type StepWitness struct { ...@@ -13,7 +16,7 @@ type StepWitness struct {
PreimageKey [32]byte // zeroed when no pre-image is accessed PreimageKey [32]byte // zeroed when no pre-image is accessed
PreimageValue []byte // including the 8-byte length prefix PreimageValue []byte // including the 8-byte length prefix
PreimageOffset uint32 PreimageOffset arch.Word
} }
func (wit *StepWitness) HasPreimage() bool { func (wit *StepWitness) HasPreimage() bool {
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"slices"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/versions" "github.com/ethereum-optimism/optimism/cannon/mipsevm/versions"
) )
...@@ -20,9 +21,7 @@ var vmFS embed.FS ...@@ -20,9 +21,7 @@ var vmFS embed.FS
const baseDir = "embeds" const baseDir = "embeds"
func ExecuteCannon(ctx context.Context, args []string, ver versions.StateVersion) error { func ExecuteCannon(ctx context.Context, args []string, ver versions.StateVersion) error {
switch ver { if !slices.Contains(versions.StateVersionTypes, ver) {
case versions.VersionSingleThreaded, versions.VersionSingleThreaded2, versions.VersionMultiThreaded:
default:
return errors.New("unsupported version") return errors.New("unsupported version")
} }
......
...@@ -10,7 +10,6 @@ import ( ...@@ -10,7 +10,6 @@ import (
) )
func Run(ctx *cli.Context) error { func Run(ctx *cli.Context) error {
fmt.Printf("args %v\n", os.Args[:])
if len(os.Args) == 3 && os.Args[2] == "--help" { if len(os.Args) == 3 && os.Args[2] == "--help" {
if err := list(); err != nil { if err := list(); err != nil {
return err return err
......
...@@ -49,6 +49,7 @@ require ( ...@@ -49,6 +49,7 @@ require (
golang.org/x/sync v0.8.0 golang.org/x/sync v0.8.0
golang.org/x/term v0.24.0 golang.org/x/term v0.24.0
golang.org/x/time v0.6.0 golang.org/x/time v0.6.0
lukechampine.com/uint128 v1.3.0
) )
require ( require (
......
...@@ -1098,6 +1098,8 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh ...@@ -1098,6 +1098,8 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo=
lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment