ed22c0c144800ab8f46e9a37156162ccc098c197
[akaros.git] / user / parlib / include / parlib / riscv / vcore.h
1 #pragma once
2
3 #include <parlib/common.h>
4 #include <ros/trapframe.h>
5 #include <parlib/arch/arch.h>
6 #include <ros/syscall.h>
7 #include <ros/procdata.h>
8 #include <parlib/assert.h>
9 #include <sys/tls.h>
10
11 __BEGIN_DECLS
12
13 #ifdef __riscv64
14 # define REG_L "ld"
15 # define REG_S "sd"
16 #else
17 # define REG_L "lw"
18 # define REG_S "sw"
19 #endif
20
21 /* Register saves and restores happen in asm. */
22 typedef void (*helper_fn)(struct hw_trapframe*, struct preempt_data*, uint32_t);
23 void __pop_ros_tf_regs(struct hw_trapframe *tf, struct preempt_data* vcpd,
24                     uint32_t vcoreid, helper_fn helper) __attribute__((noreturn));
25 void __save_ros_tf_regs(struct hw_trapframe *tf) __attribute__((returns_twice));
26
27 /* Helper function that may handle notifications after re-enabling them. */
28 static void __pop_ros_tf_notifs(struct hw_trapframe *tf,
29                                 struct preempt_data* vcpd, uint32_t vcoreid)
30 {
31         vcpd->notif_disabled = FALSE;
32
33         __sync_synchronize();
34
35         if(vcpd->notif_pending)
36                 ros_syscall(SYS_self_notify, vcoreid, 0, 0, 0, 0, 0);
37 }
38
39 /* Helper function that won't handle notifications after re-enabling them. */
40 static void __pop_ros_tf_notifs_raw(struct hw_trapframe *tf,
41                                     struct preempt_data* vcpd, uint32_t vcoreid)
42 {
43         vcpd->notif_disabled = FALSE;
44 }
45
46 static inline void __pop_ros_tf(struct hw_trapframe *tf, uint32_t vcoreid,
47                                 helper_fn helper)
48 {
49         // since we're changing the stack, move stuff into regs for now
50         register uint32_t _vcoreid = vcoreid;
51         register struct hw_trapframe* _tf = tf;
52
53         set_stack_pointer((void*)tf->gpr[GPR_SP]);
54
55 #warning "Need to worry about notif_pending and stack growth?"
56         tf = _tf;
57         vcoreid = _vcoreid;
58         struct preempt_data* vcpd = &__procdata.vcore_preempt_data[vcoreid];
59         __pop_ros_tf_regs(tf, vcpd, vcoreid, helper);
60 }
61
62 /* Pops a user context, reanabling notifications at the same time.  A Userspace
63  * scheduler can call this when transitioning off the transition stack.
64  *
65  * Make sure you clear the notif_pending flag, and then check the queue before
66  * calling this.  If notif_pending is not clear, this will self_notify this
67  * core, since it should be because we missed a notification message while
68  * notifs were disabled. 
69  *
70  * The important thing is that it can a notification after it enables
71  * notifications, and when it gets resumed it can ultimately run the new
72  * context.  Enough state is saved in the running context and stack to continue
73  * running. */
74 static inline void pop_user_ctx(struct user_context *ctx, uint32_t vcoreid)
75 {
76         struct hw_trapframe *tf = &ctx->tf.hw_tf;
77         assert(ctx->type == ROS_HW_CTX);
78         __pop_ros_tf(tf, vcoreid, &__pop_ros_tf_notifs);
79 }
80
81 /* Like the regular pop_user_ctx, but this one doesn't check or clear
82  * notif_pending. */
83 static inline void pop_user_ctx_raw(struct user_context *ctx, uint32_t vcoreid)
84 {
85         struct hw_trapframe *tf = &ctx->tf.hw_tf;
86         assert(ctx->type == ROS_HW_CTX);
87         __pop_ros_tf(tf, vcoreid, &__pop_ros_tf_notifs_raw);
88 }
89
90 /* Save the current context/registers into the given ctx, setting the pc of the
91  * tf to the end of this function.  You only need to save that which you later
92  * restore with pop_user_ctx(). */
93 static inline void save_user_ctx(struct user_context *ctx)
94 {
95         struct hw_trapframe *tf = &ctx->tf.hw_tf;
96         ctx->type = ROS_HW_CTX;
97         __save_ros_tf_regs(tf);
98 }
99
100 static inline void init_user_ctx(struct user_context *ctx, uint32_t entry_pt,
101                                  uint32_t stack_top)
102 {
103         struct hw_trapframe *u_tf = &ctx->tf.hw_tf;
104         ctx->type = ROS_HW_CTX;
105         memset(u_tf, 0, sizeof(*u_tf));
106         u_tf->gpr[GPR_SP] = stack_top;
107         u_tf->epc = entry_pt;
108 }
109
110 static inline uintptr_t get_user_ctx_stack(struct user_context *ctx)
111 {
112         return ctx->tf.hw_tf.gpr[GPR_SP];
113 }
114
115 #define __vcore_id_on_entry \
116 ({ \
117         register int temp asm ("a0"); \
118         temp; \
119 })
120
121 __END_DECLS