#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mips_emul.c,v 1.32 2025/05/03 02:00:46 riastradh Exp $");
#ifdef _KERNEL_OPT
#include "opt_cputype.h"
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cpu.h>
#include <sys/proc.h>
#include <mips/locore.h>
#include <mips/mips_opcode.h>
#include <mips/reg.h>
#include <mips/regnum.h>
#include <mips/pcb.h>
#include <mips/vmparam.h>
#include <mips/trap.h>
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);
struct {
struct lwp *lwp;
vaddr_t addr;
uint32_t value;
} llstate;
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:
case OP_BLTZALL:
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:
case OP_BGEZALL:
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:
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:
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:
if ((int)(tf->tf_regs[inst.RType.rs]) <= 0)
nextpc = BRANCHTARGET(instpc, inst);
else
nextpc = instpc + 8;
break;
case OP_BGTZ:
case OP_BGTZL:
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
}
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;
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;
#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;
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;
}
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;
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);
}
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;
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]);
if (curlwp == llstate.lwp && vaddr == llstate.addr) {
llstate.lwp = NULL;
if (copyin((void *)vaddr, &value, 4) != 0) {
send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
return;
}
if (value == llstate.value) {
if (copyout(t, (void *)vaddr, 4) != 0) {
send_sigsegv(vaddr, T_TLB_ST_MISS,
tf, cause);
return;
}
*t = 1;
update_pc(tf, cause);
return;
}
}
*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:
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;
}
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;
if (vaddr < 0 || (vaddr & 3)) {
send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
return;
}
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];
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;
if (vaddr < 0 || (vaddr & 3)) {
send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
return;
}
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;
if (vaddr < 0 || (vaddr & 7)) {
send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
return;
}
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;
if (vaddr < 0 || (vaddr & 3)) {
send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
return;
}
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];
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;
if (vaddr < 0 || (vaddr & 3)) {
send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
return;
}
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;
if (vaddr < 0 || (vaddr & 7)) {
send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
return;
}
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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
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;
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;
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;
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;
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;
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;
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;
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;
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
#endif