did something
This commit is contained in:
1 parent
ffb640b15d
commit
4bed603e6d
144 files changed
+21464
-6
No files matched your search
+182
@@ -0,0 +1,182 @@
|
||||
// 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 (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const pageSize = 4096
|
||||
|
||||
// Mem represents a section of memory that is usable by the DMA controller.
|
||||
//
|
||||
// Since this is physically allocated memory, that could potentially have been
|
||||
// allocated in spite of OS consent, for example by asking the GPU directly, it
|
||||
// is important to call Close() before process exit.
|
||||
type Mem interface {
|
||||
io.Closer
|
||||
// Bytes returns the user space memory mapped buffer address as a slice of
|
||||
// bytes.
|
||||
//
|
||||
// It is the raw view of the memory from this process.
|
||||
Bytes() []byte
|
||||
// AsPOD initializes a pointer to a POD (plain old data) to point to the
|
||||
// memory mapped region.
|
||||
//
|
||||
// pp must be a pointer to:
|
||||
//
|
||||
// - pointer to a base size type (uint8, int64, float32, etc)
|
||||
// - struct
|
||||
// - array of the above
|
||||
// - slice of the above
|
||||
//
|
||||
// and the value must be nil. Returns an error otherwise.
|
||||
//
|
||||
// If a pointer to a slice is passed in, it is initialized to the length and
|
||||
// capacity set to the maximum number of elements this slice can represent.
|
||||
//
|
||||
// The pointer initialized points to the same address as Bytes().
|
||||
AsPOD(pp interface{}) error
|
||||
// PhysAddr is the physical address. It can be either 32 bits or 64 bits,
|
||||
// depending on the bitness of the OS kernel, not on the user mode build,
|
||||
// e.g. you could have compiled on a 32 bits Go toolchain but running on a
|
||||
// 64 bits kernel.
|
||||
PhysAddr() uint64
|
||||
}
|
||||
|
||||
// MemAlloc represents contiguous physically locked memory that was allocated.
|
||||
//
|
||||
// The memory is mapped in user space.
|
||||
//
|
||||
// MemAlloc implements Mem.
|
||||
type MemAlloc struct {
|
||||
View
|
||||
}
|
||||
|
||||
// Close unmaps the physical memory allocation.
|
||||
func (m *MemAlloc) Close() error {
|
||||
if err := munlock(m.orig); err != nil {
|
||||
return err
|
||||
}
|
||||
return munmap(m.orig)
|
||||
}
|
||||
|
||||
// Alloc allocates a continuous chunk of physical memory.
|
||||
//
|
||||
// Size must be rounded to 4Kb. Allocations of 4Kb will normally succeed.
|
||||
// Allocations larger than 64Kb will likely fail due to kernel memory
|
||||
// fragmentation; rebooting the host or reducing the number of running programs
|
||||
// may help.
|
||||
//
|
||||
// The allocated memory is uncached.
|
||||
func Alloc(size int) (*MemAlloc, error) {
|
||||
if size == 0 || size&(pageSize-1) != 0 {
|
||||
return nil, wrapf("allocated memory must be rounded to %d bytes", pageSize)
|
||||
}
|
||||
if isLinux && !isWSL() {
|
||||
return allocLinux(size)
|
||||
}
|
||||
return nil, wrapf("memory allocation is not supported on this platform")
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var (
|
||||
wslOnce sync.Once
|
||||
isWSLValue bool
|
||||
)
|
||||
|
||||
// uallocMemLocked allocates user space memory and requests the OS to have the
|
||||
// chunk to be locked into physical memory.
|
||||
func uallocMemLocked(size int) ([]byte, error) {
|
||||
// It is important to write to the memory so it is forced to be present.
|
||||
b, err := uallocMem(size)
|
||||
if err == nil {
|
||||
for i := range b {
|
||||
b[i] = 0
|
||||
}
|
||||
if err = mlock(b); err != nil {
|
||||
// Ignore the unmap error.
|
||||
_ = munmap(b)
|
||||
return nil, wrapf("locking %d bytes failed: %v", size, err)
|
||||
}
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
|
||||
// allocLinux allocates physical memory and returns a user view to it.
|
||||
func allocLinux(size int) (*MemAlloc, error) {
|
||||
// TODO(maruel): Implement the "shotgun approach". Allocate a ton of 4Kb
|
||||
// pages and lock them. Then look at their physical pages and only keep the
|
||||
// one useful. Then create a linear mapping in memory to simplify the user
|
||||
// mode with a single linear user space virtual address but keep the
|
||||
// individual page alive with their initial allocation. When done release
|
||||
// each individual page.
|
||||
if size > pageSize {
|
||||
return nil, wrapf("large allocation is not yet implemented")
|
||||
}
|
||||
// First allocate a chunk of user space memory.
|
||||
b, err := uallocMemLocked(size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pages := make([]uint64, (size+pageSize-1)/pageSize)
|
||||
// Figure out the physical memory addresses.
|
||||
for i := range pages {
|
||||
pages[i], err = virtToPhys(toRaw(b[pageSize*i:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pages[i] == 0 {
|
||||
return nil, wrapf("failed to read page %d", i)
|
||||
}
|
||||
}
|
||||
for i := 1; i < len(pages); i++ {
|
||||
// Fail if the memory is not contiguous.
|
||||
if pages[i] != pages[i-1]+pageSize {
|
||||
return nil, wrapf("failed to allocate %d bytes of continugous physical memory; page %d =0x%x; page %d=0x%x", size, i, pages[i], i-1, pages[i-1])
|
||||
}
|
||||
}
|
||||
|
||||
return &MemAlloc{View{Slice: b, phys: pages[0], orig: b}}, nil
|
||||
}
|
||||
|
||||
// virtToPhys returns the physical memory address backing a virtual
|
||||
// memory address.
|
||||
func virtToPhys(virt uintptr) (uint64, error) {
|
||||
physPage, err := ReadPageMap(virt)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if physPage&(1<<63) == 0 {
|
||||
// If high bit is not set, the page doesn't exist.
|
||||
return 0, wrapf("0x%08x has no physical address", virt)
|
||||
}
|
||||
// Strip flags. See linux documentation on kernel.org for more details.
|
||||
physPage &^= 0x1FF << 55
|
||||
return physPage * pageSize, nil
|
||||
}
|
||||
|
||||
func toRaw(b []byte) uintptr {
|
||||
h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
return h.Data
|
||||
}
|
||||
|
||||
// isWSL returns true if running under Windows Subsystem for Linux.
|
||||
func isWSL() bool {
|
||||
wslOnce.Do(func() {
|
||||
if c, err := os.ReadFile("/proc/sys/kernel/osrelease"); err == nil {
|
||||
isWSLValue = bytes.Contains(c, []byte("Microsoft"))
|
||||
}
|
||||
})
|
||||
return isWSLValue
|
||||
}
|
||||
|
||||
var _ Mem = &MemAlloc{}
|
||||
Reference in new issue
Block a user