pmem

package
v3.6.8+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 24, 2021 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package pmem implements handling of physical memory for user space programs.

To make things confusing, a modern computer has many view of the memory (address spaces):

User

User mode address space is the virtual address space that an application runs in. It is generally a tad less than half the addressable memory, so on a 32 bits system, the addressable range is 1.9Gb. For 64 bits OS, it depends but it usually at least 3.5Gb. The memory is virtual and can be flushed to disk in the swap file unless individual pages are locked.

Kernel

Kernel address space is the virtual address space the kernel sees. It often can see the currently active user space program on the current CPU core in addition to all the memory the kernel sees. The kernel memory pages that are not mlock()'ed are 'virtual' and can be flushed to disk in the swap file when there's not enough RAM available. On linux systems, the kernel addressed memory can be mapped in user space via `/dev/kmem`.

Physical

Physical memory address space is the actual address of each page in the DRAM chip and anything connected to the memory controller. The mapping may be different depending on what controller looks at the bus, like with IOMMU. So a peripheral (GPU, DMA controller) may have a different view of the physical memory than the host CPU. On linux systems, this memory can be mapped in user space via `/dev/mem`.

CPU

The CPU or its subsystems may memory map registers (for example, to control GPIO pins, clock speed, etc). This is not "real" memory, this is a view of registers but it still follows "mostly" the same semantic as DRAM backed physical memory.

Some CPU memory may have very special semantic where the mere fact of reading has side effects. For example reading a specific register may latches another.

CPU memory accesses are layered with multiple caches, usually named L1, L2 and optionally L3. Some controllers (DMA) can see some cache levels (L2) but not others (L1) on some CPU architecture (bcm283x). This means that a user space program writing data to a memory page and immediately asking the DMA controller to read it may cause stale data to be read!

Hypervisor

Hypervisor can change the complete memory mapping as seen by the kernel. This is outside the scope of this project. :)

Summary

In practice, the semantics change between CPU manufacturers (Broadcom vs Allwinner) and between architectures (ARM vs x86). The most tricky one is to understand cached memory and how it affects coherence and performance. Uncached memory is extremely slow so it must only be used when necessary.

References

Overview of IOMMU: https://en.wikipedia.org/wiki/Input-output_memory_management_unit

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func MapAsPOD

func MapAsPOD(base uint64, i interface{}) error

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.

Example
package main

import (
	"log"

	"periph.io/x/periph/host/pmem"
)

func main() {
	// Let's say the CPU has 4 x 32 bits memory mapped registers at the address
	// 0xDEADBEEF.
	var reg *[4]uint32
	if err := pmem.MapAsPOD(0xDEADBEAF, reg); err != nil {
		log.Fatal(err)
	}
	// reg now points to physical memory.
}
Output:

func ReadPageMap

func ReadPageMap(virtAddr uintptr) (uint64, error)

ReadPageMap reads a physical address mapping for a virtual page address from /proc/self/pagemap.

It returns the physical address that corresponds to the start of the virtual page within which the virtual address virtAddr is located.

The meaning of the return value is documented at https://www.kernel.org/doc/Documentation/vm/pagemap.txt

func TestCopy

func TestCopy(size, holeSize int, alloc func(size int) (Mem, error), copyMem func(pDst, pSrc uint64) error) error

TestCopy is used by CPU drivers to verify that the DMA engine works correctly.

It is not meant to be used by end users.

TestCopy allocates two buffer via `alloc`, once as the source and one as the destination. It fills the source with random data and the destination with 0x11.

`copyMem` is expected to copy the memory from pSrc to pDst, with an offset of `hole` and size `size-2*hole`.

The function `copyMem` being tested is only given the buffer physical addresses and must copy the data without other help. It is expected to

This confirm misaligned DMA copying works. leverage the host's DMA engine.

Types

type Mem

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
}

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 MemAlloc

type MemAlloc struct {
	View
}

MemAlloc represents contiguous physically locked memory that was allocated.

The memory is mapped in user space.

MemAlloc implements Mem.

func Alloc

func Alloc(size int) (*MemAlloc, error)

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 (*MemAlloc) Close

func (m *MemAlloc) Close() error

Close unmaps the physical memory allocation.

type Slice

type Slice []byte

Slice can be transparently viewed as []byte, []uint32 or a struct.

func (*Slice) AsPOD

func (s *Slice) AsPOD(pp interface{}) error

AsPOD implements Mem.

func (*Slice) Bytes

func (s *Slice) Bytes() []byte

Bytes implements Mem.

func (*Slice) Uint32

func (s *Slice) Uint32() []uint32

Uint32 returns a view of the byte slice as a []uint32.

type View

type View struct {
	Slice
	// contains filtered or unexported fields
}

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.

func Map

func Map(base uint64, size int) (*View, error)

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 MapGPIO

func MapGPIO() (*View, error)

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 (*View) Close

func (v *View) Close() error

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 (*View) PhysAddr

func (v *View) PhysAddr() uint64

PhysAddr implements Mem.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL