diff --git a/internal/polkavm/abi.go b/internal/polkavm/abi.go index 6e9609e..ebb142a 100644 --- a/internal/polkavm/abi.go +++ b/internal/polkavm/abi.go @@ -14,66 +14,58 @@ import ( const ( AddressSpaceSize = 0x100000000 // 2^32 - VmMinPageSize = 0x1000 // The minimum page size of the VM VmMaxPageSize = 0x10000 // The maximum page size of the VM. + VMPageSize = uint32(1 << 12) // ZP: 4096 (2^12) The pvm memory page size. VmAddressReturnToHost = 0xffff0000 // The address which, when jumped to, will return to the host. VmAddressSpaceTop = AddressSpaceSize - VmMaxPageSize // The address at which the program's stackData starts inside of the VM. VmAddressSpaceBottom = VmMaxPageSize // The bottom of the accessible address space inside the VM (ZQ?) ) var ( - ErrPageValueTooLarge = errors.New("page value too large") - ErrPageSizeNotPowerOfTwo = errors.New("page size is not a power of two") + ErrPageValueTooLarge = errors.New("page value too large") ) -func NewMemoryMap(pageSize, roDataSize, rwDataSize, stackSize, argsDataSize uint) (*MemoryMap, error) { - if pageSize < VmMinPageSize { - return nil, fmt.Errorf("invalid page size: page size is too small") - } - - if pageSize > VmMaxPageSize { - return nil, fmt.Errorf("invalid page size: page size is too big") - } - roDataAddressSpace, err := AlignToNextPage(VmMaxPageSize, roDataSize) +func NewMemoryMap(roDataSize, rwDataSize, stackSize, argsDataSize uint32) (*MemoryMap, error) { + roDataAddressSpace, err := AlignToNextPage(roDataSize) if err != nil { return nil, fmt.Errorf("failed to instantiate memory map ro data address space: %w", err) } - roDataSize, err = AlignToNextPage(pageSize, roDataSize) + roDataSize, err = AlignToNextPage(roDataSize) if err != nil { return nil, fmt.Errorf("failed to instantiate memory map ro data size: %w", err) } - rwDataAddressSpace, err := AlignToNextPage(VmMaxPageSize, rwDataSize) + rwDataAddressSpace, err := AlignToNextPage(rwDataSize) if err != nil { return nil, fmt.Errorf("failed to instantiate memory map rw data address space: %w", err) } originalRwDataSize := rwDataSize - rwDataSize, err = AlignToNextPage(pageSize, rwDataSize) + rwDataSize, err = AlignToNextPage(rwDataSize) if err != nil { return nil, fmt.Errorf("failed to instantiate memory map rw data size: %w", err) } - stackAddressSpace, err := AlignToNextPage(VmMaxPageSize, stackSize) + stackAddressSpace, err := AlignToNextPage(stackSize) if err != nil { return nil, fmt.Errorf("failed to instantiate memory map stackData address space: %w", err) } - stackSize, err = AlignToNextPage(pageSize, stackSize) + stackSize, err = AlignToNextPage(stackSize) if err != nil { return nil, fmt.Errorf("failed to instantiate memory map stackData size: %w", err) } - argsDataAddressSpace, err := AlignToNextPage(VmMaxPageSize, argsDataSize) + argsDataAddressSpace, err := AlignToNextPage(argsDataSize) if err != nil { return nil, fmt.Errorf("the size of the arguments data is too big %w", err) } - argsDataSize, err = AlignToNextPage(pageSize, argsDataSize) + argsDataSize, err = AlignToNextPage(argsDataSize) if err != nil { return nil, fmt.Errorf("the size of the arguments data is too big %w", err) } - var addressLow uint + var addressLow uint32 addressLow += VmAddressSpaceBottom addressLow += roDataAddressSpace addressLow += VmMaxPageSize @@ -84,7 +76,7 @@ func NewMemoryMap(pageSize, roDataSize, rwDataSize, stackSize, argsDataSize uint heapSlack := addressLow - heapBase addressLow += VmMaxPageSize - var addressHigh uint = VmAddressSpaceTop + var addressHigh uint32 = VmAddressSpaceTop addressHigh -= argsDataAddressSpace argsDataAddress := addressHigh addressHigh -= VmMaxPageSize @@ -98,23 +90,21 @@ func NewMemoryMap(pageSize, roDataSize, rwDataSize, stackSize, argsDataSize uint maxHeapSize := addressHigh - addressLow + heapSlack return &MemoryMap{ - PageSize: uint32(pageSize), - RODataSize: uint32(roDataSize), - RWDataAddress: uint32(rwDataAddress), - RWDataSize: uint32(rwDataSize), - StackAddressHigh: uint32(stackAddressHigh), - StackAddressLow: uint32(stackAddressHigh - stackSize), - StackSize: uint32(stackSize), - HeapBase: uint32(heapBase), - MaxHeapSize: uint32(maxHeapSize), + RODataSize: roDataSize, + RWDataAddress: rwDataAddress, + RWDataSize: rwDataSize, + StackAddressHigh: stackAddressHigh, + StackAddressLow: stackAddressHigh - stackSize, + StackSize: stackSize, + HeapBase: heapBase, + MaxHeapSize: maxHeapSize, RODataAddress: VmMaxPageSize, - ArgsDataAddress: uint32(argsDataAddress), - ArgsDataSize: uint32(argsDataSize), + ArgsDataAddress: argsDataAddress, + ArgsDataSize: argsDataSize, }, nil } type MemoryMap struct { - PageSize uint32 RODataSize uint32 RWDataSize uint32 StackSize uint32 @@ -146,16 +136,13 @@ func copySized(data []byte, size uint32) []byte { return dst } -func AlignToNextPage(pageSize uint, value uint) (uint, error) { - if !(pageSize != 0 && (pageSize&(pageSize-1)) == 0) { - return 0, ErrPageSizeNotPowerOfTwo - } - if value&(pageSize-1) == 0 { +func AlignToNextPage(value uint32) (uint32, error) { + if value&(VMPageSize-1) == 0 { return value, nil - } else { - if value <= math.MaxUint-pageSize { - return (value + pageSize) & ^(pageSize - 1), nil - } } + if value <= (math.MaxUint32 - VmMaxPageSize) { + return (value + VMPageSize) & ^(VMPageSize - 1), nil + } + return 0, ErrPageValueTooLarge } diff --git a/internal/polkavm/abi_test.go b/internal/polkavm/abi_test.go index 2ed0772..ecd3d78 100644 --- a/internal/polkavm/abi_test.go +++ b/internal/polkavm/abi_test.go @@ -10,24 +10,30 @@ import ( func Test_memoryMap(t *testing.T) { maxSize := uint32(AddressSpaceSize - uint64(VmMaxPageSize)*5) tests := []struct { - expectError bool - pageSize, roDataSize, rwDataSize, stackSize uint32 + expectError bool + roDataSize, rwDataSize, stackSize uint32 expectedRODataAddress, expectedRODataSize, expectedRWDataSize, expectedRWDataAddress, expectedStackSize, expectedStackAddressHigh, expectedStackAddressLow, expectedHeapBase uint32 expectedMaxHeapSize uint64 }{{ - pageSize: 0x4000, roDataSize: 1, rwDataSize: 1, stackSize: 1, + roDataSize: 1, rwDataSize: 1, stackSize: 1, expectedRODataAddress: 0x10000, - expectedRODataSize: 0x4000, - expectedRWDataSize: 0x4000, - expectedStackSize: 0x4000, - expectedRWDataAddress: 0x30000, - expectedStackAddressHigh: 0xfffe0000, - expectedStackAddressLow: 0xfffdc000, - expectedHeapBase: 0x30001, - expectedMaxHeapSize: AddressSpaceSize - uint64(VmMaxPageSize)*4 - uint64(0x30001), + expectedRODataSize: 0x1000, + expectedRWDataSize: 0x1000, + expectedStackSize: 0x1000, + expectedRWDataAddress: VmAddressSpaceBottom + 0x1000 + VmMaxPageSize, + expectedStackAddressHigh: VmAddressSpaceTop - VmMaxPageSize, + expectedStackAddressLow: VmAddressSpaceTop - VmMaxPageSize - 0x1000, + expectedHeapBase: VmAddressSpaceBottom + 0x1000 + VmMaxPageSize + 1, + expectedMaxHeapSize: func() uint64 { + addressLow := VmAddressSpaceBottom + uint32(0x1000) + VmMaxPageSize + uint32(0x1000) + VmMaxPageSize + heapSlack := uint32(0x1000) - 1 + addressHigh := VmAddressSpaceTop - VmMaxPageSize - uint32(0x1000) + + return uint64(addressHigh - addressLow + heapSlack) + }(), }, { - pageSize: 0x4000, roDataSize: maxSize, rwDataSize: 0, stackSize: 0, + roDataSize: maxSize, rwDataSize: 0, stackSize: 0, expectedRODataAddress: 0x10000, expectedRODataSize: maxSize, expectedRWDataAddress: 0x10000 + VmMaxPageSize + maxSize, @@ -38,16 +44,16 @@ func Test_memoryMap(t *testing.T) { expectedHeapBase: 0x10000 + VmMaxPageSize + maxSize, expectedMaxHeapSize: 0, }, { - pageSize: 0x4000, roDataSize: maxSize + 1, rwDataSize: 0, stackSize: 0, + roDataSize: maxSize + 1, rwDataSize: 0, stackSize: 0, expectError: true, }, { - pageSize: 0x4000, roDataSize: maxSize, rwDataSize: 1, stackSize: 0, + roDataSize: maxSize, rwDataSize: 1, stackSize: 0, expectError: true, }, { - pageSize: 0x4000, roDataSize: maxSize, rwDataSize: 0, stackSize: 1, + roDataSize: maxSize, rwDataSize: 0, stackSize: 1, expectError: true, }, { - pageSize: 0x4000, roDataSize: 0, rwDataSize: maxSize, stackSize: 0, + roDataSize: 0, rwDataSize: maxSize, stackSize: 0, expectedRODataAddress: VmMaxPageSize, expectedRODataSize: 0, expectedRWDataAddress: VmMaxPageSize * 2, @@ -58,7 +64,7 @@ func Test_memoryMap(t *testing.T) { expectedHeapBase: VmMaxPageSize*2 + maxSize, expectedMaxHeapSize: 0, }, { - pageSize: 0x4000, roDataSize: 0, rwDataSize: 0, stackSize: maxSize, + roDataSize: 0, rwDataSize: 0, stackSize: maxSize, expectedRODataAddress: VmMaxPageSize, expectedRODataSize: 0, expectedRWDataAddress: VmMaxPageSize * 2, @@ -71,7 +77,7 @@ func Test_memoryMap(t *testing.T) { }} for _, tc := range tests { t.Run("", func(t *testing.T) { - m, err := NewMemoryMap(uint(tc.pageSize), uint(tc.roDataSize), uint(tc.rwDataSize), uint(tc.stackSize), 0) + m, err := NewMemoryMap(tc.roDataSize, tc.rwDataSize, tc.stackSize, 0) if err != nil { if tc.expectError { return @@ -91,19 +97,19 @@ func Test_memoryMap(t *testing.T) { } func Test_alignToNextPageUint32(t *testing.T) { - v, _ := AlignToNextPage(4096, 0) - assert.Equal(t, uint(0), v) - v, _ = AlignToNextPage(4096, 1) - assert.Equal(t, uint(4096), v) - v, _ = AlignToNextPage(4096, 4095) - assert.Equal(t, uint(4096), v) - v, _ = AlignToNextPage(4096, 4096) - assert.Equal(t, uint(4096), v) - v, _ = AlignToNextPage(4096, 4097) - assert.Equal(t, uint(8192), v) - var maxVal uint = math.MaxUint + 1 - 4096 - v, _ = AlignToNextPage(4096, maxVal) + v, _ := AlignToNextPage(0) + assert.Equal(t, uint32(0), v) + v, _ = AlignToNextPage(1) + assert.Equal(t, uint32(4096), v) + v, _ = AlignToNextPage(4095) + assert.Equal(t, uint32(4096), v) + v, _ = AlignToNextPage(uint32(4096)) + assert.Equal(t, uint32(4096), v) + v, _ = AlignToNextPage(4097) + assert.Equal(t, uint32(8192), v) + var maxVal uint32 = math.MaxUint32 + 1 - 4096 + v, _ = AlignToNextPage(maxVal) assert.Equal(t, maxVal, v) - _, err := AlignToNextPage(4096, maxVal+1) + _, err := AlignToNextPage(maxVal + 1) assert.Error(t, err) } diff --git a/internal/polkavm/common.go b/internal/polkavm/common.go index b66435b..9d9e9f3 100644 --- a/internal/polkavm/common.go +++ b/internal/polkavm/common.go @@ -49,14 +49,14 @@ func (m *Memory) inRange(address uint32) *memorySegment { return nil } -func (m *Memory) Sbrk(pageSize, heapTop uint32) error { +func (m *Memory) Sbrk(heapTop uint32) error { if heapTop > m.data[2].end { - nextPage, err := AlignToNextPage(uint(pageSize), uint(heapTop)) + nextPage, err := AlignToNextPage(heapTop) if err != nil { return err } - m.data[2].end += uint32(nextPage) + m.data[2].end += nextPage } return nil } diff --git a/internal/polkavm/host_call/accumulate_functions_test.go b/internal/polkavm/host_call/accumulate_functions_test.go index e3e6f84..3d592fb 100644 --- a/internal/polkavm/host_call/accumulate_functions_test.go +++ b/internal/polkavm/host_call/accumulate_functions_test.go @@ -649,7 +649,7 @@ func TestAccumulate(t *testing.T) { } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - memoryMap, err := NewMemoryMap(VmMinPageSize, 0, 0, 1<<19, 0) + memoryMap, err := NewMemoryMap(0, 0, 1<<19, 0) require.NoError(t, err) mem := memoryMap.NewMemory(nil, nil, nil) diff --git a/internal/polkavm/host_call/general_functions_test.go b/internal/polkavm/host_call/general_functions_test.go index f30f777..0f3a29f 100644 --- a/internal/polkavm/host_call/general_functions_test.go +++ b/internal/polkavm/host_call/general_functions_test.go @@ -23,7 +23,7 @@ func TestGasRemaining(t *testing.T) { }, } - memoryMap, err := polkavm.NewMemoryMap(polkavm.VmMaxPageSize, 0, 0, 4096, 0) + memoryMap, err := polkavm.NewMemoryMap(0, 0, 4096, 0) require.NoError(t, err) mem := memoryMap.NewMemory(nil, nil, nil) @@ -60,7 +60,7 @@ func TestLookup(t *testing.T) { Exports: []polkavm.ProgramExport{{TargetCodeOffset: 0, Symbol: "test_lookup"}}, } - memoryMap, err := polkavm.NewMemoryMap(polkavm.VmMinPageSize, 0, 256, 512, 0) + memoryMap, err := polkavm.NewMemoryMap(0, 256, 512, 0) require.NoError(t, err) t.Run("service_not_found", func(t *testing.T) { initialRegs := polkavm.Registers{ @@ -137,7 +137,7 @@ func TestRead(t *testing.T) { }, } - memoryMap, err := polkavm.NewMemoryMap(polkavm.VmMinPageSize, 0, 256, 512, 0) + memoryMap, err := polkavm.NewMemoryMap(0, 256, 512, 0) require.NoError(t, err) serviceId := block.ServiceId(1) @@ -208,7 +208,7 @@ func TestWrite(t *testing.T) { }, } - memoryMap, err := polkavm.NewMemoryMap(polkavm.VmMinPageSize, 0, 256, 512, 0) + memoryMap, err := polkavm.NewMemoryMap(0, 256, 512, 0) require.NoError(t, err) serviceId := block.ServiceId(1) @@ -290,7 +290,7 @@ func TestInfo(t *testing.T) { Exports: []polkavm.ProgramExport{{TargetCodeOffset: 0, Symbol: "test_info"}}, } - memoryMap, err := polkavm.NewMemoryMap(polkavm.VmMinPageSize, 0, 256, 512, 0) + memoryMap, err := polkavm.NewMemoryMap(0, 256, 512, 0) require.NoError(t, err) serviceId := block.ServiceId(1) diff --git a/internal/polkavm/interpreter/interpreter_test.go b/internal/polkavm/interpreter/interpreter_test.go index 14b3329..21525d8 100644 --- a/internal/polkavm/interpreter/interpreter_test.go +++ b/internal/polkavm/interpreter/interpreter_test.go @@ -4,9 +4,10 @@ import ( "slices" "testing" - "github.com/eigerco/strawberry/internal/polkavm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/eigerco/strawberry/internal/polkavm" ) func TestInstance_Execute(t *testing.T) { @@ -30,7 +31,7 @@ func TestInstance_Execute(t *testing.T) { Exports: []polkavm.ProgramExport{{TargetCodeOffset: 0, Symbol: "add_numbers"}}, } - memoryMap, err := polkavm.NewMemoryMap(0x1000, uint(pp.RODataSize), uint(pp.RWDataSize), uint(pp.StackSize), 0) + memoryMap, err := polkavm.NewMemoryMap(pp.RODataSize, pp.RWDataSize, pp.StackSize, 0) require.NoError(t, err) memory := memoryMap.NewMemory(pp.RWData, pp.ROData, nil) diff --git a/internal/polkavm/interpreter/mutator.go b/internal/polkavm/interpreter/mutator.go index 9ffcb5d..a506f6b 100644 --- a/internal/polkavm/interpreter/mutator.go +++ b/internal/polkavm/interpreter/mutator.go @@ -175,17 +175,17 @@ func (m *Mutator) Sbrk(dst polkavm.Reg, sizeReg polkavm.Reg) error { } newHeapSize := m.instance.heapSize + size - if newHeapSize > uint32(m.memoryMap.MaxHeapSize) { + if newHeapSize > m.memoryMap.MaxHeapSize { return polkavm.ErrPanicf("max heap size exceeded") } m.instance.heapSize = newHeapSize heapTop := m.memoryMap.HeapBase + newHeapSize - if err := m.instance.memory.Sbrk(m.memoryMap.PageSize, heapTop); err != nil { + if err := m.instance.memory.Sbrk(heapTop); err != nil { return polkavm.ErrPanicf(err.Error()) } - m.setNext32(dst, uint32(heapTop)) + m.setNext32(dst, heapTop) return nil } diff --git a/internal/polkavm/interpreter/utils.go b/internal/polkavm/interpreter/utils.go index 9ec9b60..577b0e4 100644 --- a/internal/polkavm/interpreter/utils.go +++ b/internal/polkavm/interpreter/utils.go @@ -3,8 +3,9 @@ package interpreter import ( "bytes" "errors" - "github.com/eigerco/strawberry/internal/polkavm" "slices" + + "github.com/eigerco/strawberry/internal/polkavm" ) // InvokeWholeProgram the marshalling whole-program pvm machine state-transition function: (ΨM) @@ -19,7 +20,7 @@ func InvokeWholeProgram[X any](p []byte, entryPoint uint32, gas uint64, args []b if err != nil { return 0, nil, x, polkavm.ErrPanicf(err.Error()) } - memMap, err := polkavm.NewMemoryMap(polkavm.VmMinPageSize, uint(program.RODataSize), uint(program.RWDataSize), uint(program.StackSize), uint(len(args))) + memMap, err := polkavm.NewMemoryMap(program.RODataSize, program.RWDataSize, program.StackSize, uint32(len(args))) if err != nil { return 0, nil, x, polkavm.ErrPanicf(err.Error()) } diff --git a/tests/integration/pvm_integration_test.go b/tests/integration/pvm_integration_test.go index 0f56fa6..fad85be 100644 --- a/tests/integration/pvm_integration_test.go +++ b/tests/integration/pvm_integration_test.go @@ -97,7 +97,7 @@ func Test_Vectors(t *testing.T) { } func getMemoryMap(pageMap []Page) *polkavm.MemoryMap { - mm := &polkavm.MemoryMap{PageSize: polkavm.VmMinPageSize, ArgsDataAddress: 1<<32 - 1} + mm := &polkavm.MemoryMap{ArgsDataAddress: 1<<32 - 1} for _, page := range pageMap { if !page.IsWritable { mm.RODataAddress = page.Address