aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xarch/riscv64/conf/initbin2664 -> 3448 bytes
-rw-r--r--arch/riscv64/conf/init.c94
-rw-r--r--arch/riscv64/conf/initrdbin3072 -> 4096 bytes
-rw-r--r--arch/riscv64/config.h4
-rw-r--r--arch/riscv64/kernel/proc.c6
-rw-r--r--arch/riscv64/kernel/vmem.c2
-rw-r--r--common/proc.c4
-rw-r--r--common/tcb.c83
-rw-r--r--common/uapi/conf.c4
-rw-r--r--common/uapi/ipc.c16
-rw-r--r--common/uapi/proc.c10
-rw-r--r--include/apos/caps.h15
-rw-r--r--include/apos/sizes.h30
-rw-r--r--include/apos/tcb.h29
-rw-r--r--include/arch/proc.h8
15 files changed, 254 insertions, 51 deletions
diff --git a/arch/riscv64/conf/init b/arch/riscv64/conf/init
index 11d838c..7ab8388 100755
--- a/arch/riscv64/conf/init
+++ b/arch/riscv64/conf/init
Binary files differ
diff --git a/arch/riscv64/conf/init.c b/arch/riscv64/conf/init.c
index 45274d9..01e219e 100644
--- a/arch/riscv64/conf/init.c
+++ b/arch/riscv64/conf/init.c
@@ -9,8 +9,13 @@
#include <stdint.h>
#include "../../../include/apos/syscalls.h"
-#define ecall() do { asm ("ecall" : : : "a0", "a1", "a2", "a3", "a4", "a5"); \
+#define ecall() do { asm volatile ("ecall" \
+ : \
+ : \
+ : "a0", "a1", "a2", "a3", "a4", "a5", \
+ "memory"); \
} while (0)
+
static void sys_noop()
{
long register a0 asm ("a0") = SYS_NOOP;
@@ -108,6 +113,60 @@ static uint64_t sys_swap(long tid)
return a0;
}
+static void sys_ipc_server(void *f)
+{
+ long register a0 asm ("a0") = SYS_IPC_SERVER;
+ long register a1 asm ("a1") = (long)f;
+ ecall();
+ if (a0 != 0)
+ print_value("ipc_server() failed with error ", a0);
+}
+
+static uint64_t sys_ipc_req(long tid, long *d0, long *d1, long *d2, long *d3)
+{
+ long register a0 asm ("a0") = SYS_IPC_REQ;
+ long register a1 asm ("a1") = tid;
+ long register a2 asm ("a2") = *d0;
+ long register a3 asm ("a3") = *d1;
+ long register a4 asm ("a4") = *d2;
+ long register a5 asm ("a5") = *d3;
+ ecall();
+ if (a0)
+ print_value("ipc_req() failed with error ", a0);
+
+ *d0 = a2;
+ *d1 = a3;
+ *d2 = a4;
+ *d3 = a5;
+ return a0;
+}
+
+static void sys_ipc_resp(long d0, long d1, long d2, long d3)
+{
+ long register a0 asm ("a0") = SYS_IPC_RESP;
+ long register a1 asm ("a1") = d0;
+ long register a2 asm ("a2") = d1;
+ long register a3 asm ("a3") = d2;
+ long register a4 asm ("a4") = d3;
+ ecall();
+}
+
+static void sys_poweroff(long type)
+{
+ long register a0 asm ("a0") = SYS_POWEROFF;
+ long register a1 asm ("a1") = type;
+ ecall();
+}
+
+void callback(long status, long tid, long d0, long d1, long d2, long d3)
+{
+ sys_ipc_resp(d0, d1, d2, d3);
+}
+
+#define CSR_TIME "0xc01"
+#define csr_read(csr, \
+ res) __asm__ volatile ("csrr %0, " csr : "=r" (res) :: "memory")
+
void _start()
{
sys_noop();
@@ -128,20 +187,41 @@ void _start()
print_value("End ticks", i);
print_value("Syscalls per second", n);
+ puts("Setting callback...");
+ sys_ipc_server(callback);
+
puts("Starting fork():\n");
long pid = sys_fork();
if (pid != 0) {
- print_value("Child pid: ", pid);
+ print_value("Child pid", pid);
puts("Swapping to child...\n");
while(1) sys_swap(pid);
}
puts("Hello from child!\n");
- puts("Doing 1M swaps...\n");
- start = sys_ticks();
- for (long i = 0; i < 1000000; ++i)
+ puts("Doing swaps...\n");
+ csr_read(CSR_TIME, i);
+ start = i; n = 0;
+ while (i < start + second) {
sys_swap(1);
- uint64_t ticks = sys_ticks() - start;
- print_value("1M swaps took ", ticks / second);
+ csr_read(CSR_TIME, i);
+ n++;
+ }
+
+ print_value("Swaps (both ways) per second", n);
+
+ puts("Doing ipc requests...\n");
+ long d0, d1, d2, d3;
+ csr_read(CSR_TIME, i);
+ start = i; n = 0;
+ while (i < start + second) {
+ sys_ipc_req(1, &d0, &d1, &d2, &d3);
+ csr_read(CSR_TIME, i);
+ n++;
+ }
+
+ print_value("IPC requests per second", n);
+
+ sys_poweroff(0);
}
diff --git a/arch/riscv64/conf/initrd b/arch/riscv64/conf/initrd
index 5c0c9bb..492b80c 100644
--- a/arch/riscv64/conf/initrd
+++ b/arch/riscv64/conf/initrd
Binary files differ
diff --git a/arch/riscv64/config.h b/arch/riscv64/config.h
index 2c3dce6..857f885 100644
--- a/arch/riscv64/config.h
+++ b/arch/riscv64/config.h
@@ -77,10 +77,10 @@
#define UVMEM_END (SZ_256G - SZ_1G)
/** RPC stack top. */
-#define RPC_STACK_TOP (UVMEM_END)
+#define RPC_STACK_TOP (UVMEM_END + SZ_1G)
/** RPC stack base. */
-#define RPC_STACK_BASE (RPC_STACK_TOP - SZ_1G)
+#define RPC_STACK_BASE (UVMEM_END)
/**
* Size of the default top page.
diff --git a/arch/riscv64/kernel/proc.c b/arch/riscv64/kernel/proc.c
index 2ccc5bc..3c2ea59 100644
--- a/arch/riscv64/kernel/proc.c
+++ b/arch/riscv64/kernel/proc.c
@@ -56,6 +56,12 @@ void set_thread(struct tcb *t)
r->tp = (long)t->thread_storage;
}
+vm_t get_stack(struct tcb *t)
+{
+ struct riscv_regs *r = (struct riscv_regs *)(t) - 1;
+ return r->sp;
+}
+
void save_regs(struct tcb *t, void *p)
{
struct riscv_regs *r = (struct riscv_regs *)(t) - 1;
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c
index 8954ee3..d12015c 100644
--- a/arch/riscv64/kernel/vmem.c
+++ b/arch/riscv64/kernel/vmem.c
@@ -316,7 +316,7 @@ vm_t setup_kernel_io(struct vmem *b, vm_t paddr)
stat_t clone_uvmem(struct vmem *r, struct vmem *b)
{
/** \todo error checking? */
- for (size_t i = 0; i <= CSTACK_PAGE; ++i)
+ for (size_t i = 0; i < CSTACK_PAGE; ++i)
b->leaf[i] = r->leaf[i];
return OK;
diff --git a/common/proc.c b/common/proc.c
index 137a2fe..1acd5b8 100644
--- a/common/proc.c
+++ b/common/proc.c
@@ -21,7 +21,7 @@ stat_t prepare_proc(struct tcb *t, vm_t bin, vm_t interp)
if (!entry)
return ERR_INVAL;
- alloc_stacks(t);
+ alloc_stack(t);
set_thread(t);
set_return(t, entry);
return OK;
@@ -40,7 +40,7 @@ stat_t init_proc(void *fdt)
use_tcb(t);
/* init process has all capabilities */
- set_caps(t->caps, 0, CAP_CAPS | CAP_PROC | CAP_CALL);
+ set_caps(t->caps, 0, CAP_CAPS | CAP_PROC | CAP_CALL | CAP_POWER);
/* allocate stacks after ELF file to make sure nothing of importance
* clashes */
diff --git a/common/tcb.c b/common/tcb.c
index 0a676af..aa66d3f 100644
--- a/common/tcb.c
+++ b/common/tcb.c
@@ -96,11 +96,11 @@ static vm_t __setup_rpc_stack(struct tcb *t, size_t bytes)
vmflags_t flags = VM_V | VM_R | VM_W | VM_U;
for (size_t i = 1; i <= pages; ++i) {
offset = alloc_page(BASE_PAGE);
- map_vpage(t->proc.vmem, offset,
+ map_vpage(t->rpc.vmem, offset,
RPC_STACK_TOP - BASE_PAGE_SIZE * i,
flags, BASE_PAGE);
}
-
+ t->rpc_stack = RPC_STACK_TOP;
return RPC_STACK_TOP - BASE_PAGE_SIZE * pages;
}
@@ -116,7 +116,7 @@ static vm_t __setup_thread_stack(struct tcb *t, size_t bytes)
return alloc_uvmem(t, bytes, VM_V | VM_R | VM_W | VM_U);
}
-stat_t alloc_stacks(struct tcb *t)
+stat_t alloc_stack(struct tcb *t)
{
/* get parent process */
struct tcb *p = get_tcb(t->eid);
@@ -125,11 +125,6 @@ stat_t alloc_stacks(struct tcb *t)
if (!t->thread_stack)
return ERR_OOMEM;
- /* rpc stack always starts at the same place in vmem.
- * \todo: is this a security issue? */
- if (!__setup_rpc_stack(p, __call_stack_size))
- return ERR_OOMEM;
-
/** \todo this only allows for a global stack size, what if a user wants
* per thread stack sizes? */
t->thread_stack_top = t->thread_stack + __thread_stack_size;
@@ -166,6 +161,7 @@ struct tcb *create_thread(struct tcb *p)
t->eid = t->pid;
t->rid = p->rid;
t->rpc.vmem = create_vmem();
+ __setup_rpc_stack(t, __call_stack_size);
set_canary(t);
return t;
@@ -186,6 +182,10 @@ static stat_t __copy_proc(struct tcb *p, struct tcb *n)
* need to duplicate stack info, whatever we do. */
/** @todo should there be in-kernel child tracking? */
n->exec = p->exec;
+ n->callback = p->callback;
+ n->thread_stack = p->thread_stack;
+ n->thread_stack_top = p->thread_stack_top;
+
clone_regs(n, p);
copy_caps(n->caps, p->caps);
return clone_mem_regions(n, p);
@@ -374,3 +374,70 @@ bool running(struct tcb *t)
{
return cpu_tcb(t->cpu_id) == t;
}
+
+/**
+ * Mark rpc stack between \p start and \p end inaccessible.
+ *
+ * @param t Thread whose rpc stack to modify.
+ * @param start Start address of rpc stack to mark inaccessible.
+ * @param end End address of rpc stack to mark inaccessible.
+ */
+static void mark_rpc_inaccessible(struct tcb *t, vm_t start, vm_t end)
+{
+ size_t page_size = BASE_PAGE_SIZE;
+ size_t size = end - start;
+ size_t pages = size / page_size;
+ while (pages--) {
+ /** @todo something like mod_vpage_flags could be faster */
+ vmflags_t flags; pm_t paddr;
+ stat_vpage(t->rpc.vmem, start, &paddr, NULL, &flags);
+ mod_vpage(t->rpc.vmem, start, paddr, clear_bits(flags, VM_U));
+ }
+}
+
+/**
+ * Mark rpc stack between \p start and \p end accessible.
+ *
+ * @param t Thread whose rpc stack to modify.
+ * @param start Start address of rpc stack to mark accessible.
+ * @param end End address of rpc stack to mark accessible.
+ */
+static void mark_rpc_accessible(struct tcb *t, vm_t start, vm_t end)
+{
+ size_t page_size = BASE_PAGE_SIZE;
+ size_t size = end - start;
+ size_t pages = size / page_size;
+ while (pages--) {
+ /** @todo something like mod_vpage_flags could be faster */
+ vmflags_t flags; pm_t paddr;
+ stat_vpage(t->rpc.vmem, start, &paddr, NULL, &flags);
+ mod_vpage(t->rpc.vmem, start, paddr, set_bits(flags, VM_U));
+ }
+}
+
+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? */
+ rpc_stack = align_down(get_stack(t), BASE_PAGE_SIZE);
+
+ struct tcb *copy = (struct tcb *)(rpc_stack - sizeof(struct tcb));
+ memcpy(copy, t, sizeof(struct tcb));
+ clone_regs(copy, t);
+
+ rpc_stack -= BASE_PAGE_SIZE;
+ mark_rpc_inaccessible(t, rpc_stack, t->rpc_stack);
+ t->rpc_stack = rpc_stack;
+}
+
+void load_context(struct tcb *t)
+{
+ vm_t rpc_stack = t->rpc_stack;
+ struct tcb *copy =
+ (struct tcb *)(rpc_stack + BASE_PAGE_SIZE - sizeof(struct tcb));
+ memcpy(t, copy, sizeof(struct tcb));
+ clone_regs(t, copy);
+
+ mark_rpc_accessible(t, rpc_stack, t->rpc_stack);
+}
diff --git a/common/uapi/conf.c b/common/uapi/conf.c
index 7e88358..2094604 100644
--- a/common/uapi/conf.c
+++ b/common/uapi/conf.c
@@ -57,6 +57,10 @@ SYSCALL_DEFINE2(conf_set)(sys_arg_t param, sys_arg_t val)
*/
SYSCALL_DEFINE1(poweroff)(sys_arg_t type)
{
+ struct tcb *t = cur_tcb();
+ if (!(has_cap(t->caps, CAP_POWER)))
+ return SYS_RET1(ERR_PERM);
+
switch (type) {
case SHUTDOWN:
case COLD_REBOOT:
diff --git a/common/uapi/ipc.c b/common/uapi/ipc.c
index cb53ee3..e45c7dd 100644
--- a/common/uapi/ipc.c
+++ b/common/uapi/ipc.c
@@ -18,7 +18,7 @@
*/
SYSCALL_DEFINE1(ipc_server)(sys_arg_t callback)
{
- cur_tcb()->callback = callback;
+ cur_proc()->callback = callback;
return SYS_RET1(OK);
}
@@ -50,8 +50,12 @@ static struct sys_ret do_ipc(sys_arg_t pid,
if (!r->callback)
return SYS_RET1(ERR_NOINIT);
- /** \todo place data on rpc stack and clone into virtual memory */
+ clone_uvmem(r->proc.vmem, t->rpc.vmem);
+ use_vmem(t->rpc.vmem);
+ save_context(t);
set_return(t, r->callback);
+ /** @todo associate thread with new proc, should be done in tcb.c I
+ * think */
if (!fwd)
t->eid = t->pid;
@@ -105,7 +109,13 @@ 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();
- /* something like return_from_callback(t, r) */
+ load_context(t);
+
+ if (is_rpc(t))
+ use_vmem(t->rpc.vmem);
+ else
+ use_vmem(t->proc.vmem);
+
return SYS_RET6(OK, t->tid, d0, d1, d2, d3);
}
diff --git a/common/uapi/proc.c b/common/uapi/proc.c
index 969dcd6..7a078da 100644
--- a/common/uapi/proc.c
+++ b/common/uapi/proc.c
@@ -32,6 +32,8 @@ SYSCALL_DEFINE5(create)(sys_arg_t func,
if (!t)
return SYS_RET1(ERR_OOMEM);
+ alloc_stack(t);
+
set_args(t, SYS_RET5(t->tid, d0, d1, d2, d3));
set_return(t, func);
@@ -55,7 +57,7 @@ SYSCALL_DEFINE5(create)(sys_arg_t func,
*/
SYSCALL_DEFINE0(fork)(){
struct tcb *c = cur_proc();
- if (!(get_caps(c->caps, 0) & CAP_PROC))
+ if (!(has_cap(c->caps, CAP_PROC)))
return SYS_RET1(ERR_PERM);
struct tcb *t = create_proc(eff_proc());
@@ -118,7 +120,7 @@ SYSCALL_DEFINE2(exec)(sys_arg_t bin, sys_arg_t interp){
SYSCALL_DEFINE2(spawn)(sys_arg_t bin, sys_arg_t interp)
{
struct tcb *c = cur_proc();
- if (!(get_caps(c->caps, 0) & CAP_PROC))
+ if (!(has_cap(c->caps, CAP_PROC)))
return SYS_RET1(ERR_PERM);
struct tcb *t = create_proc(NULL);
@@ -139,7 +141,7 @@ SYSCALL_DEFINE2(spawn)(sys_arg_t bin, sys_arg_t interp)
SYSCALL_DEFINE1(kill)(sys_arg_t tid)
{
struct tcb *c = cur_proc();
- if (!(get_caps(c->caps, 0) & CAP_PROC))
+ if (!(has_cap(c->caps, CAP_PROC)))
return SYS_RET1(ERR_PERM);
return SYS_RET1(OK);
@@ -157,7 +159,7 @@ SYSCALL_DEFINE1(kill)(sys_arg_t tid)
*/
SYSCALL_DEFINE1(swap)(sys_arg_t tid){
struct tcb *c = cur_proc();
- if (!(get_caps(c->caps, 0) & CAP_PROC))
+ if (!(has_cap(c->caps, CAP_PROC)))
return SYS_RET1(ERR_PERM);
struct tcb *t = get_tcb(tid);
diff --git a/include/apos/caps.h b/include/apos/caps.h
index e8ef76b..be64663 100644
--- a/include/apos/caps.h
+++ b/include/apos/caps.h
@@ -6,6 +6,9 @@
* Capabilities of threads.
* \todo The list of capabilities should maybe be placed in some other file so
* as to easier extract it into userspace programs.
+ *
+ * @todo is it realistic to assume we will never need more than 32 capabilities?
+ * If so, we can remove the offset nonsense.
*/
#include <apos/bits.h>
@@ -22,6 +25,9 @@ enum {
/** Thread is allowed to force interrupt to callback in other thread. */
CAP_CALL = (1 << 2),
+
+ /** Thread is allowed to shut down system. */
+ CAP_POWER = (1 << 3),
};
/**
@@ -67,4 +73,13 @@ enum {
*/
#define get_caps(x, o) (x)
+/**
+ * Check if something has capability.
+ *
+ * @param x Capabilities to check in.
+ * @param c Capability to check for.
+ * @return \ref true if \p x has \p c, \ref false otherwise.
+ */
+#define has_cap(x, c) (x & c)
+
#endif /* APOS_CAPS_H */
diff --git a/include/apos/sizes.h b/include/apos/sizes.h
index 2eafa2e..40c3590 100644
--- a/include/apos/sizes.h
+++ b/include/apos/sizes.h
@@ -122,46 +122,40 @@
#define SZ_32G 0x000800000000UL
/** 64GiB. */
-#define SZ_64G 0x000400000000UL
+#define SZ_64G 0x001000000000UL
/** 128GiB. */
-#define SZ_128G 0x000800000000UL
+#define SZ_128G 0x002000000000UL
/** 256GiB. */
-#define SZ_256G 0x001000000000UL
+#define SZ_256G 0x004000000000UL
/** 512GiB. */
-#define SZ_512G 0x002000000000UL
+#define SZ_512G 0x008000000000UL
/** 1TiB. */
-#define SZ_1T 0x004000000000UL
+#define SZ_1T 0x010000000000UL
/** 2TiB. */
-#define SZ_2T 0x008000000000UL
+#define SZ_2T 0x020000000000UL
/** 4TiB. */
-#define SZ_4T 0x010000000000UL
+#define SZ_4T 0x040000000000UL
/** 8TiB. */
-#define SZ_8T 0x020000000000UL
+#define SZ_8T 0x080000000000UL
/** 16TiB. */
-#define SZ_16T 0x040000000000UL
+#define SZ_16T 0x100000000000UL
/** 32TiB. */
-#define SZ_32T 0x080000000000UL
+#define SZ_32T 0x200000000000UL
/** 64TiB. */
-#define SZ_64T 0x100000000000UL
+#define SZ_64T 0x400000000000UL
/** 128TiB. */
-#define SZ_128T 0x200000000000UL
-
-/** 256TiB. */
-#define SZ_256T 0x400000000000UL
-
-/** 512TiB. */
-#define SZ_512T 0x800000000000UL
+#define SZ_128T 0x8000000000000UL
#else
diff --git a/include/apos/tcb.h b/include/apos/tcb.h
index 0368957..9ed916f 100644
--- a/include/apos/tcb.h
+++ b/include/apos/tcb.h
@@ -28,7 +28,7 @@
* @param t Thread to check.
* @return \c true if thread is in RPC, \c false otherwise.
*/
-#define is_rpc(t) (t->rid == t->pid)
+#define is_rpc(t) (t->rid != t->pid)
/**
* Get the process thread of current thread.
@@ -63,14 +63,14 @@ struct tcb_ctx {
/** Enum for notification states. */
enum tcb_notify {
+ /** Thread is free to be notified. */
+ NOTIFY_WAITING = 0,
+
/** Thread has notifcations queued. */
NOTIFY_QUEUED,
/** Thread is running notification handler. */
NOTIFY_RUNNING,
-
- /** Thread is free to be notified. */
- NOTIFY_WAITING
};
/** Thread control block. Main way to handle threads. */
@@ -133,6 +133,9 @@ struct tcb {
/** Address of this thread's stack top. */
vm_t thread_stack_top;
+ /** Current address of usable rpc stack. */
+ vm_t rpc_stack;
+
/** \todo Check if each thread should be allowed more than just one
* region of thread local storage. */
/** Possible thread local storage. */
@@ -171,7 +174,7 @@ void destroy_tcbs();
* and the \ref tcb structure itself with a unique thread ID. If in a new
* process context, a new process address space is created as well.
*
- * Userspace stack is allocated with \ref alloc_stacks().
+ * Userspace stack is allocated with \ref alloc_stack().
*
* \todo Thread local storage?
*
@@ -338,7 +341,7 @@ stat_t clone_rpc_maps(struct tcb *r);
* @param t Thread whose stacks to allocate.
* @return \ref OK on success, \ref ERR_OOMEM if out of memory.
*/
-stat_t alloc_stacks(struct tcb *t);
+stat_t alloc_stack(struct tcb *t);
/**
* Set address to jump to when returning to userspace.
@@ -356,4 +359,18 @@ void set_return(struct tcb *t, vm_t r);
*/
bool running(struct tcb *t);
+/**
+ * Save thread context for rpc call.
+ *
+ * @param t Thread whose context to save.
+ */
+void save_context(struct tcb *t);
+
+/**
+ * Load thread context from rpc call.
+ *
+ * @param t Thread whose context to restore.
+ */
+void load_context(struct tcb *t);
+
#endif /* APOS_TCB_H */
diff --git a/include/arch/proc.h b/include/arch/proc.h
index e2b99ea..6a2c036 100644
--- a/include/arch/proc.h
+++ b/include/arch/proc.h
@@ -49,6 +49,14 @@ struct sys_ret get_args(struct tcb *t);
void set_thread(struct tcb *t);
/**
+ * Get current userspace stack.
+ *
+ * @param t Thread whose stack to query.
+ * @return Current stack address.
+ */
+vm_t get_stack(struct tcb *t);
+
+/**
* Copy registers from tcb save area to address \p p.
* Intended to be used for copying thread state to rpc stack.
*