Move trap reflection bits into their own header
[akaros.git] / user / parlib / include / 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         tf = _tf;
56         vcoreid = _vcoreid;
57         struct preempt_data* vcpd = &__procdata.vcore_preempt_data[vcoreid];
58         __pop_ros_tf_regs(tf, vcpd, vcoreid, helper);
59 }
60
61 /* Pops a user context, reanabling notifications at the same time.  A Userspace
62  * scheduler can call this when transitioning off the transition stack.
63  *
64  * Make sure you clear the notif_pending flag, and then check the queue before
65  * calling this.  If notif_pending is not clear, this will self_notify this
66  * core, since it should be because we missed a notification message while
67  * notifs were disabled. 
68  *
69  * The important thing is that it can a notification after it enables
70  * notifications, and when it gets resumed it can ultimately run the new
71  * context.  Enough state is saved in the running context and stack to continue
72  * running. */
73 static inline void pop_user_ctx(struct user_context *ctx, uint32_t vcoreid)
74 {
75         struct hw_trapframe *tf = &ctx->tf.hw_tf;
76         assert(ctx->type == ROS_HW_CTX);
77         __pop_ros_tf(tf, vcoreid, &__pop_ros_tf_notifs);
78 }
79
80 /* Like the regular pop_user_ctx, but this one doesn't check or clear
81  * notif_pending. */
82 static inline void pop_user_ctx_raw(struct user_context *ctx, uint32_t vcoreid)
83 {
84         struct hw_trapframe *tf = &ctx->tf.hw_tf;
85         assert(ctx->type == ROS_HW_CTX);
86         __pop_ros_tf(tf, vcoreid, &__pop_ros_tf_notifs_raw);
87 }
88
89 /* Save the current context/registers into the given ctx, setting the pc of the
90  * tf to the end of this function.  You only need to save that which you later
91  * restore with pop_user_ctx(). */
92 static inline void save_user_ctx(struct user_context *ctx)
93 {
94         struct hw_trapframe *tf = &ctx->tf.hw_tf;
95         ctx->type = ROS_HW_CTX;
96         __save_ros_tf_regs(tf);
97 }
98
99 static inline void init_user_ctx(struct user_context *ctx, uint32_t entry_pt,
100                                  uint32_t stack_top)
101 {
102         struct hw_trapframe *u_tf = &ctx->tf.hw_tf;
103         ctx->type = ROS_HW_CTX;
104         memset(u_tf, 0, sizeof(*u_tf));
105         u_tf->gpr[GPR_SP] = stack_top;
106         u_tf->epc = entry_pt;
107 }
108
109 static inline uintptr_t get_user_ctx_stack(struct user_context *ctx)
110 {
111         return ctx->tf.hw_tf.gpr[GPR_SP];
112 }
113
114 #define __vcore_id_on_entry \
115 ({ \
116         register int temp asm ("a0"); \
117         temp; \
118 })
119
120 __END_DECLS