aboutsummaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/debug.c4
-rw-r--r--src/tcb.c36
-rw-r--r--src/uapi/conf.c12
-rw-r--r--src/uapi/mem.c36
-rw-r--r--src/vmem.c131
5 files changed, 136 insertions, 83 deletions
diff --git a/src/debug.c b/src/debug.c
index d87588d..950f73b 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -455,7 +455,7 @@ static size_t __print_sign(ssize_t value, size_t flags)
* @return \see __integral_val().
*/
#define __integral_len(value, base, flags) __integral_val((value), (base), \
- (flags), false)
+ (flags), false)
/**
* Print integral value as string.
@@ -466,7 +466,7 @@ static size_t __print_sign(ssize_t value, size_t flags)
* @return \see __integral_val().
*/
#define __integral_print(value, base, flags) __integral_val((value), (base), \
- (flags), true)
+ (flags), true)
/**
* Print integral value.
diff --git a/src/tcb.c b/src/tcb.c
index 053ea6f..48f1d3b 100644
--- a/src/tcb.c
+++ b/src/tcb.c
@@ -123,11 +123,14 @@ struct tcb *create_thread(struct tcb *p)
assert(tcbs);
vm_t bottom = alloc_page(KERNEL_STACK_PAGE_ORDER);
+ if (!bottom)
+ return NULL;
+
/* move tcb to top of kernel stack, keeping alignment in check
* (hopefully) */
/** \todo check alignment */
- bottom = bottom + order_size(MM_O0) - sizeof(struct tcb);
- struct tcb *t = (struct tcb *)align_down(bottom, sizeof(long));
+ vm_t top = bottom + order_size(MM_O0) - sizeof(struct tcb);
+ struct tcb *t = (struct tcb *)align_down(top, sizeof(long));
memset(t, 0, sizeof(struct tcb));
id_t tid = __alloc_tid(t);
@@ -141,9 +144,19 @@ struct tcb *create_thread(struct tcb *p)
* structure, this works on riscv but in the event that other
* systems don't we can easily turn this into a clone_uvmem. */
t->proc.vmem = p->proc.vmem;
- } else {
- t->proc.vmem = create_vmem();
- init_uvmem(t, UVMEM_START, UVMEM_END);
+ }
+ else {
+ if (!(t->proc.vmem = create_vmem())) {
+ free_page(MM_O0, bottom);
+ return NULL;
+ }
+
+ if (init_uvmem(t)) {
+ destroy_vmem(t->proc.vmem);
+ free_page(MM_O0, bottom);
+ return NULL;
+ }
+
t->pid = t->tid;
t->rid = t->tid;
p = t;
@@ -151,7 +164,16 @@ struct tcb *create_thread(struct tcb *p)
t->eid = t->pid;
t->rid = p->rid;
- t->rpc.vmem = create_vmem();
+
+ if (!(t->rpc.vmem = create_vmem())) {
+ if (likely(p))
+ return NULL;
+
+ destroy_vmem(t->proc.vmem);
+ free_page(MM_O0, bottom);
+ return NULL;
+ }
+
setup_rpc_stack(t);
reference_proc(p);
@@ -191,7 +213,7 @@ struct tcb *create_proc(struct tcb *p)
/* create a new thread outside the current process */
struct tcb *n = create_thread(NULL);
if (!n)
- return 0;
+ return NULL;
if (p)
__copy_proc(p, n); /* we have a parent process i.e. fork */
diff --git a/src/uapi/conf.c b/src/uapi/conf.c
index 3a45d30..c8ec964 100644
--- a/src/uapi/conf.c
+++ b/src/uapi/conf.c
@@ -42,9 +42,10 @@ size_t rpc_stack_size()
*
* @param t Current tcb.
* @param param Parameter to read.
+ * @param d0 Optional data argument for parameter.
* @return \ref OK and parameter value.
*/
-SYSCALL_DEFINE1(conf_get)(struct tcb *t, sys_arg_t param)
+SYSCALL_DEFINE2(conf_get)(struct tcb *t, sys_arg_t param, sys_arg_t d0)
{
/* anyone can read any current parameter, I don't think they should be
* hidden. */
@@ -66,6 +67,15 @@ SYSCALL_DEFINE1(conf_get)(struct tcb *t, sys_arg_t param)
val = get_ram_size();
break;
+ case CONF_PAGE_SIZE:
+ if (d0 < 0 || d0 > max_order()) {
+ val = 0;
+ break;
+ }
+
+ val = order_size(d0);
+ break;
+
default:
return_args1(t, ERR_NF);
}
diff --git a/src/uapi/mem.c b/src/uapi/mem.c
index 49370fc..b4b2df8 100644
--- a/src/uapi/mem.c
+++ b/src/uapi/mem.c
@@ -24,10 +24,10 @@
SYSCALL_DEFINE2(req_mem)(struct tcb *t, sys_arg_t size, sys_arg_t flags)
{
struct tcb *r = get_cproc(t);
- vm_t start = 0;
flags = sanitize_uvflags(flags);
- if (!(start = alloc_uvmem(r, size, flags)))
- return_args1(t, ERR_OOMEM);
+ vm_t start = alloc_uvmem(r, size, flags);
+ if (ERR_CODE(start))
+ return_args1(t, start);
return_args2(t, OK, start);
}
@@ -47,10 +47,10 @@ SYSCALL_DEFINE3(req_fixmem)(struct tcb *t, sys_arg_t fixed, sys_arg_t size,
sys_arg_t flags)
{
struct tcb *r = get_cproc(t);
- vm_t start = 0;
flags = sanitize_uvflags(flags);
- if (!(start = alloc_fixed_uvmem(r, fixed, size, flags)))
- return_args1(t, ERR_OOMEM);
+ vm_t start = alloc_fixed_uvmem(r, fixed, size, flags);
+ if (ERR_CODE(start))
+ return_args1(t, start);
return_args2(t, OK, start);
}
@@ -97,10 +97,10 @@ SYSCALL_DEFINE3(req_pmem)(struct tcb *t, sys_arg_t paddr, sys_arg_t size,
* that keeps track of used regions outside of RAM. We'll see.
*/
struct tcb *r = get_cproc(t);
- vm_t start = 0;
flags = sanitize_uvflags(flags);
- if (!(start = alloc_devmem(r, paddr, size, flags)))
- return_args1(t, ERR_OOMEM);
+ vm_t start = alloc_devmem(r, paddr, size, flags);
+ if (ERR_CODE(start))
+ return_args1(t, start);
return_args2(t, OK, start);
}
@@ -127,8 +127,9 @@ SYSCALL_DEFINE2(req_page)(struct tcb *t, sys_arg_t size, sys_arg_t flags)
vm_t start = 0;
size_t asize = 0;
flags = sanitize_uvflags(flags);
- if (!(start = alloc_uvpage(r, size, flags, &addr, &asize)))
- return_args1(t, ERR_OOMEM);
+ start = alloc_uvpage(r, size, flags, &addr, &asize);
+ if (ERR_CODE(start))
+ return_args1(t, start);
return_args4(t, OK, start, addr, asize);
}
@@ -148,10 +149,10 @@ SYSCALL_DEFINE2(req_sharedmem)(struct tcb *t, sys_arg_t size, sys_arg_t flags)
if (!has_cap(c->caps, CAP_SHARED))
return_args1(t, ERR_PERM);
- vm_t start = 0;
flags = sanitize_uvflags(flags);
- if (!(start = alloc_shared_uvmem(c, size, flags)))
- return_args1(t, ERR_OOMEM);
+ vm_t start = alloc_shared_uvmem(c, size, flags);
+ if (ERR_CODE(start))
+ return_args1(t, start);
return_args3(t, OK, start, size);
}
@@ -178,10 +179,11 @@ SYSCALL_DEFINE3(ref_sharedmem)(struct tcb *t, sys_arg_t tid, sys_arg_t addr,
if (!r || zombie(r))
return_args1(t, ERR_INVAL);
- vm_t start = 0; size_t size = 0;
+ size_t size = 0;
flags = sanitize_uvflags(flags);
- if (!(start = ref_shared_uvmem(r, c, addr, flags)))
- return_args1(t, ERR_OOMEM);
+ vm_t start = ref_shared_uvmem(r, c, addr, flags);
+ if (ERR_CODE(start))
+ return_args1(t, start);
return_args3(t, OK, start, size);
}
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;