aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2021-12-30 20:48:26 +0200
committerKimplul <kimi.h.kuparinen@gmail.com>2021-12-30 20:48:26 +0200
commit6f052e236715846b1a14f9fe59f89346efb809e6 (patch)
treec1bbca5a063c428f65c46b34aaf4dbf47e61c213 /common
parent4f4d9fdc89c27aa3346467a24b621954f4564d3b (diff)
downloadkmi-6f052e236715846b1a14f9fe59f89346efb809e6.tar.gz
kmi-6f052e236715846b1a14f9fe59f89346efb809e6.zip
Add initial version of device memory mapping
Diffstat (limited to 'common')
-rw-r--r--common/dev.c89
-rw-r--r--common/elf.c48
-rw-r--r--common/pmem.c4
-rw-r--r--common/vmem.c57
4 files changed, 171 insertions, 27 deletions
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 <apos/dev.h>
+#include <apos/vmem.h>
+
+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 <apos/bytes.h>
#include <apos/string.h>
+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 <apos/pmem.h>
+#include <apos/dev.h>
#include <apos/debug.h>
#include <apos/initrd.h>
#include <apos/string.h> /* 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);
}