blob: a6605d6eb50926a3e956d77d48e6f311d5f1e9dd [file] [log] [blame]
#ifndef PARLIB_ARCH_VCORE_H
#define PARLIB_ARCH_VCORE_H
#include <ros/common.h>
#include <ros/trapframe.h>
#include <arch/arch.h>
#include <ros/syscall.h>
#include <ros/procdata.h>
#include <assert.h>
#include <sys/vcore-tls.h>
#ifdef __riscv64
# define REG_L "ld"
# define REG_S "sd"
#else
# define REG_L "lw"
# define REG_S "sw"
#endif
/* Register saves and restores happen in asm. */
typedef void (*helper_fn)(struct hw_trapframe*, struct preempt_data*, uint32_t);
void __pop_ros_tf_regs(struct hw_trapframe *tf, struct preempt_data* vcpd,
uint32_t vcoreid, helper_fn helper) __attribute__((noreturn));
void __save_ros_tf_regs(struct hw_trapframe *tf) __attribute__((returns_twice));
/* Helper function that may handle notifications after re-enabling them. */
static void __pop_ros_tf_notifs(struct hw_trapframe *tf,
struct preempt_data* vcpd, uint32_t vcoreid)
{
vcpd->notif_disabled = FALSE;
__sync_synchronize();
if(vcpd->notif_pending)
ros_syscall(SYS_self_notify, vcoreid, 0, 0, 0, 0, 0);
}
/* Helper function that won't handle notifications after re-enabling them. */
static void __pop_ros_tf_notifs_raw(struct hw_trapframe *tf,
struct preempt_data* vcpd, uint32_t vcoreid)
{
vcpd->notif_disabled = FALSE;
}
static inline void __pop_ros_tf(struct hw_trapframe *tf, uint32_t vcoreid,
helper_fn helper)
{
// since we're changing the stack, move stuff into regs for now
register uint32_t _vcoreid = vcoreid;
register struct hw_trapframe* _tf = tf;
set_stack_pointer((void*)tf->gpr[GPR_SP]);
tf = _tf;
vcoreid = _vcoreid;
struct preempt_data* vcpd = &__procdata.vcore_preempt_data[vcoreid];
__pop_ros_tf_regs(tf, vcpd, vcoreid, helper);
}
/* Pops a user context, reanabling notifications at the same time. A Userspace
* scheduler can call this when transitioning off the transition stack.
*
* Make sure you clear the notif_pending flag, and then check the queue before
* calling this. If notif_pending is not clear, this will self_notify this
* core, since it should be because we missed a notification message while
* notifs were disabled.
*
* The important thing is that it can a notification after it enables
* notifications, and when it gets resumed it can ultimately run the new
* context. Enough state is saved in the running context and stack to continue
* running. */
static inline void pop_user_ctx(struct user_context *ctx, uint32_t vcoreid)
{
struct hw_trapframe *tf = &ctx->tf.hw_tf;
assert(ctx->type == ROS_HW_CTX);
__pop_ros_tf(tf, vcoreid, &__pop_ros_tf_notifs);
}
/* Like the regular pop_user_ctx, but this one doesn't check or clear
* notif_pending. */
static inline void pop_user_ctx_raw(struct user_context *ctx, uint32_t vcoreid)
{
struct hw_trapframe *tf = &ctx->tf.hw_tf;
assert(ctx->type == ROS_HW_CTX);
__pop_ros_tf(tf, vcoreid, &__pop_ros_tf_notifs_raw);
}
/* Save the current context/registers into the given ctx, setting the pc of the
* tf to the end of this function. You only need to save that which you later
* restore with pop_user_ctx(). */
static inline void save_user_ctx(struct user_context *ctx)
{
struct hw_trapframe *tf = &ctx->tf.hw_tf;
ctx->type = ROS_HW_CTX;
__save_ros_tf_regs(tf);
}
static inline void init_user_ctx(struct user_context *ctx, uint32_t entry_pt,
uint32_t stack_top)
{
struct hw_trapframe *u_tf = &ctx->tf.hw_tf;
ctx->type = ROS_HW_CTX;
memset(u_tf, 0, sizeof(*u_tf));
u_tf->gpr[GPR_SP] = stack_top;
u_tf->epc = entry_pt;
}
#define __vcore_id_on_entry \
({ \
register int temp asm ("a0"); \
temp; \
})
#error implement these
static bool has_refl_fault(struct user_context *ctx)
{
return 0;
}
static void clear_refl_fault(struct user_context *ctx)
{
}
static unsigned int __arch_refl_get_nr(struct user_context *ctx)
{
return 0;
}
static unsigned int __arch_refl_get_err(struct user_context *ctx)
{
return 0;
}
static unsigned long __arch_refl_get_aux(struct user_context *ctx)
{
return 0;
}
#endif /* PARLIB_ARCH_VCORE_H */