aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/riscv64/include/tcb.h3
-rw-r--r--arch/riscv64/kernel/proc.c58
-rw-r--r--arch/riscv64/kernel/pte.h78
-rw-r--r--arch/riscv64/kernel/vmem.c81
-rw-r--r--include/arch/proc.h25
-rw-r--r--src/elf.c5
-rw-r--r--src/uapi/ipc.c6
-rw-r--r--src/uapi/proc.c13
8 files changed, 161 insertions, 108 deletions
diff --git a/arch/riscv64/include/tcb.h b/arch/riscv64/include/tcb.h
index f0cce37..b3baf46 100644
--- a/arch/riscv64/include/tcb.h
+++ b/arch/riscv64/include/tcb.h
@@ -19,6 +19,9 @@ struct arch_tcbd {
/** RPC stack page table leaf node. */
struct vmem *rpc_leaf;
+ /** O1 page that contains the whole stack */
+ pm_t rpc_page;
+
/** Index into \p rpc_leaf with the lowest accessed page so far in a
* certain context. */
int rpc_idx;
diff --git a/arch/riscv64/kernel/proc.c b/arch/riscv64/kernel/proc.c
index ba5c8e7..9dd34a0 100644
--- a/arch/riscv64/kernel/proc.c
+++ b/arch/riscv64/kernel/proc.c
@@ -15,6 +15,7 @@
#include <arch/proc.h>
#include "regs.h"
+#include "pte.h"
#include "csr.h"
/** Assembly implementation for actually jumping to the init process, defined in
@@ -58,9 +59,35 @@ void run_init(struct tcb *t, vm_t fdt, vm_t initrd)
unreachable();
}
-void set_ret(struct tcb *t, size_t n, struct sys_ret a)
+/**
+ * Safely calculate where t->regs actually is, so we can access the rpc stack
+ * even without being in the same address space. Allows us to avoid doing some
+ * cache flushes in src/uapi/proc.c.
+ *
+ * @param t Thread whose registers we want to access.
+ * @return The physical address of the current registers.
+ */
+static __inline pm_t __physical_regs(struct tcb *t)
+{
+ pm_t offset = (vm_t)t->regs - RPC_STACK_BASE;
+ return t->arch.rpc_page + offset;
+}
+
+void set_ret_fast(struct tcb *t, struct sys_ret a)
{
struct riscv_regs *r = (struct riscv_regs *)(t->regs) - 1;
+ r->a0 = a.s;
+ r->a1 = a.id;
+ r->a2 = a.a0;
+ r->a3 = a.a1;
+ r->a4 = a.a2;
+ r->a5 = a.a3;
+}
+
+void set_ret(struct tcb *t, size_t n, struct sys_ret a)
+{
+ pm_t regs = __physical_regs(t);
+ struct riscv_regs *r = (struct riscv_regs *)(regs) - 1;
if (n >= 1) r->a0 = a.s;
if (n >= 2) r->a1 = a.id;
if (n >= 3) r->a2 = a.a0;
@@ -71,15 +98,17 @@ void set_ret(struct tcb *t, size_t n, struct sys_ret a)
struct sys_ret get_ret(struct tcb *t)
{
- struct riscv_regs *r = (struct riscv_regs *)(t->regs) - 1;
+ pm_t regs = __physical_regs(t);
+ struct riscv_regs *r = (struct riscv_regs *)(regs) - 1;
return SYS_RET6(r->a0, r->a1, r->a2, r->a3, r->a4, r->a5);
}
void set_thread(struct tcb *t)
{
+ pm_t regs = __physical_regs(t);
/* get location of registers in memory */
/** \todo check alignment, should be fine but just to be sure */
- struct riscv_regs *r = (struct riscv_regs *)(t->regs) - 1;
+ struct riscv_regs *r = (struct riscv_regs *)(regs) - 1;
/* insert important values into register slots */
r->sp = (long)t->rpc_stack - BASE_PAGE_SIZE;
@@ -88,27 +117,32 @@ void set_thread(struct tcb *t)
void set_stack(struct tcb *t, vm_t s)
{
/** @todo also set frame pointer on architectures that need it? */
+ pm_t regs = __physical_regs(t);
+ struct riscv_regs *r = (struct riscv_regs *)(regs) - 1;
+ r->sp = s;
+}
+
+void set_stack_fast(struct tcb *t, vm_t s)
+{
struct riscv_regs *r = (struct riscv_regs *)(t->regs) - 1;
r->sp = s;
}
vm_t get_stack(struct tcb *t)
{
- struct riscv_regs *r = (struct riscv_regs *)(t->regs) - 1;
+ pm_t regs = __physical_regs(t);
+ struct riscv_regs *r = (struct riscv_regs *)(regs) - 1;
return r->sp;
}
void copy_regs(struct tcb *d, struct tcb *s)
{
- struct riscv_regs rs = *((struct riscv_regs *)(s->regs) - 1);
- /* again, not a fan of this, I guess we could query the underlying
- * physical page? Would that be any faster? */
- use_vmem(d->rpc.vmem);
-
- struct riscv_regs *rd = ((struct riscv_regs *)(d->regs) - 1);
- *rd = rs;
+ pm_t rgs = __physical_regs(s);
+ pm_t rgd = __physical_regs(d);
- use_vmem(s->rpc.vmem);
+ struct riscv_regs *rs = (struct riscv_regs *)(rgs) - 1;
+ struct riscv_regs *rd = (struct riscv_regs *)(rgd) - 1;
+ *rd = *rs;
}
void adjust_ipi(struct tcb *t)
diff --git a/arch/riscv64/kernel/pte.h b/arch/riscv64/kernel/pte.h
new file mode 100644
index 0000000..03c3d05
--- /dev/null
+++ b/arch/riscv64/kernel/pte.h
@@ -0,0 +1,78 @@
+#ifndef KMI_RISCV_PTE_H
+#define KMI_RISCV_PTE_H
+
+/**
+ * Get page table entry physical page number.
+ *
+ * @param pte Page table entry.
+ * @return Corresponding physical page number.
+ */
+#define pte_ppn(pte) (((pm_t)(pte)) >> 10)
+
+/**
+ * Get page table entry flags.
+ *
+ * @param pte Page table entry.
+ * @return Corresponding flags.
+ */
+#define pte_flags(pte) (((pm_t)(pte)) & 0xff)
+
+/**
+ * Convert physical memory address to page table entry.
+ *
+ * @param p Physical memory address.
+ * @param f Flags to use.
+ * @return Corresponding page table entry.
+ */
+#define to_pte(p, f) ((((p) >> page_shift()) << 10) | (f))
+
+/**
+ * Get physical address in page table entry.
+ *
+ * @param pte Page table entry.
+ * @return Corresponding physical address.
+ */
+#define pte_paddr(pte) (pte_ppn(pte) << page_shift())
+
+/**
+ * Get virtual address in page table entry.
+ *
+ * @param pte Page table entry.
+ * @return Corresponding virtual address.
+ */
+#define pte_addr(pte) __va(pte_paddr(pte))
+
+/**
+ * Virtual memory address to page order index.
+ *
+ * @param a Virtual address.
+ * @param o Order of page.
+ * @return Corresponding page index.
+ */
+#define vm_to_index(a, o) (pm_to_index(a, o))
+
+/**
+ * Check if page table entry is active.
+ *
+ * @param pte Page table entry.
+ * @return \c 0 if entry is not active, non-zero otherwise.
+ */
+#define is_active(pte) (pte_flags(pte) &VM_V)
+
+/**
+ * Check if page table entry is a leaf.
+ *
+ * @param pte Page table entry.
+ * @return \c 0 if entry is not leaf, non-zero otherwise.
+ */
+#define is_leaf(pte) (is_active(pte) && (pte_flags(pte) & ~VM_V))
+
+/**
+ * Check if page table entry is a branch.
+ *
+ * @param pte Page table entry.
+ * @return \c 0 if entry is not branch, non-zero otherwise.
+ */
+#define is_branch(pte) (is_active(pte) && !(pte_flags(pte) & ~VM_V))
+
+#endif /* KMI_RISCV_PTE_H */
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c
index 1b70493..2a08854 100644
--- a/arch/riscv64/kernel/vmem.c
+++ b/arch/riscv64/kernel/vmem.c
@@ -16,83 +16,10 @@
#include <arch/cpu.h>
#include "pages.h"
#include "arch.h"
+#include "pte.h"
#include "csr.h"
/**
- * Get page table entry physical page number.
- *
- * @param pte Page table entry.
- * @return Corresponding physical page number.
- */
-#define pte_ppn(pte) (((pm_t)(pte)) >> 10)
-
-/**
- * Get page table entry flags.
- *
- * @param pte Page table entry.
- * @return Corresponding flags.
- */
-#define pte_flags(pte) (((pm_t)(pte)) & 0xff)
-
-/**
- * Convert physical memory address to page table entry.
- *
- * @param p Physical memory address.
- * @param f Flags to use.
- * @return Corresponding page table entry.
- */
-#define to_pte(p, f) ((((p) >> page_shift()) << 10) | (f))
-
-/**
- * Get physical address in page table entry.
- *
- * @param pte Page table entry.
- * @return Corresponding physical address.
- */
-#define pte_paddr(pte) (pte_ppn(pte) << page_shift())
-
-/**
- * Get virtual address in page table entry.
- *
- * @param pte Page table entry.
- * @return Corresponding virtual address.
- */
-#define pte_addr(pte) __va(pte_paddr(pte))
-
-/**
- * Virtual memory address to page order index.
- *
- * @param a Virtual address.
- * @param o Order of page.
- * @return Corresponding page index.
- */
-#define vm_to_index(a, o) (pm_to_index(a, o))
-
-/**
- * Check if page table entry is active.
- *
- * @param pte Page table entry.
- * @return \c 0 if entry is not active, non-zero otherwise.
- */
-#define is_active(pte) (pte_flags(pte) &VM_V)
-
-/**
- * Check if page table entry is a leaf.
- *
- * @param pte Page table entry.
- * @return \c 0 if entry is not leaf, non-zero otherwise.
- */
-#define is_leaf(pte) (is_active(pte) && (pte_flags(pte) & ~VM_V))
-
-/**
- * Check if page table entry is a branch.
- *
- * @param pte Page table entry.
- * @return \c 0 if entry is not branch, non-zero otherwise.
- */
-#define is_branch(pte) (is_active(pte) && !(pte_flags(pte) & ~VM_V))
-
-/**
* Gravestone marker.
*
* Riscv allows us to have arbitrary data in page entries, as long as they're
@@ -607,6 +534,8 @@ stat_t setup_rpc_stack(struct tcb *t)
if (!page)
return ERR_OOMEM;
+ t->arch.rpc_page = page;
+
if (map_vpage(t->rpc.vmem, page, RPC_STACK_BASE, flags, BASE_PAGE)) {
free_page(MM_O1, page);
return ERR_OOMEM;
@@ -636,9 +565,7 @@ stat_t setup_rpc_stack(struct tcb *t)
void destroy_rpc_stack(struct tcb *t)
{
- pm_t *pte = (pm_t *)t->arch.rpc_leaf;
- pm_t page = (pm_t)pte_addr(*pte);
- free_page(MM_O1, page);
+ free_page(MM_O1, t->arch.rpc_page);
}
void reset_rpc_stack(struct tcb *t)
diff --git a/include/arch/proc.h b/include/arch/proc.h
index b0f0944..ec01a55 100644
--- a/include/arch/proc.h
+++ b/include/arch/proc.h
@@ -7,6 +7,11 @@
/**
* @file proc.h
* Arch-specific process related stuff.
+ *
+ * Note that all functions except *_fast may not assume that the rpc stack is
+ * mapped into the current address space, so they might have to do some extra
+ * calculations to find the underlying physical stack or something along those
+ * lines.
*/
#if defined(__riscv)
@@ -30,6 +35,16 @@
void set_ret(struct tcb *t, size_t n, struct sys_ret a);
/**
+ * Attach argument data to thread, to be returned to userspace.
+ * Must only be called when cur_tcb() == t. Currently only used by do_ipc() so
+ * no need to figure out how many registers to set, just set them all.
+ *
+ * @param t Current thread.
+ * @param a Values to place in return registers.
+ */
+void set_ret_fast(struct tcb *t, struct sys_ret a);
+
+/**
* Get argument data attached to thread.
* To some extent a hack, used by swap.
*
@@ -62,6 +77,16 @@ void set_thread(struct tcb *t);
void set_stack(struct tcb *t, vm_t s);
/**
+ * Set userspace stack, but do it very quickly.
+ * Must only be called when cur_tcb() == t, allowing us to do a fast but 'unsafe'
+ * direct write to the rpc stack.
+ *
+ * @param t Current thread.
+ * @param s Address to place into the stack register.
+ */
+void set_stack_fast(struct tcb *t, vm_t s);
+
+/**
* Get current userspace stack.
*
* @param t Thread whose stack to query.
diff --git a/src/elf.c b/src/elf.c
index 85b4b2e..f643d41 100644
--- a/src/elf.c
+++ b/src/elf.c
@@ -93,10 +93,9 @@ static stat_t __map_exec(struct tcb *t,
/* create empty vmem so we don't have to worry about possible overlaps */
struct vmem *new_vmem = create_vmem();
- if (!new_vmem) {
- use_vmem(t->rpc.vmem);
+ if (!new_vmem)
return ERR_OOMEM;
- }
+
struct vmem *old_vmem = t->proc.vmem;
t->proc.vmem = new_vmem;
diff --git a/src/uapi/ipc.c b/src/uapi/ipc.c
index 3f94e63..e323478 100644
--- a/src/uapi/ipc.c
+++ b/src/uapi/ipc.c
@@ -61,7 +61,7 @@ static __inline void enter_rpc(struct tcb *t, struct tcb *r, struct sys_ret a,
/* try to get rid of args as fast as possible to free up registers for
* later use */
- set_ret(t, 6, a);
+ set_ret_fast(t, a);
if (!is_set(flags, IPC_TAIL)) {
ctx->rpc_stack = t->rpc_stack;
@@ -79,7 +79,7 @@ static __inline void enter_rpc(struct tcb *t, struct tcb *r, struct sys_ret a,
}
- set_stack(t, rpc_stack - BASE_PAGE_SIZE);
+ set_stack_fast(t, rpc_stack - BASE_PAGE_SIZE);
clone_uvmem(r->proc.vmem, t->rpc.vmem);
flush_tlb_full();
@@ -203,7 +203,7 @@ static void leave_rpc(struct tcb *t, struct sys_ret a)
/* again, get rid of args as fast as possible */
if (!ctx->notify)
- set_ret(t, 6, a);
+ set_ret_fast(t, a);
struct tcb *r = get_tcb(ctx->pid);
while (!r || !is_proc(r) || zombie(r)) {
diff --git a/src/uapi/proc.c b/src/uapi/proc.c
index 4b6865c..52511f9 100644
--- a/src/uapi/proc.c
+++ b/src/uapi/proc.c
@@ -46,18 +46,12 @@ SYSCALL_DEFINE5(create)(struct tcb *t, sys_arg_t func,
if (!n)
return_args1(t, ERR_OOMEM);
- /* temporarily jump into new thread memory to set arguments */
- use_vmem(n->rpc.vmem);
-
/* this visit is likely not the cheapest thing in the universe, are
* there ways to speed up thread creation? */
set_thread(n);
set_ret5(n, n->tid, d0, d1, d2, d3);
set_return(n, func);
- /* return back */
- use_vmem(t->rpc.vmem);
-
n->notify_id = t->notify_id;
return_args1(t, n->tid);
}
@@ -91,13 +85,9 @@ SYSCALL_DEFINE0(fork)(struct tcb *t)
if (!n)
return_args1(t, ERR_OOMEM);
- /* again, probably not fantastic that we're jumping between address
- * spaces like this */
- use_vmem(n->rpc.vmem);
/* prepare args for when we eventually swap to the new proc, giving
* parent ID as third return value */
set_args2(n, 0, get_eproc(t)->pid);
- use_vmem(t->rpc.vmem);
n->notify_id = c->notify_id;
return_args1(t, n->pid);
@@ -180,12 +170,9 @@ SYSCALL_DEFINE2(spawn)(struct tcb *t, sys_arg_t bin, sys_arg_t interp)
/** @todo should permissions be transferred? Probably, not but now there
* is a slightly annoying asymmetry between fork+exec vs spawn... */
- /* temporarily switch to new thread to manipulate stack */
- use_vmem(n->rpc.vmem);
set_thread(n);
set_return(n, n->callback);
set_ret4(n, 0, n->tid, SYS_USER_SPAWNED, n->pid);
- use_vmem(t->rpc.vmem);
return_args1(t, n->pid);
}