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