#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. // 16 MB, not 4. // // 4 MB was one framebuffer at 1280x800, and that was fine while exactly one // driver wanted one. It stopped being fine the day a display driver could be // handed to userspace (docs/plan/hybrid.md): for the moment either side of the // handover both drivers exist, and the incoming one has to allocate its // framebuffer before the outgoing one is gone. // // It is carved off the top of physical memory at boot and never grows, so the // cost of being wrong in this direction is 12 MB and the cost of being wrong // in the other is a driver that cannot start. #define DMA_ARENA_BYTES (16*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);