aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/riscv64/asm/asm-offsets.c3
-rw-r--r--arch/riscv64/conf/init.c46
-rw-r--r--arch/riscv64/include/tcb.h4
-rw-r--r--arch/riscv64/kernel/proc.c14
-rw-r--r--arch/riscv64/kernel/vmem.c85
-rw-r--r--common/tcb.c39
-rw-r--r--common/uapi/ipc.c5
-rw-r--r--common/vmem.c5
-rw-r--r--docs/doxygen.conf2
-rw-r--r--include/apos/tcb.h14
-rw-r--r--include/arch/vmem.h5
11 files changed, 158 insertions, 64 deletions
diff --git a/arch/riscv64/asm/asm-offsets.c b/arch/riscv64/asm/asm-offsets.c
index 1239489..731dd4a 100644
--- a/arch/riscv64/asm/asm-offsets.c
+++ b/arch/riscv64/asm/asm-offsets.c
@@ -86,6 +86,9 @@ void asm_offsets()
OFFSETOF(exec, struct tcb);
OFFSETOF(regs, struct tcb);
+ /* At the moment tcbd is just a single register slot, so this works, but
+ * if it's expanded in the future I'll need to figure out a way to
+ * target specific substructure members. */
OFFSETOF(tcbd, struct tcb);
SIZEOF(tcb, struct tcb);
}
diff --git a/arch/riscv64/conf/init.c b/arch/riscv64/conf/init.c
index 438bff4..de9814e 100644
--- a/arch/riscv64/conf/init.c
+++ b/arch/riscv64/conf/init.c
@@ -1,16 +1,25 @@
-/* This file is allowed to be undocumented as it is only temporarily in this
- * tree. At some point in the (hopefully) near future, I intend to move the
+/**
+ * Header for silencing scripts/warn-undocumented
+ *
+ * @file init.c
+ *
+ * Test init program.
+ *
+ * This file is only temporarily in this tree.
+ * At some point in the (hopefully) near future, I intend to move the
* kernel code and initrd generation stuff into separate repositories, to make
* things easier for myself. For now though, this is good enough.
+ *
+ * Compile with
+ * riscv64-unknown-elf-gcc -ffreestanding -nostdlib
+ *
+ * Create initrd with
+ * echo init | cpio -H newc -o > initrd
*/
-/* compile with riscv64-unknown-elf-gcc -ffreestanding -nostdlib */
-/* create initrd with echo init | cpio -H newc -o > initrd */
#include <stdint.h>
#include "../../../include/apos/syscalls.h"
-#define CLOBBER_LIST "a0", "a1", "a2", "a3", "a4", "a5"
-
struct sys_ret {
long a0, a1, a2, a3, a4, a5;
};
@@ -25,8 +34,10 @@ struct sys_ret ecall(struct sys_ret s)
register long a5 asm ("a5") = s.a5;
asm volatile ("ecall"
- : "=r"(a0), "=r"(a1), "=r"(a2), "=r"(a3), "=r"(a4), "=r"(a5)
- : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5));
+ : "=r" (a0), "=r" (a1), "=r" (a2), "=r" (a3), "=r" (a4),
+ "=r" (a5)
+ : "r" (a0), "r" (a1), "r" (a2), "r" (a3), "r" (a4),
+ "r" (a5));
return (struct sys_ret){a0, a1, a2, a3, a4, a5};
}
@@ -134,6 +145,7 @@ static void sys_ipc_server(void *f)
print_value("ipc_server() failed with error ", r.a0);
}
+/** Helper for ipc arguments/return values. */
struct ipc_args {
long a0, a1, a2, a3;
};
@@ -141,11 +153,11 @@ struct ipc_args {
static struct ipc_args sys_ipc_req(long tid, long d0, long d1, long d2, long d3)
{
struct sys_ret r = {.a0 = SYS_IPC_REQ,
- .a1 = tid,
- .a2 = d0,
- .a3 = d1,
- .a4 = d2,
- .a5 = d3};
+ .a1 = tid,
+ .a2 = d0,
+ .a3 = d1,
+ .a4 = d2,
+ .a5 = d3};
r = ecall(r);
@@ -158,10 +170,10 @@ static struct ipc_args sys_ipc_req(long tid, long d0, long d1, long d2, long d3)
static void sys_ipc_resp(long d0, long d1, long d2, long d3)
{
struct sys_ret r = {.a0 = SYS_IPC_RESP,
- .a1 = d0,
- .a2 = d1,
- .a3 = d2,
- .a4 = d3};
+ .a1 = d0,
+ .a2 = d1,
+ .a3 = d2,
+ .a4 = d3};
ecall(r);
}
diff --git a/arch/riscv64/include/tcb.h b/arch/riscv64/include/tcb.h
index 829ce6a..f3e4cd0 100644
--- a/arch/riscv64/include/tcb.h
+++ b/arch/riscv64/include/tcb.h
@@ -11,11 +11,9 @@
/**
* riscv-specific thread handling stuff.
- *
- * Empty for now, but should probably be filled with stuff like register
- * saving of something
*/
struct arch_tcbd {
+ /** Extra scratch register. */
long scratch;
};
diff --git a/arch/riscv64/kernel/proc.c b/arch/riscv64/kernel/proc.c
index 4180bf8..1990523 100644
--- a/arch/riscv64/kernel/proc.c
+++ b/arch/riscv64/kernel/proc.c
@@ -62,20 +62,6 @@ vm_t get_stack(struct tcb *t)
return r->sp;
}
-void save_regs(struct tcb *t, void *p)
-{
- struct riscv_regs *r = (struct riscv_regs *)(t->regs) - 1;
- struct riscv_regs *rp = (struct riscv_regs *)(p) - 1;
- *rp = *r;
-}
-
-void load_regs(void *p, struct tcb *t)
-{
- struct riscv_regs *r = (struct riscv_regs *)(t->regs) - 1;
- struct riscv_regs *rp = (struct riscv_regs *)(p) - 1;
- *r = *rp;
-}
-
void clone_regs(struct tcb *d, struct tcb *s)
{
struct riscv_regs *rd = (struct riscv_regs *)(d->regs) - 1;
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c
index b24be83..547cbfc 100644
--- a/arch/riscv64/kernel/vmem.c
+++ b/arch/riscv64/kernel/vmem.c
@@ -89,8 +89,48 @@
*/
#define is_branch(pte) (is_active(pte) && !(pte_flags(pte) & ~VM_V))
-#define GRAVESTONE 2
+/**
+ * Gravestone marker.
+ *
+ * Riscv allows us to have arbitrary data in page entries, as long as they're
+ * not marked active (VM_V) the content is ignored. Here we use this to our
+ * advantage by differentiating between empty entries (NULL) and filler entries
+ * (GRAVESTONE).
+ *
+ * A gravestone tells us that somewhere above it (= higher index) there is an
+ * active entry. This is useful mainly in \ref clone_uvmem(), where we can stop
+ * copying data as soon as we hit an empty entry. I expect typical programs to
+ * generally have most active entries in relatively low addresses, and allowing
+ * us to skip copying 'obvious' entries is way quicker than copying the whole
+ * 2048 byte user virtual memory.
+ *
+ * Current optimisations also include setting the uvmem to stop on an 8-page
+ * boundary, allowing \ref clone_uvmem() to work in eight page increments for a
+ * bit of extra speed. Gravestones are only applied to userspace virtual memory,
+ * that is kernel and rpc memory regions are ignored.
+ *
+ * Example of how stuff should look like:
+ *
+ * Startin with page entries:
+ * 1 2 3 4 0 0 0 ...
+ *
+ * Mapping a page:
+ * 1 2 3 4 0 5 0 ...
+ *
+ * Adding gravestones:
+ * 1 2 3 4 G 5 0 ...
+ *
+ * More testing is probably necessary, as the init tests program doesn't really
+ * excercise the mapping utilities.
+ */
+#define GRAVESTONE VM_G
+/**
+ * Check if pte is unused, i.e. either a gravestone or empty.
+ *
+ * @param b pte to check.
+ * @return \ref true if \p b is unused.
+ */
static bool __unused(pm_t b)
{
return b == GRAVESTONE || b == NULL;
@@ -228,12 +268,21 @@ static void __destroy_branch(struct vmem *b)
free_page(MM_KPAGE, (pm_t)__pa(b));
}
-static void add_graves(struct vmem *branch, size_t idx)
+/**
+ * Add graves if necessary.
+ *
+ * Checks that the index is within user virtual memory. If it is, change all
+ * NULL-entries to gravestones at lower addresses than \p idx.
+ *
+ * @param branch Top level branch to add graves to.
+ * @param idx Index of new entry just added.
+ */
+static void __add_graves(struct vmem *branch, size_t idx)
{
if (idx >= CSTACK_PAGE)
return;
- for (size_t i = idx; i < CSTACK_PAGE; ++i) {
+ for (ssize_t i = idx - 1; i >= 0; --i) {
if (!__unused((pm_t)branch->leaf[i]))
return;
@@ -265,20 +314,30 @@ stat_t map_vpage(struct vmem *branch, pm_t paddr, vm_t vaddr, vmflags_t flags,
branch->leaf[idx] =
(struct vmem *)to_pte((pm_t)__pa(paddr), vp_flags(flags));
- add_graves(root, vm_to_index(vaddr, __mm_max_order));
+ __add_graves(root, vm_to_index(vaddr, __mm_max_order));
return top == __mm_max_order ? INFO_SEFF : OK;
}
-static void remove_graves(struct vmem *branch, size_t idx)
+/**
+ * Remove graves if possible.
+ *
+ * Checks if \p idx is in user virtual memory. If it is, check if the entry at
+ * \p idx was the top page and was turned into a gravestone. If it was, start
+ * removing gravestoned until we hit the next top.
+ *
+ * @param branch Top level branch to remove gravestones in.
+ * @param idx Index of just unmapped page at the top level.
+ */
+static void __remove_graves(struct vmem *branch, size_t idx)
{
- if (idx > CSTACK_PAGE)
+ if (idx >= CSTACK_PAGE)
return;
- if (__unused((pm_t)branch->leaf[idx + 1]))
+ if ((pm_t)branch->leaf[idx + 1] != NULL)
return;
- for (size_t i = idx; i < CSTACK_PAGE; ++i) {
- if (!__unused((pm_t)branch->leaf[i]))
+ for (ssize_t i = idx; i >= 0; --i) {
+ if ((pm_t)branch->leaf[i] != GRAVESTONE)
return;
branch->leaf[i] = NULL;
@@ -290,7 +349,7 @@ stat_t unmap_vpage(struct vmem *branch, vm_t vaddr)
pm_t *pte = __find_vmem(branch, vaddr, 0);
if (pte) {
*pte = GRAVESTONE;
- remove_graves(branch, vm_to_index(vaddr, __mm_max_order));
+ __remove_graves(branch, vm_to_index(vaddr, __mm_max_order));
return OK;
}
@@ -385,12 +444,6 @@ vm_t setup_kernel_io(struct vmem *b, vm_t paddr)
}
#endif
-/* something of an optimisation, letting the compiler know which parts to copy
- * however it sees best */
-struct uvmem_sv39_map {
- struct vmem *leaf[CSTACK_PAGE - 1];
-};
-
void clone_uvmem(struct vmem *r, struct vmem *b)
{
size_t i = 0;
diff --git a/common/tcb.c b/common/tcb.c
index 201b15e..5121940 100644
--- a/common/tcb.c
+++ b/common/tcb.c
@@ -149,6 +149,9 @@ struct tcb *create_thread(struct tcb *p)
if (likely(p)) {
t->pid = p->pid;
+ /** @todo I'm assuming two threads can share the same vmem
+ * 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 {
init_uvmem(t, UVMEM_START, UVMEM_END);
@@ -259,23 +262,24 @@ stat_t destroy_proc(struct tcb *p)
*
* @param name name of function to define.
* @param type Field name of type \c tcb_ctx.
+ * @param root Root \ref tcb_ctx to attach to.
*/
-#define DEFINE_ATTACH(name, type) \
+#define DEFINE_ATTACH(name, type, root) \
stat_t name(struct tcb *r, struct tcb *t) \
{ \
hard_assert(r != t, ERR_INVAL); \
- struct tcb *next = r->type.next; \
+ struct tcb *next = r->root.next; \
t->type.next = next; \
\
if (next) { next->type.prev = t; } \
\
t->type.prev = r; \
- r->type.next = t; \
+ r->root.next = t; \
return OK; \
}
-DEFINE_ATTACH(attach_rpc, rpc);
-DEFINE_ATTACH(attach_proc, proc);
+DEFINE_ATTACH(attach_rpc, rpc, server);
+DEFINE_ATTACH(attach_proc, proc, proc);
/**
* Convenience marco for defining function to detach a thread from either process
@@ -283,8 +287,9 @@ DEFINE_ATTACH(attach_proc, proc);
*
* @param name name of function to define.
* @param type Field name of type \c tcb_ctx.
+ * @param root Root \ref tcb_ctx to detach from.
*/
-#define DEFINE_DETACH(name, type) \
+#define DEFINE_DETACH(name, type, root) \
stat_t name(struct tcb *r, struct tcb *t) \
{ \
MAYBE_UNUSED(r); \
@@ -298,8 +303,8 @@ DEFINE_ATTACH(attach_proc, proc);
return OK; \
}
-DEFINE_DETACH(detach_rpc, rpc);
-DEFINE_DETACH(detach_proc, proc);
+DEFINE_DETACH(detach_rpc, rpc, server);
+DEFINE_DETACH(detach_proc, proc, proc);
/* weak to allow optimisation on risc-v, but provide fallback for future */
__weak struct tcb *cur_tcb()
@@ -402,18 +407,32 @@ static void mark_rpc_accessible(struct tcb *t, vm_t start, vm_t end)
set_vpage_flags(t->rpc.vmem, start + pages * page_size, VM_U);
}
+/** Structure for maintaingin the required context data for an rpc call. */
struct call_ctx {
+ /** Execution continuation point. */
vm_t exec;
+
+ /** Register save area. */
vm_t regs;
+
+ /** Position in rpc stack. */
vm_t rpc_stack;
- id_t eid, pid;
+
+ /** Effective process ID. */
+ id_t eid;
+
+ /** Current process ID. */
+ id_t pid;
};
void save_context(struct tcb *t)
{
vm_t rpc_stack = t->rpc_stack;
if (is_rpc(t))
- /** @todo what if user uses their own stack? */
+ /** @todo what if user uses their own stack? Or is a dick and
+ * sets the stack pointer to RPC_STACK_TOP or something? It'll
+ * likely only cause a fuckup in the process who did the dumb
+ * thing, so maybe just consider it user error? */
rpc_stack = align_down(get_stack(t), BASE_PAGE_SIZE);
diff --git a/common/uapi/ipc.c b/common/uapi/ipc.c
index e9a6e6b..052e0ad 100644
--- a/common/uapi/ipc.c
+++ b/common/uapi/ipc.c
@@ -55,8 +55,7 @@ static struct sys_ret do_ipc(sys_arg_t pid,
save_context(t);
set_return(t, r->callback);
- /** @todo associate thread with new proc, should be done in tcb.c I
- * think */
+ attach_rpc(r, t);
if (!fwd)
t->eid = t->pid;
@@ -110,7 +109,9 @@ SYSCALL_DEFINE4(ipc_resp)(sys_arg_t d0, sys_arg_t d1, sys_arg_t d2,
sys_arg_t d3)
{
struct tcb *t = cur_tcb();
+ struct tcb *r = cur_proc();
load_context(t);
+ detach_rpc(r, t);
if (is_rpc(t))
use_vmem(t->rpc.vmem);
diff --git a/common/vmem.c b/common/vmem.c
index 458624c..56bc01a 100644
--- a/common/vmem.c
+++ b/common/vmem.c
@@ -140,6 +140,8 @@ 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->sp_r, start, size, &size, flags);
const vm_t w = map_allocd_region(t->proc.vmem, v, size, flags, &status);
+ /** @todo should probably update rpc maps even if the thread that does
+ * the allocation isn't in an ipc? */
if (is_rpc(t) && status == INFO_SEFF)
clone_rpc_maps(t);
@@ -273,6 +275,9 @@ stat_t free_uvmem_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
if (order != v_order)
return INFO_TRGN;
+ /** @todo we might need to cause an ipi to flush the tlb for other
+ * cores */
+
stat_t *status = (stat_t *)data, ret;
ret = unmap_vpage(b, vaddr);
if (status)
diff --git a/docs/doxygen.conf b/docs/doxygen.conf
index 7cdfa37..34b7b48 100644
--- a/docs/doxygen.conf
+++ b/docs/doxygen.conf
@@ -1000,7 +1000,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
-EXCLUDE =
+EXCLUDE = arch/riscv64/conf/init.c
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
diff --git a/include/apos/tcb.h b/include/apos/tcb.h
index 23e6451..6097153 100644
--- a/include/apos/tcb.h
+++ b/include/apos/tcb.h
@@ -78,6 +78,10 @@ struct tcb {
/** Execution continuation point. Important that it is first. */
vm_t exec;
+ /**
+ * Address where to save registers.
+ * @note his address is the top of the register save structure.
+ */
vm_t regs;
/** Arch-specific data. */
@@ -149,6 +153,16 @@ struct tcb {
/** RPC context of thread. */
struct tcb_ctx rpc;
+ /**
+ * RPC server context of thread. When a thread attaches itself to this
+ * process, its \ref rpc member is added to the list maintained in this
+ * variable. This allows the original thread to do rpc calls without
+ * messing up other threads' rpc status.
+ *
+ * I think, more testing required.
+ */
+ struct tcb_ctx server;
+
/** Notifcation state of thread. */
enum tcb_notify notify_state;
diff --git a/include/arch/vmem.h b/include/arch/vmem.h
index f6660ed..c829629 100644
--- a/include/arch/vmem.h
+++ b/include/arch/vmem.h
@@ -53,6 +53,8 @@ stat_t unmap_vpage(struct vmem *branch, vm_t vaddr);
* @param branch Branch in which to work.
* @param vaddr Virtual address of page.
* @param flags Flags to set.
+ * @return \ref ERR_NF if no page could be found at \p vaddr,
+ * \ref INFO_SEFF if modification has side effects, otherwise \ref OK.
*/
stat_t set_vpage_flags(struct vmem *branch, vm_t vaddr, vmflags_t flags);
@@ -62,6 +64,8 @@ stat_t set_vpage_flags(struct vmem *branch, vm_t vaddr, vmflags_t flags);
* @param branch Branch in which to work.
* @param vaddr Virtual address of page.
* @param flags Flags to clear.
+ * @return \ref ERR_NF if no page could be found at \p vaddr,
+ * \ref INFO_SEFF if modification has side effects, otherwise \ref OK.
*/
stat_t clear_vpage_flags(struct vmem *branch, vm_t vaddr, vmflags_t flags);
@@ -157,7 +161,6 @@ stat_t destroy_vmem(struct vmem *b);
*
* @param r Source virtual memory of clone.
* @param b Destination virtual memory of clone.
- * @return \ref OK.
*/
void clone_uvmem(struct vmem *r, struct vmem *b);