/* $NetBSD: mips_emul.c,v 1.31 2021/11/16 06:15:48 simonb Exp $ */ /* * Copyright (c) 1999 Shuichiro URATA. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD: mips_emul.c,v 1.31 2021/11/16 06:15:48 simonb Exp $"); #include #include #include #include #include #include #include #include /* symbolic register indices */ #include #include /* for VM_MAX_ADDRESS */ #include static inline void send_sigsegv(intptr_t, uint32_t, struct trapframe *, uint32_t); static inline void update_pc(struct trapframe *, uint32_t); static void mips_emul_ll(uint32_t, struct trapframe *, uint32_t); static void mips_emul_sc(uint32_t, struct trapframe *, uint32_t); /* * MIPS2 LL instruction emulation state */ struct { struct lwp *lwp; vaddr_t addr; uint32_t value; } llstate; /* * Analyse 'next' PC address taking account of branch/jump instructions */ vaddr_t mips_emul_branch(struct trapframe *tf, vaddr_t instpc, uint32_t fpuCSR, bool allowNonBranch) { #define BRANCHTARGET(pc, i) (4 + (pc) + ((short)(i).IType.imm << 2)) InstFmt inst; vaddr_t nextpc; if (instpc < MIPS_KSEG0_START) { inst.word = mips_ufetch32((void *)instpc); } else { inst.word = *(uint32_t *)instpc; } switch ((int)inst.JType.op) { case OP_SPECIAL: if (inst.RType.func == OP_JR || inst.RType.func == OP_JALR) nextpc = tf->tf_regs[inst.RType.rs]; else if (allowNonBranch) nextpc = instpc + 4; else panic("%s: %s instruction %08x at pc 0x%"PRIxVADDR, __func__, "non-branch", inst.word, instpc); break; case OP_REGIMM: switch ((int)inst.IType.rt) { case OP_BLTZ: case OP_BLTZAL: case OP_BLTZL: /* squashed */ case OP_BLTZALL: /* squashed */ if ((int)(tf->tf_regs[inst.RType.rs]) < 0) nextpc = BRANCHTARGET(instpc, inst); else nextpc = instpc + 8; break; case OP_BGEZ: case OP_BGEZAL: case OP_BGEZL: /* squashed */ case OP_BGEZALL: /* squashed */ if ((int)(tf->tf_regs[inst.RType.rs]) >= 0) nextpc = BRANCHTARGET(instpc, inst); else nextpc = instpc + 8; break; default: panic("%s: %s instruction 0x%08x at pc 0x%"PRIxVADDR, __func__, "bad branch", inst.word, instpc); } break; case OP_J: case OP_JAL: nextpc = (inst.JType.target << 2) | ((intptr_t)instpc & 0xF0000000); break; case OP_BEQ: case OP_BEQL: /* squashed */ if (tf->tf_regs[inst.RType.rs] == tf->tf_regs[inst.RType.rt]) nextpc = BRANCHTARGET(instpc, inst); else nextpc = instpc + 8; break; case OP_BNE: case OP_BNEL: /* squashed */ if (tf->tf_regs[inst.RType.rs] != tf->tf_regs[inst.RType.rt]) nextpc = BRANCHTARGET(instpc, inst); else nextpc = instpc + 8; break; case OP_BLEZ: case OP_BLEZL: /* squashed */ if ((int)(tf->tf_regs[inst.RType.rs]) <= 0) nextpc = BRANCHTARGET(instpc, inst); else nextpc = instpc + 8; break; case OP_BGTZ: case OP_BGTZL: /* squashed */ if ((int)(tf->tf_regs[inst.RType.rs]) > 0) nextpc = BRANCHTARGET(instpc, inst); else nextpc = instpc + 8; break; case OP_COP1: if (inst.RType.rs == OP_BCx || inst.RType.rs == OP_BCy) { int condition = (fpuCSR & MIPS_FCSR_FCC0) != 0; if ((inst.RType.rt & COPz_BC_TF_MASK) != COPz_BC_TRUE) condition = !condition; if (condition) nextpc = BRANCHTARGET(instpc, inst); else nextpc = instpc + 8; } else if (allowNonBranch) nextpc = instpc + 4; else panic("%s: %s instruction 0x%08x at pc 0x%"PRIxVADDR, __func__, "bad COP1 branch", inst.word, instpc); break; default: if (!allowNonBranch) panic("%s: %s instruction 0x%08x at pc 0x%"PRIxVADDR, __func__, "non-branch", inst.word, instpc); nextpc = instpc + 4; } KASSERT((nextpc & 0x3) == 0); return nextpc; #undef BRANCHTARGET } /* * Emulate instructions (including floating-point instructions) */ void mips_emul_inst(uint32_t status, uint32_t cause, vaddr_t opc, struct trapframe *tf) { uint32_t inst; ksiginfo_t ksi; int code = ILL_ILLOPC; /* * Fetch the instruction. */ if (cause & MIPS_CR_BR_DELAY) inst = mips_ufetch32((uint32_t *)opc+1); else inst = mips_ufetch32((uint32_t *)opc); switch (((InstFmt)inst).FRType.op) { case OP_LL: mips_emul_ll(inst, tf, cause); break; case OP_SC: mips_emul_sc(inst, tf, cause); break; case OP_SPECIAL: mips_emul_special(inst, tf, cause); break; case OP_SPECIAL3: mips_emul_special3(inst, tf, cause); break; case OP_COP1: #if defined(FPEMUL) mips_emul_fp(inst, tf, cause); break; #endif case OP_LWC1: #if defined(FPEMUL) mips_emul_lwc1(inst, tf, cause); break; #endif case OP_LDC1: #if defined(FPEMUL) mips_emul_ldc1(inst, tf, cause); break; #endif case OP_SWC1: #if defined(FPEMUL) mips_emul_swc1(inst, tf, cause); break; #endif case OP_SDC1: #if defined(FPEMUL) mips_emul_sdc1(inst, tf, cause); break; #else code = ILL_COPROC; /* FALLTHROUGH */ #endif default: #ifdef DEBUG printf("pid %d (%s): trap: bad insn @ %#"PRIxVADDR " cause %#x status %#"PRIxREGISTER" insn %#x code %d\n", curproc->p_pid, curproc->p_comm, opc, cause, tf->tf_regs[_R_SR], inst, code); #endif tf->tf_regs[_R_CAUSE] = cause; tf->tf_regs[_R_BADVADDR] = opc; KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_trap = cause; /* XXX */ ksi.ksi_code = code; ksi.ksi_addr = (void *)opc; (*curproc->p_emul->e_trapsignal)(curlwp, &ksi); break; } } static inline void send_sigsegv(intptr_t vaddr, uint32_t exccode, struct trapframe *tf, uint32_t cause) { ksiginfo_t ksi; cause = (cause & ~0xFF) | (exccode << MIPS_CR_EXC_CODE_SHIFT); tf->tf_regs[_R_CAUSE] = cause; tf->tf_regs[_R_BADVADDR] = vaddr; KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGSEGV; ksi.ksi_trap = cause; ksi.ksi_code = SEGV_MAPERR; ksi.ksi_addr = (void *)vaddr; (*curproc->p_emul->e_trapsignal)(curlwp, &ksi); } static inline void update_pc(struct trapframe *tf, uint32_t cause) { if (cause & MIPS_CR_BR_DELAY) tf->tf_regs[_R_PC] = mips_emul_branch(tf, tf->tf_regs[_R_PC], PCB_FSR(curpcb), 0); else tf->tf_regs[_R_PC] += 4; } /* * MIPS2 LL instruction */ void mips_emul_ll(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; void *t; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 3)) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } t = &(tf->tf_regs[(inst>>16)&0x1F]); if (copyin((void *)vaddr, t, 4) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } llstate.lwp = curlwp; llstate.addr = vaddr; llstate.value = *((uint32_t *)t); update_pc(tf, cause); } /* * MIPS2 SC instruction */ void mips_emul_sc(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; uint32_t value; int16_t offset; mips_reg_t *t; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 3)) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } t = (mips_reg_t *)&(tf->tf_regs[(inst>>16)&0x1F]); /* * Check that the process and address match the last * LL instruction. */ if (curlwp == llstate.lwp && vaddr == llstate.addr) { llstate.lwp = NULL; /* * Check that the data at the address hasn't changed * since the LL instruction. */ if (copyin((void *)vaddr, &value, 4) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } if (value == llstate.value) { /* SC successful */ if (copyout(t, (void *)vaddr, 4) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } *t = 1; update_pc(tf, cause); return; } } /* SC failed */ *t = 0; update_pc(tf, cause); } void mips_emul_special(uint32_t inst, struct trapframe *tf, uint32_t cause) { ksiginfo_t ksi; const InstFmt instfmt = { .word = inst }; switch (instfmt.RType.func) { case OP_SYNC: /* nothing */ break; default: tf->tf_regs[_R_CAUSE] = cause; tf->tf_regs[_R_BADVADDR] = tf->tf_regs[_R_PC]; KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_trap = cause; ksi.ksi_code = ILL_ILLOPC; ksi.ksi_addr = (void *)(intptr_t)tf->tf_regs[_R_PC]; (*curproc->p_emul->e_trapsignal)(curlwp, &ksi); break; } update_pc(tf, cause); } void mips_emul_special3(uint32_t inst, struct trapframe *tf, uint32_t cause) { ksiginfo_t ksi; const InstFmt instfmt = { .word = inst }; switch (instfmt.RType.func) { case OP_LX: { const intptr_t vaddr = tf->tf_regs[instfmt.RType.rs] + tf->tf_regs[instfmt.RType.rt]; mips_reg_t r; int error = EFAULT; if (vaddr < 0) { addr_err: send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } switch (instfmt.RType.shamt) { #if !defined(__mips_o32) case OP_LX_LDX: { uint64_t tmp64; if (vaddr & 7) goto addr_err; error = copyin((void *)vaddr, &tmp64, sizeof(tmp64)); r = tmp64; break; } #endif case OP_LX_LWX: { int32_t tmp32; if (vaddr & 3) goto addr_err; error = copyin((void *)vaddr, &tmp32, sizeof(tmp32)); r = tmp32; break; } case OP_LX_LHX: { int16_t tmp16; if (vaddr & 1) goto addr_err; error = copyin((void *)vaddr, &tmp16, sizeof(tmp16)); r = tmp16; break; } case OP_LX_LBUX: { uint8_t tmp8; error = copyin((void *)vaddr, &tmp8, sizeof(tmp8)); r = tmp8; break; } default: goto illopc; } if (error) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } tf->tf_regs[instfmt.RType.rd] = r; break; } case OP_RDHWR: switch (instfmt.RType.rd) { case MIPS_HWR_ULR: tf->tf_regs[instfmt.RType.rt] = (mips_reg_t)(intptr_t)curlwp->l_private; goto done; } /* FALLTHROUGH */ illopc: default: tf->tf_regs[_R_CAUSE] = cause; tf->tf_regs[_R_BADVADDR] = tf->tf_regs[_R_PC]; KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_trap = cause; ksi.ksi_code = ILL_ILLOPC; ksi.ksi_addr = (void *)(intptr_t)tf->tf_regs[_R_PC]; (*curproc->p_emul->e_trapsignal)(curlwp, &ksi); return; } done: update_pc(tf, cause); } #if defined(FPEMUL) #define LWSWC1_MAXLOOP 12 void mips_emul_lwc1(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; void *t; mips_reg_t pc; int i; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 3)) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } /* NewABI FIXME */ t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]); if (copyin((void *)vaddr, t, 4) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } pc = tf->tf_regs[_R_PC]; update_pc(tf, cause); if (cause & MIPS_CR_BR_DELAY) return; for (i = 1; i < LWSWC1_MAXLOOP; i++) { if (mips_btop(tf->tf_regs[_R_PC]) != mips_btop(pc)) return; vaddr = tf->tf_regs[_R_PC]; /* XXX truncates to 32 bits */ inst = mips_ufetch32((uint32_t *)vaddr); if (((InstFmt)inst).FRType.op != OP_LWC1) return; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 3)) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } /* NewABI FIXME */ t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]); if (copyin((void *)vaddr, t, 4) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } pc = tf->tf_regs[_R_PC]; update_pc(tf, cause); } } void mips_emul_ldc1(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; void *t; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 7)) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } /* NewABI FIXME */ t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1E]); if (copyin((void *)vaddr, t, 8) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } update_pc(tf, cause); } void mips_emul_swc1(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; void *t; mips_reg_t pc; int i; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 3)) { send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause); return; } /* NewABI FIXME */ t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]); if (copyout(t, (void *)vaddr, 4) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } pc = tf->tf_regs[_R_PC]; update_pc(tf, cause); if (cause & MIPS_CR_BR_DELAY) return; for (i = 1; i < LWSWC1_MAXLOOP; i++) { if (mips_btop(tf->tf_regs[_R_PC]) != mips_btop(pc)) return; vaddr = tf->tf_regs[_R_PC]; /* XXX truncates to 32 bits */ inst = mips_ufetch32((uint32_t *)vaddr); if (((InstFmt)inst).FRType.op != OP_SWC1) return; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 3)) { send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause); return; } /* NewABI FIXME */ t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]); if (copyout(t, (void *)vaddr, 4) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } pc = tf->tf_regs[_R_PC]; update_pc(tf, cause); } } void mips_emul_sdc1(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; void *t; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 7)) { send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause); return; } /* NewABI FIXME */ t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1E]); if (copyout(t, (void *)vaddr, 8) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } update_pc(tf, cause); } void mips_emul_lb(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; int8_t x; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment check */ if (vaddr < 0) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } if (copyin((void *)vaddr, &x, 1) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } tf->tf_regs[(inst>>16)&0x1F] = x; update_pc(tf, cause); } void mips_emul_lbu(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; uint8_t x; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment check */ if (vaddr < 0) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } if (copyin((void *)vaddr, &x, 1) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } tf->tf_regs[(inst>>16)&0x1F] = x; update_pc(tf, cause); } void mips_emul_lh(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; int16_t x; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 1)) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } if (copyin((void *)vaddr, &x, 2) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } tf->tf_regs[(inst>>16)&0x1F] = x; update_pc(tf, cause); } void mips_emul_lhu(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; uint16_t x; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 1)) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } if (copyin((void *)vaddr, &x, 2) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } tf->tf_regs[(inst>>16)&0x1F] = (mips_ureg_t)x; update_pc(tf, cause); } void mips_emul_lw(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; int32_t x; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 3)) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } if (copyin((void *)vaddr, &x, 4) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } tf->tf_regs[(inst>>16)&0x1F] = x; update_pc(tf, cause); } void mips_emul_lwl(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; uint32_t a, x, shift; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment check */ if (vaddr < 0) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } if (copyin((void *)(vaddr & ~3), &a, 4) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } x = tf->tf_regs[(inst>>16)&0x1F]; shift = (3 - (vaddr & 0x00000003)) * 8; a <<= shift; x &= ~(0xFFFFFFFFUL << shift); x |= a; tf->tf_regs[(inst>>16)&0x1F] = x; update_pc(tf, cause); } void mips_emul_lwr(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; uint32_t a, x, shift; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment check */ if (vaddr & 0x80000000) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } if (copyin((void *)(vaddr & ~3), &a, 4) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } x = tf->tf_regs[(inst>>16)&0x1F]; shift = (vaddr & 0x00000003) * 8; a >>= shift; x &= ~(0xFFFFFFFFUL >> shift); x |= a; tf->tf_regs[(inst>>16)&0x1F] = x; update_pc(tf, cause); } #if defined(__mips_n32) || defined(__mips_n64) || defined(__mips_o64) void mips_emul_lwu(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; uint32_t x; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr > VM_MAX_ADDRESS || vaddr & 0x3) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } if (copyin((void *)vaddr, &x, 4) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } tf->tf_regs[(inst>>16)&0x1F] = x; update_pc(tf, cause); } void mips_emul_ld(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr > VM_MAX_ADDRESS || vaddr & 0x7) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } if (copyin((void *)vaddr, &(tf->tf_regs[(inst>>16)&0x1F]), 8) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } update_pc(tf, cause); } void mips_emul_ldl(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; uint64_t a, x; uint32_t shift; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment check */ if (vaddr & 0x80000000) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } if (copyin((void *)(vaddr & ~0x7), &a, 8) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } x = tf->tf_regs[(inst>>16)&0x1F]; shift = (7 - (vaddr & 0x7)) * 8; a <<= shift; x &= ~(~(uint64_t)0UL << shift); x |= a; tf->tf_regs[(inst>>16)&0x1F] = x; update_pc(tf, cause); } void mips_emul_ldr(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; uint64_t a, x; uint32_t shift; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment check */ if (vaddr < 0) { send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause); return; } if (copyin((void *)(vaddr & ~0x7), &a, 8) != 0) { send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause); return; } x = tf->tf_regs[(inst>>16)&0x1F]; shift = (vaddr & 0x7) * 8; a >>= shift; x &= ~(~(uint64_t)0UL >> shift); x |= a; tf->tf_regs[(inst>>16)&0x1F] = x; update_pc(tf, cause); } #endif /* defined(__mips_n32) || defined(__mips_n64) || defined(__mips_o64) */ void mips_emul_sb(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment check */ if (vaddr < 0) { send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause); return; } if (ustore_8((void *)vaddr, tf->tf_regs[(inst>>16)&0x1F]) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } update_pc(tf, cause); } void mips_emul_sh(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || vaddr & 1) { send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause); return; } if (ustore_16((void *)vaddr, tf->tf_regs[(inst>>16)&0x1F]) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } update_pc(tf, cause); } void mips_emul_sw(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || (vaddr & 3)) { send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause); return; } if (ustore_32((void *)vaddr, tf->tf_regs[(inst>>16)&0x1F]) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } update_pc(tf, cause); } void mips_emul_swl(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; uint32_t a, x, shift; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment check */ if (vaddr < 0) { send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause); return; } if (copyin((void *)(vaddr & ~3), &a, 4) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } x = tf->tf_regs[(inst>>16)&0x1F]; shift = (3 - (vaddr & 3)) * 8; x >>= shift; a &= ~(0xFFFFFFFFUL >> shift); a |= x; if (ustore_32((void *)vaddr, a) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } update_pc(tf, cause); } void mips_emul_swr(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; uint32_t a, x, shift; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment check */ if (vaddr < 0) { send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause); return; } if (copyin((void *)(vaddr & ~3), &a, 4) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } x = tf->tf_regs[(inst>>16)&0x1F]; shift = (vaddr & 3) * 8; x <<= shift; a &= ~(0xFFFFFFFFUL << shift); a |= x; if (ustore_32((void *)vaddr, a) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } update_pc(tf, cause); } #if defined(__mips_n32) || defined(__mips_n64) || defined(__mips_o64) void mips_emul_sd(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment and alignment check */ if (vaddr < 0 || vaddr & 0x7) { send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause); return; } if (copyout((void *)vaddr, &tf->tf_regs[(inst>>16)&0x1F], 8) < 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } update_pc(tf, cause); } void mips_emul_sdl(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; uint64_t a, x; uint32_t shift; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment check */ if (vaddr < 0) { send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause); return; } if (copyin((void *)(vaddr & ~0x7), &a, 8) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } x = tf->tf_regs[(inst>>16)&0x1F]; shift = (7 - (vaddr & 7)) * 8; x >>= shift; a &= ~(~(uint64_t)0U >> shift); a |= x; if (copyout((void *)(vaddr & ~0x7), &a, 8) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } update_pc(tf, cause); } void mips_emul_sdr(uint32_t inst, struct trapframe *tf, uint32_t cause) { intptr_t vaddr; uint64_t a, x; uint32_t shift; int16_t offset; offset = inst & 0xFFFF; vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset; /* segment check */ if (vaddr < 0) { send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause); return; } if (copyin((void *)(vaddr & ~0x7), &a, 8) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } x = tf->tf_regs[(inst>>16)&0x1F]; shift = (vaddr & 7) * 8; x <<= shift; a &= ~(~(uint64_t)0U << shift); a |= x; if (copyout((void *)(vaddr & ~0x7), &a, 8) != 0) { send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause); return; } update_pc(tf, cause); } #endif /* defined(__mips_n32) || defined(__mips_n64) || defined(__mips_o64) */ #endif /* defined(FPEMUL) */