#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/exec.h>
#include <sys/vnode.h>
#include <sys/signalvar.h>
#include <uvm/uvm_extern.h>
#include <machine/cpu.h>
#include <machine/frame.h>
#include <machine/trap.h>
#include <machine/bus.h>
#include <sparc64/sparc64/cache.h>
#define TOPFRAMEOFF (USPACE-sizeof(struct trapframe)-CC64FSZ)
#ifdef DEBUG
char cpu_forkname[] = "cpu_fork()";
#endif
void
cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb,
void (*func)(void *), void *arg)
{
struct pcb *opcb = &p1->p_addr->u_pcb;
struct pcb *npcb = &p2->p_addr->u_pcb;
struct trapframe *tf2;
struct rwindow *rp;
size_t pcbsz;
extern struct proc proc0;
(void)pmap_extract(pmap_kernel(), (vaddr_t)npcb,
&p2->p_md.md_pcbpaddr);
#ifdef NOTDEF_DEBUG
printf("cpu_fork()\n");
#endif
if (p1 == curproc) {
write_user_windows();
opcb->pcb_pstate = getpstate();
opcb->pcb_cwp = getcwp();
}
#ifdef DIAGNOSTIC
else if (p1 != &proc0)
panic("cpu_fork: curproc");
#endif
#ifdef DEBUG
opcb->lastcall = cpu_forkname;
#else
opcb->lastcall = NULL;
#endif
if (stack != NULL)
pcbsz = offsetof(struct pcb, pcb_rw);
else
pcbsz = sizeof(struct pcb);
bcopy((caddr_t)opcb, (caddr_t)npcb, pcbsz);
if (p1->p_md.md_fpstate) {
fpusave_proc(p1, 1);
p2->p_md.md_fpstate = malloc(sizeof(struct fpstate),
M_SUBPROC, M_WAITOK);
bcopy(p1->p_md.md_fpstate, p2->p_md.md_fpstate,
sizeof(struct fpstate));
} else
p2->p_md.md_fpstate = NULL;
tf2 = p2->p_md.md_tf = (struct trapframe *)
((long)npcb + USPACE - sizeof(*tf2));
*tf2 = *(struct trapframe *)((long)opcb + USPACE - sizeof(*tf2));
if (stack != NULL) {
npcb->pcb_nsaved = 0;
tf2->tf_out[6] = (u_int64_t)(u_long)stack - (BIAS + CC64FSZ);
tf2->tf_in[6] = 0;
}
if (tcb != NULL)
tf2->tf_global[7] = (u_int64_t)tcb;
rp = (struct rwindow *)((u_long)npcb + TOPFRAMEOFF);
*rp = *(struct rwindow *)((u_long)opcb + TOPFRAMEOFF);
rp->rw_local[0] = (long)func;
rp->rw_local[1] = (long)arg;
npcb->pcb_pc = (long)proc_trampoline - 8;
npcb->pcb_sp = (long)rp - BIAS;
if (p1 == &proc0)
tf2->tf_tstate =
((u_int64_t)ASI_PRIMARY_NO_FAULT << TSTATE_ASI_SHIFT) |
((PSTATE_USER) << TSTATE_PSTATE_SHIFT);
else
tf2->tf_tstate &=
~((PSTATE_PEF << TSTATE_PSTATE_SHIFT) | TSTATE_CCR);
#ifdef NOTDEF_DEBUG
printf("cpu_fork: Copying over trapframe: otf=%p ntf=%p sp=%p opcb=%p npcb=%p\n",
(struct trapframe *)((char *)opcb + USPACE - sizeof(*tf2)), tf2, rp, opcb, npcb);
printf("cpu_fork: tstate=%lx pc=%lx npc=%lx rsp=%lx\n",
(long)tf2->tf_tstate, (long)tf2->tf_pc, (long)tf2->tf_npc,
(long)(tf2->tf_out[6]));
db_enter();
#endif
}
void ipi_save_fpstate(void);
void ipi_drop_fpstate(void);
void
fpusave_cpu(struct cpu_info *ci, int save)
{
struct proc *p;
KDASSERT(ci == curcpu());
p = ci->ci_fpproc;
if (p == NULL)
return;
if (save)
savefpstate(p->p_md.md_fpstate);
else
clearfpstate();
ci->ci_fpproc = NULL;
}
void
fpusave_proc(struct proc *p, int save)
{
struct cpu_info *ci = curcpu();
#ifdef MULTIPROCESSOR
if (p == ci->ci_fpproc) {
u_int64_t s = intr_disable();
fpusave_cpu(ci, save);
intr_restore(s);
return;
}
for (ci = cpus; ci != NULL; ci = ci->ci_next) {
if (ci == curcpu())
continue;
if (ci->ci_fpproc != p)
continue;
sparc64_send_ipi(ci->ci_itid,
save ? ipi_save_fpstate : ipi_drop_fpstate, (vaddr_t)p, 0);
while (ci->ci_fpproc == p)
membar_sync();
break;
}
#else
if (p == ci->ci_fpproc)
fpusave_cpu(ci, save);
#endif
}
void
cpu_exit(struct proc *p)
{
if (p->p_md.md_fpstate != NULL) {
fpusave_proc(p, 0);
free(p->p_md.md_fpstate, M_SUBPROC, sizeof(struct fpstate));
p->p_md.md_fpstate = NULL;
}
}
struct kmem_va_mode kv_physwait = {
.kv_map = &phys_map,
.kv_wait = 1,
};
void
vmapbuf(struct buf *bp, vsize_t len)
{
struct kmem_dyn_mode kd_prefer = { .kd_waitok = 1 };
struct pmap *pm = vm_map_pmap(&bp->b_proc->p_vmspace->vm_map);
vaddr_t kva, uva;
vsize_t size, off;
#ifdef DIAGNOSTIC
if ((bp->b_flags & B_PHYS) == 0)
panic("vmapbuf");
#endif
bp->b_saveaddr = bp->b_data;
uva = trunc_page((vaddr_t)bp->b_data);
off = (vaddr_t)bp->b_data - uva;
size = round_page(off + len);
kd_prefer.kd_prefer = uva;
kva = (vaddr_t)km_alloc(size, &kv_physwait, &kp_none, &kd_prefer);
bp->b_data = (caddr_t)(kva + off);
while (size > 0) {
paddr_t pa;
if (pmap_extract(pm, uva, &pa) == FALSE)
panic("vmapbuf: null page frame");
else
pmap_kenter_pa(kva, pa, PROT_READ | PROT_WRITE);
uva += PAGE_SIZE;
kva += PAGE_SIZE;
size -= PAGE_SIZE;
}
pmap_update(pmap_kernel());
}
void
vunmapbuf(struct buf *bp, vsize_t len)
{
vaddr_t addr, off;
#ifdef DIAGNOSTIC
if ((bp->b_flags & B_PHYS) == 0)
panic("vunmapbuf");
#endif
addr = trunc_page((vaddr_t)bp->b_data);
off = (vaddr_t)bp->b_data - addr;
len = round_page(off + len);
pmap_kremove(addr, len);
pmap_update(pmap_kernel());
km_free((void *)addr, len, &kv_physwait, &kp_none);
bp->b_data = bp->b_saveaddr;
bp->b_saveaddr = NULL;
}