aboutsummaryrefslogtreecommitdiff
path: root/src/vmem.c
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-07-09 17:35:56 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-07-09 17:35:56 +0300
commit298636079d912d0936f8156a609fe74b839c547b (patch)
treee0c22ce0e7b7c8a29e3937616076e8fb327fde8b /src/vmem.c
parente134202611a50b358c147c92bfb8a7f443030b8b (diff)
downloadkmi-298636079d912d0936f8156a609fe74b839c547b.tar.gz
kmi-298636079d912d0936f8156a609fe74b839c547b.zip
allow mapping null page
+ User has to 'free' the 0 page before it becomes accessible to mapping. Probably worth noting that this is likely VERY niche and mainly concerns stuff like certain kinds of emulators that I'm still eons from implementing, but still. Unbacked pages can also be useful for some kinds of notifications, like 'if someone writes to this page, please report it to me with this ID' or whatever, I remember seeing some discussion about it somewhere but that's also not really relevant for the moment. Most significantly, at least with the current design, after the null page is freed it becomes available for use to regular req_mem() calls, so users should probably using locks around memory requests. That's probably a good idea anyway as internally the kernal has to lock the virtual memory, and without a scheduler it might cause threads to spin for a while in the kernel which is rather bad.
Diffstat (limited to 'src/vmem.c')
-rw-r--r--src/vmem.c131
1 files changed, 75 insertions, 56 deletions
diff --git a/src/vmem.c b/src/vmem.c
index 261ef42..52e7bd2 100644
--- a/src/vmem.c
+++ b/src/vmem.c
@@ -14,11 +14,23 @@
#include <kmi/vmem.h>
#include <arch/vmem.h>
-stat_t init_uvmem(struct tcb *t, vm_t base, vm_t top)
+stat_t init_uvmem(struct tcb *t)
{
t->uvmem.owner = t->tid;
t->uvmem.vmem = t->proc.vmem;
- return init_region(&t->uvmem.region, base, top);
+
+ stat_t ret = OK;
+ if ((ret = init_region(&t->uvmem.region, UVMEM_START, UVMEM_END)))
+ return ret;
+
+ /* if a user really wants to use the first page for something, they'll
+ * have to free it first, 'accepting' that no null-page is dangerous. */
+ size_t size = 0;
+ vm_t v = alloc_fixed_region(&t->uvmem.region,
+ UVMEM_START, BASE_PAGE_SIZE, &size,
+ MR_NONBACKED);
+ assert(v == UVMEM_START && size == BASE_PAGE_SIZE);
+ return OK;
}
/**
@@ -40,6 +52,12 @@ static stat_t __copy_mapped_region(struct tcb *d, struct tcb *s,
size_t size = end - start;
vm_t v = alloc_fixed_region(&d->uvmem.region, start, size, &size,
m->flags);
+ if (ERR_CODE(v))
+ return v;
+
+ if (is_set(m->flags, MR_NONBACKED))
+ return v;
+
assert(v == start);
/* note that we use uvmem.vmem instead of proc.vmem, this is just to
@@ -76,6 +94,8 @@ static stat_t __copy_shared_region(struct tcb *d, struct mem_region *m)
size_t size = end - start;
vm_t v = alloc_fixed_region(&d->uvmem.region, start, size, &size,
m->flags);
+ if (ERR_CODE(v))
+ return v;
assert(v == start);
stat_t res = clone_region(d->uvmem.vmem, s->uvmem.vmem, start, v, size,
@@ -107,8 +127,11 @@ static vm_t __clone_shared_region(struct tcb *d, struct tcb *s,
reference_proc(s);
size_t size = end - start;
- vm_t v = alloc_shared_region(&d->uvmem.region, size, &size, m->flags,
- s->rid);
+ vm_t v = alloc_shared_region(&d->uvmem.region, size, &size,
+ MR_NONBACKED | m->flags, s->rid);
+ if (ERR_CODE(v))
+ return v;
+
stat_t res = clone_region(d->uvmem.vmem, s->uvmem.vmem, start, v, size,
flags);
if (res == OK)
@@ -118,7 +141,7 @@ static vm_t __clone_shared_region(struct tcb *d, struct tcb *s,
unreference_proc(s);
free_region(&d->uvmem.region, v);
unmap_fixed_region(d->uvmem.vmem, v, size);
- return NULL;
+ return res;
}
/**
@@ -127,44 +150,23 @@ static vm_t __clone_shared_region(struct tcb *d, struct tcb *s,
* @param t Current thread.
* @param m Memory region to free.
*/
-static void __free_private_mapping(struct tcb *t, struct mem_region *m)
+static void __free_mapping(struct tcb *t, struct mem_region *m)
{
+ struct tcb *owner = get_tcb(m->pid);
+ if (owner)
+ unreference_proc(owner);
+
+ if (is_set(m->flags, MR_NONBACKED))
+ return;
+
pm_t start = __addr(m->start);
pm_t end = __addr(m->end);
size_t size = end - start;
- unmap_region(t->proc.vmem, start, size);
-}
-
-/**
- * Unmap shared region and free associated physical pages if they're not being
- * used by the other process.
- *
- * @param t Current thread.
- * @param m Memory region to free.
- */
-static void __free_shared_mapping(struct tcb *t, struct mem_region *m)
-{
- vm_t start = __addr(m->start);
- vm_t end = __addr(m->end);
- unreference_proc(get_tcb(m->pid));
-
- size_t bytes = end - start;
- unmap_fixed_region(t->proc.vmem, start, bytes);
-}
-
-/**
- * Convenience function for freeing mapped regions.
- *
- * @param t Thread to work in.
- * @param m Memory region to free.
- */
-static void __free_mapping(struct tcb *t, struct mem_region *m)
-{
- if (m->pid != 0)
- return __free_shared_mapping(t, m);
-
- return __free_private_mapping(t, m);
+ if (m->pid)
+ unmap_fixed_region(t->proc.vmem, start, size);
+ else
+ unmap_region(t->proc.vmem, start, size);
}
void clear_uvmem(struct tcb *t)
@@ -174,13 +176,11 @@ void clear_uvmem(struct tcb *t)
struct mem_region *m = find_closest_used_region(&t->uvmem.region, 0);
for (; m; m = m->next) {
- if (is_region_kept(m)) {
+ if (is_region_kept(m))
continue;
- }
- if (!is_set(m->flags, MR_USED)) {
+ if (!is_set(m->flags, MR_USED))
continue;
- }
__free_mapping(t, m);
free_known_region(&t->uvmem.region, m);
@@ -243,10 +243,14 @@ vm_t alloc_uvmem(struct tcb *t, size_t size, vmflags_t flags)
assert(t && is_proc(t));
const vm_t v = alloc_region(&t->uvmem.region, size, &size, flags);
- if (map_region(t->proc.vmem, v, size, max_order(), flags)) {
+ if (ERR_CODE(v))
+ return v;
+
+ stat_t ret = OK;
+ if ((ret = map_region(t->proc.vmem, v, size, max_order(), flags))) {
unmap_region(t->proc.vmem, v, size);
free_region(&t->uvmem.region, v);
- return NULL;
+ return ret;
}
return v;
@@ -258,10 +262,14 @@ vm_t alloc_fixed_uvmem(struct tcb *t, vm_t start, size_t size, vmflags_t flags)
const vm_t v = alloc_fixed_region(&t->uvmem.region, start, size, &size,
flags);
- if (map_region(t->proc.vmem, v, size, max_order(), flags)) {
+ if (ERR_CODE(v))
+ return v;
+
+ stat_t ret = OK;
+ if ((ret = map_region(t->proc.vmem, v, size, max_order(), flags))) {
unmap_region(t->proc.vmem, v, size);
free_region(&t->uvmem.region, v);
- return NULL;
+ return ret;
}
return v;
@@ -269,14 +277,20 @@ vm_t alloc_fixed_uvmem(struct tcb *t, vm_t start, size_t size, vmflags_t flags)
vm_t map_fixed_uvmem(struct tcb *t, pm_t start, size_t size, vmflags_t flags)
{
+ assert(is_aligned(start, BASE_PAGE_SIZE));
+
const vm_t v = alloc_region(&t->uvmem.region, size, &size, flags);
- if (map_fixed_region(t->proc.vmem, v, start, size, flags)) {
+ if (ERR_CODE(v))
+ return v;
+
+ stat_t ret = OK;
+ if ((ret = map_fixed_region(t->proc.vmem, v, start, size, flags))) {
unmap_region(t->proc.vmem, v, size);
free_region(&t->uvmem.region, v);
- return NULL;
+ return ret;
}
- return v + (start % BASE_PAGE_SIZE);
+ return v;
}
vm_t alloc_uvpage(struct tcb *t, size_t size, vmflags_t flags, pm_t *startp,
@@ -286,19 +300,20 @@ vm_t alloc_uvpage(struct tcb *t, size_t size, vmflags_t flags, pm_t *startp,
size = order_size(order);
const vm_t v = alloc_region(&t->uvmem.region, size, &size, flags);
- if (!v)
- return 0;
+ if (ERR_CODE(v))
+ return v;
pm_t start = alloc_page(order);
if (!start) {
free_region(&t->uvmem.region, size);
- return 0;
+ return ERR_OOMEM;
}
- if (map_fixed_region(t->proc.vmem, v, start, size, flags)) {
+ stat_t ret = OK;
+ if ((ret = map_fixed_region(t->proc.vmem, v, start, size, flags))) {
unmap_region(t->proc.vmem, v, size);
free_region(&t->uvmem.region, v);
- return NULL;
+ return ret;
}
*startp = (pm_t)__pa(start);
@@ -313,11 +328,15 @@ vm_t alloc_shared_uvmem(struct tcb *s, size_t size, vmflags_t flags)
assert(s && is_proc(s));
const vm_t v = alloc_region(&s->uvmem.region, size, &size,
MR_SHARED | flags);
+ if (ERR_CODE(v))
+ return v;
+
/* use base pages to make clone more likely to succeed */
- if (map_region(s->proc.vmem, v, size, BASE_PAGE, flags)) {
+ stat_t ret = OK;
+ if ((ret = map_region(s->proc.vmem, v, size, BASE_PAGE, flags))) {
unmap_region(s->proc.vmem, v, size);
free_region(&s->uvmem.region, v);
- return NULL;
+ return ret;
}
return v;