#include <sys/sysctl.h>
#include <sys/vmmeter.h>
#include <sys/bus.h>
-#include <sys/upcall.h>
#include <sys/usched.h>
#include <sys/reg.h>
}
/*
- * Stack frame on entry to function. %rax will contain the function vector,
- * %rcx will contain the function data. flags, rcx, and rax will have
- * already been pushed on the stack.
- */
-struct upc_frame {
- register_t rax;
- register_t rcx;
- register_t rdx;
- register_t flags;
- register_t oldip;
-};
-
-void
-sendupcall(struct vmupcall *vu, int morepending)
-{
- struct lwp *lp = curthread->td_lwp;
- struct trapframe *regs;
- struct upcall upcall;
- struct upc_frame upc_frame;
- int crit_count = 0;
-
- /*
- * If we are a virtual kernel running an emulated user process
- * context, switch back to the virtual kernel context before
- * trying to post the signal.
- */
- if (lp->lwp_vkernel && lp->lwp_vkernel->ve) {
- lp->lwp_md.md_regs->tf_trapno = 0;
- vkernel_trap(lp, lp->lwp_md.md_regs);
- }
-
- /*
- * Get the upcall data structure
- */
- if (copyin(lp->lwp_upcall, &upcall, sizeof(upcall)) ||
- copyin((char *)upcall.upc_uthread + upcall.upc_critoff, &crit_count, sizeof(int))
- ) {
- vu->vu_pending = 0;
- kprintf("bad upcall address\n");
- return;
- }
-
- /*
- * If the data structure is already marked pending or has a critical
- * section count, mark the data structure as pending and return
- * without doing an upcall. vu_pending is left set.
- */
- if (upcall.upc_pending || crit_count >= vu->vu_pending) {
- if (upcall.upc_pending < vu->vu_pending) {
- upcall.upc_pending = vu->vu_pending;
- copyout(&upcall.upc_pending, &lp->lwp_upcall->upc_pending,
- sizeof(upcall.upc_pending));
- }
- return;
- }
-
- /*
- * We can run this upcall now, clear vu_pending.
- *
- * Bump our critical section count and set or clear the
- * user pending flag depending on whether more upcalls are
- * pending. The user will be responsible for calling
- * upc_dispatch(-1) to process remaining upcalls.
- */
- vu->vu_pending = 0;
- upcall.upc_pending = morepending;
- ++crit_count;
- copyout(&upcall.upc_pending, &lp->lwp_upcall->upc_pending,
- sizeof(upcall.upc_pending));
- copyout(&crit_count, (char *)upcall.upc_uthread + upcall.upc_critoff,
- sizeof(int));
-
- /*
- * Construct a stack frame and issue the upcall
- */
- regs = lp->lwp_md.md_regs;
- upc_frame.rax = regs->tf_rax;
- upc_frame.rcx = regs->tf_rcx;
- upc_frame.rdx = regs->tf_rdx;
- upc_frame.flags = regs->tf_rflags;
- upc_frame.oldip = regs->tf_rip;
- if (copyout(&upc_frame, (void *)(regs->tf_rsp - sizeof(upc_frame)),
- sizeof(upc_frame)) != 0) {
- kprintf("bad stack on upcall\n");
- } else {
- regs->tf_rax = (register_t)vu->vu_func;
- regs->tf_rcx = (register_t)vu->vu_data;
- regs->tf_rdx = (register_t)lp->lwp_upcall;
- regs->tf_rip = (register_t)vu->vu_ctx;
- regs->tf_rsp -= sizeof(upc_frame);
- }
-}
-
-/*
- * fetchupcall occurs in the context of a system call, which means that
- * we have to return EJUSTRETURN in order to prevent eax and edx from
- * being overwritten by the syscall return value.
- *
- * if vu is not NULL we return the new context in %edx, the new data in %ecx,
- * and the function pointer in %eax.
- */
-int
-fetchupcall(struct vmupcall *vu, int morepending, void *rsp)
-{
- struct upc_frame upc_frame;
- struct lwp *lp = curthread->td_lwp;
- struct trapframe *regs;
- int error;
- struct upcall upcall;
- int crit_count;
-
- regs = lp->lwp_md.md_regs;
-
- error = copyout(&morepending, &lp->lwp_upcall->upc_pending, sizeof(int));
- if (error == 0) {
- if (vu) {
- /*
- * This jumps us to the next ready context.
- */
- vu->vu_pending = 0;
- error = copyin(lp->lwp_upcall, &upcall, sizeof(upcall));
- crit_count = 0;
- if (error == 0)
- error = copyin((char *)upcall.upc_uthread + upcall.upc_critoff, &crit_count, sizeof(int));
- ++crit_count;
- if (error == 0)
- error = copyout(&crit_count, (char *)upcall.upc_uthread + upcall.upc_critoff, sizeof(int));
- regs->tf_rax = (register_t)vu->vu_func;
- regs->tf_rcx = (register_t)vu->vu_data;
- regs->tf_rdx = (register_t)lp->lwp_upcall;
- regs->tf_rip = (register_t)vu->vu_ctx;
- regs->tf_rsp = (register_t)rsp;
- } else {
- /*
- * This returns us to the originally interrupted code.
- */
- error = copyin(rsp, &upc_frame, sizeof(upc_frame));
- regs->tf_rax = upc_frame.rax;
- regs->tf_rcx = upc_frame.rcx;
- regs->tf_rdx = upc_frame.rdx;
- regs->tf_rflags = (regs->tf_rflags & ~PSL_USERCHANGE) |
- (upc_frame.flags & PSL_USERCHANGE);
- regs->tf_rip = upc_frame.oldip;
- regs->tf_rsp = (register_t)((char *)rsp + sizeof(upc_frame));
- }
- }
- if (error == 0)
- error = EJUSTRETURN;
- return(error);
-}
-
-/*
* cpu_idle() represents the idle LWKT. You cannot return from this function
* (unless you want to blow things up!). Instead we look for runnable threads
* and loop or halt as appropriate. Giant is not held on entry to the thread.