/* SPDX-License-Identifier: copyleft-next-0.3.1 */ /* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ /** * @file dmem.c * Handle device memory, i.e. anything outside the physical RAM. * * \todo Handle NUMA. */ #include #include /** Region before RAM. */ static struct mem_region_root pre_ram = { 0 }; /** Region after RAM. */ static struct mem_region_root post_ram = { 0 }; stat_t init_devmem(pm_t ram_base, pm_t ram_top) { init_region(&pre_ram, 0, ram_base, 0); init_region(&post_ram, ram_top, -1, 0); return OK; } /** * Select between the area before RAM or after RAM, depending on \p addr. * * @param addr Physical address of device. * @return Corresponding device memory region or NULL if \p addr is within RAM. */ static struct mem_region_root *__select_region(pm_t addr) { if (addr < get_ram_base()) return &pre_ram; if (addr > (get_ram_base() + get_ram_size())) return &post_ram; return NULL; } vm_t alloc_devmem(struct tcb *t, pm_t start, size_t bytes, vmflags_t flags) { assert(t && is_proc(t)); struct mem_region_root *region = __select_region(start); if (!region) return NULL; /* carve out physical region (vm_t is misleading, this region is * physical memory) */ vm_t p = alloc_fixed_region(region, start, bytes, &bytes, flags); if (!p) return NULL; /* carve out virtual region */ vm_t v = alloc_region(&t->uvmem.region, bytes, &bytes, flags | MR_DEV); if (!v) { free_region(region, p); return NULL; } /* convert from literal physical address to direct mapping address, * which map_* expects (kind of annoying to deal with, admittedly, is * there maybe a better way to handle these things?) */ p = (pm_t)__va(p); if (map_fixed_region(t->proc.vmem, v, p, bytes, flags)) { unmap_region(t->proc.vmem, v, bytes); free_region(&t->uvmem.region, v); free_region(region, p); return NULL; } return v; } stat_t free_known_devmem(struct tcb *t, struct mem_region *m) { assert(t && is_proc(t)); /* ensure region is a device memory region and is currently not shared */ assert(is_set(m->flags, MR_DEV)); assert(m->pid != 0 || m->refcount == 1); pm_t addr = 0; stat_vpage(t->proc.vmem, __addr(m->start), &addr, NULL, NULL); /* free physical device memory */ pm_t p = (pm_t)__pa(addr); struct mem_region_root *region = __select_region(p); if (!region) return ERR_INVAL; free_region(region, p); /* free virtual allocation associated with device mem */ vm_t base = __addr(m->start); vm_t end = __addr(m->end); /* free virtual memory associated with physical mapping */ size_t size = end - base; unmap_fixed_region(t->proc.vmem, base, size); free_known_region(&t->uvmem.region, m); return OK; } stat_t free_devmem(struct tcb *t, vm_t start) { assert(t && is_proc(t)); struct mem_region *m = find_used_region(&t->uvmem.region, start); if (!m) return ERR_NF; /* not a devmem region */ if (!is_set(m->flags, MR_DEV)) return ERR_INVAL; /* mapping is shared, can't free right now */ if (m->pid == 0 && m->refcount > 1) return ERR_INVAL; stat_t ret = free_known_devmem(t, m); if (ret) return ret; free_known_region(&t->uvmem.region, m); return OK; }