SchOS/include/core/dma.h
Nexoniarz cb3d28e3d6 docs: README, plan, guides and the site describe what is built now
README: rewritten around what runs (Linux software, Xorg, apk, Linux's drivers), the honest gaps, measured numbers.
plan/: 1.0.md is the finish line, where each clause of the acceptance test stands, and the register (one failing
clause: a root that survives a reboot); built plans (foundations, hybrid, alpine, xorg) and the decided-against one
(wayland) are gone, their content lives in architecture/; roadmap.md is only what is still open.
guides: building/layout/testing/adding-a-driver (Linux code may be used). history: four entries (ls -lR, TLB
shootdowns, MDIO 0x02460246, netlink MSG_TRUNC). Website: cards, stack, download page, docs index.
Code comments that pointed at deleted plan docs point at the architecture docs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
2026-10-26 08:08:05 +01:00

73 lines
3.7 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.
// 64 MB, and the arithmetic is worth writing down because it has been got
// wrong twice.
//
// 4 MB was one framebuffer at 1280x800, which was fine while exactly one driver
// wanted one. It stopped being fine the day a display driver could be handed to
// userspace (docs/architecture/devices.md): either side of that handover **both drivers
// exist**, and the incoming one has to allocate its framebuffer before the
// outgoing one lets go of its own. So the real requirement is two framebuffers
// at once, not one.
//
// 16 MB was chosen for two at 1280x800 (4 MB each) and that is what it holds.
// It does not hold two of anything larger, and virtio-gpu's size is **the size
// of the host's window**, which the user resizes. A 1920x982 window is 7365 KB
// a copy, so the handover needs 14.4 MB of a 16 MB arena and then fails on the
// rings and the alignment:
//
// vgpud: wanted 7365 KB of DMA memory for a 1920x982 framebuffer
// vgpud: not enough contiguous DMA memory
// schinitd: restarting os.schos.vgpud (and again, and again)
//
// 64 MB is two framebuffers at 2560x1440 with room to spare, which covers any
// window somebody is likely to drag out on a desktop. It is carved off the top
// of physical memory at boot and never grows, so it is 64 MB this kernel cannot
// use for anything else -- 6% of the 1 GB this is usually run with, against a
// display driver that otherwise cannot start at all.
#define DMA_ARENA_BYTES (64*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);
// The biggest single block still available. When an allocation fails, the
// difference between this and dma_free_bytes() is the difference between a
// full arena and a fragmented one -- see the message in dma_alloc().
uint64 dma_largest_bytes(void);
uint64 dma_total_bytes(void);