From cd41ed1758f4842d7b78870385e16b579cc89c38 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sun, 25 Jan 2026 17:14:53 +0200 Subject: implement dmem semi-properly + Add tests and fix virtual mapping, this is enough to start writing proper userspace drivers --- src/dmem.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 19 deletions(-) (limited to 'src/dmem.c') diff --git a/src/dmem.c b/src/dmem.c index d21a9c5..6b98003 100644 --- a/src/dmem.c +++ b/src/dmem.c @@ -19,13 +19,8 @@ static struct mem_region_root post_ram = { 0 }; stat_t init_devmem(pm_t ram_base, pm_t ram_top) { - size_t pre_pages = __pages(ram_base); - size_t ram_pages = __pages(ram_top - ram_base); - /* -1 being the effective highest address possible */ - size_t post_pages = __pages(-1) - ram_pages - pre_pages; - - init_region(&pre_ram, 0, pre_pages, 0); - init_region(&post_ram, ram_top, post_pages, 0); + init_region(&pre_ram, 0, ram_base, 0); + init_region(&post_ram, ram_top, -1, 0); return OK; } @@ -54,39 +49,83 @@ vm_t alloc_devmem(struct tcb *t, pm_t start, size_t bytes, vmflags_t flags) if (!region) return NULL; - vm_t v = alloc_region(region, bytes, &bytes, flags); - if (!v) + /* 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; + } - if (map_fixed_region(t->proc.vmem, v, start, bytes, flags)) { + /* 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(region, v); + free_region(&t->uvmem.region, v); + free_region(region, p); return NULL; } return v; } -stat_t free_devmem(struct tcb *t, vm_t start) +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, start, &addr, NULL, NULL); + stat_vpage(t->proc.vmem, __addr(m->start), &addr, NULL, NULL); - struct mem_region_root *region = __select_region((pm_t)__pa(addr)); + /* 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); - struct mem_region *m = find_used_region(region, start); - if (!m) - return ERR_NF; - + /* 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_region(region, base); + 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; } -- cgit v1.3