diff options
| -rw-r--r-- | arch/riscv64/kernel/cpu.c | 34 | ||||
| -rw-r--r-- | arch/riscv64/kernel/entry.S | 14 | ||||
| -rw-r--r-- | arch/riscv64/kernel/proc.c | 23 | ||||
| -rw-r--r-- | arch/riscv64/kernel/sbi.h | 20 | ||||
| -rw-r--r-- | common/dispatch.c | 12 | ||||
| -rw-r--r-- | common/ipi.c | 32 | ||||
| -rw-r--r-- | common/tcb.c | 16 | ||||
| -rw-r--r-- | common/uapi/dispatch.c | 9 | ||||
| -rw-r--r-- | common/uapi/ipc.c | 42 | ||||
| -rw-r--r-- | include/apos/ipi.h | 37 | ||||
| -rw-r--r-- | include/apos/tcb.h | 19 | ||||
| -rw-r--r-- | include/apos/uapi.h | 10 | ||||
| -rw-r--r-- | include/arch/cpu.h | 7 | ||||
| -rw-r--r-- | include/arch/proc.h | 21 |
14 files changed, 250 insertions, 46 deletions
diff --git a/arch/riscv64/kernel/cpu.c b/arch/riscv64/kernel/cpu.c index d6d07ec..60140e6 100644 --- a/arch/riscv64/kernel/cpu.c +++ b/arch/riscv64/kernel/cpu.c @@ -11,6 +11,8 @@ #include <arch/cpu.h> +#include "sbi.h" + /** Keeps track of initialized cpus. */ static atomic_long cpus = 0; @@ -40,3 +42,35 @@ struct tcb *cur_tcb() register struct tcb *t __asm__ ("tp"); return t; } + +/** + * Helper for cpu_send_ipi() to convert linear \p cpu_id to SBI \c + * hart_mask_base. + * + * @param cpu_id CPU id to convert. + * @return Corresponding \c hart_mask_base. + */ +static long __cpu_offset(id_t cpu_id) +{ + return cpu_id / (sizeof(long) * 8); +} + +/** + * Helper for cpu_send_ipi() to convert linear \p cpu_id to SBI \c + * hart_mask. + * + * @param cpu_id CPU id to convert. + * @param offset Offset from __cpu_offset(). + * @return Corresponding \c hart_mask. + */ +static long __cpu_mask(id_t cpu_id, long offset) +{ + return cpu_id - offset * sizeof(long) * 8; +} + +void cpu_send_ipi(id_t cpu_id) +{ + long offset = __cpu_offset(cpu_id); + long mask = __cpu_mask(cpu_id, offset); + sbi_send_ipi(mask, offset); +} diff --git a/arch/riscv64/kernel/entry.S b/arch/riscv64/kernel/entry.S index db662bb..355cd3d 100644 --- a/arch/riscv64/kernel/entry.S +++ b/arch/riscv64/kernel/entry.S @@ -77,17 +77,14 @@ _save_context: handle_exception: li t0, EXC_SYSCALL - /* system exceptions fall through, syscalls jump */ + /* system exceptions fall through, syscalls and ipis jump */ /* temp, at some point we want to handle system exceptions as well */ - beq s4, t0, handle_syscall + beq s4, t0, handle_dispatch j restore_all -handle_syscall: - /* add 4 (size of ecall) to EPC to avoid running the same instruction - * twice */ - csrr s0, CSR_SEPC - addi s0, s0, 4 +handle_dispatch: /* store execution continuation point */ + csrr s0, CSR_SEPC sr s0, 0(tp) /* allocate space for sys_ret structure on stack and shift argument * registers down one to make room for the pointer to this structure */ @@ -99,7 +96,8 @@ handle_syscall: mv a2, a1 mv a1, a0 mv a0, sp - jal syscall_dispatch + /* jump to C */ + jal dispatch /* move structure from stack into registers. */ lr a0, offsetof_s(sp) lr a1, offsetof_ar0(sp) diff --git a/arch/riscv64/kernel/proc.c b/arch/riscv64/kernel/proc.c index ed8bf93..d2b8dfb 100644 --- a/arch/riscv64/kernel/proc.c +++ b/arch/riscv64/kernel/proc.c @@ -8,6 +8,7 @@ #include <apos/tcb.h> #include <apos/elf.h> +#include <apos/string.h> #include <arch/proc.h> @@ -54,3 +55,25 @@ void set_thread(struct tcb *t) r->sp = (long)t->thread_stack_top; r->tp = (long)t->thread_storage; } + +void save_regs(struct tcb *t, void *p) +{ + struct riscv_regs *r = (struct riscv_regs *)(t++); + memcpy(p, r, sizeof(*r)); +} + +void load_regs(void *p, struct tcb *t) +{ + struct riscv_regs *r = (struct riscv_regs *)(t++); + memcpy(r, p, sizeof(*r)); +} + +void adjust_ipi(struct tcb *t) +{ + UNUSED(t); +} + +void adjust_syscall(struct tcb *t) +{ + t->exec += 4; +} diff --git a/arch/riscv64/kernel/sbi.h b/arch/riscv64/kernel/sbi.h index 2add35f..19cc595 100644 --- a/arch/riscv64/kernel/sbi.h +++ b/arch/riscv64/kernel/sbi.h @@ -106,4 +106,24 @@ static inline struct sbiret sbi_system_reset(uint32_t reset_type, 0); } +/** Supervisor IPI extension ID. */ +#define EID_sPI 0x735049 + +/** Supervisor IPI function ID. */ +#define FID_IPI 0 + +/** + * Send IPI. + * + * @param hart_mask Bitmap of harts to send IPI to. + * @param hart_mask_base Base of \p hart_mask. + * @return SBI call return. \see sbiret. + */ +static inline struct sbiret sbi_send_ipi(unsigned long hart_mask, + unsigned long hart_mask_base) +{ + return sbi_ecall(EID_sPI, FID_IPI, hart_mask, hart_mask_base, 0, 0, 0, + 0); +} + #endif /* APOS_RISCV_SBI_H */ diff --git a/common/dispatch.c b/common/dispatch.c new file mode 100644 index 0000000..1325d62 --- /dev/null +++ b/common/dispatch.c @@ -0,0 +1,12 @@ +#include <apos/uapi.h> +#include <apos/ipi.h> + +struct sys_ret dispatch(sys_arg_t a, sys_arg_t b, sys_arg_t c, + sys_arg_t d, sys_arg_t e, sys_arg_t f) +{ + struct tcb *t = cur_tcb(); + if (clear_ipi(t)) + return handle_ipi(t); + + return handle_syscall(t, a, b, c, d, e, f); +} diff --git a/common/ipi.c b/common/ipi.c new file mode 100644 index 0000000..32f1432 --- /dev/null +++ b/common/ipi.c @@ -0,0 +1,32 @@ +#include <apos/ipi.h> +#include <arch/cpu.h> + +#include <arch/proc.h> + +/** + * @file ipi.c + * + * IPI function implementations. + */ + +bool clear_ipi(struct tcb *t) +{ + bool r = t->ipi; + t->ipi = false; + return r; +} + +void send_ipi(struct tcb *t) +{ + t->ipi = true; + cpu_send_ipi(t->cpu_id); +} + +struct sys_ret handle_ipi(struct tcb *t) +{ + adjust_ipi(t); + + /** @todo use rpc stack */ + set_return(t, t->callback); + return get_args(t); +} diff --git a/common/tcb.c b/common/tcb.c index 9000941..417ca45 100644 --- a/common/tcb.c +++ b/common/tcb.c @@ -36,7 +36,7 @@ static struct tcb **tcbs; * \todo If we ever support systems with massive amounts of cpus, this should probably * be allocated at runtime. */ -static struct tcb *cpu_tcb[MAX_CPUS] = { 0 }; +static struct tcb *__cpu_tcb[MAX_CPUS] = { 0 }; void init_tcbs() { @@ -294,7 +294,12 @@ DEFINE_DETACH(detach_proc, proc); /* weak to allow optimisation on risc-v, but provide fallback for future */ __weak struct tcb *cur_tcb() { - return cpu_tcb[cpu_id()]; + return cpu_tcb(cpu_id()); +} + +struct tcb *cpu_tcb(id_t cpu_id) +{ + return __cpu_tcb[cpu_id]; } struct tcb *cur_proc() @@ -313,7 +318,7 @@ void use_tcb(struct tcb *t) { cpu_assign(t); - cpu_tcb[t->cpu_id] = t; + __cpu_tcb[t->cpu_id] = t; use_vmem(t->proc.vmem); } @@ -355,3 +360,8 @@ void set_return(struct tcb *t, vm_t v) { t->exec = v; } + +bool running(struct tcb *t) +{ + return cpu_tcb(t->cpu_id) == t; +} diff --git a/common/uapi/dispatch.c b/common/uapi/dispatch.c index 27cc001..2cf19fb 100644 --- a/common/uapi/dispatch.c +++ b/common/uapi/dispatch.c @@ -10,6 +10,8 @@ #include <apos/debug.h> #include <apos/uapi.h> +#include <arch/proc.h> + /** Syscall number to syscall handler conversion. */ static const sys_t syscall_table[] = { /* noop */ @@ -79,10 +81,11 @@ SYSCALL_DEFINE1(putch)(sys_arg_t a){ return SYS_RET1(OK); } -struct sys_ret syscall_dispatch(sys_arg_t syscall, sys_arg_t a, sys_arg_t b, - sys_arg_t c, sys_arg_t d, sys_arg_t e) +struct sys_ret handle_syscall(struct tcb *t, + sys_arg_t syscall, sys_arg_t a, sys_arg_t b, + sys_arg_t c, sys_arg_t d, sys_arg_t e) { - struct tcb *t = cur_tcb(); + adjust_syscall(t); size_t sc = syscall; if (sc >= ARRAY_SIZE(syscall_table)) { diff --git a/common/uapi/ipc.c b/common/uapi/ipc.c index 34f707a..cb53ee3 100644 --- a/common/uapi/ipc.c +++ b/common/uapi/ipc.c @@ -8,6 +8,7 @@ #include <apos/uapi.h> #include <apos/tcb.h> +#include <apos/ipi.h> /** * IPC server notification syscall handler. @@ -114,36 +115,21 @@ SYSCALL_DEFINE4(ipc_resp)(sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, * \todo Implement. * * @param tid Thread ID to notify. - * @param swap Whether to swap immediately if possible. * @return \ref OK and 0. */ -SYSCALL_DEFINE2(ipc_notify)(sys_arg_t tid, sys_arg_t swap){ - /** \todo masquerade as kernel call, set from to 0 and set us as - * notify type, no arguments as that would require too much state - * handling for my liking. Instead, a server and a client have to agree - * on some rpc API, and ipc_notify is just used to asynchronously inform - * the client that it should check the status of its async operations. - * Arguably slower than directly telling the client which operation was - * finished, but this would require the kernel to keep track of a notify - * stack. While not impossible, probably too complex. */ +SYSCALL_DEFINE1(ipc_notify)(sys_arg_t tid){ + struct tcb *t = get_tcb(tid); + if (t->notify_state == NOTIFY_QUEUED) + return SYS_RET1(OK); + + if (t->notify_state == NOTIFY_RUNNING) { + t->notify_state = NOTIFY_QUEUED; + return SYS_RET1(OK); + } + + t->notify_state = NOTIFY_QUEUED; + if (running(t)) + send_ipi(t); - /* Something like - * - * struct tcb *t = get_tcb(tid); - * if (t->notify_state == NOTIFY_QUEUED) - * return; - * - * if (t->notify_state == NOTIFY_RUNNING) { - * t->notify = NOTIFY_QUEUED; - * return; - * } - * - * t->notify_state = NOTIFY_QUEUED; - * if (swap) - * do_swap(); // clears t->notify when swapped to - * // if already running in base state, interrupt, - * otherwise wait for return from rpc. If not running, - * just queue the interrupt. - */ return SYS_RET1(OK); } diff --git a/include/apos/ipi.h b/include/apos/ipi.h new file mode 100644 index 0000000..c895d58 --- /dev/null +++ b/include/apos/ipi.h @@ -0,0 +1,37 @@ +#ifndef APOS_IPI_H +#define APOS_IPI_H + +/** + * @file ipi.h + * + * IPI function definitions. + */ + +#include <apos/types.h> +#include <apos/uapi.h> +#include <apos/tcb.h> + +/** + * Clear potential IPI in \p t, and return its value. + * + * @param t \ref tcb to clear possible IPI status of. + * @return \ref true if \p was interrupted by IPI, \ref false otherwise. + */ +bool clear_ipi(struct tcb *t); + +/** + * Send IPI to \p t. Assumes \c running(t). + * + * @param t \ref tcb to send IPI to. + */ +void send_ipi(struct tcb *t); + +/** + * Handle IPI. + * + * @param t Thread who was interrupted by IPI. + * @return Possible arguments to IPI. + */ +struct sys_ret handle_ipi(struct tcb *t); + +#endif /* APOS_IPI_H */ diff --git a/include/apos/tcb.h b/include/apos/tcb.h index b92cd10..0368957 100644 --- a/include/apos/tcb.h +++ b/include/apos/tcb.h @@ -146,6 +146,9 @@ struct tcb { /** Notifcation state of thread. */ enum tcb_notify notify_state; + + /** Whether thread has gotten an IPI */ + bool ipi; }; /** @@ -266,6 +269,14 @@ stat_t detach_proc(struct tcb *r, struct tcb *t); struct tcb *cur_tcb(); /** + * Get thread currently running on cpu \p cpu_id. + * + * @param cpu_id CPU whose currently running thread to get. + * @return \ref tcb running on cpu. + */ +struct tcb *cpu_tcb(id_t cpu_id); + +/** * Get currently executing process. * * @return Effective process \ref tcb. @@ -337,4 +348,12 @@ stat_t alloc_stacks(struct tcb *t); */ void set_return(struct tcb *t, vm_t r); +/** + * Check whether \p t is currently running on some cpu. + * + * @param t \ref tcb to check. + * @return \c true if it is running, \c false otherwise. + */ +bool running(struct tcb *t); + #endif /* APOS_TCB_H */ diff --git a/include/apos/uapi.h b/include/apos/uapi.h index 084c1fa..83de7eb 100644 --- a/include/apos/uapi.h +++ b/include/apos/uapi.h @@ -500,13 +500,13 @@ SYSCALL_DECLARE4(ipc_resp, d0, d1, d2, d3); * Notify thread syscall. * * @param tid Thread ID to notify. - * @param swap Whether to immediately swap. + * @param b Unused. * @param c Unused. * @param d Unused. * @param e Unused. * @return \ref OK and 0. */ -SYSCALL_DECLARE2(ipc_notify, tid, swap); +SYSCALL_DECLARE1(ipc_notify, tid); /** @} */ /** @name Process handling syscalls. */ @@ -683,6 +683,7 @@ SYSCALL_DECLARE1(poweroff, type); /** * Dispatch to correct syscall handler. * + * @param t Thread to do syscall on. * @param syscall Syscall number. * @param a Syscall argument 0. * @param b Syscall argument 1. @@ -691,8 +692,9 @@ SYSCALL_DECLARE1(poweroff, type); * @param e Syscall argument 4. * @return Whatever the specified syscall returns. */ -struct sys_ret syscall_dispatch(sys_arg_t syscall, sys_arg_t a, sys_arg_t b, - sys_arg_t c, sys_arg_t d, sys_arg_t e); +struct sys_ret handle_syscall(struct tcb *t, + sys_arg_t syscall, sys_arg_t a, sys_arg_t b, + sys_arg_t c, sys_arg_t d, sys_arg_t e); /** \todo Should I add variable names as well, to make the documentation a bit * more readable? */ diff --git a/include/arch/cpu.h b/include/arch/cpu.h index 66d5d45..c5a83d5 100644 --- a/include/arch/cpu.h +++ b/include/arch/cpu.h @@ -36,4 +36,11 @@ id_t cpu_id(); */ void cpu_assign(struct tcb *t); +/** + * Send inter-processor interrupt to cpu \p cpu_id. + * + * @param cpu_id CPU to send ipi to. + */ +void cpu_send_ipi(id_t cpu_id); + #endif /* APOS_CPU_H */ diff --git a/include/arch/proc.h b/include/arch/proc.h index 5047eab..d01674d 100644 --- a/include/arch/proc.h +++ b/include/arch/proc.h @@ -49,6 +49,27 @@ struct sys_ret get_args(struct tcb *t); void set_thread(struct tcb *t); /** + * Copy registers from tcb save area to address \p p. + * Intended to be used for copying thread state to rpc stack. + * + * @param t Thread whose registers to save. + * @param p Address to save to. + */ +void save_regs(struct tcb *t, void *p); + +/** + * Copy registers from address \p p to tcb save registers. + * Intended to be used for copying thread state from rpc stack. + * + * @param p Address to load from. + * @param t Thread whose registers to load. + */ +void load_regs(void *p, struct tcb *t); + +void adjust_ipi(struct tcb *t); +void adjust_syscall(struct tcb *t); + +/** * Run \c init program. * * @param t Thread that \c init is attached to. |
