An x86-64 operating system built from xv6, now largely rewritten: SMP, a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF rasteriser, and a Linux-numbered syscall ABI. No history before this point -- the tree was never under version control. This commit exists so the rename that follows has something to undo to.
43 lines
2 KiB
C
43 lines
2 KiB
C
#pragma once
|
|
|
|
// Physically contiguous memory for devices that read and write it themselves.
|
|
//
|
|
// kalloc() hands out 4 KB pages from a free list, in whatever order they were
|
|
// released. That is exactly what a page table wants and exactly what a device
|
|
// cannot use: an xHCI command ring, a NIC's descriptor array or a virtio
|
|
// queue is a single physically contiguous object that the hardware walks
|
|
// without any idea that paging exists. Two kalloc'd pages are adjacent in the
|
|
// kernel's address space only by luck.
|
|
//
|
|
// So DMA memory comes from an arena carved out of the top of physical memory
|
|
// before kalloc ever sees it, and is handed out contiguous and aligned.
|
|
//
|
|
// On x86 this memory needs no special cacheability. The PCI bus is cache
|
|
// coherent: a device's write invalidates the corresponding cache line, and a
|
|
// CPU write is snooped out of the store buffer before the device sees stale
|
|
// data. Architectures without that guarantee need explicit cache maintenance
|
|
// around every descriptor access, and porting this file is where that would
|
|
// go. What is *not* free even here is ordering -- the compiler and the CPU
|
|
// will happily reorder stores to a ring, so a driver still needs a barrier
|
|
// between filling a descriptor and telling the device about it.
|
|
|
|
#define DMA_ARENA_BYTES (4*1024*1024)
|
|
|
|
void dma_init(void *vstart, void *vend);
|
|
|
|
// Zeroed, physically contiguous, at least `align`-aligned (align must be a
|
|
// power of two). Returns a kernel virtual address, or 0.
|
|
void *dma_alloc(uint64 size, uint64 align);
|
|
|
|
// `size` must be the size the allocation was made with, as in Linux's
|
|
// dma_free_coherent(). Tracking it here would mean a second table whose only
|
|
// job is to remember something the caller already knows.
|
|
void dma_free(void *p, uint64 size);
|
|
|
|
// The address to give the device. Kernel memory is direct-mapped, so this is
|
|
// just V2P -- but drivers should say what they mean, because on a machine
|
|
// with an IOMMU these stop being the same number.
|
|
addr_t dma_phys(const void *p);
|
|
|
|
uint64 dma_free_bytes(void);
|
|
uint64 dma_total_bytes(void);
|