From 6f052e236715846b1a14f9fe59f89346efb809e6 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Thu, 30 Dec 2021 20:48:26 +0200 Subject: Add initial version of device memory mapping --- TODO.txt | 10 ++++++ common/dev.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++ common/elf.c | 48 +++++++++++++++++++---------- common/pmem.c | 4 +++ common/vmem.c | 57 +++++++++++++++++++++++++++------- include/apos/dev.h | 16 ++++++++++ include/apos/vmem.h | 17 +++++++++- 7 files changed, 213 insertions(+), 28 deletions(-) create mode 100644 common/dev.c create mode 100644 include/apos/dev.h diff --git a/TODO.txt b/TODO.txt index a552121..086df92 100644 --- a/TODO.txt +++ b/TODO.txt @@ -19,3 +19,13 @@ well + check elf.c "skip while testing" comment, remove when sufficiently pleased with the system. Christ, what a terrible way to develop software :D + Start working on syscalls etc. + ++ NUMA is probably fairly easy to setup, just add more pmaps I guess, although +it does sort of mess with the current device memory setup, which assumes there +is only one memory region and everything below/above it is device memory +regions. Not entirely sure how to handle that situation, i.e. is it safe to +assume all devices are below the first NUMA node? No clue. + ++ The dev interface can be abused, if a program decides to spam through all +memory addresses the system will probably run out of memory. Not sure if it's +worh doing anything to. diff --git a/common/dev.c b/common/dev.c new file mode 100644 index 0000000..b89fe3a --- /dev/null +++ b/common/dev.c @@ -0,0 +1,89 @@ +#include +#include + +static struct sp_reg_root pre_ram = {0}; +static struct sp_reg_root post_ram = {0}; +pm_t __pre_base = 0; +pm_t __pre_top = 0; +pm_t __post_base = 0; +pm_t __post_top = 0; + +void init_devmem(pm_t ram_base, pm_t ram_top) +{ + pm_t mem_top = (pm_t)-1; + + __pre_base = 0; + __pre_top = ram_base - 1; + + __post_base = ram_top; + __post_top = mem_top; + + size_t pre_pages = __pages(__pre_top); + size_t post_pages = __pages(__post_top) - __pages(__post_base); + + sp_mem_init(&pre_ram, __pre_base, pre_pages); + sp_mem_init(&post_ram, __post_base, post_pages); +} + +static int dev_alloc_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, uint8_t flags, enum mm_order order) +{ + map_vmem(b, *offset, vaddr, flags, order); + *offset += __o_size(order); + return 0; +} + +static int dev_free_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, uint8_t flags, enum mm_order order) +{ + UNUSED(offset); UNUSED(flags); + pm_t paddr = 0; + enum mm_order v_order = 0; + stat_vmem(b, vaddr, &paddr, &v_order, 0); + if(order != v_order) + return -1; + + unmap_vmem(b, vaddr); + return 0; +} + +vm_t alloc_devmem(struct tcb *t, pm_t dev_start, size_t bytes, uint8_t flags) +{ + vm_t region = 0; + if(dev_start < __pre_top) + region = alloc_region(&pre_ram, bytes, 0); + + if(dev_start > __post_base) + region = alloc_region(&post_ram, bytes, 0); + + if(!region) + return 0; + + return map_fill_region(t->b_r, &dev_alloc_wrapper, dev_start, region, bytes, flags); +} + +void free_devmem(struct tcb *t, vm_t dev_start) +{ + pm_t dev_paddr = 0; + stat_vmem(t->b_r, dev_start, &dev_paddr, 0, 0); + + if(dev_paddr >= __pre_top && dev_paddr <= __post_base) + return; + + struct sp_mem *m = 0; + if(dev_paddr < __pre_top) + m = sp_used_find(&pre_ram, dev_paddr); + + if(dev_paddr > __post_base) + m = sp_used_find(&post_ram, dev_paddr); + + if(!m) + return; + + size_t region_size = __addr(m->end - m->start); + map_fill_region(t->b_r, &dev_free_wrapper, dev_paddr, dev_start, region_size, 0); + + if(dev_paddr < __pre_top) + free_region(&pre_ram, dev_paddr); + + if(dev_paddr > __post_base) + free_region(&post_ram, dev_paddr); +} diff --git a/common/elf.c b/common/elf.c index aab00d1..4378d52 100644 --- a/common/elf.c +++ b/common/elf.c @@ -3,9 +3,31 @@ #include #include +static uint8_t __elf_to_uvflags(uint8_t elf_flags) +{ + uint8_t uvflags = VM_V | VM_U; + if(elf_flags & PF_X) + uvflags |= VM_X; + + if(elf_flags & PF_W) + uvflags |= VM_W; + + if(elf_flags & PF_R) + uvflags |= VM_R; + + return uvflags; +} + +/* useful bit of info: all segments are sorted in ascending order of p_vaddr */ static void __map_exec(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart, size_t phnum, size_t phsize) { /* TODO: take alignment into consideration? */ + /* TODO: take overlapping memory regions into account, probably mostly + * by keeping track of previously allocated area and seeing if the + * segment fits into it */ + /* TODO: check if p_memsz is larger than p_filesz, the segment should be + * filled with zeroes. */ + /* TODO: in general, make this a low more clean. */ vm_t runner = phstart; for(size_t i = 0; i < phnum; ++i, runner += phsize){ if(program_header_prop(ei_c, runner, p_type) != PT_LOAD) @@ -14,41 +36,35 @@ static void __map_exec(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart, size vm_t va = program_header_prop(ei_c, runner, p_vaddr); size_t vsz = program_header_prop(ei_c, runner, p_memsz); - vm_t start = 0; - if(!(start = alloc_fixed_region(&t->sp_r, va, vsz, &vsz))) + vm_t start = alloc_fixed_region(&t->sp_r, va, vsz, &vsz); + if(!start) return; /* out of memory or something */ - uint8_t vflags = VM_V | VM_U; - uint8_t bflags = program_header_prop(ei_c, runner, p_flags); - if(bflags & PF_X) - vflags |= VM_X; - - if(bflags & PF_W) - vflags |= VM_W; - - if(bflags & PF_R) - vflags |= VM_R; + uint8_t elf_flags = program_header_prop(ei_c, runner, p_flags); + uint8_t uvflags = __elf_to_uvflags(elf_flags); - map_fill_region(t->b_r, start, vsz, VM_V | VM_X | VM_R | VM_W | VM_U); + map_allocd_region(t->b_r, start, vsz, VM_V | VM_X | VM_R | VM_W | VM_U); vm_t vo = bin + program_header_prop(ei_c, runner, p_offset); vm_t vfz = program_header_prop(ei_c, runner, p_filesz); memcpy((void *)va, (void *)vo, vfz); /* skip while testing - * TODO: also fix, this fixes only the first region. Create new + * TODO: also fix, this modifies only the first region. Create new * function? * pm_t paddr = 0; stat_vmem(t->b_r, va, &paddr, 0, 0); - mod_vmem(t->b_r, va, paddr, vflags); + mod_vmem(t->b_r, va, paddr, uvflags); */ } } static vm_t __map_dyn(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart, size_t phnum, size_t phsize) { - /* TODO */ + /* TODO: this path should only be taken when no PT_INTERP is defined, as + * making sure ld is loaded should be done in userspace. Maybe a bit + * hacky, I know.*/ } static vm_t __prepare_proc(struct tcb *t, uint8_t ei_c, vm_t elf) diff --git a/common/pmem.c b/common/pmem.c index d9cdd50..c919204 100644 --- a/common/pmem.c +++ b/common/pmem.c @@ -1,4 +1,5 @@ #include +#include #include #include #include /* memset */ @@ -72,6 +73,7 @@ static void __mark_free(mm_node_t * op, pnum_t pnum, enum mm_order tgt, __clear_nbit(o->full[__o_container(idx)], __o_bit(idx)); } +/* this could probably use an int for status, but eh */ void free_page(enum mm_order order, pm_t paddr) { for (size_t i = MM_O0; i <= __mm_max_order; ++i) { @@ -443,4 +445,6 @@ void init_pmem(void *fdt) /* mark reserved mem */ mark_reserved_mem(fdt); + + init_devmem((pm_t)__pa(ram_base), (pm_t)__pa(ram_base + ram_size)); } diff --git a/common/vmem.c b/common/vmem.c index 9ff3b96..7fe0ab5 100644 --- a/common/vmem.c +++ b/common/vmem.c @@ -137,7 +137,7 @@ void sp_mem_destroy(struct sp_reg_root *r) * eh, it's not a massive thing I guess, maybe the code could be a bit quicker * but I mean 10 000 000 memory allocations in 20 s is good enough for now * */ -static struct sp_mem *sp_used_find(struct sp_reg_root *r, vm_t start) +struct sp_mem *sp_used_find(struct sp_reg_root *r, vm_t start) { struct sp_node *n = sp_root(r->used_regions); while(n){ @@ -388,7 +388,7 @@ static void sp_mem_try_coalesce(struct sp_reg_root *r, struct sp_mem *m) void free_region(struct sp_reg_root *r, vm_t start) { /* addr not aligned to page boundary, corrupted or incorrect pointer */ - if(start != __addr(__page(start))) + if(!aligned(start, BASE_PAGE_SIZE)) return; struct sp_mem *m = sp_used_find(r, __page(start)); @@ -418,9 +418,10 @@ size_t uvmem_size() * NOTE: not actually optimal, this doesn't bother to go through possible * permutations etc. which would be slow and I don't want to implement it. */ -vm_t map_fill_region(struct vm_branch *b, vm_t start, size_t bytes, uint8_t flags) +vm_t map_fill_region(struct vm_branch *b, + int (*vmem_handler)(struct vm_branch *, pm_t *, vm_t, uint8_t, enum mm_order), + pm_t offset, vm_t start, size_t bytes, uint8_t flags) { - pm_t offset = 0; pm_t runner = __page(start); size_t pages = __pages(bytes); enum mm_order top = __mm_max_order; @@ -439,11 +440,13 @@ vm_t map_fill_region(struct vm_branch *b, vm_t start, size_t bytes, uint8_t flag continue; while(pages >= o_pages){ - offset = alloc_page(top, offset); - if(!offset) + int res = vmem_handler(b, &offset, __addr(runner), flags, top); + if(res > 0) break; - map_vmem(b, offset, __addr(runner), flags, top); + if(res < 0) + return 0; + pages -= o_pages; runner += o_pages; } @@ -452,19 +455,51 @@ vm_t map_fill_region(struct vm_branch *b, vm_t start, size_t bytes, uint8_t flag return start; } +int alloc_mem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, uint8_t flags, enum mm_order order) +{ + *offset = alloc_page(order, *offset); + if(!*offset) + return 1; /* try again */ + + map_vmem(b, *offset, vaddr, flags, order); + return 0; +} + +int free_mem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, uint8_t flags, enum mm_order order) +{ + UNUSED(flags); UNUSED(offset); + + pm_t paddr = 0; + enum mm_order v_order = 0; + stat_vmem(b, vaddr, &paddr, &v_order, 0); + if(order != v_order) + return -1; + + unmap_vmem(b, vaddr); + free_page(order, paddr); + return 0; +} + vm_t alloc_uvmem(struct tcb *t, size_t size, uint8_t flags) { vm_t v = alloc_region(&t->sp_r, size, &size); - return map_fill_region(t->b_r, v, size, flags); + return map_allocd_region(t->b_r, v, size, flags); } vm_t alloc_fixed_uvmem(struct tcb *t, vm_t start, size_t size, uint8_t flags) { vm_t v = alloc_fixed_region(&t->sp_r, start, size, &size); - return map_fill_region(t->b_r, v, size, flags); + return map_allocd_region(t->b_r, v, size, flags); } -void free_uvmem(struct tcb *t, vm_t a) +void free_uvmem(struct tcb *t, vm_t va) { - free_region(&t->sp_r, a); + struct sp_mem *m = sp_used_find(&t->sp_r, va); + if(!m) + return; + + pm_t pa = __addr(m->end - m->start); + + free_region(&t->sp_r, va); + unmap_freed_region(t->b_r, va, pa); } diff --git a/include/apos/dev.h b/include/apos/dev.h new file mode 100644 index 0000000..1e710a1 --- /dev/null +++ b/include/apos/dev.h @@ -0,0 +1,16 @@ +#ifndef APOS_DEV_H +#define APOS_DEV_H + +#include +#include + +extern pm_t __pre_base; +extern pm_t __pre_top; +extern pm_t __post_base; +extern pm_t __post_top; + +void init_devmem(pm_t ram_base, pm_t ram_top); +vm_t alloc_devmem(struct tcb *t, pm_t dev_start, size_t bytes, uint8_t flags); +void free_devmem(struct tcb *t, vm_t dev_start); + +#endif /* APOS_DEV_H */ diff --git a/include/apos/vmem.h b/include/apos/vmem.h index 87a3aa6..8057631 100644 --- a/include/apos/vmem.h +++ b/include/apos/vmem.h @@ -43,6 +43,7 @@ void map_vmem(struct vm_branch *branch, pm_t paddr, vm_t vaddr, uint8_t flags, enum mm_order order); void unmap_vmem(struct vm_branch *branch, vm_t vaddr); + int mod_vmem(struct vm_branch *branch, vm_t vaddr, pm_t paddr, uint8_t flags); int stat_vmem(struct vm_branch *branch, vm_t vaddr, pm_t *paddr, enum mm_order *order, uint8_t *flags); @@ -66,9 +67,23 @@ vm_t alloc_uvmem(struct tcb *r, size_t size, uint8_t flags); vm_t alloc_fixed_uvmem(struct tcb *r, vm_t start, size_t size, uint8_t flags); void free_uvmem(struct tcb *r, vm_t a); -vm_t map_fill_region(struct vm_branch *b, vm_t start, size_t bytes, uint8_t flags); +vm_t map_fill_region(struct vm_branch *b, + int (*vmem_handler)(struct vm_branch *, pm_t *, vm_t, uint8_t, enum mm_order), + pm_t offset, vm_t start, size_t bytes, uint8_t flags); + +#define map_allocd_region(b, start, bytes, flags)\ + map_fill_region(b, &alloc_mem_wrapper, 0, start, bytes, flags) + +#define unmap_freed_region(b, start, bytes)\ + map_fill_region(b, &free_mem_wrapper, 0, start, bytes, 0) + +int alloc_mem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, uint8_t flags, enum mm_order order); +int free_mem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, uint8_t flags, enum mm_order order); size_t uvmem_size(); void set_uvmem_size(size_t s); +/* not entirely sure if this is clean enough, but it'll do for now. */ +struct sp_mem *sp_used_find(struct sp_reg_root *r, vm_t start); + #endif /* APOS_VMEM_H */ -- cgit v1.3