Commit 93ca2941 authored by Inphi's avatar Inphi Committed by GitHub

cannon: Finish emulating rest of 64-bit instructions (#12483)

* cannon: Finish emulating rest of 64-bit instructions

This fixes the 64-bit stubs for various instructions (except lld/scd).

* review comments; fix dmult

* add todo

* test div by zero

* add a couple more dmultu tests

* remove dead code

* cannon: Fix remaining mips64 emulation bugs

* fix 64-bit Makefile build script; review comments

* fix build script
parent 239330b4
...@@ -43,7 +43,7 @@ elf: ...@@ -43,7 +43,7 @@ elf:
make -C ./testdata/example elf make -C ./testdata/example elf
sanitize-program: sanitize-program:
@if ! { mips-linux-gnu-objdump -d -j .text $$GUEST_PROGRAM | awk '{print $3}' | grep -Ew -m1 '(bgezal|bltzal)'; }; then \ @if ! { mips-linux-gnu-objdump -d -j .text $$GUEST_PROGRAM | awk '{print $3}' | grep -Ew -m1 '(bltzal)'; }; then \
echo "guest program is sanitized for unsupported instructions"; \ echo "guest program is sanitized for unsupported instructions"; \
else \ else \
echo "found unsupported instructions in the guest program"; \ echo "found unsupported instructions in the guest program"; \
...@@ -93,6 +93,8 @@ fuzz: ...@@ -93,6 +93,8 @@ fuzz:
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests
# Multi-threaded tests # Multi-threaded tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests
# 64-bit tests
go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateDmultInsn ./mipsevm/tests
.PHONY: \ .PHONY: \
cannon32-impl \ cannon32-impl \
......
...@@ -12,6 +12,7 @@ Supported 63 instructions: ...@@ -12,6 +12,7 @@ Supported 63 instructions:
| `Conditional Branch` | `beq` | Branch on equal. | | `Conditional Branch` | `beq` | Branch on equal. |
| `Conditional Branch` | `bgez` | Branch on greater than or equal to zero. | | `Conditional Branch` | `bgez` | Branch on greater than or equal to zero. |
| `Conditional Branch` | `bgtz` | Branch on greater than zero. | | `Conditional Branch` | `bgtz` | Branch on greater than zero. |
| `Conditional Branch` | `bgezal` | Branch and link on greater than or equal to zero. |
| `Conditional Branch` | `blez` | Branch on less than or equal to zero. | | `Conditional Branch` | `blez` | Branch on less than or equal to zero. |
| `Conditional Branch` | `bltz` | Branch on less than zero. | | `Conditional Branch` | `bltz` | Branch on less than zero. |
| `Conditional Branch` | `bne` | Branch on not equal. | | `Conditional Branch` | `bne` | Branch on not equal. |
......
...@@ -79,6 +79,8 @@ const ( ...@@ -79,6 +79,8 @@ const (
SysLlseek = 4140 SysLlseek = 4140
SysMinCore = 4217 SysMinCore = 4217
SysTgkill = 4266 SysTgkill = 4266
SysGetRLimit = 4076
SysLseek = 4019
// Profiling-related syscalls // Profiling-related syscalls
SysSetITimer = 4104 SysSetITimer = 4104
SysTimerCreate = 4257 SysTimerCreate = 4257
......
...@@ -25,10 +25,12 @@ const ( ...@@ -25,10 +25,12 @@ const (
AddressMask = 0xFFFFFFFFFFFFFFF8 AddressMask = 0xFFFFFFFFFFFFFFF8
ExtMask = 0x7 ExtMask = 0x7
HeapStart = 0x10_00_00_00_00_00_00_00 // Ensure virtual address is limited to 48-bits as many user programs assume such to implement packed pointers
HeapEnd = 0x60_00_00_00_00_00_00_00 // limit 0x00_00_FF_FF_FF_FF_FF_FF
ProgramBreak = 0x40_00_00_00_00_00_00_00 HeapStart = 0x00_00_10_00_00_00_00_00
HighMemoryStart = 0x7F_FF_FF_FF_D0_00_00_00 HeapEnd = 0x00_00_60_00_00_00_00_00
ProgramBreak = 0x00_00_40_00_00_00_00_00
HighMemoryStart = 0x00_00_7F_FF_FF_FF_F0_00
) )
// MIPS64 syscall table - https://github.com/torvalds/linux/blob/3efc57369a0ce8f76bf0804f7e673982384e4ac9/arch/mips/kernel/syscalls/syscall_n64.tbl. Generate the syscall numbers using the Makefile in that directory. // MIPS64 syscall table - https://github.com/torvalds/linux/blob/3efc57369a0ce8f76bf0804f7e673982384e4ac9/arch/mips/kernel/syscalls/syscall_n64.tbl. Generate the syscall numbers using the Makefile in that directory.
...@@ -85,6 +87,8 @@ const ( ...@@ -85,6 +87,8 @@ const (
SysLlseek = UndefinedSysNr SysLlseek = UndefinedSysNr
SysMinCore = 5026 SysMinCore = 5026
SysTgkill = 5225 SysTgkill = 5225
SysGetRLimit = 5095
SysLseek = 5008
// Profiling-related syscalls // Profiling-related syscalls
SysSetITimer = 5036 SysSetITimer = 5036
SysTimerCreate = 5216 SysTimerCreate = 5216
......
This diff is collapsed.
...@@ -11,11 +11,12 @@ import ( ...@@ -11,11 +11,12 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"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/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
) )
func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM { func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta *program.Metadata) mipsevm.FPVM {
return NewInstrumentedState(state, po, stdOut, stdErr, log, nil) return NewInstrumentedState(state, po, stdOut, stdErr, log, meta)
} }
func TestInstrumentedState_OpenMips(t *testing.T) { func TestInstrumentedState_OpenMips(t *testing.T) {
...@@ -35,7 +36,7 @@ func TestInstrumentedState_Claim(t *testing.T) { ...@@ -35,7 +36,7 @@ func TestInstrumentedState_Claim(t *testing.T) {
func TestInstrumentedState_MultithreadedProgram(t *testing.T) { func TestInstrumentedState_MultithreadedProgram(t *testing.T) {
t.Parallel() t.Parallel()
state, _ := testutil.LoadELFProgram(t, "../../testdata/example/bin/multithreaded.elf", CreateInitialState, false) state, _ := testutil.LoadELFProgram(t, testutil.ProgramPath("multithreaded"), CreateInitialState, false)
oracle := testutil.StaticOracle(t, []byte{}) oracle := testutil.StaticOracle(t, []byte{})
var stdOutBuf, stdErrBuf bytes.Buffer var stdOutBuf, stdErrBuf bytes.Buffer
...@@ -78,7 +79,7 @@ func TestInstrumentedState_Alloc(t *testing.T) { ...@@ -78,7 +79,7 @@ func TestInstrumentedState_Alloc(t *testing.T) {
test := test test := test
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
t.Parallel() t.Parallel()
state, meta := testutil.LoadELFProgram(t, "../../testdata/example/bin/alloc.elf", CreateInitialState, false) state, meta := testutil.LoadELFProgram(t, testutil.ProgramPath("alloc"), CreateInitialState, false)
oracle := testutil.AllocOracle(t, test.numAllocs, test.allocSize) oracle := testutil.AllocOracle(t, test.numAllocs, test.allocSize)
us := NewInstrumentedState(state, oracle, os.Stdout, os.Stderr, testutil.CreateLogger(), meta) us := NewInstrumentedState(state, oracle, os.Stdout, os.Stderr, testutil.CreateLogger(), meta)
......
...@@ -168,9 +168,9 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -168,9 +168,9 @@ func (m *InstrumentedState) handleSyscall() error {
m.memoryTracker.TrackMemAccess(effAddr) m.memoryTracker.TrackMemAccess(effAddr)
m.state.Memory.SetWord(effAddr, secs) m.state.Memory.SetWord(effAddr, secs)
m.handleMemoryUpdate(effAddr) m.handleMemoryUpdate(effAddr)
m.memoryTracker.TrackMemAccess2(effAddr + 4) m.memoryTracker.TrackMemAccess2(effAddr + arch.WordSizeBytes)
m.state.Memory.SetWord(effAddr+4, nsecs) m.state.Memory.SetWord(effAddr+arch.WordSizeBytes, nsecs)
m.handleMemoryUpdate(effAddr + 4) m.handleMemoryUpdate(effAddr + arch.WordSizeBytes)
default: default:
v0 = exec.SysErrorSignal v0 = exec.SysErrorSignal
v1 = exec.MipsEINVAL v1 = exec.MipsEINVAL
...@@ -206,6 +206,8 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -206,6 +206,8 @@ func (m *InstrumentedState) handleSyscall() error {
case arch.SysTimerCreate: case arch.SysTimerCreate:
case arch.SysTimerSetTime: case arch.SysTimerSetTime:
case arch.SysTimerDelete: case arch.SysTimerDelete:
case arch.SysGetRLimit:
case arch.SysLseek:
default: default:
// These syscalls have the same values on 64-bit. So we use if-stmts here to avoid "duplicate case" compiler error for the cannon64 build // These syscalls have the same values on 64-bit. So we use if-stmts here to avoid "duplicate case" compiler error for the cannon64 build
if arch.IsMips32 && syscallNum == arch.SysFstat64 || syscallNum == arch.SysStat64 || syscallNum == arch.SysLlseek { if arch.IsMips32 && syscallNum == arch.SysFstat64 || syscallNum == arch.SysStat64 || syscallNum == arch.SysLlseek {
......
...@@ -7,10 +7,11 @@ import ( ...@@ -7,10 +7,11 @@ 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/program"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
) )
func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM { func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta *program.Metadata) mipsevm.FPVM {
return NewInstrumentedState(state, po, stdOut, stdErr, nil) return NewInstrumentedState(state, po, stdOut, stdErr, nil)
} }
......
This diff is collapsed.
...@@ -991,3 +991,94 @@ func TestEntryEVM(t *testing.T) { ...@@ -991,3 +991,94 @@ func TestEntryEVM(t *testing.T) {
}) })
} }
} }
func TestEVMSingleStepBranch(t *testing.T) {
var tracer *tracing.Hooks
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
pc Word
expectNextPC Word
opcode uint32
regimm uint32
expectLink bool
rs arch.SignedInteger
rt Word
offset uint16
}{
// blez
{name: "blez", pc: 0, opcode: 0x6, rs: 0x5, offset: 0x100, expectNextPC: 0x8},
{name: "blez large rs", pc: 0x10, opcode: 0x6, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x18},
{name: "blez zero rs", pc: 0x10, opcode: 0x6, rs: 0x0, offset: 0x100, expectNextPC: 0x414},
{name: "blez sign rs", pc: 0x10, opcode: 0x6, rs: -1, offset: 0x100, expectNextPC: 0x414},
{name: "blez rs only sign bit set", pc: 0x10, opcode: 0x6, rs: testutil.ToSignedInteger(0x80_00_00_00), offset: 0x100, expectNextPC: 0x414},
{name: "blez sign-extended offset", pc: 0x10, opcode: 0x6, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14},
// bgtz
{name: "bgtz", pc: 0, opcode: 0x7, rs: 0x5, offset: 0x100, expectNextPC: 0x404},
{name: "bgtz sign-extended offset", pc: 0x10, opcode: 0x7, rs: 0x5, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14},
{name: "bgtz large rs", pc: 0x10, opcode: 0x7, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x414},
{name: "bgtz zero rs", pc: 0x10, opcode: 0x7, rs: 0x0, offset: 0x100, expectNextPC: 0x18},
{name: "bgtz sign rs", pc: 0x10, opcode: 0x7, rs: -1, offset: 0x100, expectNextPC: 0x18},
{name: "bgtz rs only sign bit set", pc: 0x10, opcode: 0x7, rs: testutil.ToSignedInteger(0x80_00_00_00), offset: 0x100, expectNextPC: 0x18},
// bltz t0, $x
{name: "bltz", pc: 0, opcode: 0x1, regimm: 0x0, rs: 0x5, offset: 0x100, expectNextPC: 0x8},
{name: "bltz large rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x18},
{name: "bltz zero rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: 0x0, offset: 0x100, expectNextPC: 0x18},
{name: "bltz sign rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x100, expectNextPC: 0x414},
{name: "bltz rs only sign bit set", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: testutil.ToSignedInteger(0x80_00_00_00), offset: 0x100, expectNextPC: 0x414},
{name: "bltz sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14},
{name: "bltz large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x7F_FF, expectNextPC: 0x2_00_10},
// bgez t0, $x
{name: "bgez", pc: 0, opcode: 0x1, regimm: 0x1, rs: 0x5, offset: 0x100, expectNextPC: 0x404},
{name: "bgez large rs", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x414},
{name: "bgez zero rs", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 0x0, offset: 0x100, expectNextPC: 0x414},
{name: "bgez branch not taken", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: -1, offset: 0x100, expectNextPC: 0x18},
{name: "bgez sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14},
{name: "bgez large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x70_00, expectNextPC: 0x1_C0_14},
{name: "bgez fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10},
// bgezal t0, $x
{name: "bgezal", pc: 0, opcode: 0x1, regimm: 0x11, rs: 0x5, offset: 0x100, expectNextPC: 0x404, expectLink: true},
{name: "bgezal large rs", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x414, expectLink: true},
{name: "bgezal zero rs", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 0x0, offset: 0x100, expectNextPC: 0x414, expectLink: true},
{name: "bgezal branch not taken", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: -1, offset: 0x100, expectNextPC: 0x18, expectLink: true},
{name: "bgezal sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14, expectLink: true},
{name: "bgezal large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x70_00, expectNextPC: 0x1_C0_14, expectLink: true},
{name: "bgezal fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10, expectLink: true},
}
for _, v := range versions {
for i, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc))
state := goVm.GetState()
const rsReg = 8 // t0
insn := tt.opcode<<26 | rsReg<<21 | tt.regimm<<16 | uint32(tt.offset)
state.GetMemory().SetUint32(tt.pc, insn)
state.GetRegistersRef()[rsReg] = Word(tt.rs)
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.Step += 1
expected.PC = state.GetCpu().NextPC
expected.NextPC = tt.expectNextPC
if tt.expectLink {
expected.Registers[31] = state.GetPC() + 8
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer)
})
}
}
}
...@@ -1011,7 +1011,7 @@ func testEVM_SysClockGettime(t *testing.T, clkid Word) { ...@@ -1011,7 +1011,7 @@ func testEVM_SysClockGettime(t *testing.T, clkid Word) {
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 & arch.AddressMask effAddr := c.timespecAddr & arch.AddressMask
effAddr2 := effAddr + 4 effAddr2 := effAddr + arch.WordSizeBytes
step := state.Step step := state.Step
// Define LL-related params // Define LL-related params
...@@ -1121,6 +1121,8 @@ var NoopSyscalls = map[string]uint32{ ...@@ -1121,6 +1121,8 @@ var NoopSyscalls = map[string]uint32{
"SysLlseek": 4140, "SysLlseek": 4140,
"SysMinCore": 4217, "SysMinCore": 4217,
"SysTgkill": 4266, "SysTgkill": 4266,
"SysGetRLimit": 4076,
"SysLseek": 4019,
"SysMunmap": 4091, "SysMunmap": 4091,
"SysSetITimer": 4104, "SysSetITimer": 4104,
"SysTimerCreate": 4257, "SysTimerCreate": 4257,
......
...@@ -40,3 +40,9 @@ func SetMemoryUint64(t require.TestingT, mem *memory.Memory, addr Word, value ui ...@@ -40,3 +40,9 @@ func SetMemoryUint64(t require.TestingT, mem *memory.Memory, addr Word, value ui
actual := mem.GetWord(effAddr) actual := mem.GetWord(effAddr)
require.Equal(t, Word(value), actual) require.Equal(t, Word(value), actual)
} }
// ToSignedInteger converts the unsigend Word to a SignedInteger.
// Useful for avoiding Go compiler warnings for literals that don't fit in a signed type
func ToSignedInteger(x Word) arch.SignedInteger {
return arch.SignedInteger(x)
}
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,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"
) )
...@@ -26,3 +27,12 @@ func LoadELFProgram[T mipsevm.FPVMState](t require.TestingT, name string, initSt ...@@ -26,3 +27,12 @@ func LoadELFProgram[T mipsevm.FPVMState](t require.TestingT, name string, initSt
require.NoError(t, program.PatchStack(state), "add initial stack") require.NoError(t, program.PatchStack(state), "add initial stack")
return state, meta return state, meta
} }
// ProgramPath returns the appropriate ELF test program for the current architecture
func ProgramPath(programName string) string {
basename := programName + ".elf"
if !arch.IsMips32 {
basename = programName + ".64.elf"
}
return "../../testdata/example/bin/" + basename
}
...@@ -67,7 +67,7 @@ func StaticPrecompileOracle(t *testing.T, precompile common.Address, requiredGas ...@@ -67,7 +67,7 @@ func StaticPrecompileOracle(t *testing.T, precompile common.Address, requiredGas
} }
func ClaimTestOracle(t *testing.T) (po mipsevm.PreimageOracle, stdOut string, stdErr string) { func ClaimTestOracle(t *testing.T) (po mipsevm.PreimageOracle, stdOut string, stdErr string) {
s := uint64(1000) s := uint64(0x00FFFFFF_00001000)
a := uint64(3) a := uint64(3)
b := uint64(4) b := uint64(4)
......
...@@ -16,10 +16,14 @@ import ( ...@@ -16,10 +16,14 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
type VMFactory[T mipsevm.FPVMState] func(state T, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM type VMFactory[T mipsevm.FPVMState] func(state T, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta *program.Metadata) mipsevm.FPVM
type StateFactory[T mipsevm.FPVMState] func() T type StateFactory[T mipsevm.FPVMState] func() T
func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFactory[T], vmFactory VMFactory[T], excludedTests ...string) { func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFactory[T], vmFactory VMFactory[T], excludedTests ...string) {
if !arch.IsMips32 {
// TODO: guard these tests by the cannon32 build tag
t.Skip("Open MIPS tests are not appropriate for cannon64")
}
testFiles, err := os.ReadDir("../tests/open_mips_tests/test/bin") testFiles, err := os.ReadDir("../tests/open_mips_tests/test/bin")
require.NoError(t, err) require.NoError(t, err)
...@@ -52,7 +56,7 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa ...@@ -52,7 +56,7 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa
// set the return address ($ra) to jump into when test completes // set the return address ($ra) to jump into when test completes
state.GetRegistersRef()[31] = EndAddr state.GetRegistersRef()[31] = EndAddr
us := vmFactory(state, oracle, os.Stdout, os.Stderr, CreateLogger()) us := vmFactory(state, oracle, os.Stdout, os.Stderr, CreateLogger(), nil)
// Catch panics and check if they are expected // Catch panics and check if they are expected
defer func() { defer func() {
...@@ -94,12 +98,13 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa ...@@ -94,12 +98,13 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa
} }
func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.CreateInitialFPVMState[T], vmFactory VMFactory[T], doPatchGo bool) { func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.CreateInitialFPVMState[T], vmFactory VMFactory[T], doPatchGo bool) {
state, _ := LoadELFProgram(t, "../../testdata/example/bin/hello.elf", initState, doPatchGo) state, meta := LoadELFProgram(t, ProgramPath("hello"), initState, doPatchGo)
var stdOutBuf, stdErrBuf bytes.Buffer var stdOutBuf, stdErrBuf bytes.Buffer
us := vmFactory(state, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger()) us := vmFactory(state, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger(), meta)
for i := 0; i < 400_000; i++ { maxSteps := 430_000
for i := 0; i < maxSteps; i++ {
if us.GetState().GetExited() { if us.GetState().GetExited() {
break break
} }
...@@ -107,7 +112,7 @@ func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.Create ...@@ -107,7 +112,7 @@ func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.Create
require.NoError(t, err) require.NoError(t, err)
} }
require.True(t, state.GetExited(), "must complete program") require.Truef(t, state.GetExited(), "must complete program. reached %d of max %d steps", state.GetStep(), maxSteps)
require.Equal(t, uint8(0), state.GetExitCode(), "exit with 0") require.Equal(t, uint8(0), state.GetExitCode(), "exit with 0")
require.Equal(t, "hello world!\n", stdOutBuf.String(), "stdout says hello") require.Equal(t, "hello world!\n", stdOutBuf.String(), "stdout says hello")
...@@ -115,12 +120,12 @@ func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.Create ...@@ -115,12 +120,12 @@ func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.Create
} }
func RunVMTest_Claim[T mipsevm.FPVMState](t *testing.T, initState program.CreateInitialFPVMState[T], vmFactory VMFactory[T], doPatchGo bool) { func RunVMTest_Claim[T mipsevm.FPVMState](t *testing.T, initState program.CreateInitialFPVMState[T], vmFactory VMFactory[T], doPatchGo bool) {
state, _ := LoadELFProgram(t, "../../testdata/example/bin/claim.elf", initState, doPatchGo) state, meta := LoadELFProgram(t, ProgramPath("claim"), initState, doPatchGo)
oracle, expectedStdOut, expectedStdErr := ClaimTestOracle(t) oracle, expectedStdOut, expectedStdErr := ClaimTestOracle(t)
var stdOutBuf, stdErrBuf bytes.Buffer var stdOutBuf, stdErrBuf bytes.Buffer
us := vmFactory(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger()) us := vmFactory(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger(), meta)
for i := 0; i < 2000_000; i++ { for i := 0; i < 2000_000; i++ {
if us.GetState().GetExited() { if us.GetState().GetExited() {
......
all: elf all: elf
.PHONY: elf32
elf32: $(patsubst %/go.mod,bin/%.elf,$(wildcard */go.mod))
.PHONY: elf64
elf64: $(patsubst %/go.mod,bin/%.64.elf,$(wildcard */go.mod))
.PHONY: elf .PHONY: elf
elf: $(patsubst %/go.mod,bin/%.elf,$(wildcard */go.mod)) elf: elf32 elf64
.PHONY: dump .PHONY: dump
dump: $(patsubst %/go.mod,bin/%.dump,$(wildcard */go.mod)) dump: $(patsubst %/go.mod,bin/%.dump,$(wildcard */go.mod))
...@@ -9,6 +15,9 @@ dump: $(patsubst %/go.mod,bin/%.dump,$(wildcard */go.mod)) ...@@ -9,6 +15,9 @@ dump: $(patsubst %/go.mod,bin/%.dump,$(wildcard */go.mod))
bin: bin:
mkdir bin mkdir bin
bin/%.64.elf: bin
cd $(@:bin/%.64.elf=%) && GOOS=linux GOARCH=mips64 GOMIPS64=softfloat go build -o ../$@ .
# take any directory with a go mod, and build an ELF # take any directory with a go mod, and build an ELF
# verify output with: readelf -h bin/<name>.elf # verify output with: readelf -h bin/<name>.elf
# result is mips32, big endian, R3000 # result is mips32, big endian, R3000
......
module alloc module alloc
go 1.22 go 1.22.0
toolchain go1.22.7 toolchain go1.22.7
......
module claim module claim
go 1.22 go 1.22.0
toolchain go1.22.7 toolchain go1.22.7
......
module hello module hello
go 1.22 go 1.22.0
toolchain go1.22.0 toolchain go1.22.7
module github.com/ethereum-optimism/optimism module github.com/ethereum-optimism/optimism
go 1.22 go 1.22.0
toolchain go1.22.7 toolchain go1.22.7
...@@ -47,11 +47,10 @@ require ( ...@@ -47,11 +47,10 @@ require (
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
github.com/urfave/cli/v2 v2.27.5 github.com/urfave/cli/v2 v2.27.5
golang.org/x/crypto v0.28.0 golang.org/x/crypto v0.28.0
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
golang.org/x/sync v0.8.0 golang.org/x/sync v0.8.0
golang.org/x/term v0.25.0 golang.org/x/term v0.25.0
golang.org/x/time v0.7.0 golang.org/x/time v0.7.0
lukechampine.com/uint128 v1.3.0
) )
require ( require (
...@@ -95,7 +94,7 @@ require ( ...@@ -95,7 +94,7 @@ require (
github.com/ethereum/c-kzg-4844 v1.0.0 // indirect github.com/ethereum/c-kzg-4844 v1.0.0 // indirect
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect
github.com/fatih/color v1.16.0 // indirect github.com/fatih/color v1.16.0 // indirect
github.com/felixge/fgprof v0.9.3 // indirect github.com/felixge/fgprof v0.9.5 // indirect
github.com/ferranbt/fastssz v0.1.2 // indirect github.com/ferranbt/fastssz v0.1.2 // indirect
github.com/flynn/noise v1.1.0 // indirect github.com/flynn/noise v1.1.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect github.com/francoispqt/gojay v1.2.13 // indirect
...@@ -114,7 +113,7 @@ require ( ...@@ -114,7 +113,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gopacket v1.1.19 // indirect github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect github.com/google/pprof v0.0.0-20241009165004-a3522334989c // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect github.com/gorilla/websocket v1.5.3 // indirect
github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect
...@@ -234,11 +233,11 @@ require ( ...@@ -234,11 +233,11 @@ require (
go.uber.org/mock v0.4.0 // indirect go.uber.org/mock v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect go.uber.org/zap v1.27.0 // indirect
golang.org/x/mod v0.20.0 // indirect golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.30.0 // indirect golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect golang.org/x/text v0.19.0 // indirect
golang.org/x/tools v0.24.0 // indirect golang.org/x/tools v0.26.0 // indirect
google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e // indirect google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 // indirect
......
This diff is collapsed.
...@@ -140,12 +140,12 @@ ...@@ -140,12 +140,12 @@
"sourceCodeHash": "0x2ab6be69795109a1ee04c5693a34d6ce0ff90b62e404cdeb18178bab18d06784" "sourceCodeHash": "0x2ab6be69795109a1ee04c5693a34d6ce0ff90b62e404cdeb18178bab18d06784"
}, },
"src/cannon/MIPS.sol": { "src/cannon/MIPS.sol": {
"initCodeHash": "0x8fb590d45fa06fdc7ae55860827d6b1abf070c9dc5e18cd28176e844fa1da6c9", "initCodeHash": "0x696c3ce334c11d0f9633945babac22b1b65848ff00d2cf6c4cb18116bbf138b2",
"sourceCodeHash": "0x01d3d59020ec29ce78f68f977da5b7754455743121bda65626509864ab6c9da9" "sourceCodeHash": "0x3c2e5d6d39b29a60dcd1a776363518c87fcfef9a8d80e38696f56b6eec5bd53a"
}, },
"src/cannon/MIPS2.sol": { "src/cannon/MIPS2.sol": {
"initCodeHash": "0x88acf3297642c2c9e0c1e92b48f59f7015915f25fb816cac44ee0073a1c93ccb", "initCodeHash": "0x5c292882075bd06e68b89119be9096040bc913f51bb878ce4a9dfdd674330dd5",
"sourceCodeHash": "0x3dd89839f268569cbf2659beb42e3ac3c53887472cfc94a6e339d2b3a963b941" "sourceCodeHash": "0x17c7b0edb9d20932eaf1b038e3e05e457f0461d2c8691ba1940fb4c2b0dfd123"
}, },
"src/cannon/PreimageOracle.sol": { "src/cannon/PreimageOracle.sol": {
"initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c", "initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c",
......
...@@ -44,8 +44,8 @@ contract MIPS is ISemver { ...@@ -44,8 +44,8 @@ contract MIPS is ISemver {
} }
/// @notice The semantic version of the MIPS contract. /// @notice The semantic version of the MIPS contract.
/// @custom:semver 1.2.1-beta.4 /// @custom:semver 1.2.1-beta.5
string public constant version = "1.2.1-beta.4"; string public constant version = "1.2.1-beta.5";
/// @notice The preimage oracle contract. /// @notice The preimage oracle contract.
IPreimageOracle internal immutable ORACLE; IPreimageOracle internal immutable ORACLE;
......
...@@ -60,8 +60,8 @@ contract MIPS2 is ISemver { ...@@ -60,8 +60,8 @@ contract MIPS2 is ISemver {
} }
/// @notice The semantic version of the MIPS2 contract. /// @notice The semantic version of the MIPS2 contract.
/// @custom:semver 1.0.0-beta.17 /// @custom:semver 1.0.0-beta.18
string public constant version = "1.0.0-beta.17"; string public constant version = "1.0.0-beta.18";
/// @notice The preimage oracle contract. /// @notice The preimage oracle contract.
IPreimageOracle internal immutable ORACLE; IPreimageOracle internal immutable ORACLE;
...@@ -593,6 +593,10 @@ contract MIPS2 is ISemver { ...@@ -593,6 +593,10 @@ contract MIPS2 is ISemver {
// ignored // ignored
} else if (syscall_no == sys.SYS_TIMERDELETE) { } else if (syscall_no == sys.SYS_TIMERDELETE) {
// ignored // ignored
} else if (syscall_no == sys.SYS_GETRLIMIT) {
// ignored
} else if (syscall_no == sys.SYS_LSEEK) {
// ignored
} else { } else {
if (syscall_no == sys.SYS_FSTAT64 || syscall_no == sys.SYS_STAT64 || syscall_no == sys.SYS_LLSEEK) { if (syscall_no == sys.SYS_FSTAT64 || syscall_no == sys.SYS_STAT64 || syscall_no == sys.SYS_LLSEEK) {
// noop // noop
......
...@@ -7,6 +7,7 @@ import { MIPSState as st } from "src/cannon/libraries/MIPSState.sol"; ...@@ -7,6 +7,7 @@ import { MIPSState as st } from "src/cannon/libraries/MIPSState.sol";
library MIPSInstructions { library MIPSInstructions {
uint32 internal constant OP_LOAD_LINKED = 0x30; uint32 internal constant OP_LOAD_LINKED = 0x30;
uint32 internal constant OP_STORE_CONDITIONAL = 0x38; uint32 internal constant OP_STORE_CONDITIONAL = 0x38;
uint32 internal constant REG_RA = 31;
struct CoreStepLogicParams { struct CoreStepLogicParams {
/// @param opcode The opcode value parsed from insn_. /// @param opcode The opcode value parsed from insn_.
...@@ -501,6 +502,11 @@ library MIPSInstructions { ...@@ -501,6 +502,11 @@ library MIPSInstructions {
if (rtv == 1) { if (rtv == 1) {
shouldBranch = int32(_rs) >= 0; shouldBranch = int32(_rs) >= 0;
} }
// bgezal (i.e. bal mnemonic)
if (rtv == 0x11) {
shouldBranch = int32(_rs) >= 0;
_registers[REG_RA] = _cpu.pc + 8; // always set regardless of branch taken
}
} }
// Update the state's previous PC // Update the state's previous PC
......
...@@ -71,6 +71,8 @@ library MIPSSyscalls { ...@@ -71,6 +71,8 @@ library MIPSSyscalls {
uint32 internal constant SYS_LLSEEK = 4140; uint32 internal constant SYS_LLSEEK = 4140;
uint32 internal constant SYS_MINCORE = 4217; uint32 internal constant SYS_MINCORE = 4217;
uint32 internal constant SYS_TGKILL = 4266; uint32 internal constant SYS_TGKILL = 4266;
uint32 internal constant SYS_GETRLIMIT = 4076;
uint32 internal constant SYS_LSEEK = 4019;
// profiling-related syscalls - ignored // profiling-related syscalls - ignored
uint32 internal constant SYS_SETITIMER = 4104; uint32 internal constant SYS_SETITIMER = 4104;
......
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