did something
This commit is contained in:
1 parent
ffb640b15d
commit
4bed603e6d
144 files changed
+21464
-6
No files matched your search
+285
@@ -0,0 +1,285 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package pmem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"periph.io/x/host/v3/fs"
|
||||
)
|
||||
|
||||
// Slice can be transparently viewed as []byte, []uint32 or a struct.
|
||||
type Slice []byte
|
||||
|
||||
// Uint32 returns a view of the byte slice as a []uint32.
|
||||
func (s *Slice) Uint32() []uint32 {
|
||||
// It's important to make a copy here.
|
||||
h := new(reflect.SliceHeader)
|
||||
h.Data = (*reflect.SliceHeader)(unsafe.Pointer(s)).Data
|
||||
h.Len = (*reflect.SliceHeader)(unsafe.Pointer(s)).Len / 4
|
||||
h.Cap = (*reflect.SliceHeader)(unsafe.Pointer(s)).Cap / 4
|
||||
return *(*[]uint32)(unsafe.Pointer(h))
|
||||
}
|
||||
|
||||
// Bytes implements Mem.
|
||||
func (s *Slice) Bytes() []byte {
|
||||
return *s
|
||||
}
|
||||
|
||||
// AsPOD implements Mem.
|
||||
func (s *Slice) AsPOD(pp interface{}) error {
|
||||
if pp == nil {
|
||||
return wrapf("require Ptr, got nil")
|
||||
}
|
||||
vpp := reflect.ValueOf(pp)
|
||||
if elemSize, err := isPS(len(*s), vpp); err == nil {
|
||||
p := vpp.Elem()
|
||||
t := p.Type().Elem()
|
||||
if elemSize > len(*s) {
|
||||
return wrapf("can't map slice of struct %s (size %d) on [%d]byte", t, elemSize, len(*s))
|
||||
}
|
||||
nbElems := len(*s) / elemSize
|
||||
// Use casting black magic to set the internal slice headers.
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(p.UnsafeAddr()))
|
||||
hdr.Data = ((*reflect.SliceHeader)(unsafe.Pointer(s))).Data
|
||||
hdr.Len = nbElems
|
||||
hdr.Cap = nbElems
|
||||
return nil
|
||||
}
|
||||
|
||||
size, err := isPP(vpp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p := vpp.Elem()
|
||||
t := p.Type().Elem()
|
||||
if size > len(*s) {
|
||||
return wrapf("can't map struct %s (size %d) on [%d]byte", t, size, len(*s))
|
||||
}
|
||||
// Use casting black magic to read the internal slice headers.
|
||||
dest := unsafe.Pointer(((*reflect.SliceHeader)(unsafe.Pointer(s))).Data)
|
||||
// Use reflection black magic to write to the original pointer.
|
||||
p.Set(reflect.NewAt(t, dest))
|
||||
return nil
|
||||
}
|
||||
|
||||
// View represents a view of physical memory memory mapped into user space.
|
||||
//
|
||||
// It is usually used to map CPU registers into user space, usually I/O
|
||||
// registers and the likes.
|
||||
//
|
||||
// It is not required to call Close(), the kernel will clean up on process
|
||||
// shutdown.
|
||||
type View struct {
|
||||
Slice
|
||||
orig []uint8 // Reference rounded to the lowest 4Kb page containing Slice.
|
||||
phys uint64 // physical address of the base of Slice.
|
||||
}
|
||||
|
||||
// Close unmaps the memory from the user address space.
|
||||
//
|
||||
// This is done naturally by the OS on process teardown (when the process
|
||||
// exits) so this is not a hard requirement to call this function.
|
||||
func (v *View) Close() error {
|
||||
return munmap(v.orig)
|
||||
}
|
||||
|
||||
// PhysAddr implements Mem.
|
||||
func (v *View) PhysAddr() uint64 {
|
||||
return v.phys
|
||||
}
|
||||
|
||||
// MapGPIO returns a CPU specific memory mapping of the CPU I/O registers using
|
||||
// /dev/gpiomem.
|
||||
//
|
||||
// At the moment, /dev/gpiomem is only supported on Raspbian Jessie via a
|
||||
// specific kernel driver.
|
||||
func MapGPIO() (*View, error) {
|
||||
if isLinux {
|
||||
return mapGPIOLinux()
|
||||
}
|
||||
return nil, wrapf("/dev/gpiomem is not supported on this platform")
|
||||
}
|
||||
|
||||
// Map returns a memory mapped view of arbitrary physical memory range using OS
|
||||
// provided functionality.
|
||||
//
|
||||
// Maps size of memory, rounded on a 4kb window.
|
||||
//
|
||||
// This function is dangerous and should be used wisely. It normally requires
|
||||
// super privileges (root). On Linux, it leverages /dev/mem.
|
||||
func Map(base uint64, size int) (*View, error) {
|
||||
if isLinux {
|
||||
return mapLinux(base, size)
|
||||
}
|
||||
return nil, wrapf("physical memory mapping is not supported on this platform")
|
||||
}
|
||||
|
||||
// MapAsPOD is a leaky shorthand of calling Map(base, sizeof(v)) then AsPOD(v).
|
||||
//
|
||||
// There is no way to reclaim the memory map.
|
||||
//
|
||||
// A slice cannot be used, as it does not have inherent size. Use an aray
|
||||
// instead.
|
||||
func MapAsPOD(base uint64, i interface{}) error {
|
||||
// Automatically determine the necessary size. Because of this, slice of
|
||||
// unspecified length cannot be used here.
|
||||
if i == nil {
|
||||
return wrapf("require Ptr, got nil")
|
||||
}
|
||||
v := reflect.ValueOf(i)
|
||||
size, err := isPP(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := Map(base, size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.AsPOD(i)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Keep a cache of open file handles instead of opening and closing repeatedly.
|
||||
var (
|
||||
mu sync.Mutex
|
||||
gpioMemErr error
|
||||
gpioMemView *View
|
||||
devMem fileIO
|
||||
devMemErr error
|
||||
openFile = openFileOrig
|
||||
)
|
||||
|
||||
type fileIO interface {
|
||||
io.Closer
|
||||
io.Seeker
|
||||
io.Reader
|
||||
Fd() uintptr
|
||||
}
|
||||
|
||||
func openFileOrig(path string, flag int) (fileIO, error) {
|
||||
f, err := fs.Open(path, flag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// mapGPIOLinux is purely Raspbian specific.
|
||||
func mapGPIOLinux() (*View, error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if gpioMemView == nil && gpioMemErr == nil {
|
||||
if f, err := openFile("/dev/gpiomem", os.O_RDWR|os.O_SYNC); err == nil {
|
||||
defer f.Close()
|
||||
if i, err2 := mmap(f.Fd(), 0, pageSize); err2 == nil {
|
||||
gpioMemView = &View{Slice: i, orig: i, phys: 0}
|
||||
} else {
|
||||
gpioMemErr = wrapf("failed to memory map in user space GPIO memory: %w", err2)
|
||||
}
|
||||
} else {
|
||||
gpioMemErr = wrapf("failed to open GPIO memory: %w", err)
|
||||
}
|
||||
}
|
||||
return gpioMemView, gpioMemErr
|
||||
}
|
||||
|
||||
// mapLinux leverages /dev/mem to map a view of physical memory.
|
||||
func mapLinux(base uint64, size int) (*View, error) {
|
||||
f, err := openDevMemLinux()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Align base and size at 4Kb.
|
||||
offset := int(base & 0xFFF)
|
||||
i, err := mmap(f.Fd(), int64(base&^0xFFF), (size+offset+0xFFF)&^0xFFF)
|
||||
if err != nil {
|
||||
return nil, wrapf("mapping at 0x%x failed: %w", base, err)
|
||||
}
|
||||
return &View{Slice: i[offset : offset+size], orig: i, phys: base + uint64(offset)}, nil
|
||||
}
|
||||
|
||||
func openDevMemLinux() (fileIO, error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if devMem == nil && devMemErr == nil {
|
||||
if devMem, devMemErr = openFile("/dev/mem", os.O_RDWR|os.O_SYNC); devMemErr != nil {
|
||||
devMemErr = wrapf("failed to open physical memory: %v", devMemErr)
|
||||
}
|
||||
}
|
||||
return devMem, devMemErr
|
||||
}
|
||||
|
||||
func isAcceptableInner(t reflect.Type) error {
|
||||
switch k := t.Kind(); k {
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64:
|
||||
return nil
|
||||
case reflect.Array:
|
||||
return isAcceptableInner(t.Elem())
|
||||
case reflect.Struct:
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if err := isAcceptableInner(t.Field(i).Type); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return wrapf("require Ptr to Ptr to a POD type, got Ptr to Ptr to %s", k)
|
||||
}
|
||||
}
|
||||
|
||||
// isPP makes sure it is a pointer to a nil-pointer to something. It does
|
||||
// sanity checks to reduce likelihood of a panic().
|
||||
func isPP(pp reflect.Value) (int, error) {
|
||||
if k := pp.Kind(); k != reflect.Ptr {
|
||||
return 0, wrapf("require Ptr, got %s of %s", k, pp.Type().Name())
|
||||
}
|
||||
p := pp.Elem()
|
||||
if k := p.Kind(); k != reflect.Ptr {
|
||||
return 0, wrapf("require Ptr to Ptr, got %s", k)
|
||||
}
|
||||
if !p.IsNil() {
|
||||
return 0, wrapf("require Ptr to Ptr to be nil")
|
||||
}
|
||||
// p.Elem() can't be used since it's a nil pointer. Use the type instead.
|
||||
t := p.Type().Elem()
|
||||
if err := isAcceptableInner(t); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(t.Size()), nil
|
||||
}
|
||||
|
||||
// isPS makes sure it is a pointer to a nil-slice of something. It does
|
||||
// sanity checks to reduce likelihood of a panic().
|
||||
func isPS(bufSize int, ps reflect.Value) (int, error) {
|
||||
if k := ps.Kind(); k != reflect.Ptr {
|
||||
return 0, wrapf("require Ptr, got %s of %s", k, ps.Type().Name())
|
||||
}
|
||||
s := ps.Elem()
|
||||
if k := s.Kind(); k != reflect.Slice {
|
||||
return 0, wrapf("require Ptr to Slice, got %s", k)
|
||||
}
|
||||
if !s.IsNil() {
|
||||
return 0, wrapf("require Ptr to Slice to be nil")
|
||||
}
|
||||
// s.Elem() can't be used since it's a nil slice. Use the type instead.
|
||||
t := s.Type().Elem()
|
||||
if err := isAcceptableInner(t); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(t.Size()), nil
|
||||
}
|
||||
|
||||
func wrapf(format string, a ...interface{}) error {
|
||||
return fmt.Errorf("pmem: "+format, a...)
|
||||
}
|
||||
Reference in new issue
Block a user