int __unaligned_fixup(struct utrapframe *uf) { u_char *addr; u_long val; u_int insn; int sig; sig = 0; addr = (u_char *)uf->uf_sfar; insn = *(u_int *)uf->uf_pc; flushw(); switch (IF_OP(insn)) { case IOP_LDST: switch (IF_F3_OP3(insn)) { case INS3_LDUH: val = __unaligned_load(addr, 2); __emul_store_reg(uf, IF_F3_RD(insn), val); break; case INS3_LDUW: val = __unaligned_load(addr, 4); __emul_store_reg(uf, IF_F3_RD(insn), val); break; case INS3_LDX: val = __unaligned_load(addr, 8); __emul_store_reg(uf, IF_F3_RD(insn), val); break; case INS3_LDSH: val = __unaligned_load(addr, 2); __emul_store_reg(uf, IF_F3_RD(insn), IF_SEXT(val, 16)); break; case INS3_LDSW: val = __unaligned_load(addr, 4); __emul_store_reg(uf, IF_F3_RD(insn), IF_SEXT(val, 32)); break; case INS3_STH: val = __emul_fetch_reg(uf, IF_F3_RD(insn)); __unaligned_store(addr, val, 2); break; case INS3_STW: val = __emul_fetch_reg(uf, IF_F3_RD(insn)); __unaligned_store(addr, val, 4); break; case INS3_STX: val = __emul_fetch_reg(uf, IF_F3_RD(insn)); __unaligned_store(addr, val, 8); break; default: sig = SIGILL; break; } break; default: sig = SIGILL; break; } return (sig); }
int cpu_set_user_tls(struct thread *td, void *tls_base) { if (td == curthread) flushw(); td->td_frame->tf_global[7] = (uint64_t) tls_base; return (0); }
int __emul_insn(struct utrapframe *uf) { u_long reg, res; u_long *addr; u_int insn; int sig; int rd; int i; sig = 0; insn = *(u_int *)uf->uf_pc; flushw(); switch (IF_OP(insn)) { case IOP_MISC: switch (IF_F3_OP3(insn)) { case INS2_POPC: if (IF_F3_RS1(insn) != 0) { sig = SIGILL; break; } reg = __emul_f3_op2(uf, insn); for (i = 0; i < 64; i++) res += (reg >> i) & 1; __emul_store_reg(uf, IF_F3_RD(insn), res); break; default: sig = SIGILL; break; } break; case IOP_LDST: switch (IF_F3_OP3(insn)) { case INS3_LDQF: rd = INSFPdq_RN(IF_F3_RD(insn)); addr = (u_long *)__emul_f3_memop_addr(uf, insn); __fpu_setreg64(rd, addr[0]); __fpu_setreg64(rd + 2, addr[1]); break; case INS3_STQF: rd = INSFPdq_RN(IF_F3_RD(insn)); addr = (u_long *)__emul_f3_memop_addr(uf, insn); addr[0] = __fpu_getreg64(rd); addr[1] = __fpu_getreg64(rd + 2); break; default: sig = SIGILL; break; } break; default: sig = SIGILL; break; } return (sig); }
void cpu_set_upcall_kse(struct thread *td, void (*entry)(void *), void *arg, stack_t *stack) { struct trapframe *tf; uint64_t sp; if (td == curthread) flushw(); tf = td->td_frame; sp = (uint64_t)stack->ss_sp + stack->ss_size; tf->tf_out[0] = (uint64_t)arg; tf->tf_out[6] = sp - SPOFF - sizeof(struct frame); tf->tf_tpc = (uint64_t)entry; tf->tf_tnpc = tf->tf_tpc + 4; td->td_retval[0] = tf->tf_out[0]; td->td_retval[1] = tf->tf_out[1]; }
int rwindow_save(struct thread *td) { struct rwindow *rw; struct pcb *pcb; u_long *ausp; u_long usp; int error; int i; pcb = td->td_pcb; CTR3(KTR_TRAP, "rwindow_save: td=%p (%s) nsaved=%d", td, td->td_proc->p_comm, pcb->pcb_nsaved); flushw(); KASSERT(pcb->pcb_nsaved < MAXWIN, ("rwindow_save: pcb_nsaved > MAXWIN")); if ((i = pcb->pcb_nsaved) == 0) return (0); ausp = pcb->pcb_rwsp; rw = pcb->pcb_rw; error = 0; do { usp = *ausp; CTR1(KTR_TRAP, "rwindow_save: usp=%#lx", usp); usp += SPOFF; if ((error = (usp & 0x7)) != 0) break; error = copyout(rw, (void *)usp, sizeof *rw); if (error) break; ausp++; rw++; } while (--i > 0); CTR1(KTR_TRAP, "rwindow_save: error=%d", error); if (error == 0) pcb->pcb_nsaved = 0; return (error == 0 ? 0 : SIGILL); }
/* * Finish a fork operation, with process p2 nearly set up. * Copy and update the pcb, set up the stack so that the child * ready to run and return to user mode. */ void cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) { struct trapframe *tf; struct frame *fp; struct pcb *pcb1; struct pcb *pcb2, *pcb2orig; vm_offset_t sp; int error; int i; KASSERT(td1 == curthread || td1 == &thread0, ("cpu_fork: p1 not curproc and not proc0")); if ((flags & RFPROC) == 0) return; p2->p_md.md_sigtramp = td1->td_proc->p_md.md_sigtramp; p2->p_md.md_utrap = utrap_hold(td1->td_proc->p_md.md_utrap); /* The pcb must be aligned on a 64-byte boundary. */ pcb1 = td1->td_pcb; pcb2orig = (struct pcb *)((td2->td_kstack + td2->td_kstack_pages * PAGE_SIZE - sizeof(struct pcb)) & ~0x3fUL); pcb2 = (struct pcb *)TLB_PHYS_TO_DIRECT(vtophys((vm_offset_t)pcb2orig)); td2->td_pcb = pcb2; /* * Ensure that p1's pcb is up to date. */ critical_enter(); if ((td1->td_frame->tf_fprs & FPRS_FEF) != 0) savefpctx(pcb1->pcb_ufp); critical_exit(); /* Make sure the copied windows are spilled. */ flushw(); /* Copy the pcb (this will copy the windows saved in the pcb, too). */ bcopy(pcb1, pcb2, sizeof(*pcb1)); /* * If we're creating a new user process and we're sharing the address * space, the parent's top most frame must be saved in the pcb. The * child will pop the frame when it returns to user mode, and may * overwrite it with its own data causing much suffering for the * parent. We check if its already in the pcb, and if not copy it * in. Its unlikely that the copyin will fail, but if so there's not * much we can do. The parent will likely crash soon anyway in that * case. */ if ((flags & RFMEM) != 0 && td1 != &thread0) { sp = td1->td_frame->tf_sp; for (i = 0; i < pcb1->pcb_nsaved; i++) { if (pcb1->pcb_rwsp[i] == sp) break; } if (i == pcb1->pcb_nsaved) { error = copyin((caddr_t)sp + SPOFF, &pcb1->pcb_rw[i], sizeof(struct rwindow)); if (error == 0) { pcb1->pcb_rwsp[i] = sp; pcb1->pcb_nsaved++; } } } /* * Create a new fresh stack for the new process. * Copy the trap frame for the return to user mode as if from a * syscall. This copies most of the user mode register values. */ tf = (struct trapframe *)pcb2orig - 1; bcopy(td1->td_frame, tf, sizeof(*tf)); tf->tf_out[0] = 0; /* Child returns zero */ tf->tf_out[1] = 0; tf->tf_tstate &= ~TSTATE_XCC_C; /* success */ tf->tf_fprs = 0; tf->tf_wstate = WSTATE_U64; td2->td_frame = tf; fp = (struct frame *)tf - 1; fp->fr_local[0] = (u_long)fork_return; fp->fr_local[1] = (u_long)td2; fp->fr_local[2] = (u_long)tf; /* Terminate stack traces at this frame. */ fp->fr_pc = fp->fr_fp = 0; pcb2->pcb_sp = (u_long)fp - SPOFF; pcb2->pcb_pc = (u_long)fork_trampoline - 8; pcb2->pcb_kstack = (uint64_t)(((char *)pcb2orig) - (CCFSZ + SPOFF)); /* Setup to release spin count in fork_exit(). */ td2->td_md.md_spinlock_count = 1; td2->td_md.md_saved_pil = 0; /* * Now, cpu_switch() can schedule the new process. */ }