From a68b49f246ddc1726c062af945eb989c52a4d5a5 Mon Sep 17 00:00:00 2001 From: clabby Date: Wed, 7 Aug 2024 02:42:49 -0600 Subject: [PATCH 1/4] init: Cannon -> MIPS64 --- cannon/cmd/run.go | 10 +- cannon/mipsevm/exec/memory.go | 10 +- cannon/mipsevm/exec/mips_instructions.go | 247 ++++++++++++------ cannon/mipsevm/exec/mips_syscalls.go | 148 +++++------ cannon/mipsevm/exec/preimage.go | 12 +- cannon/mipsevm/exec/stack.go | 10 +- cannon/mipsevm/iface.go | 6 +- cannon/mipsevm/memory/memory.go | 103 +++++--- cannon/mipsevm/memory/memory_test.go | 22 +- cannon/mipsevm/memory/page.go | 2 +- cannon/mipsevm/multithreaded/instrumented.go | 4 +- cannon/mipsevm/multithreaded/mips.go | 18 +- cannon/mipsevm/multithreaded/stack.go | 12 +- cannon/mipsevm/multithreaded/state.go | 30 +-- cannon/mipsevm/multithreaded/state_test.go | 20 +- cannon/mipsevm/multithreaded/thread.go | 30 +-- cannon/mipsevm/program/load.go | 11 +- cannon/mipsevm/program/metadata.go | 14 +- cannon/mipsevm/program/patch.go | 29 +- cannon/mipsevm/singlethreaded/instrumented.go | 6 +- cannon/mipsevm/singlethreaded/mips.go | 10 +- cannon/mipsevm/singlethreaded/state.go | 46 ++-- cannon/mipsevm/state.go | 8 +- cannon/mipsevm/tests/evm_test.go | 18 +- cannon/mipsevm/tests/fuzz_evm_test.go | 44 ++-- .../mipsevm/tests/open_mips_tests/README.md | 11 +- .../tests/open_mips_tests/builder.dockerfile | 19 ++ cannon/mipsevm/tests/open_mips_tests/justfile | 23 ++ .../tests/open_mips_tests/make_tests.sh | 42 +++ .../tests/open_mips_tests/maketests.py | 37 --- .../tests/open_mips_tests/test/bin/brk.bin | Bin 48 -> 48 bytes .../tests/open_mips_tests/test/bin/clone.bin | Bin 48 -> 48 bytes .../open_mips_tests/test/bin/exit_group.bin | Bin 44 -> 44 bytes .../tests/open_mips_tests/test/bin/fcntl.bin | Bin 48 -> 48 bytes .../tests/open_mips_tests/test/bin/mmap.bin | Bin 56 -> 56 bytes .../tests/open_mips_tests/test/bin/oracle.bin | Bin 312 -> 312 bytes .../open_mips_tests/test/bin/oracle_kzg.bin | Bin 296 -> 296 bytes .../test/bin/oracle_unaligned_read.bin | Bin 420 -> 404 bytes .../test/bin/oracle_unaligned_write.bin | Bin 380 -> 380 bytes .../tests/open_mips_tests/test/brk.asm | 2 +- .../tests/open_mips_tests/test/clone.asm | 2 +- .../tests/open_mips_tests/test/dadd.asm | 59 +++++ .../tests/open_mips_tests/test/daddi.asm | 57 ++++ .../tests/open_mips_tests/test/daddiu.asm | 57 ++++ .../tests/open_mips_tests/test/daddu.asm | 59 +++++ .../tests/open_mips_tests/test/ddiv.asm | 53 ++++ .../tests/open_mips_tests/test/ddivu.asm | 53 ++++ .../tests/open_mips_tests/test/dmult.asm | 62 +++++ .../tests/open_mips_tests/test/dmultu.asm | 62 +++++ .../tests/open_mips_tests/test/dsll.asm | 46 ++++ .../tests/open_mips_tests/test/dsll32.asm | 55 ++++ .../tests/open_mips_tests/test/dsllv.asm | 47 ++++ .../tests/open_mips_tests/test/dsra.asm | 53 ++++ .../tests/open_mips_tests/test/dsra32.asm | 55 ++++ .../tests/open_mips_tests/test/dsrav.asm | 48 ++++ .../tests/open_mips_tests/test/dsrl.asm | 49 ++++ .../tests/open_mips_tests/test/dsrl32.asm | 49 ++++ .../tests/open_mips_tests/test/dsrlv.asm | 48 ++++ .../tests/open_mips_tests/test/dsub.asm | 59 +++++ .../tests/open_mips_tests/test/dsubu.asm | 59 +++++ .../tests/open_mips_tests/test/exit_group.asm | 2 +- .../tests/open_mips_tests/test/fcntl.asm | 2 +- .../mipsevm/tests/open_mips_tests/test/ld.asm | 54 ++++ .../tests/open_mips_tests/test/lld.asm | 54 ++++ .../tests/open_mips_tests/test/lwu.asm | 48 ++++ .../tests/open_mips_tests/test/mmap.asm | 2 +- .../tests/open_mips_tests/test/oracle.asm | 8 +- .../tests/open_mips_tests/test/oracle_kzg.asm | 8 +- .../test/oracle_unaligned_read.asm | 13 +- .../test/oracle_unaligned_write.asm | 12 +- .../tests/open_mips_tests/test/scd.asm | 74 ++++++ .../mipsevm/tests/open_mips_tests/test/sd.asm | 60 +++++ .../tests/open_mips_tests/test/sdl.asm | 109 ++++++++ .../tests/open_mips_tests/test/sdr.asm | 115 ++++++++ cannon/mipsevm/testutil/constants.go | 2 +- cannon/mipsevm/testutil/vmtests.go | 10 +- cannon/mipsevm/witness.go | 2 +- cannon/testdata/example/Makefile | 4 +- go.mod | 1 + go.sum | 2 + .../game/fault/trace/cannon/prestate_test.go | 2 +- 81 files changed, 2173 insertions(+), 447 deletions(-) create mode 100644 cannon/mipsevm/tests/open_mips_tests/builder.dockerfile create mode 100644 cannon/mipsevm/tests/open_mips_tests/justfile create mode 100644 cannon/mipsevm/tests/open_mips_tests/make_tests.sh delete mode 100755 cannon/mipsevm/tests/open_mips_tests/maketests.py create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dadd.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/daddi.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/daddiu.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/daddu.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/ddiv.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/ddivu.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dmult.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dmultu.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dsll.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dsll32.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dsllv.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dsra.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dsra32.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dsrav.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dsrl.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dsrl32.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dsrlv.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dsub.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/dsubu.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/ld.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/lld.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/lwu.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/scd.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/sd.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/sdl.asm create mode 100644 cannon/mipsevm/tests/open_mips_tests/test/sdr.asm diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index 23b6e1b75719..1c675333c35a 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -126,7 +126,7 @@ type Proof struct { OracleKey hexutil.Bytes `json:"oracle-key,omitempty"` OracleValue hexutil.Bytes `json:"oracle-value,omitempty"` - OracleOffset uint32 `json:"oracle-offset,omitempty"` + OracleOffset uint64 `json:"oracle-offset,omitempty"` } type rawHint string @@ -270,7 +270,7 @@ func Run(ctx *cli.Context) error { stopAtAnyPreimage := false var stopAtPreimageKeyPrefix []byte - stopAtPreimageOffset := uint32(0) + stopAtPreimageOffset := uint64(0) if ctx.IsSet(RunStopAtPreimageFlag.Name) { val := ctx.String(RunStopAtPreimageFlag.Name) parts := strings.Split(val, "@") @@ -279,11 +279,11 @@ func Run(ctx *cli.Context) error { } stopAtPreimageKeyPrefix = common.FromHex(parts[0]) if len(parts) == 2 { - x, err := strconv.ParseUint(parts[1], 10, 32) + x, err := strconv.ParseUint(parts[1], 10, 64) if err != nil { return fmt.Errorf("invalid preimage offset: %w", err) } - stopAtPreimageOffset = uint32(x) + stopAtPreimageOffset = uint64(x) } } else { switch ctx.String(RunStopAtPreimageTypeFlag.Name) { @@ -468,7 +468,7 @@ func Run(ctx *cli.Context) error { } lastPreimageKey, lastPreimageValue, lastPreimageOffset := vm.LastPreimage() - if lastPreimageOffset != ^uint32(0) { + if lastPreimageOffset != ^uint64(0) { if stopAtAnyPreimage { l.Info("Stopping at preimage read") break diff --git a/cannon/mipsevm/exec/memory.go b/cannon/mipsevm/exec/memory.go index d006a6f90560..af4396c0164a 100644 --- a/cannon/mipsevm/exec/memory.go +++ b/cannon/mipsevm/exec/memory.go @@ -7,12 +7,12 @@ import ( ) type MemTracker interface { - TrackMemAccess(addr uint32) + TrackMemAccess(addr uint64) } type MemoryTrackerImpl struct { memory *memory.Memory - lastMemAccess uint32 + lastMemAccess uint64 memProofEnabled bool memProof [memory.MEM_PROOF_SIZE]byte } @@ -21,9 +21,9 @@ func NewMemoryTracker(memory *memory.Memory) *MemoryTrackerImpl { return &MemoryTrackerImpl{memory: memory} } -func (m *MemoryTrackerImpl) TrackMemAccess(effAddr uint32) { +func (m *MemoryTrackerImpl) TrackMemAccess(effAddr uint64) { if m.memProofEnabled && m.lastMemAccess != effAddr { - if m.lastMemAccess != ^uint32(0) { + if m.lastMemAccess != ^uint64(0) { panic(fmt.Errorf("unexpected different mem access at %08x, already have access at %08x buffered", effAddr, m.lastMemAccess)) } m.lastMemAccess = effAddr @@ -33,7 +33,7 @@ func (m *MemoryTrackerImpl) TrackMemAccess(effAddr uint32) { func (m *MemoryTrackerImpl) Reset(enableProof bool) { m.memProofEnabled = enableProof - m.lastMemAccess = ^uint32(0) + m.lastMemAccess = ^uint64(0) } func (m *MemoryTrackerImpl) MemProof() [memory.MEM_PROOF_SIZE]byte { diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index b036940cbf93..97c1ce197f70 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -1,34 +1,37 @@ package exec import ( + "fmt" + "github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" + u128 "lukechampine.com/uint128" ) -func GetInstructionDetails(pc uint32, memory *memory.Memory) (insn, opcode, fun uint32) { - insn = memory.GetMemory(pc) +func GetInstructionDetails(pc uint64, memory *memory.Memory) (insn, opcode, fun uint64) { + insn = uint64(memory.GetMemory(pc)) opcode = insn >> 26 // First 6-bits fun = insn & 0x3f // Last 6-bits return insn, opcode, fun } -func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memory *memory.Memory, insn, opcode, fun uint32, memTracker MemTracker, stackTracker StackTracker) error { +func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint64, memory *memory.Memory, insn, opcode, fun uint64, memTracker MemTracker, stackTracker StackTracker) error { // j-type j/jal if opcode == 2 || opcode == 3 { - linkReg := uint32(0) + linkReg := uint64(0) if opcode == 3 { linkReg = 31 } // Take top 4 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 & 0xFFFFFFFFF0000000) | ((uint64(insn) & 0x03FFFFFF) << 2) stackTracker.PushStack(cpu.PC, target) return HandleJump(cpu, registers, linkReg, target) } // register fetch - rs := uint32(0) // source register 1 value - rt := uint32(0) // source register 2 / temp value + rs := uint64(0) // source register 1 value + rt := uint64(0) // source register 2 / temp value rtReg := (insn >> 16) & 0x1F // R-type or I-type (stores rt) @@ -48,11 +51,11 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memor // SignExtImm rt = SignExtend(insn&0xFFFF, 16) } - } else if opcode >= 0x28 || opcode == 0x22 || opcode == 0x26 { + } else if opcode >= 0x28 || opcode == 0x22 || opcode == 0x26 || opcode == 0x1A || opcode == 0x1B { // store rt value with store rt = registers[rtReg] - // store actual rt with lwl and lwr + // store actual rt with lwl, lwr, ldl, and ldr rdReg = rtReg } @@ -60,17 +63,17 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memor return HandleBranch(cpu, registers, opcode, insn, rtReg, rs) } - storeAddr := uint32(0xFF_FF_FF_FF) + storeAddr := uint64(0xFF_FF_FF_FF_FF_FF_FF_FF) // memory fetch (all I-type) // we do the load for stores also - mem := uint32(0) + mem := uint64(0) if opcode >= 0x20 { // M[R[rs]+SignExtImm] rs += SignExtend(insn&0xFFFF, 16) - addr := rs & 0xFFFFFFFC + addr := rs & 0xFFFFFFFFFFFFFFF8 memTracker.TrackMemAccess(addr) - mem = memory.GetMemory(addr) - if opcode >= 0x28 && opcode != 0x30 { + mem = memory.GetDoubleWord(addr) + if opcode >= 0x28 && opcode != 0x30 && opcode != 0x34 && opcode != 0x37 { // store storeAddr = addr // store opcodes don't write back to a register @@ -81,9 +84,9 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memor // ALU val := ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem) - if opcode == 0 && fun >= 8 && fun < 0x1c { + if opcode == 0 && fun >= 8 && fun < 0x20 { if fun == 8 || fun == 9 { // jr/jalr - linkReg := uint32(0) + linkReg := uint64(0) if fun == 9 { linkReg = rdReg } @@ -100,28 +103,28 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint32, memor // lo and hi registers // can write back - if fun >= 0x10 && fun < 0x1c { + if fun >= 0x10 && fun < 0x20 { return HandleHiLo(cpu, registers, fun, rs, rt, rdReg) } } // store conditional, write a 1 to rt - if opcode == 0x38 && rtReg != 0 { + if (opcode == 0x38 || opcode == 0x3C) && rtReg != 0 { registers[rtReg] = 1 } // write memory - if storeAddr != 0xFF_FF_FF_FF { + if storeAddr != 0xFF_FF_FF_FF_FF_FF_FF_FF { memTracker.TrackMemAccess(storeAddr) - memory.SetMemory(storeAddr, val) + memory.SetDoubleWord(storeAddr, val) } // write back the value to destination register return HandleRd(cpu, registers, rdReg, val, true) } -func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { - if opcode == 0 || (opcode >= 8 && opcode < 0xF) { +func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint64) uint64 { + if opcode == 0 || (opcode >= 8 && opcode < 0xF) || opcode == 0x18 || opcode == 0x19 { // transform ArithLogI to SPECIAL switch opcode { case 8: @@ -138,23 +141,27 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { fun = 0x25 // ori case 0xE: fun = 0x26 // xori + case 0x18: + fun = 0x2c // daddi + case 0x19: + fun = 0x2d // daddiu } switch fun { case 0x00: // sll - return rt << ((insn >> 6) & 0x1F) + return SignExtend((rt&0xFFFFFFFF)<<((insn>>6)&0x1F), 32) case 0x02: // srl - return rt >> ((insn >> 6) & 0x1F) + return SignExtend((rt&0xFFFFFFFF)>>((insn>>6)&0x1F), 32) case 0x03: // sra shamt := (insn >> 6) & 0x1F - return SignExtend(rt>>shamt, 32-shamt) + return SignExtend((rt&0xFFFFFFFF)>>shamt, 32-shamt) case 0x04: // sllv - return rt << (rs & 0x1F) + return SignExtend((rt&0xFFFFFFFF)<<(rs&0x1F), 32) case 0x06: // srlv - return rt >> (rs & 0x1F) + return SignExtend((rt&0xFFFFFFFF)>>(rs&0x1F), 32) case 0x07: // srav - return SignExtend(rt>>rs, 32-rs) - // functs in range [0x8, 0x1b] are handled specially by other functions + return SignExtend((rt&0xFFFFFFFF)>>rs, 32-rs) + // MIPS32 functs in range [0x8, 0x1f] are handled specially by other functions case 0x08: // jr return rs case 0x09: // jalr @@ -176,6 +183,12 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { return rs case 0x13: // mtlo return rs + case 0x14: // dsllv + return rt + case 0x16: // dsrlv + return rt + case 0x17: // dsrav + return rt case 0x18: // mult return rs case 0x19: // multu @@ -184,15 +197,23 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { return rs case 0x1b: // divu return rs + case 0x1C: // dmult + return rs + case 0x1D: // dmultu + return rs + case 0x1E: // ddiv + return rs + case 0x1F: // ddivu + return rs // The rest includes transformed R-type arith imm instructions case 0x20: // add - return rs + rt + return SignExtend(uint64(int32(rs)+int32(rt)), 32) case 0x21: // addu - return rs + rt + return SignExtend(uint64(uint32(rs)+uint32(rt)), 32) case 0x22: // sub - return rs - rt + return SignExtend(uint64(int32(rs)-int32(rt)), 32) case 0x23: // subu - return rs - rt + return SignExtend(uint64(uint32(rs)-uint32(rt)), 32) case 0x24: // and return rs & rt case 0x25: // or @@ -202,7 +223,7 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { case 0x27: // nor return ^(rs | rt) case 0x2a: // slti - if int32(rs) < int32(rt) { + if int64(rs) < int64(rt) { return 1 } return 0 @@ -211,8 +232,28 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { return 1 } return 0 + case 0x2c: // dadd + return rs + rt + case 0x2d: // daddu + return rs + rt + case 0x2e: // dsub + return rs - rt + case 0x2f: // dsubu + return rs - rt + case 0x38: // dsll + return rt << ((insn >> 6) & 0x1f) + case 0x3A: // dsrl + return rt >> ((insn >> 6) & 0x1f) + case 0x3B: // dsra + return uint64(int64(rt) >> ((insn >> 6) & 0x1f)) + case 0x3C: // dsll32 + return rt << (((insn >> 6) & 0x1f) + 32) + case 0x3E: // dsll32 + return rt >> (((insn >> 6) & 0x1f) + 32) + case 0x3F: // dsll32 + return uint64(int64(rt) >> (((insn >> 6) & 0x1f) + 32)) default: - panic("invalid instruction") + panic(fmt.Sprintf("invalid instruction: %x", insn)) } } else { switch opcode { @@ -220,7 +261,7 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { case 0x1C: switch fun { case 0x2: // mul - return uint32(int32(rs) * int32(rt)) + return SignExtend(uint64(uint32(int32(rs)*int32(rt))), 32) case 0x20, 0x21: // clz, clo if fun == 0x20 { rs = ^rs @@ -229,61 +270,98 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 { for ; rs&0x80000000 != 0; i++ { rs <<= 1 } - return i + return uint64(i) } case 0x0F: // lui - return rt << 16 + return SignExtend(rt<<16, 32) case 0x20: // lb - return SignExtend((mem>>(24-(rs&3)*8))&0xFF, 8) + return SignExtend((mem>>(56-(rs&7)*8))&0xFF, 8) case 0x21: // lh - return SignExtend((mem>>(16-(rs&2)*8))&0xFFFF, 16) + return SignExtend((mem>>(48-(rs&6)*8))&0xFFFF, 16) case 0x22: // lwl val := mem << ((rs & 3) * 8) - mask := uint32(0xFFFFFFFF) << ((rs & 3) * 8) - return (rt & ^mask) | val + mask := uint64(uint32(0xFFFFFFFF) << ((rs & 3) * 8)) + return SignExtend((rt & ^mask)|val, 32) case 0x23: // lw - return mem + return SignExtend((mem>>(32-((rs&0x4)<<3)))&0xFFFFFFFF, 32) case 0x24: // lbu - return (mem >> (24 - (rs&3)*8)) & 0xFF + return (mem >> (56 - (rs&7)*8)) & 0xFF case 0x25: // lhu - return (mem >> (16 - (rs&2)*8)) & 0xFFFF + return (mem >> (48 - (rs&6)*8)) & 0xFFFF case 0x26: // lwr val := mem >> (24 - (rs&3)*8) - mask := uint32(0xFFFFFFFF) >> (24 - (rs&3)*8) - return (rt & ^mask) | val + mask := uint64(uint32(0xFFFFFFFF) >> (24 - (rs&3)*8)) + return SignExtend((rt & ^mask)|val, 32) case 0x28: // sb - val := (rt & 0xFF) << (24 - (rs&3)*8) - mask := 0xFFFFFFFF ^ uint32(0xFF<<(24-(rs&3)*8)) + val := (rt & 0xFF) << (56 - (rs&7)*8) + mask := 0xFFFFFFFFFFFFFFFF ^ uint64(0xFF<<(56-(rs&7)*8)) return (mem & mask) | val case 0x29: // sh - val := (rt & 0xFFFF) << (16 - (rs&2)*8) - mask := 0xFFFFFFFF ^ uint32(0xFFFF<<(16-(rs&2)*8)) + sl := 48 - ((rs & 0x6) << 3) + val := (rt & 0xFFFF) << sl + mask := 0xFFFFFFFFFFFFFFFF ^ uint64(0xFFFF<> ((rs & 3) * 8) - mask := uint32(0xFFFFFFFF) >> ((rs & 3) * 8) + sr := (rs & 3) << 3 + val := ((rt & 0xFFFFFFFF) >> sr) << (32 - ((rs & 0x4) << 3)) + mask := (uint64(0xFFFFFFFF) >> sr) << (32 - ((rs & 0x4) << 3)) return (mem & ^mask) | val case 0x2b: // sw - return rt + sl := 32 - ((rs & 0x4) << 3) + val := (rt & 0xFFFFFFFF) << sl + mask := 0xFFFFFFFFFFFFFFFF ^ uint64(0xFFFFFFFF<>(32-((rs&0x4)<<3)))&0xFFFFFFFF, 32) case 0x38: // sc return rt + // MIPS64 + case 0x1A: // ldl + sl := (rs & 0x7) << 3 + val := mem << sl + mask := uint64(0xFFFFFFFFFFFFFFFF) << sl + return val | (rt & ^mask) + case 0x1B: // ldr + sr := 56 - ((rs & 0x7) << 3) + val := mem >> sr + mask := uint64(0xFFFFFFFFFFFFFFFF) << (64 - sr) + return val | (rt & mask) + case 0x27: // lwu + return (mem >> (32 - ((rs & 0x4) << 3))) & 0xFFFFFFFF + case 0x2C: // sdl + sr := (rs & 0x7) << 3 + val := rt >> sr + mask := uint64(0xFFFFFFFFFFFFFFFF) >> sr + return val | (mem & ^mask) + case 0x2D: // sdr + sl := 56 - ((rs & 0x7) << 3) + val := rt << sl + mask := uint64(0xFFFFFFFFFFFFFFFF) << sl + return val | (mem & ^mask) + case 0x34: // lld + return mem + case 0x37: // ld + return mem + case 0x3C: // scd + return rt + case 0x3F: // sd + return rt default: - panic("invalid instruction") + panic(fmt.Sprintf("invalid instruction: %x", insn)) } } - panic("invalid instruction") + panic(fmt.Sprintf("invalid instruction: %x", insn)) } -func SignExtend(dat uint32, idx uint32) uint32 { +func SignExtend(dat uint64, idx uint64) uint64 { isSigned := (dat >> (idx - 1)) != 0 - signed := ((uint32(1) << (32 - idx)) - 1) << idx - mask := (uint32(1) << idx) - 1 + signed := ((uint64(1) << (64 - idx)) - 1) << idx + mask := (uint64(1) << idx) - 1 if isSigned { return dat&mask | signed } else { @@ -291,7 +369,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]uint64, opcode, insn, rtReg, rs uint64) error { if cpu.NextPC != cpu.PC+4 { panic("branch in delay slot") } @@ -325,8 +403,8 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]uint32, opcode uint32, return nil } -func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]uint32, fun uint32, rs uint32, rt uint32, storeReg uint32) error { - val := uint32(0) +func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]uint64, fun, rs, rt, storeReg uint64) error { + val := uint64(0) switch fun { case 0x10: // mfhi val = cpu.HI @@ -338,16 +416,37 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]uint32, fun uint32, rs u cpu.LO = rs case 0x18: // mult acc := uint64(int64(int32(rs)) * int64(int32(rt))) - cpu.HI = uint32(acc >> 32) - cpu.LO = uint32(acc) + cpu.HI = SignExtend(acc>>32, 32) + cpu.LO = SignExtend(uint64(uint32(acc)), 32) case 0x19: // multu - acc := uint64(uint64(rs) * uint64(rt)) - cpu.HI = uint32(acc >> 32) - cpu.LO = uint32(acc) + acc := uint64(uint32(rs)) * uint64(uint32(rt)) + cpu.HI = SignExtend(acc>>32, 32) + cpu.LO = SignExtend(uint64(uint32(acc)), 32) case 0x1a: // div - cpu.HI = uint32(int32(rs) % int32(rt)) - cpu.LO = uint32(int32(rs) / int32(rt)) + cpu.HI = SignExtend(uint64(int32(rs)%int32(rt)), 32) + cpu.LO = SignExtend(uint64(int32(rs)/int32(rt)), 32) case 0x1b: // divu + cpu.HI = SignExtend(uint64(uint32(rs)%uint32(rt)), 32) + cpu.LO = SignExtend(uint64(uint32(rs)/uint32(rt)), 32) + case 0x14: // dsllv + val = rt << (rs & 0x3F) + case 0x16: // dsrlv + val = rt >> (rs & 0x3F) + case 0x17: // dsrav + val = uint64(int64(rt) >> (rs & 0x3F)) + case 0x1c: // dmult + // TODO: Signed mult for dmult? + acc := u128.From64(rs).Mul(u128.From64(rt)) + cpu.HI = acc.Hi + cpu.LO = acc.Lo + case 0x1d: // dmultu + acc := u128.From64(rs).Mul(u128.From64(rt)) + cpu.HI = acc.Hi + cpu.LO = acc.Lo + case 0x1e: // ddiv + cpu.HI = uint64(int64(rs) % int64(rt)) + cpu.LO = uint64(int64(rs) / int64(rt)) + case 0x1f: // ddivu cpu.HI = rs % rt cpu.LO = rs / rt } @@ -361,7 +460,7 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]uint32, fun uint32, rs u return nil } -func HandleJump(cpu *mipsevm.CpuScalars, registers *[32]uint32, linkReg uint32, dest uint32) error { +func HandleJump(cpu *mipsevm.CpuScalars, registers *[32]uint64, linkReg uint64, dest uint64) error { if cpu.NextPC != cpu.PC+4 { panic("jump in delay slot") } @@ -374,7 +473,7 @@ func HandleJump(cpu *mipsevm.CpuScalars, registers *[32]uint32, linkReg uint32, return nil } -func HandleRd(cpu *mipsevm.CpuScalars, registers *[32]uint32, storeReg uint32, val uint32, conditional bool) error { +func HandleRd(cpu *mipsevm.CpuScalars, registers *[32]uint64, storeReg uint64, val uint64, conditional bool) error { if storeReg >= 32 { panic("invalid register") } diff --git a/cannon/mipsevm/exec/mips_syscalls.go b/cannon/mipsevm/exec/mips_syscalls.go index 0f60650f778d..87d189770b2c 100644 --- a/cannon/mipsevm/exec/mips_syscalls.go +++ b/cannon/mipsevm/exec/mips_syscalls.go @@ -13,60 +13,60 @@ import ( // Syscall codes const ( - SysMmap = 4090 - SysMunmap = 4091 - SysBrk = 4045 - SysClone = 4120 - SysExitGroup = 4246 - SysRead = 4003 - SysWrite = 4004 - SysFcntl = 4055 - SysExit = 4001 - SysSchedYield = 4162 - SysGetTID = 4222 - SysFutex = 4238 - SysOpen = 4005 - SysNanosleep = 4166 + SysMmap = 5009 + SysMunmap = 5011 + SysBrk = 5012 + SysClone = 5055 + SysExitGroup = 5205 + SysRead = 5000 + SysWrite = 5001 + SysFcntl = 5070 + SysExit = 5058 + SysSchedYield = 5023 + SysGetTID = 5178 + SysFutex = 5194 + SysOpen = 5002 + SysNanosleep = 5034 ) // Noop Syscall codes const ( - SysGetAffinity = 4240 - SysMadvise = 4218 - SysRtSigprocmask = 4195 - SysSigaltstack = 4206 - SysRtSigaction = 4194 - SysPrlimit64 = 4338 - SysClose = 4006 - SysPread64 = 4200 - SysFstat64 = 4215 - SysOpenAt = 4288 - SysReadlink = 4085 - SysReadlinkAt = 4298 - SysIoctl = 4054 - SysEpollCreate1 = 4326 - SysPipe2 = 4328 - SysEpollCtl = 4249 - SysEpollPwait = 4313 - SysGetRandom = 4353 - SysUname = 4122 - SysStat64 = 4213 - SysGetuid = 4024 - SysGetgid = 4047 - SysLlseek = 4140 - SysMinCore = 4217 - SysTgkill = 4266 + SysGetAffinity = 5196 + SysMadvise = 5027 + SysRtSigprocmask = 5014 + SysSigaltstack = 5129 + SysRtSigaction = 5013 + SysPrlimit64 = 5297 + SysClose = 5003 + SysPread64 = 5016 + SysFstat64 = 5005 + SysOpenAt = 5247 + SysReadlink = 5087 + SysReadlinkAt = 5257 + SysIoctl = 5015 + SysEpollCreate1 = 5207 + SysPipe2 = 5287 + SysEpollCtl = 5208 + SysEpollPwait = 5272 + SysGetRandom = 5313 + SysUname = 5061 + SysStat64 = 5004 + SysGetuid = 5100 + SysGetgid = 5102 + SysLlseek = 5008 + SysMinCore = 5026 + SysTgkill = 5225 ) // Profiling-related syscalls // Should be able to ignore if we patch out prometheus calls and disable memprofiling // TODO(cp-903) - Update patching for mt-cannon so that these can be ignored const ( - SysSetITimer = 4104 - SysTimerCreate = 4257 - SysTimerSetTime = 4258 - SysTimerDelete = 4261 - SysClockGetTime = 4263 + SysSetITimer = 5036 + SysTimerCreate = 5216 + SysTimerSetTime = 5217 + SysTimerDelete = 5220 + SysClockGetTime = 5222 ) // File descriptors @@ -82,7 +82,7 @@ const ( // Errors const ( - SysErrorSignal = ^uint32(0) + SysErrorSignal = ^uint64(0) MipsEBADF = 0x9 MipsEINVAL = 0x16 MipsEAGAIN = 0xb @@ -95,7 +95,7 @@ const ( FutexWakePrivate = 129 FutexTimeoutSteps = 10_000 FutexNoTimeout = ^uint64(0) - FutexEmptyAddr = ^uint32(0) + FutexEmptyAddr = ^uint64(0) ) // SysClone flags @@ -135,7 +135,7 @@ const ( BrkStart = 0x40000000 ) -func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) { +func GetSyscallArgs(registers *[32]uint64) (syscallNum, a0, a1, a2, a3 uint64) { syscallNum = registers[2] // v0 a0 = registers[4] @@ -146,8 +146,8 @@ func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) { return syscallNum, a0, a1, a2, a3 } -func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) { - v1 = uint32(0) +func HandleSysMmap(a0, a1, heap uint64) (v0, v1, newHeap uint64) { + v1 = uint64(0) newHeap = heap sz := a1 @@ -166,34 +166,34 @@ func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) { 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) { +func HandleSysRead(a0, a1, a2 uint64, preimageKey [32]byte, preimageOffset uint64, preimageReader PreimageReader, memory *memory.Memory, memTracker MemTracker) (v0, v1, newPreimageOffset uint64) { // args: a0 = fd, a1 = addr, a2 = count // returns: v0 = read, v1 = err code - v0 = uint32(0) - v1 = uint32(0) + v0 = uint64(0) + v1 = uint64(0) newPreimageOffset = preimageOffset switch a0 { case FdStdin: // leave v0 and v1 zero: read nothing, no error case FdPreimageRead: // pre-image oracle - effAddr := a1 & 0xFFffFFfc + effAddr := a1 & 0xFFFFFFFFFFFFFFF8 memTracker.TrackMemAccess(effAddr) - mem := memory.GetMemory(effAddr) + mem := memory.GetDoubleWord(effAddr) 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, m.state.PreimageOffset, datLen, dat[:datLen], m.state.PreimageKey, a2) - alignment := a1 & 3 - space := 4 - alignment + alignment := a1 & 7 + space := 8 - alignment if space < datLen { datLen = space } if a2 < datLen { datLen = a2 } - var outMem [4]byte - binary.BigEndian.PutUint32(outMem[:], mem) + var outMem [8]byte + binary.BigEndian.PutUint64(outMem[:], mem) copy(outMem[alignment:], dat[:datLen]) - memory.SetMemory(effAddr, binary.BigEndian.Uint32(outMem[:])) + memory.SetDoubleWord(effAddr, binary.BigEndian.Uint64(outMem[:])) newPreimageOffset += datLen v0 = datLen //fmt.Printf("read %d pre-image bytes, new offset: %d, eff addr: %08x mem: %08x\n", datLen, m.state.PreimageOffset, effAddr, outMem) @@ -201,17 +201,17 @@ 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 v0 = a2 default: - v0 = 0xFFffFFff + v0 = 0xFFffFFffFFffFFff v1 = MipsEBADF } return v0, v1, newPreimageOffset } -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 uint64, lastHint hexutil.Bytes, preimageKey [32]byte, preimageOffset uint64, oracle mipsevm.PreimageOracle, memory *memory.Memory, memTracker MemTracker, stdOut, stdErr io.Writer) (v0, v1 uint64, newLastHint hexutil.Bytes, newPreimageKey common.Hash, newPreimageOffset uint64) { // args: a0 = fd, a1 = addr, a2 = count // returns: v0 = written, v1 = err code - v1 = uint32(0) + v1 = uint64(0) newLastHint = lastHint newPreimageKey = preimageKey newPreimageOffset = preimageOffset @@ -239,34 +239,34 @@ func HandleSysWrite(a0, a1, a2 uint32, lastHint hexutil.Bytes, preimageKey [32]b newLastHint = lastHint v0 = a2 case FdPreimageWrite: - effAddr := a1 & 0xFFffFFfc + effAddr := a1 & 0xFFFFFFFFFFFFFFF8 memTracker.TrackMemAccess(effAddr) - mem := memory.GetMemory(effAddr) + mem := memory.GetDoubleWord(effAddr) key := preimageKey - alignment := a1 & 3 - space := 4 - alignment + alignment := a1 & 7 + space := 8 - alignment if space < a2 { a2 = space } copy(key[:], key[a2:]) - var tmp [4]byte - binary.BigEndian.PutUint32(tmp[:], mem) + var tmp [8]byte + binary.BigEndian.PutUint64(tmp[:], mem) copy(key[32-a2:], tmp[alignment:]) newPreimageKey = key newPreimageOffset = 0 //fmt.Printf("updating pre-image key: %s\n", m.state.PreimageKey) v0 = a2 default: - v0 = 0xFFffFFff + v0 = 0xFFffFFffFFffFFff v1 = MipsEBADF } return v0, v1, newLastHint, newPreimageKey, newPreimageOffset } -func HandleSysFcntl(a0, a1 uint32) (v0, v1 uint32) { +func HandleSysFcntl(a0, a1 uint64) (v0, v1 uint64) { // args: a0 = fd, a1 = cmd - v1 = uint32(0) + v1 = uint64(0) if a1 == 3 { // F_GETFL: get file descriptor flags switch a0 { @@ -275,18 +275,18 @@ func HandleSysFcntl(a0, a1 uint32) (v0, v1 uint32) { case FdStdout, FdStderr, FdPreimageWrite, FdHintWrite: v0 = 1 // O_WRONLY default: - v0 = 0xFFffFFff + v0 = 0xFFffFFffFFffFFff v1 = MipsEBADF } } else { - v0 = 0xFFffFFff + v0 = 0xFFffFFffFFffFFff v1 = MipsEINVAL // cmd not recognized by this kernel } return v0, v1 } -func HandleSyscallUpdates(cpu *mipsevm.CpuScalars, registers *[32]uint32, v0, v1 uint32) { +func HandleSyscallUpdates(cpu *mipsevm.CpuScalars, registers *[32]uint64, v0, v1 uint64) { registers[2] = v0 registers[7] = v1 diff --git a/cannon/mipsevm/exec/preimage.go b/cannon/mipsevm/exec/preimage.go index f8039447d483..37f671940387 100644 --- a/cannon/mipsevm/exec/preimage.go +++ b/cannon/mipsevm/exec/preimage.go @@ -7,7 +7,7 @@ import ( ) type PreimageReader interface { - ReadPreimage(key [32]byte, offset uint32) (dat [32]byte, datLen uint32) + ReadPreimage(key [32]byte, offset uint64) (dat [32]byte, datLen uint64) } // TrackingPreimageOracleReader wraps around a PreimageOracle, implements the PreimageOracle interface, and adds tracking functionality. @@ -23,7 +23,7 @@ type TrackingPreimageOracleReader struct { // key for above preimage lastPreimageKey [32]byte // offset we last read from, or max uint32 if nothing is read this step - lastPreimageOffset uint32 + lastPreimageOffset uint64 } func NewTrackingPreimageOracleReader(po mipsevm.PreimageOracle) *TrackingPreimageOracleReader { @@ -31,7 +31,7 @@ func NewTrackingPreimageOracleReader(po mipsevm.PreimageOracle) *TrackingPreimag } func (p *TrackingPreimageOracleReader) Reset() { - p.lastPreimageOffset = ^uint32(0) + p.lastPreimageOffset = ^uint64(0) } func (p *TrackingPreimageOracleReader) Hint(v []byte) { @@ -45,7 +45,7 @@ func (p *TrackingPreimageOracleReader) GetPreimage(k [32]byte) []byte { return preimage } -func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset uint32) (dat [32]byte, datLen uint32) { +func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset uint64) (dat [32]byte, datLen uint64) { preimage := p.lastPreimage if key != p.lastPreimageKey { p.lastPreimageKey = key @@ -57,11 +57,11 @@ func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset uint32) p.lastPreimage = preimage } p.lastPreimageOffset = offset - datLen = uint32(copy(dat[:], preimage[offset:])) + datLen = uint64(copy(dat[:], preimage[offset:])) return } -func (p *TrackingPreimageOracleReader) LastPreimage() ([32]byte, []byte, uint32) { +func (p *TrackingPreimageOracleReader) LastPreimage() ([32]byte, []byte, uint64) { return p.lastPreimageKey, p.lastPreimage, p.lastPreimageOffset } diff --git a/cannon/mipsevm/exec/stack.go b/cannon/mipsevm/exec/stack.go index 378c07b558c0..747ea6cd0b7b 100644 --- a/cannon/mipsevm/exec/stack.go +++ b/cannon/mipsevm/exec/stack.go @@ -9,7 +9,7 @@ import ( ) type StackTracker interface { - PushStack(caller uint32, target uint32) + PushStack(caller uint64, target uint64) PopStack() } @@ -20,7 +20,7 @@ type TraceableStackTracker interface { type NoopStackTracker struct{} -func (n *NoopStackTracker) PushStack(caller uint32, target uint32) {} +func (n *NoopStackTracker) PushStack(caller uint64, target uint64) {} func (n *NoopStackTracker) PopStack() {} @@ -29,8 +29,8 @@ func (n *NoopStackTracker) Traceback() {} type StackTrackerImpl struct { state mipsevm.FPVMState - stack []uint32 - caller []uint32 + stack []uint64 + caller []uint64 meta *program.Metadata } @@ -46,7 +46,7 @@ func NewStackTrackerUnsafe(state mipsevm.FPVMState, meta *program.Metadata) *Sta return &StackTrackerImpl{state: state, meta: meta} } -func (s *StackTrackerImpl) PushStack(caller uint32, target uint32) { +func (s *StackTrackerImpl) PushStack(caller uint64, target uint64) { s.caller = append(s.caller, caller) s.stack = append(s.stack, target) } diff --git a/cannon/mipsevm/iface.go b/cannon/mipsevm/iface.go index 1a3579deed93..142914347e20 100644 --- a/cannon/mipsevm/iface.go +++ b/cannon/mipsevm/iface.go @@ -10,10 +10,10 @@ type FPVMState interface { GetMemory() *memory.Memory // GetPC returns the currently executing program counter - GetPC() uint32 + GetPC() uint64 // GetRegisters returns the currently active registers - GetRegisters() *[32]uint32 + GetRegisters() *[32]uint64 // GetStep returns the current VM step GetStep() uint64 @@ -39,7 +39,7 @@ type FPVM interface { CheckInfiniteLoop() bool // LastPreimage returns the last preimage accessed by the VM - LastPreimage() (preimageKey [32]byte, preimage []byte, preimageOffset uint32) + LastPreimage() (preimageKey [32]byte, preimage []byte, preimageOffset uint64) // Traceback prints a traceback of the program to the console Traceback() diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index e02720a2c0d6..a54d8b8891b9 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -9,19 +9,20 @@ import ( "sort" "github.com/ethereum/go-ethereum/crypto" + u128 "lukechampine.com/uint128" ) // Note: 2**12 = 4 KiB, the min phys page size in the Go runtime. const ( PageAddrSize = 12 - PageKeySize = 32 - PageAddrSize + PageKeySize = 64 - PageAddrSize PageSize = 1 << PageAddrSize PageAddrMask = PageSize - 1 MaxPageCount = 1 << PageKeySize PageKeyMask = MaxPageCount - 1 ) -const MEM_PROOF_SIZE = 28 * 32 +const MEM_PROOF_SIZE = 60 * 32 func HashPair(left, right [32]byte) [32]byte { out := crypto.Keccak256Hash(left[:], right[:]) @@ -43,22 +44,22 @@ type Memory struct { nodes map[uint64]*[32]byte // pageIndex -> cached page - pages map[uint32]*CachedPage + pages map[uint64]*CachedPage // Note: since we don't de-alloc pages, we don't do ref-counting. // 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. // this prevents map lookups each instruction - lastPageKeys [2]uint32 + lastPageKeys [2]uint64 lastPage [2]*CachedPage } func NewMemory() *Memory { return &Memory{ nodes: make(map[uint64]*[32]byte), - pages: make(map[uint32]*CachedPage), - lastPageKeys: [2]uint32{^uint32(0), ^uint32(0)}, // default to invalid keys, to not match any pages + pages: make(map[uint64]*CachedPage), + lastPageKeys: [2]uint64{^uint64(0), ^uint64(0)}, // default to invalid keys, to not match any pages } } @@ -66,7 +67,7 @@ func (m *Memory) PageCount() int { return len(m.pages) } -func (m *Memory) ForEachPage(fn func(pageIndex uint32, page *Page) error) error { +func (m *Memory) ForEachPage(fn func(pageIndex uint64, page *Page) error) error { for pageIndex, cachedPage := range m.pages { if err := fn(pageIndex, cachedPage.Data); err != nil { return err @@ -75,7 +76,7 @@ func (m *Memory) ForEachPage(fn func(pageIndex uint32, page *Page) error) error return nil } -func (m *Memory) Invalidate(addr uint32) { +func (m *Memory) Invalidate(addr uint64) { // addr must be aligned to 4 bytes if addr&0x3 != 0 { panic(fmt.Errorf("unaligned memory access: %x", addr)) @@ -93,7 +94,7 @@ func (m *Memory) Invalidate(addr uint32) { } // find the gindex of the first page covering the address - gindex := ((uint64(1) << 32) | uint64(addr)) >> PageAddrSize + gindex := u128.From64(1).Lsh(64).Or(u128.From64(addr)).Rsh(PageAddrSize).Big().Uint64() for gindex > 0 { m.nodes[gindex] = nil @@ -103,23 +104,23 @@ func (m *Memory) Invalidate(addr uint32) { func (m *Memory) MerkleizeSubtree(gindex uint64) [32]byte { l := uint64(bits.Len64(gindex)) - if l > 28 { + if l > 60 { panic("gindex too deep") } if l > PageKeySize { depthIntoPage := l - 1 - PageKeySize pageIndex := (gindex >> depthIntoPage) & PageKeyMask - if p, ok := m.pages[uint32(pageIndex)]; ok { + if p, ok := m.pages[uint64(pageIndex)]; ok { pageGindex := (1 << depthIntoPage) | (gindex & ((1 << depthIntoPage) - 1)) return p.MerkleizeSubtree(pageGindex) } else { - return zeroHashes[28-l] // page does not exist + return zeroHashes[60-l] // page does not exist } } n, ok := m.nodes[gindex] if !ok { // if the node doesn't exist, the whole sub-tree is zeroed - return zeroHashes[28-l] + return zeroHashes[60-l] } if n != nil { return *n @@ -131,27 +132,27 @@ func (m *Memory) MerkleizeSubtree(gindex uint64) [32]byte { return r } -func (m *Memory) MerkleProof(addr uint32) (out [MEM_PROOF_SIZE]byte) { +func (m *Memory) MerkleProof(addr uint64) (out [MEM_PROOF_SIZE]byte) { proof := m.traverseBranch(1, addr, 0) // encode the proof - for i := 0; i < 28; i++ { + for i := 0; i < 60; i++ { copy(out[i*32:(i+1)*32], proof[i][:]) } return out } -func (m *Memory) traverseBranch(parent uint64, addr uint32, depth uint8) (proof [][32]byte) { - if depth == 32-5 { - proof = make([][32]byte, 0, 32-5+1) +func (m *Memory) traverseBranch(parent uint64, addr uint64, depth uint8) (proof [][32]byte) { + if depth == 64-5 { + proof = make([][32]byte, 0, 64-5+1) proof = append(proof, m.MerkleizeSubtree(parent)) return } - if depth > 32-5 { + if depth > 64-5 { panic("traversed too deep") } self := parent << 1 sibling := self | 1 - if addr&(1<<(31-depth)) != 0 { + if addr&(1<<(63-depth)) != 0 { self, sibling = sibling, self } proof = m.traverseBranch(self, addr, depth+1) @@ -164,7 +165,7 @@ func (m *Memory) MerkleRoot() [32]byte { return m.MerkleizeSubtree(1) } -func (m *Memory) pageLookup(pageIndex uint32) (*CachedPage, bool) { +func (m *Memory) pageLookup(pageIndex uint64) (*CachedPage, bool) { // hit caches if pageIndex == m.lastPageKeys[0] { return m.lastPage[0], true @@ -185,7 +186,7 @@ func (m *Memory) pageLookup(pageIndex uint32) (*CachedPage, bool) { return p, ok } -func (m *Memory) SetMemory(addr uint32, v uint32) { +func (m *Memory) SetMemory(addr uint64, v uint32) { // addr must be aligned to 4 bytes if addr&0x3 != 0 { panic(fmt.Errorf("unaligned memory access: %x", addr)) @@ -204,7 +205,26 @@ func (m *Memory) SetMemory(addr uint32, v uint32) { binary.BigEndian.PutUint32(p.Data[pageAddr:pageAddr+4], v) } -func (m *Memory) GetMemory(addr uint32) uint32 { +func (m *Memory) SetDoubleWord(addr uint64, v uint64) { + // addr must be aligned to 8 bytes + if addr&0x7 != 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 + } + binary.BigEndian.PutUint64(p.Data[pageAddr:pageAddr+8], v) +} + +func (m *Memory) GetMemory(addr uint64) uint32 { // addr must be aligned to 4 bytes if addr&0x3 != 0 { panic(fmt.Errorf("unaligned memory access: %x", addr)) @@ -217,7 +237,20 @@ func (m *Memory) GetMemory(addr uint32) uint32 { return binary.BigEndian.Uint32(p.Data[pageAddr : pageAddr+4]) } -func (m *Memory) AllocPage(pageIndex uint32) *CachedPage { +func (m *Memory) GetDoubleWord(addr uint64) uint64 { + // addr must be aligned to 8 bytes + if addr&0x7 != 0 { + panic(fmt.Errorf("unaligned memory access: %x", addr)) + } + p, ok := m.pageLookup(addr >> PageAddrSize) + if !ok { + return 0 + } + pageAddr := addr & PageAddrMask + return binary.BigEndian.Uint64(p.Data[pageAddr : pageAddr+8]) +} + +func (m *Memory) AllocPage(pageIndex uint64) *CachedPage { p := &CachedPage{Data: new(Page)} m.pages[pageIndex] = p // make nodes to root @@ -230,7 +263,7 @@ func (m *Memory) AllocPage(pageIndex uint32) *CachedPage { } type pageEntry struct { - Index uint32 `json:"index"` + Index uint64 `json:"index"` Data *Page `json:"data"` } @@ -254,8 +287,8 @@ func (m *Memory) UnmarshalJSON(data []byte) error { return err } m.nodes = make(map[uint64]*[32]byte) - m.pages = make(map[uint32]*CachedPage) - m.lastPageKeys = [2]uint32{^uint32(0), ^uint32(0)} + m.pages = make(map[uint64]*CachedPage) + m.lastPageKeys = [2]uint64{^uint64(0), ^uint64(0)} m.lastPage = [2]*CachedPage{nil, nil} for i, p := range pages { if _, ok := m.pages[p.Index]; ok { @@ -266,7 +299,7 @@ func (m *Memory) UnmarshalJSON(data []byte) error { return nil } -func (m *Memory) SetMemoryRange(addr uint32, r io.Reader) error { +func (m *Memory) SetMemoryRange(addr uint64, r io.Reader) error { for { pageIndex := addr >> PageAddrSize pageAddr := addr & PageAddrMask @@ -282,14 +315,14 @@ func (m *Memory) SetMemoryRange(addr uint32, r io.Reader) error { } return err } - addr += uint32(n) + addr += uint64(n) } } type memReader struct { m *Memory - addr uint32 - count uint32 + addr uint64 + count uint64 } func (r *memReader) Read(dest []byte) (n int, err error) { @@ -303,7 +336,7 @@ func (r *memReader) Read(dest []byte) (n int, err error) { pageIndex := r.addr >> PageAddrSize start := r.addr & PageAddrMask - end := uint32(PageSize) + end := uint64(PageSize) if pageIndex == (endAddr >> PageAddrSize) { end = endAddr & PageAddrMask @@ -314,12 +347,12 @@ func (r *memReader) Read(dest []byte) (n int, err error) { } else { n = copy(dest, make([]byte, end-start)) // default to zeroes } - r.addr += uint32(n) - r.count -= uint32(n) + r.addr += uint64(n) + r.count -= uint64(n) return n, nil } -func (m *Memory) ReadMemoryRange(addr uint32, count uint32) io.Reader { +func (m *Memory) ReadMemoryRange(addr uint64, count uint64) io.Reader { return &memReader{m: m, addr: addr, count: count} } diff --git a/cannon/mipsevm/memory/memory_test.go b/cannon/mipsevm/memory/memory_test.go index a2b0e745562b..3e01d38a91d7 100644 --- a/cannon/mipsevm/memory/memory_test.go +++ b/cannon/mipsevm/memory/memory_test.go @@ -18,7 +18,7 @@ func TestMemoryMerkleProof(t *testing.T) { m.SetMemory(0x10000, 0xaabbccdd) proof := m.MerkleProof(0x10000) require.Equal(t, uint32(0xaabbccdd), binary.BigEndian.Uint32(proof[:4])) - for i := 0; i < 32-5; i++ { + for i := 0; i < 64-5; i++ { require.Equal(t, zeroHashes[i][:], proof[32+i*32:32+i*32+32], "empty siblings") } }) @@ -49,33 +49,33 @@ func TestMemoryMerkleRoot(t *testing.T) { t.Run("empty", func(t *testing.T) { m := NewMemory() root := m.MerkleRoot() - require.Equal(t, zeroHashes[32-5], root, "fully zeroed memory should have expected zero hash") + require.Equal(t, zeroHashes[64-5], root, "fully zeroed memory should have expected zero hash") }) t.Run("empty page", func(t *testing.T) { m := NewMemory() m.SetMemory(0xF000, 0) root := m.MerkleRoot() - require.Equal(t, zeroHashes[32-5], root, "fully zeroed memory should have expected zero hash") + require.Equal(t, zeroHashes[64-5], root, "fully zeroed memory should have expected zero hash") }) t.Run("single page", func(t *testing.T) { m := NewMemory() m.SetMemory(0xF000, 1) root := m.MerkleRoot() - require.NotEqual(t, zeroHashes[32-5], root, "non-zero memory") + require.NotEqual(t, zeroHashes[64-5], root, "non-zero memory") }) t.Run("repeat zero", func(t *testing.T) { m := NewMemory() m.SetMemory(0xF000, 0) m.SetMemory(0xF004, 0) root := m.MerkleRoot() - require.Equal(t, zeroHashes[32-5], root, "zero still") + require.Equal(t, zeroHashes[64-5], root, "zero still") }) t.Run("two empty pages", func(t *testing.T) { m := NewMemory() m.SetMemory(PageSize*3, 0) m.SetMemory(PageSize*10, 0) root := m.MerkleRoot() - require.Equal(t, zeroHashes[32-5], root, "zero still") + require.Equal(t, zeroHashes[64-5], root, "zero still") }) t.Run("random few pages", func(t *testing.T) { m := NewMemory() @@ -102,11 +102,11 @@ func TestMemoryMerkleRoot(t *testing.T) { t.Run("invalidate page", func(t *testing.T) { m := NewMemory() m.SetMemory(0xF000, 0) - require.Equal(t, zeroHashes[32-5], m.MerkleRoot(), "zero at first") + require.Equal(t, zeroHashes[64-5], m.MerkleRoot(), "zero at first") m.SetMemory(0xF004, 1) - require.NotEqual(t, zeroHashes[32-5], m.MerkleRoot(), "non-zero") + require.NotEqual(t, zeroHashes[64-5], m.MerkleRoot(), "non-zero") m.SetMemory(0xF004, 0) - require.Equal(t, zeroHashes[32-5], m.MerkleRoot(), "zero again") + require.Equal(t, zeroHashes[64-5], m.MerkleRoot(), "zero again") }) } @@ -118,7 +118,7 @@ func TestMemoryReadWrite(t *testing.T) { _, err := rand.Read(data[:]) require.NoError(t, err) require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data))) - for _, i := range []uint32{0, 4, 1000, 20_000 - 4} { + for _, i := range []uint64{0, 4, 1000, 20_000 - 4} { v := m.GetMemory(i) expected := binary.BigEndian.Uint32(data[i : i+4]) require.Equalf(t, expected, v, "read at %d", i) @@ -129,7 +129,7 @@ func TestMemoryReadWrite(t *testing.T) { m := NewMemory() data := []byte(strings.Repeat("under the big bright yellow sun ", 40)) 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, uint64(len(data)+20))) require.NoError(t, err) require.Equal(t, make([]byte, 10), res[:10], "empty start") require.Equal(t, data, res[10:len(res)-10], "result") diff --git a/cannon/mipsevm/memory/page.go b/cannon/mipsevm/memory/page.go index d3c3096b418e..f8865ec9bb48 100644 --- a/cannon/mipsevm/memory/page.go +++ b/cannon/mipsevm/memory/page.go @@ -70,7 +70,7 @@ type CachedPage struct { Ok [PageSize / 32]bool } -func (p *CachedPage) Invalidate(pageAddr uint32) { +func (p *CachedPage) Invalidate(pageAddr uint64) { if pageAddr >= PageSize { panic("invalid page addr") } diff --git a/cannon/mipsevm/multithreaded/instrumented.go b/cannon/mipsevm/multithreaded/instrumented.go index 3eedab08a5ef..84258539388d 100644 --- a/cannon/mipsevm/multithreaded/instrumented.go +++ b/cannon/mipsevm/multithreaded/instrumented.go @@ -83,7 +83,7 @@ func (m *InstrumentedState) Step(proof bool) (wit *mipsevm.StepWitness, err erro memProof := m.memoryTracker.MemProof() wit.ProofData = append(wit.ProofData, memProof[:]...) lastPreimageKey, lastPreimage, lastPreimageOffset := m.preimageOracle.LastPreimage() - if lastPreimageOffset != ^uint32(0) { + if lastPreimageOffset != ^uint64(0) { wit.PreimageOffset = lastPreimageOffset wit.PreimageKey = lastPreimageKey wit.PreimageValue = lastPreimage @@ -96,7 +96,7 @@ func (m *InstrumentedState) CheckInfiniteLoop() bool { return false } -func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, uint32) { +func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, uint64) { return m.preimageOracle.LastPreimage() } diff --git a/cannon/mipsevm/multithreaded/mips.go b/cannon/mipsevm/multithreaded/mips.go index ff7d827bf4e1..bc8aa53a4358 100644 --- a/cannon/mipsevm/multithreaded/mips.go +++ b/cannon/mipsevm/multithreaded/mips.go @@ -16,13 +16,13 @@ func (m *InstrumentedState) handleSyscall() error { thread := m.state.getCurrentThread() syscallNum, a0, a1, a2, a3 := exec.GetSyscallArgs(m.state.GetRegisters()) - v0 := uint32(0) - v1 := uint32(0) + v0 := uint64(0) + v1 := uint64(0) //fmt.Printf("syscall: %d\n", syscallNum) switch syscallNum { case exec.SysMmap: - var newHeap uint32 + var newHeap uint64 v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) m.state.Heap = newHeap case exec.SysBrk: @@ -73,13 +73,13 @@ func (m *InstrumentedState) handleSyscall() error { m.state.ExitCode = uint8(a0) return nil case exec.SysRead: - var newPreimageOffset uint32 + var newPreimageOffset uint64 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 case exec.SysWrite: var newLastHint hexutil.Bytes var newPreimageKey common.Hash - var newPreimageOffset uint32 + var newPreimageOffset uint64 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.PreimageKey = newPreimageKey @@ -103,7 +103,7 @@ func (m *InstrumentedState) handleSyscall() error { case exec.FutexWaitPrivate: thread.FutexAddr = a0 m.memoryTracker.TrackMemAccess(a0) - mem := m.state.Memory.GetMemory(a0) + mem := m.state.Memory.GetDoubleWord(a0) if mem != a2 { v0 = exec.SysErrorSignal v1 = exec.MipsEAGAIN @@ -225,7 +225,7 @@ func (m *InstrumentedState) mipsStep() error { return nil } else { m.memoryTracker.TrackMemAccess(thread.FutexAddr) - mem := m.state.Memory.GetMemory(thread.FutexAddr) + mem := m.state.Memory.GetDoubleWord(thread.FutexAddr) if thread.FutexVal == mem { // still got expected value, continue sleeping, try next thread. m.preemptThread(thread) @@ -273,8 +273,8 @@ func (m *InstrumentedState) onWaitComplete(thread *ThreadState, isTimedOut bool) thread.FutexTimeoutStep = 0 // Complete the FUTEX_WAIT syscall - v0 := uint32(0) - v1 := uint32(0) + v0 := uint64(0) + v1 := uint64(0) if isTimedOut { v0 = exec.SysErrorSignal v1 = exec.MipsETIMEDOUT diff --git a/cannon/mipsevm/multithreaded/stack.go b/cannon/mipsevm/multithreaded/stack.go index dae14b034f96..04fe73c39cfc 100644 --- a/cannon/mipsevm/multithreaded/stack.go +++ b/cannon/mipsevm/multithreaded/stack.go @@ -9,7 +9,7 @@ import ( type ThreadedStackTracker interface { exec.TraceableStackTracker - DropThread(threadId uint32) + DropThread(threadId uint64) } type NoopThreadedStackTracker struct { @@ -18,12 +18,12 @@ type NoopThreadedStackTracker struct { var _ ThreadedStackTracker = (*ThreadedStackTrackerImpl)(nil) -func (n *NoopThreadedStackTracker) DropThread(threadId uint32) {} +func (n *NoopThreadedStackTracker) DropThread(threadId uint64) {} type ThreadedStackTrackerImpl struct { meta *program.Metadata state *State - trackersByThreadId map[uint32]exec.TraceableStackTracker + trackersByThreadId map[uint64]exec.TraceableStackTracker } var _ ThreadedStackTracker = (*ThreadedStackTrackerImpl)(nil) @@ -36,11 +36,11 @@ func NewThreadedStackTracker(state *State, meta *program.Metadata) (*ThreadedSta return &ThreadedStackTrackerImpl{ state: state, meta: meta, - trackersByThreadId: make(map[uint32]exec.TraceableStackTracker), + trackersByThreadId: make(map[uint64]exec.TraceableStackTracker), }, nil } -func (t *ThreadedStackTrackerImpl) PushStack(caller uint32, target uint32) { +func (t *ThreadedStackTrackerImpl) PushStack(caller uint64, target uint64) { t.getCurrentTracker().PushStack(caller, target) } @@ -62,6 +62,6 @@ func (t *ThreadedStackTrackerImpl) getCurrentTracker() exec.TraceableStackTracke return tracker } -func (t *ThreadedStackTrackerImpl) DropThread(threadId uint32) { +func (t *ThreadedStackTrackerImpl) DropThread(threadId uint64) { delete(t.trackersByThreadId, threadId) } diff --git a/cannon/mipsevm/multithreaded/state.go b/cannon/mipsevm/multithreaded/state.go index 060498704671..aebb11743396 100644 --- a/cannon/mipsevm/multithreaded/state.go +++ b/cannon/mipsevm/multithreaded/state.go @@ -14,18 +14,18 @@ import ( ) // STATE_WITNESS_SIZE is the size of the state witness encoding in bytes. -const STATE_WITNESS_SIZE = 163 +const STATE_WITNESS_SIZE = 179 const ( MEMROOT_WITNESS_OFFSET = 0 PREIMAGE_KEY_WITNESS_OFFSET = MEMROOT_WITNESS_OFFSET + 32 PREIMAGE_OFFSET_WITNESS_OFFSET = PREIMAGE_KEY_WITNESS_OFFSET + 32 - HEAP_WITNESS_OFFSET = PREIMAGE_OFFSET_WITNESS_OFFSET + 4 - EXITCODE_WITNESS_OFFSET = HEAP_WITNESS_OFFSET + 4 + HEAP_WITNESS_OFFSET = PREIMAGE_OFFSET_WITNESS_OFFSET + 8 + EXITCODE_WITNESS_OFFSET = HEAP_WITNESS_OFFSET + 8 EXITED_WITNESS_OFFSET = EXITCODE_WITNESS_OFFSET + 1 STEP_WITNESS_OFFSET = EXITED_WITNESS_OFFSET + 1 STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET = STEP_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 + 8 LEFT_THREADS_ROOT_WITNESS_OFFSET = TRAVERSE_RIGHT_WITNESS_OFFSET + 1 RIGHT_THREADS_ROOT_WITNESS_OFFSET = LEFT_THREADS_ROOT_WITNESS_OFFSET + 32 THREAD_ID_WITNESS_OFFSET = RIGHT_THREADS_ROOT_WITNESS_OFFSET + 32 @@ -35,21 +35,21 @@ type State struct { Memory *memory.Memory `json:"memory"` PreimageKey common.Hash `json:"preimageKey"` - PreimageOffset uint32 `json:"preimageOffset"` // note that the offset includes the 8-byte length prefix + PreimageOffset uint64 `json:"preimageOffset"` // note that the offset includes the 8-byte length prefix - Heap uint32 `json:"heap"` // to handle mmap growth + Heap uint64 `json:"heap"` // to handle mmap growth ExitCode uint8 `json:"exit"` Exited bool `json:"exited"` Step uint64 `json:"step"` StepsSinceLastContextSwitch uint64 `json:"stepsSinceLastContextSwitch"` - Wakeup uint32 `json:"wakeup"` + Wakeup uint64 `json:"wakeup"` TraverseRight bool `json:"traverseRight"` LeftThreadStack []*ThreadState `json:"leftThreadStack"` RightThreadStack []*ThreadState `json:"rightThreadStack"` - NextThreadId uint32 `json:"nextThreadId"` + NextThreadId uint64 `json:"nextThreadId"` // LastHint is optional metadata, and not part of the VM state itself. // It is used to remember the last pre-image hint, @@ -81,7 +81,7 @@ func CreateEmptyState() *State { } } -func CreateInitialState(pc, heapStart uint32) *State { +func CreateInitialState(pc, heapStart uint64) *State { state := CreateEmptyState() currentThread := state.getCurrentThread() currentThread.Cpu.PC = pc @@ -130,12 +130,12 @@ func (s *State) calculateThreadStackRoot(stack []*ThreadState) common.Hash { return curRoot } -func (s *State) GetPC() uint32 { +func (s *State) GetPC() uint64 { activeThread := s.getCurrentThread() return activeThread.Cpu.PC } -func (s *State) GetRegisters() *[32]uint32 { +func (s *State) GetRegisters() *[32]uint64 { activeThread := s.getCurrentThread() return &activeThread.Registers } @@ -163,21 +163,21 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) { memRoot := s.Memory.MerkleRoot() out = append(out, memRoot[:]...) out = append(out, s.PreimageKey[:]...) - out = binary.BigEndian.AppendUint32(out, s.PreimageOffset) - out = binary.BigEndian.AppendUint32(out, s.Heap) + out = binary.BigEndian.AppendUint64(out, s.PreimageOffset) + out = binary.BigEndian.AppendUint64(out, s.Heap) out = append(out, s.ExitCode) out = mipsevm.AppendBoolToWitness(out, s.Exited) out = binary.BigEndian.AppendUint64(out, s.Step) out = binary.BigEndian.AppendUint64(out, s.StepsSinceLastContextSwitch) - out = binary.BigEndian.AppendUint32(out, s.Wakeup) + out = binary.BigEndian.AppendUint64(out, s.Wakeup) leftStackRoot := s.getLeftThreadStackRoot() rightStackRoot := s.getRightThreadStackRoot() out = mipsevm.AppendBoolToWitness(out, s.TraverseRight) out = append(out, (leftStackRoot)[:]...) out = append(out, (rightStackRoot)[:]...) - out = binary.BigEndian.AppendUint32(out, s.NextThreadId) + out = binary.BigEndian.AppendUint64(out, s.NextThreadId) return out, stateHashFromWitness(out) } diff --git a/cannon/mipsevm/multithreaded/state_test.go b/cannon/mipsevm/multithreaded/state_test.go index e9dbef2db346..8d7fbd766261 100644 --- a/cannon/mipsevm/multithreaded/state_test.go +++ b/cannon/mipsevm/multithreaded/state_test.go @@ -37,9 +37,9 @@ func TestState_EncodeWitness(t *testing.T) { {exited: true, exitCode: 3}, } - heap := uint32(12) + heap := uint64(12) preimageKey := crypto.Keccak256Hash([]byte{1, 2, 3, 4}) - preimageOffset := uint32(24) + preimageOffset := uint64(24) step := uint64(33) stepsSinceContextSwitch := uint64(123) for _, c := range cases { @@ -139,7 +139,7 @@ func TestState_EncodeThreadProof_SingleThread(t *testing.T) { activeThread.Cpu.HI = 11 activeThread.Cpu.LO = 22 for i := 0; i < 32; i++ { - activeThread.Registers[i] = uint32(i) + activeThread.Registers[i] = uint64(i) } expectedProof := append([]byte{}, activeThread.serializeThread()[:]...) @@ -161,12 +161,12 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) { // Set some fields on our threads for i := 0; i < 3; i++ { curThread := state.LeftThreadStack[i] - curThread.Cpu.PC = uint32(4 * i) + curThread.Cpu.PC = uint64(4 * i) curThread.Cpu.NextPC = curThread.Cpu.PC + 4 - curThread.Cpu.HI = uint32(11 + i) - curThread.Cpu.LO = uint32(22 + i) + curThread.Cpu.HI = uint64(11 + i) + curThread.Cpu.LO = uint64(22 + i) for j := 0; j < 32; j++ { - curThread.Registers[j] = uint32(j + i) + curThread.Registers[j] = uint64(j + i) } } @@ -192,12 +192,12 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) { func TestState_EncodeThreadProof_EmptyThreadStackPanic(t *testing.T) { cases := []struct { name string - wakeupAddr uint32 + wakeupAddr uint64 traverseRight bool }{ - {"traverse left during wakeup traversal", uint32(99), false}, + {"traverse left during wakeup traversal", uint64(99), false}, {"traverse left during normal traversal", exec.FutexEmptyAddr, false}, - {"traverse right during wakeup traversal", uint32(99), true}, + {"traverse right during wakeup traversal", uint64(99), true}, {"traverse right during normal traversal", exec.FutexEmptyAddr, true}, } diff --git a/cannon/mipsevm/multithreaded/thread.go b/cannon/mipsevm/multithreaded/thread.go index fd91900d3e83..7f1f5b70a3dc 100644 --- a/cannon/mipsevm/multithreaded/thread.go +++ b/cannon/mipsevm/multithreaded/thread.go @@ -11,7 +11,7 @@ import ( ) // SERIALIZED_THREAD_SIZE is the size of a serialized ThreadState object -const SERIALIZED_THREAD_SIZE = 166 +const SERIALIZED_THREAD_SIZE = 322 // THREAD_WITNESS_SIZE is the size of a thread witness encoded in bytes. // @@ -23,18 +23,18 @@ const THREAD_WITNESS_SIZE = SERIALIZED_THREAD_SIZE + 32 var EmptyThreadsRoot common.Hash = common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5") type ThreadState struct { - ThreadId uint32 `json:"threadId"` + ThreadId uint64 `json:"threadId"` ExitCode uint8 `json:"exit"` Exited bool `json:"exited"` - FutexAddr uint32 `json:"futexAddr"` - FutexVal uint32 `json:"futexVal"` + FutexAddr uint64 `json:"futexAddr"` + FutexVal uint64 `json:"futexVal"` FutexTimeoutStep uint64 `json:"futexTimeoutStep"` Cpu mipsevm.CpuScalars `json:"cpu"` - Registers [32]uint32 `json:"registers"` + Registers [32]uint64 `json:"registers"` } func CreateEmptyThread() *ThreadState { - initThreadId := uint32(0) + initThreadId := uint64(0) return &ThreadState{ ThreadId: initThreadId, ExitCode: 0, @@ -48,27 +48,27 @@ func CreateEmptyThread() *ThreadState { FutexAddr: exec.FutexEmptyAddr, FutexVal: 0, FutexTimeoutStep: 0, - Registers: [32]uint32{}, + Registers: [32]uint64{}, } } func (t *ThreadState) serializeThread() []byte { out := make([]byte, 0, SERIALIZED_THREAD_SIZE) - out = binary.BigEndian.AppendUint32(out, t.ThreadId) + out = binary.BigEndian.AppendUint64(out, t.ThreadId) out = append(out, t.ExitCode) out = mipsevm.AppendBoolToWitness(out, t.Exited) - out = binary.BigEndian.AppendUint32(out, t.FutexAddr) - out = binary.BigEndian.AppendUint32(out, t.FutexVal) + out = binary.BigEndian.AppendUint64(out, t.FutexAddr) + out = binary.BigEndian.AppendUint64(out, t.FutexVal) out = binary.BigEndian.AppendUint64(out, t.FutexTimeoutStep) - out = binary.BigEndian.AppendUint32(out, t.Cpu.PC) - out = binary.BigEndian.AppendUint32(out, t.Cpu.NextPC) - out = binary.BigEndian.AppendUint32(out, t.Cpu.LO) - out = binary.BigEndian.AppendUint32(out, t.Cpu.HI) + out = binary.BigEndian.AppendUint64(out, t.Cpu.PC) + out = binary.BigEndian.AppendUint64(out, t.Cpu.NextPC) + out = binary.BigEndian.AppendUint64(out, t.Cpu.LO) + out = binary.BigEndian.AppendUint64(out, t.Cpu.HI) for _, r := range t.Registers { - out = binary.BigEndian.AppendUint32(out, r) + out = binary.BigEndian.AppendUint64(out, r) } return out diff --git a/cannon/mipsevm/program/load.go b/cannon/mipsevm/program/load.go index 86269cf00806..829776f68643 100644 --- a/cannon/mipsevm/program/load.go +++ b/cannon/mipsevm/program/load.go @@ -9,13 +9,13 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm" ) -const HEAP_START = 0x05000000 +const HEAP_START = 0x10_00_00_00_00_00_00_00 -type CreateInitialFPVMState[T mipsevm.FPVMState] func(pc, heapStart uint32) T +type CreateInitialFPVMState[T mipsevm.FPVMState] func(pc, heapStart uint64) T func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[T]) (T, error) { var empty T - s := initState(uint32(f.Entry), HEAP_START) + s := initState(uint64(f.Entry), HEAP_START) for i, prog := range f.Progs { if prog.Type == 0x70000003 { // MIPS_ABIFLAGS @@ -35,13 +35,10 @@ func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[ } } - 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) - } 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) } - if err := s.GetMemory().SetMemoryRange(uint32(prog.Vaddr), r); err != nil { + if err := s.GetMemory().SetMemoryRange(uint64(prog.Vaddr), r); err != nil { return empty, fmt.Errorf("failed to read program segment %d: %w", i, err) } } diff --git a/cannon/mipsevm/program/metadata.go b/cannon/mipsevm/program/metadata.go index e90cbe14815c..c5f935d236e0 100644 --- a/cannon/mipsevm/program/metadata.go +++ b/cannon/mipsevm/program/metadata.go @@ -8,8 +8,8 @@ import ( type Symbol struct { Name string `json:"name"` - Start uint32 `json:"start"` - Size uint32 `json:"size"` + Start uint64 `json:"start"` + Size uint64 `json:"size"` } type Metadata struct { @@ -27,12 +27,12 @@ func MakeMetadata(elfProgram *elf.File) (*Metadata, error) { }) out := &Metadata{Symbols: make([]Symbol, len(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: uint64(s.Value), Size: uint64(s.Size)} } return out, nil } -func (m *Metadata) LookupSymbol(addr uint32) string { +func (m *Metadata) LookupSymbol(addr uint64) string { if len(m.Symbols) == 0 { return "!unknown" } @@ -50,19 +50,19 @@ func (m *Metadata) LookupSymbol(addr uint32) string { return out.Name } -type SymbolMatcher func(addr uint32) bool +type SymbolMatcher func(addr uint64) bool func (m *Metadata) CreateSymbolMatcher(name string) SymbolMatcher { for _, s := range m.Symbols { if s.Name == name { start := s.Start end := s.Start + s.Size - return func(addr uint32) bool { + return func(addr uint64) bool { return addr >= start && addr < end } } } - return func(addr uint32) bool { + return func(addr uint64) bool { return false } } diff --git a/cannon/mipsevm/program/patch.go b/cannon/mipsevm/program/patch.go index c845488dd7b9..319d5896b802 100644 --- a/cannon/mipsevm/program/patch.go +++ b/cannon/mipsevm/program/patch.go @@ -41,7 +41,7 @@ func PatchGo(f *elf.File, st mipsevm.FPVMState) error { // MIPS32 patch: ret (pseudo instruction) // 03e00008 = jr $ra = ret (pseudo instruction) // 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(uint64(s.Value), bytes.NewReader([]byte{ 0x03, 0xe0, 0x00, 0x08, 0, 0, 0, 0, })); err != nil { @@ -59,30 +59,29 @@ func PatchGo(f *elf.File, st mipsevm.FPVMState) error { // TODO(cp-903) Consider setting envar "GODEBUG=memprofilerate=0" for go programs to disable memprofiling, instead of patching it out in PatchGo() func PatchStack(st mipsevm.FPVMState) error { // setup stack pointer - sp := uint32(0x7f_ff_d0_00) + sp := uint64(0x7F_FF_FF_FF_D0_00_00_00) // 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 { return fmt.Errorf("failed to allocate page for stack content") } st.GetRegisters()[29] = sp - storeMem := func(addr uint32, v uint32) { - var dat [4]byte - binary.BigEndian.PutUint32(dat[:], v) + storeMem := func(addr uint64, v uint64) { + var dat [8]byte + binary.BigEndian.PutUint64(dat[:], v) _ = st.GetMemory().SetMemoryRange(addr, bytes.NewReader(dat[:])) } // init argc, argv, aux on stack - storeMem(sp+4*1, 0x42) // argc = 0 (argument count) - storeMem(sp+4*2, 0x35) // argv[n] = 0 (terminating argv) - storeMem(sp+4*3, 0) // envp[term] = 0 (no env vars) - storeMem(sp+4*4, 6) // auxv[0] = _AT_PAGESZ = 6 (key) - storeMem(sp+4*5, 4096) // auxv[1] = page size of 4 KiB (value) - (== minPhysPageSize) - storeMem(sp+4*6, 25) // auxv[2] = AT_RANDOM - storeMem(sp+4*7, sp+4*9) // auxv[3] = address of 16 bytes containing random value - storeMem(sp+4*8, 0) // auxv[term] = 0 - - _ = st.GetMemory().SetMemoryRange(sp+4*9, bytes.NewReader([]byte("4;byfairdiceroll"))) // 16 bytes of "randomness" + storeMem(sp+8*1, 0x42) // argc = 0 (argument count) + storeMem(sp+8*2, 0x35) // argv[n] = 0 (terminating argv) + storeMem(sp+8*3, 0) // envp[term] = 0 (no env vars) + storeMem(sp+8*4, 6) // auxv[0] = _AT_PAGESZ = 6 (key) + storeMem(sp+8*5, 4096) // auxv[1] = page size of 4 KiB (value) - (== minPhysPageSize) + storeMem(sp+8*6, 25) // auxv[2] = AT_RANDOM + storeMem(sp+8*7, sp+8*9) // auxv[3] = address of 16 bytes containing random value + storeMem(sp+8*8, 0) // auxv[term] = 0 + _ = st.GetMemory().SetMemoryRange(sp+8*9, bytes.NewReader([]byte("4;byfairdiceroll"))) // 16 bytes of "randomness" return nil } diff --git a/cannon/mipsevm/singlethreaded/instrumented.go b/cannon/mipsevm/singlethreaded/instrumented.go index 657dd0e5e904..49b6c84d24e1 100644 --- a/cannon/mipsevm/singlethreaded/instrumented.go +++ b/cannon/mipsevm/singlethreaded/instrumented.go @@ -30,7 +30,7 @@ var _ mipsevm.FPVM = (*InstrumentedState)(nil) func NewInstrumentedState(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta *program.Metadata) *InstrumentedState { var sleepCheck program.SymbolMatcher if meta == nil { - sleepCheck = func(addr uint32) bool { return false } + sleepCheck = func(addr uint64) bool { return false } } else { sleepCheck = meta.CreateSymbolMatcher("runtime.notesleep") } @@ -86,7 +86,7 @@ func (m *InstrumentedState) Step(proof bool) (wit *mipsevm.StepWitness, err erro memProof := m.memoryTracker.MemProof() wit.ProofData = append(wit.ProofData, memProof[:]...) lastPreimageKey, lastPreimage, lastPreimageOffset := m.preimageOracle.LastPreimage() - if lastPreimageOffset != ^uint32(0) { + if lastPreimageOffset != ^uint64(0) { wit.PreimageOffset = lastPreimageOffset wit.PreimageKey = lastPreimageKey wit.PreimageValue = lastPreimage @@ -99,7 +99,7 @@ func (m *InstrumentedState) CheckInfiniteLoop() bool { return m.sleepCheck(m.state.GetPC()) } -func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, uint32) { +func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, uint64) { return m.preimageOracle.LastPreimage() } diff --git a/cannon/mipsevm/singlethreaded/mips.go b/cannon/mipsevm/singlethreaded/mips.go index cb2d5e9a63b0..92d6d09130f7 100644 --- a/cannon/mipsevm/singlethreaded/mips.go +++ b/cannon/mipsevm/singlethreaded/mips.go @@ -10,13 +10,13 @@ import ( func (m *InstrumentedState) handleSyscall() error { syscallNum, a0, a1, a2, _ := exec.GetSyscallArgs(&m.state.Registers) - v0 := uint32(0) - v1 := uint32(0) + v0 := uint64(0) + v1 := uint64(0) //fmt.Printf("syscall: %d\n", syscallNum) switch syscallNum { case exec.SysMmap: - var newHeap uint32 + var newHeap uint64 v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) m.state.Heap = newHeap case exec.SysBrk: @@ -28,13 +28,13 @@ func (m *InstrumentedState) handleSyscall() error { m.state.ExitCode = uint8(a0) return nil case exec.SysRead: - var newPreimageOffset uint32 + var newPreimageOffset uint64 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 case exec.SysWrite: var newLastHint hexutil.Bytes var newPreimageKey common.Hash - var newPreimageOffset uint32 + var newPreimageOffset uint64 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.PreimageKey = newPreimageKey diff --git a/cannon/mipsevm/singlethreaded/state.go b/cannon/mipsevm/singlethreaded/state.go index fc23b072b0c9..5d4aab195925 100644 --- a/cannon/mipsevm/singlethreaded/state.go +++ b/cannon/mipsevm/singlethreaded/state.go @@ -14,24 +14,24 @@ import ( ) // STATE_WITNESS_SIZE is the size of the state witness encoding in bytes. -const STATE_WITNESS_SIZE = 226 +const STATE_WITNESS_SIZE = 378 type State struct { Memory *memory.Memory `json:"memory"` PreimageKey common.Hash `json:"preimageKey"` - PreimageOffset uint32 `json:"preimageOffset"` // note that the offset includes the 8-byte length prefix + PreimageOffset uint64 `json:"preimageOffset"` // note that the offset includes the 8-byte length prefix Cpu mipsevm.CpuScalars `json:"cpu"` - Heap uint32 `json:"heap"` // to handle mmap growth + Heap uint64 `json:"heap"` // to handle mmap growth ExitCode uint8 `json:"exit"` Exited bool `json:"exited"` Step uint64 `json:"step"` - Registers [32]uint32 `json:"registers"` + Registers [32]uint64 `json:"registers"` // LastHint is optional metadata, and not part of the VM state itself. // It is used to remember the last pre-image hint, @@ -55,7 +55,7 @@ func CreateEmptyState() *State { HI: 0, }, Heap: 0, - Registers: [32]uint32{}, + Registers: [32]uint64{}, Memory: memory.NewMemory(), ExitCode: 0, Exited: false, @@ -63,7 +63,7 @@ func CreateEmptyState() *State { } } -func CreateInitialState(pc, heapStart uint32) *State { +func CreateInitialState(pc, heapStart uint64) *State { state := CreateEmptyState() state.Cpu.PC = pc state.Cpu.NextPC = pc + 4 @@ -75,16 +75,16 @@ func CreateInitialState(pc, heapStart uint32) *State { type stateMarshaling struct { Memory *memory.Memory `json:"memory"` PreimageKey common.Hash `json:"preimageKey"` - PreimageOffset uint32 `json:"preimageOffset"` - PC uint32 `json:"pc"` - NextPC uint32 `json:"nextPC"` - LO uint32 `json:"lo"` - HI uint32 `json:"hi"` - Heap uint32 `json:"heap"` + PreimageOffset uint64 `json:"preimageOffset"` + PC uint64 `json:"pc"` + NextPC uint64 `json:"nextPC"` + LO uint64 `json:"lo"` + HI uint64 `json:"hi"` + Heap uint64 `json:"heap"` ExitCode uint8 `json:"exit"` Exited bool `json:"exited"` Step uint64 `json:"step"` - Registers [32]uint32 `json:"registers"` + Registers [32]uint64 `json:"registers"` LastHint hexutil.Bytes `json:"lastHint,omitempty"` } @@ -128,9 +128,9 @@ func (s *State) UnmarshalJSON(data []byte) error { return nil } -func (s *State) GetPC() uint32 { return s.Cpu.PC } +func (s *State) GetPC() uint64 { return s.Cpu.PC } -func (s *State) GetRegisters() *[32]uint32 { return &s.Registers } +func (s *State) GetRegisters() *[32]uint64 { return &s.Registers } func (s *State) GetExitCode() uint8 { return s.ExitCode } @@ -151,17 +151,17 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) { memRoot := s.Memory.MerkleRoot() out = append(out, memRoot[:]...) out = append(out, s.PreimageKey[:]...) - out = binary.BigEndian.AppendUint32(out, s.PreimageOffset) - out = binary.BigEndian.AppendUint32(out, s.Cpu.PC) - out = binary.BigEndian.AppendUint32(out, s.Cpu.NextPC) - out = binary.BigEndian.AppendUint32(out, s.Cpu.LO) - out = binary.BigEndian.AppendUint32(out, s.Cpu.HI) - out = binary.BigEndian.AppendUint32(out, s.Heap) + out = binary.BigEndian.AppendUint64(out, s.PreimageOffset) + out = binary.BigEndian.AppendUint64(out, s.Cpu.PC) + out = binary.BigEndian.AppendUint64(out, s.Cpu.NextPC) + out = binary.BigEndian.AppendUint64(out, s.Cpu.LO) + out = binary.BigEndian.AppendUint64(out, s.Cpu.HI) + out = binary.BigEndian.AppendUint64(out, s.Heap) out = append(out, s.ExitCode) out = mipsevm.AppendBoolToWitness(out, s.Exited) out = binary.BigEndian.AppendUint64(out, s.Step) for _, r := range s.Registers { - out = binary.BigEndian.AppendUint32(out, r) + out = binary.BigEndian.AppendUint64(out, r) } return out, stateHashFromWitness(out) } @@ -186,7 +186,7 @@ func stateHashFromWitness(sw []byte) common.Hash { panic("Invalid witness length") } hash := crypto.Keccak256Hash(sw) - offset := 32*2 + 4*6 + offset := 32*2 + 8*6 exitCode := sw[offset] exited := sw[offset+1] status := mipsevm.VmStatus(exited == 1, exitCode) diff --git a/cannon/mipsevm/state.go b/cannon/mipsevm/state.go index 8ed6f265c894..5904983f8150 100644 --- a/cannon/mipsevm/state.go +++ b/cannon/mipsevm/state.go @@ -1,10 +1,10 @@ package mipsevm type CpuScalars struct { - PC uint32 `json:"pc"` - NextPC uint32 `json:"nextPC"` - LO uint32 `json:"lo"` - HI uint32 `json:"hi"` + PC uint64 `json:"pc"` + NextPC uint64 `json:"nextPC"` + LO uint64 `json:"lo"` + HI uint64 `json:"hi"` } const ( diff --git a/cannon/mipsevm/tests/evm_test.go b/cannon/mipsevm/tests/evm_test.go index 41826be04d72..ddb8b34dced9 100644 --- a/cannon/mipsevm/tests/evm_test.go +++ b/cannon/mipsevm/tests/evm_test.go @@ -107,13 +107,13 @@ func TestEVM_CloneFlags(t *testing.T) { cases := []struct { name string - flags uint32 + flags uint64 valid bool }{ {"the supported flags bitmask", exec.ValidCloneFlags, true}, {"no flags", 0, false}, - {"all flags", ^uint32(0), false}, - {"all unsupported flags", ^uint32(exec.ValidCloneFlags), false}, + {"all flags", ^uint64(0), false}, + {"all unsupported flags", ^uint64(exec.ValidCloneFlags), false}, {"a few supported flags", exec.CloneFs | exec.CloneSysvsem, false}, {"one supported flag", exec.CloneFs, false}, {"mixed supported and unsupported flags", exec.CloneFs | exec.CloneParentSettid, false}, @@ -161,8 +161,8 @@ func TestEVMSingleStep(t *testing.T) { cases := []struct { name string - pc uint32 - nextPC uint32 + pc uint64 + nextPC uint64 insn uint32 }{ {"j MSB set target", 0, 4, 0x0A_00_00_02}, // j 0x02_00_00_02 @@ -349,10 +349,10 @@ func TestEVMSysWriteHint(t *testing.T) { state.LastHint = tt.lastHint state.Registers[2] = exec.SysWrite state.Registers[4] = exec.FdHintWrite - state.Registers[5] = uint32(tt.memOffset) - state.Registers[6] = uint32(tt.bytesToWrite) + state.Registers[5] = uint64(tt.memOffset) + state.Registers[6] = uint64(tt.bytesToWrite) - err := state.Memory.SetMemoryRange(uint32(tt.memOffset), bytes.NewReader(tt.hintData)) + err := state.Memory.SetMemoryRange(uint64(tt.memOffset), bytes.NewReader(tt.hintData)) require.NoError(t, err) state.Memory.SetMemory(0, insn) curStep := state.Step @@ -384,7 +384,7 @@ func TestEVMFault(t *testing.T) { cases := []struct { name string - nextPC uint32 + nextPC uint64 insn uint32 }{ {"illegal instruction", 0, 0xFF_FF_FF_FF}, diff --git a/cannon/mipsevm/tests/fuzz_evm_test.go b/cannon/mipsevm/tests/fuzz_evm_test.go index 4135f036b517..73cb76b99e7c 100644 --- a/cannon/mipsevm/tests/fuzz_evm_test.go +++ b/cannon/mipsevm/tests/fuzz_evm_test.go @@ -23,7 +23,7 @@ const syscallInsn = uint32(0x00_00_00_0c) func FuzzStateSyscallBrk(f *testing.F) { contracts, addrs := testContractsSetup(f) - f.Fuzz(func(t *testing.T, pc uint32, step uint64, preimageOffset uint32) { + f.Fuzz(func(t *testing.T, pc uint64, step uint64, preimageOffset uint64) { pc = pc & 0xFF_FF_FF_FC // align PC nextPC := pc + 4 state := &singlethreaded.State{ @@ -37,7 +37,7 @@ func FuzzStateSyscallBrk(f *testing.F) { ExitCode: 0, Exited: false, Memory: memory.NewMemory(), - Registers: [32]uint32{2: exec.SysBrk}, + Registers: [32]uint64{2: exec.SysBrk}, Step: step, PreimageKey: common.Hash{}, PreimageOffset: preimageOffset, @@ -75,7 +75,7 @@ func FuzzStateSyscallBrk(f *testing.F) { func FuzzStateSyscallClone(f *testing.F) { contracts, addrs := testContractsSetup(f) - f.Fuzz(func(t *testing.T, pc uint32, step uint64, preimageOffset uint32) { + f.Fuzz(func(t *testing.T, pc uint64, step uint64, preimageOffset uint64) { pc = pc & 0xFF_FF_FF_FC // align PC nextPC := pc + 4 state := &singlethreaded.State{ @@ -89,7 +89,7 @@ func FuzzStateSyscallClone(f *testing.F) { ExitCode: 0, Exited: false, Memory: memory.NewMemory(), - Registers: [32]uint32{2: exec.SysClone}, + Registers: [32]uint64{2: exec.SysClone}, Step: step, PreimageOffset: preimageOffset, } @@ -127,7 +127,7 @@ func FuzzStateSyscallClone(f *testing.F) { func FuzzStateSyscallMmap(f *testing.F) { contracts, addrs := testContractsSetup(f) step := uint64(0) - f.Fuzz(func(t *testing.T, addr uint32, siz uint32, heap uint32) { + f.Fuzz(func(t *testing.T, addr uint64, siz uint64, heap uint64) { state := &singlethreaded.State{ Cpu: mipsevm.CpuScalars{ PC: 0, @@ -139,7 +139,7 @@ func FuzzStateSyscallMmap(f *testing.F) { ExitCode: 0, Exited: false, Memory: memory.NewMemory(), - Registers: [32]uint32{2: exec.SysMmap, 4: addr, 5: siz}, + Registers: [32]uint64{2: exec.SysMmap, 4: addr, 5: siz}, Step: step, PreimageOffset: 0, } @@ -188,7 +188,7 @@ func FuzzStateSyscallMmap(f *testing.F) { func FuzzStateSyscallExitGroup(f *testing.F) { contracts, addrs := testContractsSetup(f) - f.Fuzz(func(t *testing.T, exitCode uint8, pc uint32, step uint64) { + f.Fuzz(func(t *testing.T, exitCode uint8, pc uint64, step uint64) { pc = pc & 0xFF_FF_FF_FC // align PC nextPC := pc + 4 state := &singlethreaded.State{ @@ -202,7 +202,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) { ExitCode: 0, Exited: false, Memory: memory.NewMemory(), - Registers: [32]uint32{2: exec.SysExitGroup, 4: uint32(exitCode)}, + Registers: [32]uint64{2: exec.SysExitGroup, 4: uint64(exitCode)}, Step: step, PreimageOffset: 0, } @@ -239,7 +239,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) { func FuzzStateSyscallFcntl(f *testing.F) { contracts, addrs := testContractsSetup(f) step := uint64(0) - f.Fuzz(func(t *testing.T, fd uint32, cmd uint32) { + f.Fuzz(func(t *testing.T, fd uint64, cmd uint64) { state := &singlethreaded.State{ Cpu: mipsevm.CpuScalars{ PC: 0, @@ -251,7 +251,7 @@ func FuzzStateSyscallFcntl(f *testing.F) { ExitCode: 0, Exited: false, Memory: memory.NewMemory(), - Registers: [32]uint32{2: exec.SysFcntl, 4: fd, 5: cmd}, + Registers: [32]uint64{2: exec.SysFcntl, 4: fd, 5: cmd}, Step: step, PreimageOffset: 0, } @@ -305,7 +305,7 @@ func FuzzStateSyscallFcntl(f *testing.F) { func FuzzStateHintRead(f *testing.F) { contracts, addrs := testContractsSetup(f) step := uint64(0) - f.Fuzz(func(t *testing.T, addr uint32, count uint32) { + f.Fuzz(func(t *testing.T, addr uint64, count uint64) { preimageData := []byte("hello world") state := &singlethreaded.State{ Cpu: mipsevm.CpuScalars{ @@ -318,7 +318,7 @@ func FuzzStateHintRead(f *testing.F) { ExitCode: 0, Exited: false, Memory: memory.NewMemory(), - Registers: [32]uint32{2: exec.SysRead, 4: exec.FdHintRead, 5: addr, 6: count}, + Registers: [32]uint64{2: exec.SysRead, 4: exec.FdHintRead, 5: addr, 6: count}, Step: step, PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(), PreimageOffset: 0, @@ -358,9 +358,9 @@ func FuzzStateHintRead(f *testing.F) { func FuzzStatePreimageRead(f *testing.F) { contracts, addrs := testContractsSetup(f) step := uint64(0) - f.Fuzz(func(t *testing.T, addr uint32, count uint32, preimageOffset uint32) { + f.Fuzz(func(t *testing.T, addr uint64, count uint64, preimageOffset uint64) { preimageData := []byte("hello world") - if preimageOffset >= uint32(len(preimageData)) { + if preimageOffset >= uint64(len(preimageData)) { t.SkipNow() } state := &singlethreaded.State{ @@ -374,7 +374,7 @@ func FuzzStatePreimageRead(f *testing.F) { ExitCode: 0, Exited: false, Memory: memory.NewMemory(), - Registers: [32]uint32{2: exec.SysRead, 4: exec.FdPreimageRead, 5: addr, 6: count}, + Registers: [32]uint64{2: exec.SysRead, 4: exec.FdPreimageRead, 5: addr, 6: count}, Step: step, PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(), PreimageOffset: preimageOffset, @@ -386,8 +386,8 @@ func FuzzStatePreimageRead(f *testing.F) { if writeLen > 4 { writeLen = 4 } - if preimageOffset+writeLen > uint32(8+len(preimageData)) { - writeLen = uint32(8+len(preimageData)) - preimageOffset + if preimageOffset+writeLen > uint64(8+len(preimageData)) { + writeLen = uint64(8+len(preimageData)) - preimageOffset } oracle := testutil.StaticOracle(t, preimageData) @@ -425,7 +425,7 @@ func FuzzStatePreimageRead(f *testing.F) { func FuzzStateHintWrite(f *testing.F) { contracts, addrs := testContractsSetup(f) step := uint64(0) - f.Fuzz(func(t *testing.T, addr uint32, count uint32, randSeed int64) { + f.Fuzz(func(t *testing.T, addr uint64, count uint64, randSeed int64) { preimageData := []byte("hello world") state := &singlethreaded.State{ Cpu: mipsevm.CpuScalars{ @@ -438,7 +438,7 @@ func FuzzStateHintWrite(f *testing.F) { ExitCode: 0, Exited: false, Memory: memory.NewMemory(), - Registers: [32]uint32{2: exec.SysWrite, 4: exec.FdHintWrite, 5: addr, 6: count}, + Registers: [32]uint64{2: exec.SysWrite, 4: exec.FdHintWrite, 5: addr, 6: count}, Step: step, PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(), PreimageOffset: 0, @@ -486,7 +486,7 @@ func FuzzStateHintWrite(f *testing.F) { func FuzzStatePreimageWrite(f *testing.F) { contracts, addrs := testContractsSetup(f) step := uint64(0) - f.Fuzz(func(t *testing.T, addr uint32, count uint32) { + f.Fuzz(func(t *testing.T, addr uint64, count uint64) { preimageData := []byte("hello world") state := &singlethreaded.State{ Cpu: mipsevm.CpuScalars{ @@ -499,7 +499,7 @@ func FuzzStatePreimageWrite(f *testing.F) { ExitCode: 0, Exited: false, Memory: memory.NewMemory(), - Registers: [32]uint32{2: exec.SysWrite, 4: exec.FdPreimageWrite, 5: addr, 6: count}, + Registers: [32]uint64{2: exec.SysWrite, 4: exec.FdPreimageWrite, 5: addr, 6: count}, Step: 0, PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(), PreimageOffset: 128, @@ -539,7 +539,7 @@ func FuzzStatePreimageWrite(f *testing.F) { }) } -func randomBytes(seed int64, length uint32) ([]byte, error) { +func randomBytes(seed int64, length uint64) ([]byte, error) { r := rand.New(rand.NewSource(seed)) randBytes := make([]byte, length) if _, err := r.Read(randBytes); err != nil { diff --git a/cannon/mipsevm/tests/open_mips_tests/README.md b/cannon/mipsevm/tests/open_mips_tests/README.md index 93bbf4128e3d..947fe4439f7a 100644 --- a/cannon/mipsevm/tests/open_mips_tests/README.md +++ b/cannon/mipsevm/tests/open_mips_tests/README.md @@ -2,9 +2,16 @@ Tests from https://github.com/grantae/OpenMIPS/tree/d606b35e9d5260aef20de2a58660c8303a681e9c/software/test/macro/tests -OpenMIPS is licensed LGPLv3 (as seen in the root of the repository), see [`LICENSE`](LICENSE) file. +OpenMIPS is licensed LGPLv3 (as seen in the root of the repository), see [`LICENSE`](./LICENSE) file. Note that some build-system files from 2014/2015 in that repository by the same author are marked as BSD licensed, but the build-system is not used here. -Requires https://github.com/sergev/LiteBSD/releases/download/tools/gcc-4.8.1-mips-macosx.tgz to build +## Building +**Requirements**: + +- Docker + +```sh +just make-tests +``` diff --git a/cannon/mipsevm/tests/open_mips_tests/builder.dockerfile b/cannon/mipsevm/tests/open_mips_tests/builder.dockerfile new file mode 100644 index 000000000000..2159c3932441 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/builder.dockerfile @@ -0,0 +1,19 @@ +FROM --platform=linux/amd64 ubuntu:22.04 + +ENV SHELL=/bin/bash +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update && apt-get install --assume-yes --no-install-recommends \ + ca-certificates \ + build-essential \ + curl \ + g++-mips-linux-gnu \ + libc6-dev-mips-cross \ + binutils-mips-linux-gnu \ + llvm \ + clang \ + python3 \ + python3-pip \ + xxd + +RUN python3 -m pip install capstone pyelftools diff --git a/cannon/mipsevm/tests/open_mips_tests/justfile b/cannon/mipsevm/tests/open_mips_tests/justfile new file mode 100644 index 000000000000..d8f60bcec9f6 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/justfile @@ -0,0 +1,23 @@ +set positional-arguments := true + +help: + just --list + +# Build the MIPS64 test case assembler image +build-image: + #!/bin/bash + docker build -f builder.dockerfile -t mips-test-builder:local . + +# Assemble the test cases +make-tests *args='': + #!/bin/bash + if [ ! "$(docker images -q mips-test-builder:local 2> /dev/null)" ]; then + just build-image + fi + + docker run \ + --rm \ + --platform linux/amd64 \ + -v `pwd`/:/workdir \ + -w="/workdir" \ + mips-test-builder:local bash make_tests.sh {{args}} diff --git a/cannon/mipsevm/tests/open_mips_tests/make_tests.sh b/cannon/mipsevm/tests/open_mips_tests/make_tests.sh new file mode 100644 index 000000000000..6b19e2c121a1 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/make_tests.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +set -e + +function maketest() { + local src="$1" + local out="$2" + + printf "building %s -> %s" "$src" "$out" + + # Create a temporary file + full_bin=$(mktemp) + + # Assemble the full test vector + mips-linux-gnu-as -defsym big_endian=1 -march=mips64 -o "$full_bin" "$src" + + # Copy the `.test` section data to a temporary file + section_data=$(mktemp) + mips-linux-gnu-objcopy --dump-section .test="$section_data" "$full_bin" + + # Write the .test section data to the output file + cp "$section_data" "$out" + + # Clean up the temporary files + rm "$full_bin" "$section_data" + + printf " ✅\n" +} + +mkdir -p /tmp/mips + +if [ "$#" -gt 0 ]; then + maketest "$1" "test/bin/$(basename "$1" .asm).bin" +else + for d in test/*.asm; + do + [ -e "$d" ] || continue + maketest "$d" "test/bin/$(basename "$d" .asm).bin" + done + + echo "[🧙] All tests built successfully. God speed, space cowboy." +fi diff --git a/cannon/mipsevm/tests/open_mips_tests/maketests.py b/cannon/mipsevm/tests/open_mips_tests/maketests.py deleted file mode 100755 index 56118b135b14..000000000000 --- a/cannon/mipsevm/tests/open_mips_tests/maketests.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 -import os -import sys -import tempfile -from capstone import * -from elftools.elf.elffile import ELFFile -md = Cs(CS_ARCH_MIPS, CS_MODE_32 + CS_MODE_BIG_ENDIAN) - -def maketest(d, out): - with tempfile.NamedTemporaryFile() as nf: - print("building", d, "->", out) - # which mips is go - ret = os.system("mips-linux-gnu-as -defsym big_endian=1 -march=mips32r2 -o %s %s" % (nf.name, d)) - assert(ret == 0) - nf.seek(0) - elffile = ELFFile(nf) - #print(elffile) - for sec in elffile.iter_sections(): - #print(sec, sec.name, sec.data()) - if sec.name == ".test": - with open(out, "wb") as f: - # jump to 0xdead0000 when done - #data = b"\x24\x1f\xde\xad\x00\x1f\xfc\x00" + sec.data() - data = sec.data() - for dd in md.disasm(data, 0): - print(dd) - f.write(data) - -if __name__ == "__main__": - os.makedirs("/tmp/mips", exist_ok=True) - if len(sys.argv) > 2: - maketest(sys.argv[1], sys.argv[2]) - else: - for d in os.listdir("test/"): - if not d.endswith(".asm"): - continue - maketest("test/"+d, "test/bin/"+(d.replace(".asm", ".bin"))) diff --git a/cannon/mipsevm/tests/open_mips_tests/test/bin/brk.bin b/cannon/mipsevm/tests/open_mips_tests/test/bin/brk.bin index 4f09817c422da288dc68402fe38ca9e0359ef641..3100b330b2babe7ff02f5d885c101f16a0bb4e6f 100644 GIT binary patch delta 10 RcmXpoU{PTbo-&a|2LK6J0rLO= delta 10 RcmXpoU{PV>KRb~{2LK8v0w@3g diff --git a/cannon/mipsevm/tests/open_mips_tests/test/bin/clone.bin b/cannon/mipsevm/tests/open_mips_tests/test/bin/clone.bin index a184fcdab4528ec807b8f02551fb16e5cb0d3a8c..5e0fe67f4e83f29804bd49d5368009122abf5bc7 100644 GIT binary patch delta 10 RcmXpoU{PTb-anB=2LK8L0v-SW delta 10 RcmXpoU{PTbkeJA#0{{nE0dxQW diff --git a/cannon/mipsevm/tests/open_mips_tests/test/bin/exit_group.bin b/cannon/mipsevm/tests/open_mips_tests/test/bin/exit_group.bin index 112479216298ba86b24d3a919a26cb0914721aaa..6cd28adb48823bd2dc675f378e5ffb54b8ec66ab 100644 GIT binary patch delta 14 VcmdPV;ZR{=U{qle37yEH0ssqj0oDKj delta 14 VcmdPV;ZR{=U{qlem^P6^1po|X0uulL diff --git a/cannon/mipsevm/tests/open_mips_tests/test/bin/fcntl.bin b/cannon/mipsevm/tests/open_mips_tests/test/bin/fcntl.bin index 8d31a306e4f83f3e370046aeb53e09859b494835..1cb60eca755c94b42cb3e3a4884ff8aa62c3d8e2 100644 GIT binary patch delta 10 RcmXpoU{PTbJ~xp?2LK8{0xbXl delta 10 RcmXpoU{PV>zdn&g2LK9C0x|#q diff --git a/cannon/mipsevm/tests/open_mips_tests/test/bin/mmap.bin b/cannon/mipsevm/tests/open_mips_tests/test/bin/mmap.bin index 82d47bd239b158a9bf6921efc32c3e917a075026..90044af05943b0e25c34406ce7a19b6f940e6ca1 100644 GIT binary patch delta 10 RcmcDpU{PTbo;Z=k1ON%_0tf&A delta 10 RcmcDpU{PV>|22`t1ON+s0&M^Q diff --git a/cannon/mipsevm/tests/open_mips_tests/test/bin/oracle.bin b/cannon/mipsevm/tests/open_mips_tests/test/bin/oracle.bin index f2c5a4c2c7a7943a7c285cfa1021003cec205faf..648406ee8d4276448144c96357f60cda92979b4e 100644 GIT binary patch delta 57 zcmdnNw1a6v9g}e9#0Dib;SL4{1|Az$Lk5$j3@j>4D0~hT4hCkB*u(~riFaZEaIFk( delta 57 zcmdnNw1a6v9TWeOi496>{EHbF74D0~hOf8vo803zcG<^TWy delta 50 scmZ3%w1R0u9TWeOi496h{EHbF794c%KjA&wlAhC%LjsO67 C^b73( delta 77 zcmbQjyo7l|9TWeOi4DrOEGkU=iy0Ugcx+e=8BCTkpz}FY*ccem)NrbBFmQv!CSEuK E07lvlcmMzZ diff --git a/cannon/mipsevm/tests/open_mips_tests/test/bin/oracle_unaligned_write.bin b/cannon/mipsevm/tests/open_mips_tests/test/bin/oracle_unaligned_write.bin index a57ca2d0e70bc4f0adc204c4b397c0cc34fd42a0..bdd55540f8a48d76e0c3e672617522048f13102a 100644 GIT binary patch delta 89 zcmeyv^oMCeACqwB#0g?@!kr8Z3_L0<3~V;61`H-k1z1!#7}!> 12 (arithmetic shift right by 12 bits) + + # Compare the results + bne $t1, $t2, $finish # If $t1 != $t2, fail the test + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/dsra32.asm b/cannon/mipsevm/tests/open_mips_tests/test/dsra32.asm new file mode 100644 index 000000000000..365472721860 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/dsra32.asm @@ -0,0 +1,55 @@ +############################################################################### +# File : dsra32.asm +# Author : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'dsra32' instruction. +# +############################################################################### + + .section .test, "x" + .balign 4 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + # Load initial value into $t0 (example value: 0xFFFF123456789ABC) + li $t0, 0xFFFF1234 + dsll32 $t0, $t0, 0 # $t0 = 0xFFFF123400000000 + ori $t0, $t0, 0xBEEF # $t0 = 0xFFFF12340000BEEF + + # Calculate expected result manually (arithmetic shift right by 32 bits) + # Expected: 0xFFFFFFFFFFFF1234 + li $t2, 0xFFFF1234 # Expected result (sign extended - 0xFFFFFFFFFFFF1234) + + # Perform the dsra32 operation + dsra32 $t1, $t0, 0 # $t1 = $t0 >> 32 (arithmetic shift right by 32 bits) + + # Compare the results + bne $t1, $t2, $finish # If $t1 != $t2, fail the test + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/dsrav.asm b/cannon/mipsevm/tests/open_mips_tests/test/dsrav.asm new file mode 100644 index 000000000000..b109394577a1 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/dsrav.asm @@ -0,0 +1,48 @@ + +############################################################################### +# File : dsrav.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'dsrav' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + li $t1, 0xFFFF1234 # Load 0x12341234 set into $t1 + ori $s2, $0, 0x10 # Load shamt into $s2 + dsrav $t1, $t1, $s2 # Shift $t1 right by $s2 + li $t2, 0xFFFFFFFF # Load 0xFFFFFFFF into $t2 + bne $t1, $t2, $finish # Check if $t1 == $t2 + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/dsrl.asm b/cannon/mipsevm/tests/open_mips_tests/test/dsrl.asm new file mode 100644 index 000000000000..3583901ef013 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/dsrl.asm @@ -0,0 +1,49 @@ +############################################################################### +# File : dsrl.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'dsrl' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + lui $t0, 0xbfc0 # Load address 0xbfc007f8 (last double word in 2KB starting + ori $t0, 0x07f8 # from 0xbfc00000) + + li $t1, 0x0FFFFFFF # Load 0xFFFFFFF set into $t1 + dsrl $t1, $t1, 16 # Shift $t1 right by 16 bits + li $t2, 0xFFF # Load 0x00000FFF into $t2 + bne $t1, $t2, $finish # Check if $t1 == $t2 + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/dsrl32.asm b/cannon/mipsevm/tests/open_mips_tests/test/dsrl32.asm new file mode 100644 index 000000000000..1c38e27f981f --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/dsrl32.asm @@ -0,0 +1,49 @@ +############################################################################### +# File : dsrl32.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'dsrl32' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + lui $t0, 0xbfc0 # Load address 0xbfc007f8 (last double word in 2KB starting + ori $t0, 0x07f8 # from 0xbfc00000) + + li $t1, 0xFFFFFFFF # Load all bits set into $t1 + dsrl32 $t1, $t1, 16 # Shift $t1 right by 48 bits + li $t2, 0xFFFF # Load 0x0000FFFF into $t2 + bne $t1, $t2, $finish # Check if $t1 == $t2 + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/dsrlv.asm b/cannon/mipsevm/tests/open_mips_tests/test/dsrlv.asm new file mode 100644 index 000000000000..f0fd05066373 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/dsrlv.asm @@ -0,0 +1,48 @@ + +############################################################################### +# File : dsrlv.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'dsrlv' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + li $t1, 0x0FFFFFFF # Load 0xFFFFFFF set into $t1 + ori $s2, $0, 0x10 # Load shamt into $s2 + dsrlv $t1, $t1, $s2 # Shift $t1 right by $s2 + li $t2, 0xFFF # Load 0x00000FFF into $t2 + bne $t1, $t2, $finish # Check if $t1 == $t2 + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/dsub.asm b/cannon/mipsevm/tests/open_mips_tests/test/dsub.asm new file mode 100644 index 000000000000..ec2e6d43f276 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/dsub.asm @@ -0,0 +1,59 @@ +############################################################################### +# File : dsub.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'dsub' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + # Check that basic subtraction works + lui $t0, 0xffff # Load upper 48 bits into $t0 (0xFFFF_FFFF_FFFF_0000) - (top 32 bits are sign extended) + ori $t0, 0xffff # Load lower 16 bits into $t0 (0xFFFF_FFFF_FFFF_FFFF) + dsub $t1, $t0, $t0 # B = A - A + bne $t1, $0, $finish # If B != 0, fail + nop + + # Extended subtraction test + dsrl $t0, $t0, 16 # Shift right by 16 bits (0x0000_FFFF_FFFF_FFFF) + dsll $t0, $t0, 16 # Shift left by 16 bits (0xFFFF_FFFF_FFFF_0000) + ori $t0, $t0, 0xeeee # Set lower 16 bits to 0xFFFF (0xFFFF_FFFF_FFFF_EEEE) + ori $t1, $0, 0xeeee # B = 0xEEEE + dsub $t2, $t0, $t1 # C = A - B + + # Check that the result is 0xFFFF_FFFF_FFFF_0000 + lui $t3, 0xffff # Load upper 48 bits into $t3 (0xFFFF_FFFF_FFFF_0000) + bne $t2, $t3, $finish # If C != 0xFFFF_FFFF_FFFF_0000, fail + nop + + # Check standard 64-bit arithmetic + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/dsubu.asm b/cannon/mipsevm/tests/open_mips_tests/test/dsubu.asm new file mode 100644 index 000000000000..a52d607f5409 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/dsubu.asm @@ -0,0 +1,59 @@ +############################################################################### +# File : dsubu.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'dsubu' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + # Check that basic subtraction works + lui $t0, 0xffff # Load upper 48 bits into $t0 (0xFFFF_FFFF_FFFF_0000) - (top 32 bits are sign extended) + ori $t0, 0xffff # Load lower 16 bits into $t0 (0xFFFF_FFFF_FFFF_FFFF) + dsubu $t1, $t0, $t0 # B = A - A + bne $t1, $0, $finish # If B != 0, fail + nop + + # Extended subtraction test + dsrl $t0, $t0, 16 # Shift right by 16 bits (0x0000_FFFF_FFFF_FFFF) + dsll $t0, $t0, 16 # Shift left by 16 bits (0xFFFF_FFFF_FFFF_0000) + ori $t0, $t0, 0xeeee # Set lower 16 bits to 0xFFFF (0xFFFF_FFFF_FFFF_EEEE) + ori $t1, $0, 0xeeee # B = 0xEEEE + dsubu $t2, $t0, $t1 # C = A - B + + # Check that the result is 0xFFFF_FFFF_FFFF_0000 + lui $t3, 0xffff # Load upper 48 bits into $t3 (0xFFFF_FFFF_FFFF_0000) + bne $t2, $t3, $finish # If C != 0xFFFF_FFFF_FFFF_0000, fail + nop + + # Check standard 64-bit arithmetic + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/exit_group.asm b/cannon/mipsevm/tests/open_mips_tests/test/exit_group.asm index 57921689ca37..660157a142c5 100644 --- a/cannon/mipsevm/tests/open_mips_tests/test/exit_group.asm +++ b/cannon/mipsevm/tests/open_mips_tests/test/exit_group.asm @@ -6,7 +6,7 @@ test: li $a0, 1 - li $v0, 4246 + li $v0, 5205 syscall # Unreachable .... diff --git a/cannon/mipsevm/tests/open_mips_tests/test/fcntl.asm b/cannon/mipsevm/tests/open_mips_tests/test/fcntl.asm index 5f597bbe0c43..26f764e58bcb 100644 --- a/cannon/mipsevm/tests/open_mips_tests/test/fcntl.asm +++ b/cannon/mipsevm/tests/open_mips_tests/test/fcntl.asm @@ -6,7 +6,7 @@ test: # fcntl(0, 3) - li $v0, 4055 + li $v0, 5070 li $a0, 0x0 li $a1, 0x3 syscall diff --git a/cannon/mipsevm/tests/open_mips_tests/test/ld.asm b/cannon/mipsevm/tests/open_mips_tests/test/ld.asm new file mode 100644 index 000000000000..e1d5acaff134 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/ld.asm @@ -0,0 +1,54 @@ +############################################################################### +# File : ld.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'ld' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + # Test LD + li $t0, 0x12345678 # Load the value 0x12345678 + li $t1, 0x87654321 # Load the value 0x87654321 + sw $t0, 8($s0) # Store $t0 at 0xbffffff8 + sw $t1, 12($s0) # Store $t1 at 0xbffffffc + ld $t2, 8($s0) # Load the doubleword into $t2 + + dsll32 $t0, $t0, 0 # Shift $t0 left by 32 bits + dsll32 $t1, $t1, 0 # Shift $t1 left by 32 bits + dsrl32 $t1, $t1, 0 # Shift $t1 right by 32 bits + or $t0, $t0, $t1 # Combine $t0 and $t1 + bne $t0, $t2, $finish # If $t0 != $t2, fail + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/lld.asm b/cannon/mipsevm/tests/open_mips_tests/test/lld.asm new file mode 100644 index 000000000000..473c32b2ff1f --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/lld.asm @@ -0,0 +1,54 @@ +############################################################################### +# File : lld.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'lld' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + # Test LLD + li $t0, 0x12345678 # Load the value 0x12345678 + li $t1, 0x87654321 # Load the value 0x87654321 + sw $t0, 8($s0) # Store $t0 at 0xbffffff8 + sw $t1, 12($s0) # Store $t1 at 0xbffffffc + lld $t2, 8($s0) # Load the doubleword into $t2 + + dsll32 $t0, $t0, 0 # Shift $t0 left by 32 bits + dsll32 $t1, $t1, 0 # Shift $t1 left by 32 bits + dsrl32 $t1, $t1, 0 # Shift $t1 right by 32 bits + or $t0, $t0, $t1 # Combine $t0 and $t1 + bne $t0, $t2, $finish # If $t0 != $t2, fail + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/lwu.asm b/cannon/mipsevm/tests/open_mips_tests/test/lwu.asm new file mode 100644 index 000000000000..ac88ecebc455 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/lwu.asm @@ -0,0 +1,48 @@ +############################################################################### +# File : lwu.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'lwu' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + # Test LWU + li $t0, 0xFFFFFFFF # Load 0xFFFFFFFF into $t0 + dsrl32 $t0, $t0, 0 # Clear the upper 32 bits of $t0 + sw $t0, 8($s0) # Store 0xFFFFFFFF at 0xbffffff8 + lwu $t1, 8($s0) # Load 0xFFFFFFFF from 0xbffffff8 + bne $t1, $t0, $finish # Fail if $t1 != $t0 + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/mmap.asm b/cannon/mipsevm/tests/open_mips_tests/test/mmap.asm index 347678f2059a..11834023bdaf 100644 --- a/cannon/mipsevm/tests/open_mips_tests/test/mmap.asm +++ b/cannon/mipsevm/tests/open_mips_tests/test/mmap.asm @@ -5,7 +5,7 @@ .ent test test: - li $v0, 4090 + li $v0, 5009 lui $a0, 0x3000 li $a1, 4096 syscall diff --git a/cannon/mipsevm/tests/open_mips_tests/test/oracle.asm b/cannon/mipsevm/tests/open_mips_tests/test/oracle.asm index a8c73b2c3e16..9b7560d938c8 100644 --- a/cannon/mipsevm/tests/open_mips_tests/test/oracle.asm +++ b/cannon/mipsevm/tests/open_mips_tests/test/oracle.asm @@ -42,7 +42,7 @@ test: li $t0, 8 li $a2, 4 $writeloop: - li $v0, 4004 + li $v0, 5001 syscall addiu $a1, $a1, 4 addiu $t0, $t0, -1 @@ -54,16 +54,16 @@ $writeloop: li $a0, 5 li $a1, 0x31000000 li $a2, 4 - li $v0, 4003 + li $v0, 5000 syscall li $a1, 0x31000004 - li $v0, 4003 + li $v0, 5000 syscall # read the preimage data li $a1, 0x31000008 li $t0, 3 $readloop: - li $v0, 4003 + li $v0, 5000 syscall addiu $a1, $a1, 4 addiu $t0, $t0, -1 diff --git a/cannon/mipsevm/tests/open_mips_tests/test/oracle_kzg.asm b/cannon/mipsevm/tests/open_mips_tests/test/oracle_kzg.asm index 8faf349bf3ab..daaa0f951be1 100644 --- a/cannon/mipsevm/tests/open_mips_tests/test/oracle_kzg.asm +++ b/cannon/mipsevm/tests/open_mips_tests/test/oracle_kzg.asm @@ -43,7 +43,7 @@ test: li $t0, 8 li $a2, 4 $writeloop: - li $v0, 4004 + li $v0, 5001 syscall addiu $a1, $a1, 4 addiu $t0, $t0, -1 @@ -55,14 +55,14 @@ $writeloop: li $a0, 5 li $a1, 0x31000000 li $a2, 4 - li $v0, 4003 + li $v0, 5000 syscall li $a1, 0x31000004 - li $v0, 4003 + li $v0, 5000 syscall # read the 1 byte precompile status and 3 bytes of return data li $a1, 0x31000008 - li $v0, 4003 + li $v0, 5000 syscall nop diff --git a/cannon/mipsevm/tests/open_mips_tests/test/oracle_unaligned_read.asm b/cannon/mipsevm/tests/open_mips_tests/test/oracle_unaligned_read.asm index df47f4485e6f..00f4470854fe 100644 --- a/cannon/mipsevm/tests/open_mips_tests/test/oracle_unaligned_read.asm +++ b/cannon/mipsevm/tests/open_mips_tests/test/oracle_unaligned_read.asm @@ -42,7 +42,7 @@ test: li $t0, 8 li $a2, 4 $writeloop: - li $v0, 4004 + li $v0, 5001 syscall addiu $a1, $a1, 4 addiu $t0, $t0, -1 @@ -53,21 +53,18 @@ $writeloop: # read preimage length to unaligned addr. This will read only up to the nearest aligned byte so we have to read again. li $a0, 5 li $a1, 0x31000001 - li $a2, 4 - li $v0, 4003 - syscall - li $a1, 0x31000004 - li $v0, 4003 + li $a2, 8 + li $v0, 5000 syscall li $a1, 0x31000008 li $a2, 1 - li $v0, 4003 + li $v0, 5000 syscall # read the preimage data li $a1, 0x31000009 li $t0, 11 $readloop: - li $v0, 4003 + li $v0, 5000 li $a2, 4 syscall addu $a1, $a1, $v0 diff --git a/cannon/mipsevm/tests/open_mips_tests/test/oracle_unaligned_write.asm b/cannon/mipsevm/tests/open_mips_tests/test/oracle_unaligned_write.asm index d11ef9cbf749..7414254fdf9e 100644 --- a/cannon/mipsevm/tests/open_mips_tests/test/oracle_unaligned_write.asm +++ b/cannon/mipsevm/tests/open_mips_tests/test/oracle_unaligned_write.asm @@ -48,14 +48,14 @@ test: li $a0, 6 li $a1, 0x32000002 li $a2, 1 - li $v0, 4004 + li $v0, 5001 syscall # write 3 bytes for realignment li $a0, 6 li $a1, 0x30001001 li $a2, 3 - li $v0, 4004 + li $v0, 5001 syscall li $a0, 6 @@ -63,7 +63,7 @@ test: li $t0, 7 li $a2, 4 $writeloop: - li $v0, 4004 + li $v0, 5001 syscall addiu $a1, $a1, 4 addiu $t0, $t0, -1 @@ -75,16 +75,16 @@ $writeloop: li $a0, 5 li $a1, 0x31000000 li $a2, 4 - li $v0, 4003 + li $v0, 5000 syscall li $a1, 0x31000004 - li $v0, 4003 + li $v0, 5000 syscall # read the preimage data li $a1, 0x31000008 li $t0, 3 $readloop: - li $v0, 4003 + li $v0, 5000 syscall addiu $a1, $a1, 4 addiu $t0, $t0, -1 diff --git a/cannon/mipsevm/tests/open_mips_tests/test/scd.asm b/cannon/mipsevm/tests/open_mips_tests/test/scd.asm new file mode 100644 index 000000000000..a382cae1b6e4 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/scd.asm @@ -0,0 +1,74 @@ +############################################################################### +# File : scd.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'scd' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + # Test SCD + li $t0, 0x12345678 # Load the value 0x12345678 + dsll32 $t0, $t0, 0 # Shift $t0 left by 32 bits + li $t1, 0x87654321 # Load the value 0x87654321 + dsll32 $t1, $t1, 0 # Shift $t1 left by 32 bits + dsrl32 $t1, $t1, 0 # Shift $t1 right by 32 bits + or $t0, $t0, $t1 # Combine $t0 and $t1 + scd $t0, 8($s0) # Store the combined value to the base address + + # Check that `rt` was set to 1 + li $s2, 0x1 # Load the expected value 1 + bne $t0, $s2, $finish # Check the return value + nop + + lw $t1, 8($s0) # Load the upper 32 bits of the stored value + lw $t2, 12($s0) # Load the lower 32 bits of the stored value + li $s2, 0x12345678 # Load the expected value 0x12345678 + bne $t1, $s2, $finish # Check the upper 32 bits + nop + li $s2, 0x87654321 # Load the expected value 0x87654321 + bne $t2, $s2, $finish # Check the lower 32 bits + nop + + # Test SCD (conditional = false) + scd $0, 8($s0) # SCD w/ rt = $zero + li $s2, 0x0 # Load the expected value 0 + bne $0, $s2, $finish # Check the return value + nop + + # Check that the link reg was not updated + li $s2, 0x0 # Load the expected value 0 + bne $0, $s2, $finish # Check the return value + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/sd.asm b/cannon/mipsevm/tests/open_mips_tests/test/sd.asm new file mode 100644 index 000000000000..d7e56bf92530 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/sd.asm @@ -0,0 +1,60 @@ +############################################################################### +# File : sd.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'sd' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + # Test SD + li $t0, 0x12345678 # Load the value 0x12345678 + dsll32 $t0, $t0, 0 # Shift $t0 left by 32 bits + li $t1, 0x87654321 # Load the value 0x87654321 + dsll32 $t1, $t1, 0 # Shift $t1 left by 32 bits + dsrl32 $t1, $t1, 0 # Shift $t1 right by 32 bits + or $t0, $t0, $t1 # Combine $t0 and $t1 + sd $t0, 8($s0) # Store the combined value to the base address + + lw $t1, 8($s0) # Load the upper 32 bits of the stored value + lw $t2, 12($s0) # Load the lower 32 bits of the stored value + + li $s2, 0x12345678 # Load the expected value 0x12345678 + bne $t1, $s2, $finish # Check the upper 32 bits + nop + + li $s2, 0x87654321 # Load the expected value 0x87654321 + bne $t2, $s2, $finish # Check the lower 32 bits + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/sdl.asm b/cannon/mipsevm/tests/open_mips_tests/test/sdl.asm new file mode 100644 index 000000000000..764333432560 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/sdl.asm @@ -0,0 +1,109 @@ +############################################################################### +# File : sdl.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'sdl' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + lui $t0, 0xbfc0 # Load address 0xbfc007f8 (last double word in 2KB starting + ori $t0, 0x07f8 # from 0xbfc00000) + + lui $t1, 0xc001 # Memory double word is 0xc001cafe_babef00d + ori $t1, 0xcafe + dsll $t1, $t1, 32 # Shift left 32 bits + ori $t2, $0, 0xbabe + dsll $t2, $t2, 16 # Shift left 16 bits + ori $t2, $t2, 0xf00d + daddu $t1, $t1, $t2 # Combine the two words into $t1 + sd $t1, 0($t0) + + # Test 1 + sdl $t1, 15($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsrl32 $t2, $t2, 24 + bne $s2, $t2, $finish + nop + + # Test 2 + sdl $t1, 14($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsrl32 $t2, $t2, 16 + bne $s2, $t2, $finish + + # Test 3 + sdl $t1, 13($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsrl32 $t2, $t2, 8 + bne $s2, $t2, $finish + + # Test 4 + sdl $t1, 12($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsrl32 $t2, $t2, 0 + bne $s2, $t2, $finish + + # Test 5 + sdl $t1, 11($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsrl $t2, $t2, 24 + bne $s2, $t2, $finish + + # Test 6 + sdl $t1, 10($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsrl $t2, $t2, 16 + bne $s2, $t2, $finish + + # Test 7 + sdl $t1, 9($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsrl $t2, $t2, 8 + bne $s2, $t2, $finish + + # Test 8 + sdl $t1, 8($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + bne $s2, $t2, $finish + + nop + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/tests/open_mips_tests/test/sdr.asm b/cannon/mipsevm/tests/open_mips_tests/test/sdr.asm new file mode 100644 index 000000000000..9a09d11a4a08 --- /dev/null +++ b/cannon/mipsevm/tests/open_mips_tests/test/sdr.asm @@ -0,0 +1,115 @@ +############################################################################### +# File : sdr.asm +# Author: : clabby (github.com/clabby) +# +# Standards/Formatting: +# MIPS gas, soft tab, 80 column +# +# Description: +# Test the functionality of the 'sdr' instruction. +# +############################################################################### + + + .section .test, "x" + .balign 8 + .set noreorder + .set mips64 + .global test + .ent test +test: + lui $s0, 0xbfff # Load the base address 0xbffffff0 + ori $s0, 0xfff0 + ori $s1, $0, 1 # Prepare the 'done' status + + #### Test code start #### + + lui $t0, 0xbfc0 # Load address 0xbfc007f8 (last double word in 2KB starting + ori $t0, 0x07f8 # from 0xbfc00000) + + lui $t1, 0xc001 # Memory double word is 0xc001cafe_babef00d + ori $t1, 0xcafe + dsll $t1, $t1, 32 # Shift left 32 bits + ori $t2, $0, 0xbabe + dsll $t2, $t2, 16 # Shift left 16 bits + ori $t2, $t2, 0xf00d + daddu $t1, $t1, $t2 # Combine the two words into $t1 + sd $t1, 0($t0) + + # Test 1 + sdr $t1, 8($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsll32 $t2, $t2, 24 + bne $s2, $t2, $finish + nop + + # Test 2 + sdr $t1, 9($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsll32 $t2, $t2, 16 + bne $s2, $t2, $finish + nop + + # Test 3 + sdr $t1, 10($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsll32 $t2, $t2, 8 + bne $s2, $t2, $finish + nop + + # Test 4 + sdr $t1, 11($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsll32 $t2, $t2, 0 + bne $s2, $t2, $finish + nop + + # Test 5 + sdr $t1, 12($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsll $t2, $t2, 24 + bne $s2, $t2, $finish + nop + + # Test 6 + sdr $t1, 13($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsll $t2, $t2, 16 + bne $s2, $t2, $finish + nop + + # Test 7 + sdr $t1, 14($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + dsll $t2, $t2, 8 + bne $s2, $t2, $finish + nop + + # Test 8 + sdr $t1, 15($t0) + ld $s2, 8($t0) + or $t2, $0, $t1 + bne $s2, $t2, $finish + nop + + # Set success flag + ori $v0, $0, 1 # Set test result to success + + #### Test code end #### + +$finish: + sw $v0, 8($s0) # Set the test result + sw $s1, 4($s0) # Set 'done' + +$done: + jr $ra + nop + + .end test diff --git a/cannon/mipsevm/testutil/constants.go b/cannon/mipsevm/testutil/constants.go index ccbd86574e8b..c64c29d2f832 100644 --- a/cannon/mipsevm/testutil/constants.go +++ b/cannon/mipsevm/testutil/constants.go @@ -1,7 +1,7 @@ package testutil // 0xbf_c0_00_00 ... BaseAddrEnd is used in tests to write the results to -const BaseAddrEnd = 0xbf_ff_ff_f0 +const BaseAddrEnd = 0xFF_FF_FF_FF_BF_FF_FF_F0 // EndAddr is used as return-address for tests const EndAddr = 0xa7ef00d0 diff --git a/cannon/mipsevm/testutil/vmtests.go b/cannon/mipsevm/testutil/vmtests.go index c016b91e076a..89ad554b60d2 100644 --- a/cannon/mipsevm/testutil/vmtests.go +++ b/cannon/mipsevm/testutil/vmtests.go @@ -63,15 +63,15 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa } if exitGroup { - require.NotEqual(t, uint32(EndAddr), us.GetState().GetPC(), "must not reach end") + require.NotEqual(t, uint64(EndAddr), us.GetState().GetPC(), "must not reach end") require.True(t, us.GetState().GetExited(), "must set exited state") require.Equal(t, uint8(1), us.GetState().GetExitCode(), "must exit with 1") } else { - require.Equal(t, uint32(EndAddr), us.GetState().GetPC(), "must reach end") - done, result := state.GetMemory().GetMemory(BaseAddrEnd+4), state.GetMemory().GetMemory(BaseAddrEnd+8) + require.Equal(t, uint64(EndAddr), us.GetState().GetPC(), "must reach end") + done, result := uint64(state.GetMemory().GetMemory(BaseAddrEnd+4)), uint64(state.GetMemory().GetMemory(BaseAddrEnd+8)) // inspect test result - require.Equal(t, done, uint32(1), "must be done") - require.Equal(t, result, uint32(1), "must have success result") + require.Equal(t, done, uint64(1), "must be done") + require.Equal(t, result, uint64(1), "must have success result") } }) } diff --git a/cannon/mipsevm/witness.go b/cannon/mipsevm/witness.go index b7bf38fa528e..d3ae3ccc0b09 100644 --- a/cannon/mipsevm/witness.go +++ b/cannon/mipsevm/witness.go @@ -13,7 +13,7 @@ type StepWitness struct { PreimageKey [32]byte // zeroed when no pre-image is accessed PreimageValue []byte // including the 8-byte length prefix - PreimageOffset uint32 + PreimageOffset uint64 } func (wit *StepWitness) HasPreimage() bool { diff --git a/cannon/testdata/example/Makefile b/cannon/testdata/example/Makefile index 7b3b8fdf019d..48bccd9fce92 100644 --- a/cannon/testdata/example/Makefile +++ b/cannon/testdata/example/Makefile @@ -11,9 +11,9 @@ bin: # take any directory with a go mod, and build an ELF # verify output with: readelf -h bin/.elf -# result is mips32, big endian, R3000 +# result is mips64, big endian, R3000 bin/%.elf: bin - cd $(@:bin/%.elf=%) && GOOS=linux GOARCH=mips GOMIPS=softfloat go build -o ../$@ . + cd $(@:bin/%.elf=%) && GOOS=linux GODEBUG=memprofilerate=0 GOARCH=mips64 GOMIPS64=softfloat go build -o ../$@ . # take any ELF and dump it # TODO: currently have the little-endian toolchain, but should use the big-endian one. The -EB compat flag works though. diff --git a/go.mod b/go.mod index e4c54d456e09..80375bbbb81d 100644 --- a/go.mod +++ b/go.mod @@ -47,6 +47,7 @@ require ( golang.org/x/sync v0.8.0 golang.org/x/term v0.23.0 golang.org/x/time v0.6.0 + lukechampine.com/uint128 v1.3.0 ) require ( diff --git a/go.sum b/go.sum index 9db642d8ab04..b5e75cc7b640 100644 --- a/go.sum +++ b/go.sum @@ -1074,6 +1074,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= lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/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/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= diff --git a/op-challenger/game/fault/trace/cannon/prestate_test.go b/op-challenger/game/fault/trace/cannon/prestate_test.go index 9566e9d0664c..1574cde5348a 100644 --- a/op-challenger/game/fault/trace/cannon/prestate_test.go +++ b/op-challenger/game/fault/trace/cannon/prestate_test.go @@ -57,7 +57,7 @@ func TestAbsolutePreStateCommitment(t *testing.T) { ExitCode: 0, Exited: false, Step: 0, - Registers: [32]uint32{}, + Registers: [32]uint64{}, } _, expected := state.EncodeWitness() require.Equal(t, expected, actual) From cdcfa7735c73b173bfe64d080bed63722e0a7a15 Mon Sep 17 00:00:00 2001 From: clabby Date: Wed, 7 Aug 2024 09:31:00 -0600 Subject: [PATCH 2/4] fixes, checkpoint --- cannon/mipsevm/exec/mips_instructions.go | 10 +++++----- cannon/mipsevm/exec/mips_syscalls.go | 2 +- cannon/mipsevm/multithreaded/state_test.go | 8 ++++---- cannon/mipsevm/program/patch.go | 2 +- cannon/mipsevm/singlethreaded/state_test.go | 4 ++-- op-program/Makefile | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index 97c1ce197f70..8e0e4c0934b5 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -161,7 +161,7 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint64) uint64 { return SignExtend((rt&0xFFFFFFFF)>>(rs&0x1F), 32) case 0x07: // srav return SignExtend((rt&0xFFFFFFFF)>>rs, 32-rs) - // MIPS32 functs in range [0x8, 0x1f] are handled specially by other functions + // functs in range [0x8, 0x1f] are handled specially by other functions case 0x08: // jr return rs case 0x09: // jalr @@ -379,17 +379,17 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]uint64, opcode, insn, rt := registers[rtReg] shouldBranch = (rs == rt && opcode == 4) || (rs != rt && opcode == 5) } else if opcode == 6 { - shouldBranch = int32(rs) <= 0 // blez + shouldBranch = int64(rs) <= 0 // blez } else if opcode == 7 { - shouldBranch = int32(rs) > 0 // bgtz + shouldBranch = int64(rs) > 0 // bgtz } else if opcode == 1 { // regimm rtv := (insn >> 16) & 0x1F if rtv == 0 { // bltz - shouldBranch = int32(rs) < 0 + shouldBranch = int64(rs) < 0 } if rtv == 1 { // bgez - shouldBranch = int32(rs) >= 0 + shouldBranch = int64(rs) >= 0 } } diff --git a/cannon/mipsevm/exec/mips_syscalls.go b/cannon/mipsevm/exec/mips_syscalls.go index 87d189770b2c..7973f4fc120d 100644 --- a/cannon/mipsevm/exec/mips_syscalls.go +++ b/cannon/mipsevm/exec/mips_syscalls.go @@ -44,7 +44,7 @@ const ( SysReadlink = 5087 SysReadlinkAt = 5257 SysIoctl = 5015 - SysEpollCreate1 = 5207 + SysEpollCreate1 = 5285 SysPipe2 = 5287 SysEpollCtl = 5208 SysEpollPwait = 5272 diff --git a/cannon/mipsevm/multithreaded/state_test.go b/cannon/mipsevm/multithreaded/state_test.go index 8d7fbd766261..9540afa3b9a2 100644 --- a/cannon/mipsevm/multithreaded/state_test.go +++ b/cannon/mipsevm/multithreaded/state_test.go @@ -60,19 +60,19 @@ func TestState_EncodeWitness(t *testing.T) { expectedWitness := make(StateWitness, STATE_WITNESS_SIZE) setWitnessField(expectedWitness, MEMROOT_WITNESS_OFFSET, memRoot[:]) setWitnessField(expectedWitness, PREIMAGE_KEY_WITNESS_OFFSET, preimageKey[:]) - setWitnessField(expectedWitness, PREIMAGE_OFFSET_WITNESS_OFFSET, []byte{0, 0, 0, byte(preimageOffset)}) - setWitnessField(expectedWitness, HEAP_WITNESS_OFFSET, []byte{0, 0, 0, byte(heap)}) + setWitnessField(expectedWitness, PREIMAGE_OFFSET_WITNESS_OFFSET, []byte{0, 0, 0, 0, 0, 0, 0, byte(preimageOffset)}) + setWitnessField(expectedWitness, HEAP_WITNESS_OFFSET, []byte{0, 0, 0, 0, 0, 0, 0, byte(heap)}) setWitnessField(expectedWitness, EXITCODE_WITNESS_OFFSET, []byte{c.exitCode}) if c.exited { setWitnessField(expectedWitness, EXITED_WITNESS_OFFSET, []byte{1}) } setWitnessField(expectedWitness, STEP_WITNESS_OFFSET, []byte{0, 0, 0, 0, 0, 0, 0, byte(step)}) setWitnessField(expectedWitness, STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET, []byte{0, 0, 0, 0, 0, 0, 0, byte(stepsSinceContextSwitch)}) - setWitnessField(expectedWitness, WAKEUP_WITNESS_OFFSET, []byte{0xFF, 0xFF, 0xFF, 0xFF}) + setWitnessField(expectedWitness, WAKEUP_WITNESS_OFFSET, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) setWitnessField(expectedWitness, TRAVERSE_RIGHT_WITNESS_OFFSET, []byte{0}) setWitnessField(expectedWitness, LEFT_THREADS_ROOT_WITNESS_OFFSET, leftStackRoot[:]) setWitnessField(expectedWitness, RIGHT_THREADS_ROOT_WITNESS_OFFSET, rightStackRoot[:]) - setWitnessField(expectedWitness, THREAD_ID_WITNESS_OFFSET, []byte{0, 0, 0, 1}) + setWitnessField(expectedWitness, THREAD_ID_WITNESS_OFFSET, []byte{0, 0, 0, 0, 0, 0, 0, 1}) // Validate witness actualWitness, actualStateHash := state.EncodeWitness() diff --git a/cannon/mipsevm/program/patch.go b/cannon/mipsevm/program/patch.go index 319d5896b802..c3ebb3dc3c2e 100644 --- a/cannon/mipsevm/program/patch.go +++ b/cannon/mipsevm/program/patch.go @@ -38,7 +38,7 @@ func PatchGo(f *elf.File, st mipsevm.FPVMState) error { "flag.init", // We need to patch this out, we don't pass float64nan because we don't support floats "runtime.check": - // MIPS32 patch: ret (pseudo instruction) + // MIPS64 patch: ret (pseudo instruction) // 03e00008 = jr $ra = ret (pseudo instruction) // 00000000 = nop (executes with delay-slot, but does nothing) if err := st.GetMemory().SetMemoryRange(uint64(s.Value), bytes.NewReader([]byte{ diff --git a/cannon/mipsevm/singlethreaded/state_test.go b/cannon/mipsevm/singlethreaded/state_test.go index 27588f8b67ed..99abdf3a3f0c 100644 --- a/cannon/mipsevm/singlethreaded/state_test.go +++ b/cannon/mipsevm/singlethreaded/state_test.go @@ -29,7 +29,7 @@ func TestStateHash(t *testing.T) { {exited: true, exitCode: 3}, } - exitedOffset := 32*2 + 4*6 + exitedOffset := 32*2 + 8*6 for _, c := range cases { state := &State{ Memory: memory.NewMemory(), @@ -40,7 +40,7 @@ func TestStateHash(t *testing.T) { actualWitness, actualStateHash := state.EncodeWitness() require.Equal(t, len(actualWitness), STATE_WITNESS_SIZE, "Incorrect witness size") - expectedWitness := make(StateWitness, 226) + expectedWitness := make(StateWitness, 378) memRoot := state.Memory.MerkleRoot() copy(expectedWitness[:32], memRoot[:]) expectedWitness[exitedOffset] = c.exitCode diff --git a/op-program/Makefile b/op-program/Makefile index 29e439a28f44..97f5e892feb8 100644 --- a/op-program/Makefile +++ b/op-program/Makefile @@ -27,9 +27,9 @@ op-program-client: env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v -ldflags "$(PC_LDFLAGSSTRING)" -o ./bin/op-program-client ./client/cmd/main.go op-program-client-mips: - env GO111MODULE=on GOOS=linux GOARCH=mips GOMIPS=softfloat go build -v -ldflags "$(PC_LDFLAGSSTRING)" -o ./bin/op-program-client.elf ./client/cmd/main.go + env GO111MODULE=on GOOS=linux GOARCH=mips64 GOMIPS64=softfloat go build -v -ldflags "$(PC_LDFLAGSSTRING)" -o ./bin/op-program-client.elf ./client/cmd/main.go # verify output with: readelf -h bin/op-program-client.elf - # result is mips32, big endian, R3000 + # result is mips64, big endian, R3000 op-program-client-riscv: env GO111MODULE=on GOOS=linux GOARCH=riscv64 go build -v -gcflags="all=-d=softfloat" -ldflags "$(PC_LDFLAGSSTRING)" -o ./bin/op-program-client-riscv.elf ./client/cmd/main.go From eba86f2a911a779f190548da0877627d191b162d Mon Sep 17 00:00:00 2001 From: clabby Date: Wed, 7 Aug 2024 10:38:34 -0600 Subject: [PATCH 3/4] works! --- cannon/mipsevm/exec/mips_instructions.go | 25 +++++++++++++------ cannon/mipsevm/exec/mips_syscalls.go | 1 + .../multithreaded/instrumented_test.go | 2 +- cannon/mipsevm/multithreaded/mips.go | 1 + cannon/mipsevm/program/patch.go | 5 +--- cannon/testdata/example/Makefile | 2 +- 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index 8e0e4c0934b5..4da382ac89c0 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -51,7 +51,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint64, memor // SignExtImm rt = SignExtend(insn&0xFFFF, 16) } - } else if opcode >= 0x28 || opcode == 0x22 || opcode == 0x26 || opcode == 0x1A || opcode == 0x1B { + } else if opcode >= 0x27 || opcode == 0x22 || opcode == 0x26 || opcode == 0x1A || opcode == 0x1B { // store rt value with store rt = registers[rtReg] @@ -63,7 +63,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint64, memor return HandleBranch(cpu, registers, opcode, insn, rtReg, rs) } - storeAddr := uint64(0xFF_FF_FF_FF_FF_FF_FF_FF) + storeAddr := ^uint64(0) // memory fetch (all I-type) // we do the load for stores also mem := uint64(0) @@ -114,7 +114,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]uint64, memor } // write memory - if storeAddr != 0xFF_FF_FF_FF_FF_FF_FF_FF { + if storeAddr != ^uint64(0) { memTracker.TrackMemAccess(storeAddr) memory.SetDoubleWord(storeAddr, val) } @@ -281,7 +281,7 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint64) uint64 { case 0x22: // lwl val := mem << ((rs & 3) * 8) mask := uint64(uint32(0xFFFFFFFF) << ((rs & 3) * 8)) - return SignExtend((rt & ^mask)|val, 32) + return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) case 0x23: // lw return SignExtend((mem>>(32-((rs&0x4)<<3)))&0xFFFFFFFF, 32) case 0x24: // lbu @@ -291,7 +291,7 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint64) uint64 { case 0x26: // lwr val := mem >> (24 - (rs&3)*8) mask := uint64(uint32(0xFFFFFFFF) >> (24 - (rs&3)*8)) - return SignExtend((rt & ^mask)|val, 32) + return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) case 0x28: // sb val := (rt & 0xFF) << (56 - (rs&7)*8) mask := 0xFFFFFFFFFFFFFFFF ^ uint64(0xFF<<(56-(rs&7)*8)) @@ -319,7 +319,10 @@ func ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem uint64) uint64 { case 0x30: // ll return SignExtend((mem>>(32-((rs&0x4)<<3)))&0xFFFFFFFF, 32) case 0x38: // sc - return rt + sl := 32 - ((rs & 0x4) << 3) + val := (rt & 0xFFFFFFFF) << sl + mask := 0xFFFFFFFFFFFFFFFF ^ uint64(0xFFFFFFFF<.elf # result is mips64, big endian, R3000 bin/%.elf: bin - cd $(@:bin/%.elf=%) && GOOS=linux GODEBUG=memprofilerate=0 GOARCH=mips64 GOMIPS64=softfloat go build -o ../$@ . + cd $(@:bin/%.elf=%) && GOOS=linux GOARCH=mips64 GOMIPS64=softfloat go build -o ../$@ . # take any ELF and dump it # TODO: currently have the little-endian toolchain, but should use the big-endian one. The -EB compat flag works though. From 1d9446e0784c1b2a9ef173a140fddf9a9dcaad49 Mon Sep 17 00:00:00 2001 From: clabby Date: Sat, 10 Aug 2024 08:39:07 -0600 Subject: [PATCH 4/4] rebase on go patch --- cannon/mipsevm/program/patch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cannon/mipsevm/program/patch.go b/cannon/mipsevm/program/patch.go index b59a3a43566c..8210b4abd466 100644 --- a/cannon/mipsevm/program/patch.go +++ b/cannon/mipsevm/program/patch.go @@ -47,7 +47,7 @@ func PatchGo(f *elf.File, st mipsevm.FPVMState) error { return fmt.Errorf("failed to patch Go runtime.gcenable: %w", err) } case "runtime.MemProfileRate": - if err := st.GetMemory().SetMemoryRange(uint32(s.Value), bytes.NewReader(make([]byte, 4))); err != nil { // disable mem profiling, to avoid a lot of unnecessary floating point ops + if err := st.GetMemory().SetMemoryRange(uint64(s.Value), bytes.NewReader(make([]byte, 8))); err != nil { // disable mem profiling, to avoid a lot of unnecessary floating point ops return err } }