blob: bf1f6d652d15ff89d9b39c98665b7fe5a5628f2d [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//go:build !build_with_native_toolchain
// +build !build_with_native_toolchain
package fifo
import (
"fmt"
"reflect"
"syscall/zx"
"unsafe"
"go.uber.org/multierr"
)
// MappedVMO own a VMO and its mapping into process memory. It provides common
// operations for memory mapped VMOs. Closing it will cause the mapped region to
// be unmapped.
type MappedVMO struct {
vaddr zx.Vaddr
len uint64
}
// Close unmaps the mapped VMO region and closes the VMO object.
func (vmo *MappedVMO) Close() error {
return zx.VMARRoot.Unmap(vmo.vaddr, vmo.len)
}
// GetData returns a slice view into the mapped VMO with offset and length.
// If the provided range is not within bounds of the VMO, GetData panics.
func (vmo *MappedVMO) GetData(offset, len uint64) []byte {
if offset+len > vmo.len {
panic(fmt.Sprintf("invalid VMO data range (offset:%d, len:%d) for VMO with %d bytes", offset, len, vmo.len))
}
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(vmo.vaddr) + uintptr(offset),
Len: int(len),
Cap: int(len),
}))
}
// GetPointer returns a pointer to the mapped VMO at offset. If the offset is
// not within bounds of the mapped VMO, GetPointer panics.
func (vmo *MappedVMO) GetPointer(offset uint64) unsafe.Pointer {
if offset > vmo.len {
panic(fmt.Sprintf("invalid VMO pointer (offset:%d) for VMO with %d bytes", offset, vmo.len))
}
return unsafe.Pointer(uintptr(vmo.vaddr) + uintptr(offset))
}
// NewMappedVMO creates a new VMO the given name and size, in bytes, and maps it
// to the process' memory space.
func NewMappedVMO(size uint64, name string) (MappedVMO, zx.VMO, error) {
vmo, err := zx.NewVMO(size, 0)
if err != nil {
return MappedVMO{}, zx.VMO(zx.HandleInvalid), err
}
if err := vmo.Handle().SetProperty(zx.PropName, []byte(name)); err != nil {
err = multierr.Append(err, vmo.Close())
return MappedVMO{}, zx.VMO(zx.HandleInvalid), err
}
mappedVmo, err := MapVMO(vmo)
if err != nil {
err = multierr.Append(err, vmo.Close())
return MappedVMO{}, zx.VMO(zx.HandleInvalid), err
}
return mappedVmo, vmo, nil
}
// Len returns the length of the VMO.
func (vmo *MappedVMO) Len() uint64 {
return vmo.len
}
// MapVMO maps a vmo into the process' memory space. It does not take ownership
// of the VMO.
func MapVMO(vmo zx.VMO) (MappedVMO, error) {
size, err := vmo.Size()
if err != nil {
return MappedVMO{}, err
}
vaddr, err := zx.VMARRoot.Map(0, vmo, 0, size, zx.VMFlagPermRead|zx.VMFlagPermWrite)
if err != nil {
return MappedVMO{}, err
}
return MappedVMO{
vaddr: vaddr,
len: size,
}, nil
}