diff options
| -rw-r--r-- | arch/riscv64/include/tcb.h | 3 | ||||
| -rw-r--r-- | arch/riscv64/kernel/proc.c | 58 | ||||
| -rw-r--r-- | arch/riscv64/kernel/pte.h | 78 | ||||
| -rw-r--r-- | arch/riscv64/kernel/vmem.c | 81 | ||||
| -rw-r--r-- | include/arch/proc.h | 25 | ||||
| -rw-r--r-- | src/elf.c | 5 | ||||
| -rw-r--r-- | src/uapi/ipc.c | 6 | ||||
| -rw-r--r-- | src/uapi/proc.c | 13 |
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. @@ -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); } |
