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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions cannon/mipsevm/arch/arch32.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ const (
WordSize = 32
ExtMask = 0x3

HeapStart = 0x05_00_00_00
HeapEnd = 0x60_00_00_00
ProgramBreak = 0x40_00_00_00
HighMemoryStart = 0x7f_ff_d0_00
Limit = 0xff_ff_ff_ff
ProgramHeapStart = 0x00_c0_00_00
HeapStart = 0x05_00_00_00
HeapEnd = 0x60_00_00_00
ProgramBreak = 0x40_00_00_00
HighMemoryStart = 0x7f_ff_d0_00
)

// 32-bit Syscall codes
Expand Down
11 changes: 6 additions & 5 deletions cannon/mipsevm/arch/arch64.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ const (
ExtMask = 0x7

// Ensure virtual address is limited to 48-bits as many user programs assume such to implement packed pointers
// limit 0x00_00_FF_FF_FF_FF_FF_FF
HeapStart = 0x00_00_10_00_00_00_00_00
HeapEnd = 0x00_00_60_00_00_00_00_00
ProgramBreak = 0x00_00_40_00_00_00_00_00
HighMemoryStart = 0x00_00_7F_FF_FF_FF_F0_00
Limit = 0x00_00_FF_FF_FF_FF_FF_FF
ProgramHeapStart = 0x00_00_00_c0_00_00_00_00
HeapStart = 0x00_00_10_00_00_00_00_00
HeapEnd = 0x00_00_60_00_00_00_00_00
ProgramBreak = 0x00_00_40_00_00_00_00_00
HighMemoryStart = 0x00_00_7F_FF_FF_FF_F0_00
)

// MIPS64 syscall table - https://github.com/torvalds/linux/blob/3efc57369a0ce8f76bf0804f7e673982384e4ac9/arch/mips/kernel/syscalls/syscall_n64.tbl. Generate the syscall numbers using the Makefile in that directory.
Expand Down
24 changes: 20 additions & 4 deletions cannon/mipsevm/memory/binary_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package memory

import (
"math/bits"

"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
)

// BinaryTreeIndex is a representation of the state of the memory in a binary merkle tree.
Expand All @@ -15,10 +17,23 @@ type BinaryTreeIndex struct {
func NewBinaryTreeMemory() *Memory {
pages := make(map[Word]*CachedPage)
index := NewBinaryTreeIndex(pages)

indexedRegions := make([]MappedMemoryRegion, 2)
indexedRegions[0] = MappedMemoryRegion{
start_addr: 0,
end_addr: min(arch.ProgramHeapStart, 1<<31),
Data: make([]byte, 1<<31),
}
indexedRegions[1] = MappedMemoryRegion{
start_addr: arch.ProgramHeapStart,
end_addr: min(arch.HeapStart, arch.ProgramHeapStart+(1<<31)),
Data: make([]byte, 1<<31),
}
return &Memory{
merkleIndex: index,
pageTable: pages,
lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages
merkleIndex: index,
pageTable: pages,
lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages
MappedRegions: indexedRegions,
}
}

Expand Down Expand Up @@ -83,7 +98,8 @@ func (m *BinaryTreeIndex) MerkleizeSubtree(gindex uint64) [32]byte {
func (m *BinaryTreeIndex) MerkleProof(addr Word) (out [MemProofSize]byte) {
proof := m.traverseBranch(1, addr, 0)
// encode the proof
for i := 0; i < MemProofLeafCount; i++ {
_ = proof[MemProofLeafCount-1]
for i := 0; i <= MemProofLeafCount-1; i++ {
copy(out[i*32:(i+1)*32], proof[i][:])
}
return out
Expand Down
107 changes: 98 additions & 9 deletions cannon/mipsevm/memory/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,27 @@ const (

type Word = arch.Word

type MappedMemoryRegion struct {
start_addr Word
end_addr Word
Data []byte
}

func (m *MappedMemoryRegion) AddrInRegion(addr Word) bool {
return addr >= m.start_addr && addr < m.end_addr
}

func (m *MappedMemoryRegion) PageIndexInRegion(pageIndex Word) bool {
return pageIndex >= m.start_addr>>PageAddrSize && pageIndex < m.end_addr>>PageAddrSize
}

func (m *MappedMemoryRegion) AccessWordBytes(addr Word) ([]byte, bool) {
if m.AddrInRegion(addr) {
return m.Data[addr : addr+arch.WordSizeBytes : addr+arch.WordSizeBytes], true
}
return nil, false
}

type Memory struct {
merkleIndex PageIndex
// Note: since we don't de-alloc Pages, we don't do ref-counting.
Expand All @@ -38,6 +59,8 @@ type Memory struct {
// this prevents map lookups each instruction
lastPageKeys [2]Word
lastPage [2]*CachedPage

MappedRegions []MappedMemoryRegion
}

type PageIndex interface {
Expand All @@ -54,6 +77,45 @@ func NewMemory() *Memory {
return NewBinaryTreeMemory()
}

// start end size gap
func (m *Memory) GetAllocatedRanges() [][4]Word {
var ranges [][4]Word
if len(m.pageTable) == 0 {
return ranges
}

// Extract and sort page addresses
keys := make([]Word, 0, len(m.pageTable))
for key := range m.pageTable {
keys = append(keys, key)
}
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })

// Find contiguous ranges and gaps
start := keys[0]
prev := start
var lastEnd Word = start - 1

for i := 1; i < len(keys); i++ {
if keys[i] != prev+1 {
gap := start - lastEnd - 1 // Gap is calculated from end of prev range to start of new one
ranges = append(ranges, [4]Word{start, prev, prev - start + 1, gap})
lastEnd = prev
start = keys[i]
}
prev = keys[i]
}

// Append last range
gap := start - lastEnd - 1
ranges = append(ranges, [4]Word{start, prev, prev - start + 1, gap})
for i := 0; i < len(ranges); i++ {
ranges[i][0] <<= PageAddrSize
ranges[i][1] <<= PageAddrSize
}
return ranges
}

func (m *Memory) MerkleRoot() [32]byte {
return m.MerkleizeSubtree(1)
}
Expand All @@ -66,15 +128,14 @@ func (m *Memory) PageCount() int {
return len(m.pageTable)
}

func (m *Memory) ForEachPage(fn func(pageIndex Word, page *Page) error) error {
func (m *Memory) ForEachPage(fn func(pageIndex Word, page Page) error) error {
for pageIndex, cachedPage := range m.pageTable {
if err := fn(pageIndex, cachedPage.Data); err != nil {
return err
}
}
return nil
}

func (m *Memory) MerkleizeSubtree(gindex uint64) [32]byte {
return m.merkleIndex.MerkleizeSubtree(gindex)
}
Expand Down Expand Up @@ -145,6 +206,7 @@ func (m *Memory) SetWord(addr Word, v Word) {
m.merkleIndex.Invalidate(addr) // invalidate this branch of memory, now that the value changed
}
}

arch.ByteOrderWord.PutWord(p.Data[pageAddr:pageAddr+arch.WordSizeBytes], v)
}

Expand All @@ -155,7 +217,15 @@ func (m *Memory) GetWord(addr Word) Word {
if addr&arch.ExtMask != 0 {
panic(fmt.Errorf("unaligned memory access: %x", addr))
}
for _, region := range m.MappedRegions {
if ok := region.AddrInRegion(addr); ok {
offset := addr - region.start_addr
return arch.ByteOrderWord.Word(region.Data[offset : offset+arch.WordSizeBytes : offset+arch.WordSizeBytes])
}
}

pageIndex := addr >> PageAddrSize

p, ok := m.PageLookup(pageIndex)
if !ok {
return 0
Expand All @@ -165,7 +235,21 @@ func (m *Memory) GetWord(addr Word) Word {
}

func (m *Memory) AllocPage(pageIndex Word) *CachedPage {
p := &CachedPage{Data: new(Page)}
p := new(CachedPage)
for _, region := range m.MappedRegions {
if region.PageIndexInRegion(pageIndex) {
currLen := len(region.Data)
indexAdjusted := pageIndex - region.start_addr>>PageAddrSize
if indexAdjusted*PageSize >= Word(currLen) {
region.Data = region.Data[:(pageIndex+1)*PageSize]
}
p.Data = region.Data[indexAdjusted*PageSize : (indexAdjusted+1)*PageSize : (indexAdjusted+1)*PageSize]
break
}
}
if p.Data == nil {
p.Data = make(Page, PageSize)
}
m.pageTable[pageIndex] = p
m.merkleIndex.AddPage(pageIndex)
return p
Expand Down Expand Up @@ -237,8 +321,9 @@ func (m *Memory) Copy() *Memory {
}

for k, page := range m.pageTable {
data := new(Page)
*data = *page.Data
data := make(Page, PageSize)
// *data = *page.Data
copy(data, page.Data)
out.AllocPage(k).Data = data
}
return out
Expand Down Expand Up @@ -287,20 +372,23 @@ func (m *Memory) Deserialize(in io.Reader) error {
return err
}
}

return nil
}

type pageEntry struct {
Index Word `json:"index"`
Data *Page `json:"data"`
Index Word `json:"index"`
Data *[PageSize]byte `json:"data"`
}

func (m *Memory) MarshalJSON() ([]byte, error) { // nosemgrep
pages := make([]pageEntry, 0, len(m.pageTable))
for k, p := range m.pageTable {
data := new([PageSize]byte)
copy(data[:], p.Data)
pages = append(pages, pageEntry{
Index: k,
Data: p.Data,
Data: data,
})
}
sort.Slice(pages, func(i, j int) bool {
Expand All @@ -318,7 +406,8 @@ func (m *Memory) UnmarshalJSON(data []byte) error {
if _, ok := m.pageTable[p.Index]; ok {
return fmt.Errorf("cannot load duplicate page, entry %d, page index %d", i, p.Index)
}
m.AllocPage(p.Index).Data = p.Data
page := m.AllocPage(p.Index)
copy(page.Data, p.Data[:])
}
return nil
}
10 changes: 5 additions & 5 deletions cannon/mipsevm/memory/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ var zlibWriterPool = sync.Pool{
},
}

type Page [PageSize]byte
type Page []byte

func (p *Page) MarshalJSON() ([]byte, error) { // nosemgrep
func (p Page) MarshalJSON() ([]byte, error) { // nosemgrep
var out bytes.Buffer
w := zlibWriterPool.Get().(*zlib.Writer)
defer zlibWriterPool.Put(w)
Expand All @@ -34,7 +34,7 @@ func (p *Page) MarshalJSON() ([]byte, error) { // nosemgrep
return json.Marshal(out.Bytes())
}

func (p *Page) UnmarshalJSON(dat []byte) error {
func (p Page) UnmarshalJSON(dat []byte) error {
// Strip off the `"` characters at the start & end.
dat = dat[1 : len(dat)-1]
// Decode b64 then decompress
Expand All @@ -52,7 +52,7 @@ func (p *Page) UnmarshalJSON(dat []byte) error {
}
}

func (p *Page) UnmarshalText(dat []byte) error {
func (p Page) UnmarshalText(dat []byte) error {
if len(dat) != PageSize*2 {
return fmt.Errorf("expected %d hex chars, but got %d", PageSize*2, len(dat))
}
Expand All @@ -61,7 +61,7 @@ func (p *Page) UnmarshalText(dat []byte) error {
}

type CachedPage struct {
Data *Page
Data Page
// intermediate nodes only
Cache [PageSize / 32][32]byte
// bit set to 1 if the intermediate node is valid
Expand Down
2 changes: 1 addition & 1 deletion cannon/mipsevm/memory/page_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func TestCachedPage(t *testing.T) {
p := &CachedPage{Data: new(Page)}
p := &CachedPage{Data: make(Page, PageSize, PageSize)}
p.Data[42] = 0xab

gindex := ((uint64(1) << PageAddrSize) | 42) >> 5
Expand Down
11 changes: 11 additions & 0 deletions cannon/mipsevm/multithreaded/instrumented.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
)

type InstructionDetails struct {
insn, opcode, fun uint32
}
type InstrumentedState struct {
state *State

Expand All @@ -24,11 +27,18 @@ type InstrumentedState struct {

preimageOracle *exec.TrackingPreimageOracleReader
meta mipsevm.Metadata

cached_decode []InstructionDetails
}

var _ mipsevm.FPVM = (*InstrumentedState)(nil)

func NewInstrumentedState(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta mipsevm.Metadata) *InstrumentedState {
cached_decode := make([]InstructionDetails, 0)
for pc := Word(0); pc < Word(len(state.Memory.MappedRegions[0].Data)); pc += 4 {
insn, opcode, fun := exec.GetInstructionDetails(pc, state.Memory)
cached_decode = append(cached_decode, InstructionDetails{insn, opcode, fun})
}
return &InstrumentedState{
state: state,
log: log,
Expand All @@ -39,6 +49,7 @@ func NewInstrumentedState(state *State, po mipsevm.PreimageOracle, stdOut, stdEr
statsTracker: NoopStatsTracker(),
preimageOracle: exec.NewTrackingPreimageOracleReader(po),
meta: meta,
cached_decode: cached_decode,
}
}

Expand Down
4 changes: 2 additions & 2 deletions cannon/mipsevm/multithreaded/mips.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,8 @@ func (m *InstrumentedState) doMipsStep() error {
}
m.state.StepsSinceLastContextSwitch += 1

//instruction fetch
insn, opcode, fun := exec.GetInstructionDetails(m.state.GetPC(), m.state.Memory)
insn_detail := m.cached_decode[m.state.GetPC()/4]
insn, opcode, fun := insn_detail.insn, insn_detail.opcode, insn_detail.fun

// Handle syscall separately
// syscall (can read and write)
Expand Down