1 /*        $NetBSD: mips_emul.c,v 1.32 2025/05/03 02:00:46 riastradh Exp $ */
2 
3 /*
4  * Copyright (c) 1999 Shuichiro URATA.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: mips_emul.c,v 1.32 2025/05/03 02:00:46 riastradh Exp $");
31 
32 #ifdef _KERNEL_OPT
33 #include "opt_cputype.h"
34 #endif
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/cpu.h>
39 #include <sys/proc.h>
40 
41 #include <mips/locore.h>
42 #include <mips/mips_opcode.h>
43 
44 #include <mips/reg.h>
45 #include <mips/regnum.h>                          /* symbolic register indices */
46 #include <mips/pcb.h>
47 #include <mips/vmparam.h>                         /* for VM_MAX_ADDRESS */
48 #include <mips/trap.h>
49 
50 static inline void  send_sigsegv(intptr_t, uint32_t, struct trapframe *,
51                                   uint32_t);
52 static inline void  update_pc(struct trapframe *, uint32_t);
53 
54 static void         mips_emul_ll(uint32_t, struct trapframe *, uint32_t);
55 static void         mips_emul_sc(uint32_t, struct trapframe *, uint32_t);
56 
57 /*
58  * MIPS2 LL instruction emulation state
59  */
60 struct {
61           struct lwp *lwp;
62           vaddr_t addr;
63           uint32_t value;
64 } llstate;
65 
66 /*
67  * Analyse 'next' PC address taking account of branch/jump instructions
68  */
69 vaddr_t
mips_emul_branch(struct trapframe * tf,vaddr_t instpc,uint32_t fpuCSR,bool allowNonBranch)70 mips_emul_branch(struct trapframe *tf, vaddr_t instpc, uint32_t fpuCSR,
71     bool allowNonBranch)
72 {
73 #define   BRANCHTARGET(pc, i) (4 + (pc) + ((short)(i).IType.imm << 2))
74           InstFmt inst;
75           vaddr_t nextpc;
76 
77           if (instpc < MIPS_KSEG0_START) {
78                     inst.word = mips_ufetch32((void *)instpc);
79           } else {
80                     inst.word = *(uint32_t *)instpc;
81           }
82 
83           switch ((int)inst.JType.op) {
84           case OP_SPECIAL:
85                     if (inst.RType.func == OP_JR || inst.RType.func == OP_JALR)
86                               nextpc = tf->tf_regs[inst.RType.rs];
87                     else if (allowNonBranch)
88                               nextpc = instpc + 4;
89                     else
90                               panic("%s: %s instruction %08x at pc 0x%"PRIxVADDR,
91                                   __func__, "non-branch", inst.word, instpc);
92                     break;
93 
94           case OP_REGIMM:
95                     switch ((int)inst.IType.rt) {
96                     case OP_BLTZ:
97                     case OP_BLTZAL:
98                     case OP_BLTZL:                /* squashed */
99                     case OP_BLTZALL:    /* squashed */
100                               if ((int)(tf->tf_regs[inst.RType.rs]) < 0)
101                                         nextpc = BRANCHTARGET(instpc, inst);
102                               else
103                                         nextpc = instpc + 8;
104                               break;
105 
106                     case OP_BGEZ:
107                     case OP_BGEZAL:
108                     case OP_BGEZL:                /* squashed */
109                     case OP_BGEZALL:    /* squashed */
110                               if ((int)(tf->tf_regs[inst.RType.rs]) >= 0)
111                                         nextpc = BRANCHTARGET(instpc, inst);
112                               else
113                                         nextpc = instpc + 8;
114                               break;
115 
116                     default:
117                               panic("%s: %s instruction 0x%08x at pc 0x%"PRIxVADDR,
118                                   __func__, "bad branch", inst.word, instpc);
119                     }
120                     break;
121 
122           case OP_J:
123           case OP_JAL:
124                     nextpc = (inst.JType.target << 2) |
125                               ((intptr_t)instpc & 0xF0000000);
126                     break;
127 
128           case OP_BEQ:
129           case OP_BEQL:       /* squashed */
130                     if (tf->tf_regs[inst.RType.rs] == tf->tf_regs[inst.RType.rt])
131                               nextpc = BRANCHTARGET(instpc, inst);
132                     else
133                               nextpc = instpc + 8;
134                     break;
135 
136           case OP_BNE:
137           case OP_BNEL:       /* squashed */
138                     if (tf->tf_regs[inst.RType.rs] != tf->tf_regs[inst.RType.rt])
139                               nextpc = BRANCHTARGET(instpc, inst);
140                     else
141                               nextpc = instpc + 8;
142                     break;
143 
144           case OP_BLEZ:
145           case OP_BLEZL:      /* squashed */
146                     if ((int)(tf->tf_regs[inst.RType.rs]) <= 0)
147                               nextpc = BRANCHTARGET(instpc, inst);
148                     else
149                               nextpc = instpc + 8;
150                     break;
151 
152           case OP_BGTZ:
153           case OP_BGTZL:      /* squashed */
154                     if ((int)(tf->tf_regs[inst.RType.rs]) > 0)
155                               nextpc = BRANCHTARGET(instpc, inst);
156                     else
157                               nextpc = instpc + 8;
158                     break;
159 
160           case OP_COP1:
161                     if (inst.RType.rs == OP_BCx || inst.RType.rs == OP_BCy) {
162                               int condition = (fpuCSR & MIPS_FCSR_FCC0) != 0;
163                               if ((inst.RType.rt & COPz_BC_TF_MASK) != COPz_BC_TRUE)
164                                         condition = !condition;
165                               if (condition)
166                                         nextpc = BRANCHTARGET(instpc, inst);
167                               else
168                                         nextpc = instpc + 8;
169                     }
170                     else if (allowNonBranch)
171                               nextpc = instpc + 4;
172                     else
173                               panic("%s: %s instruction 0x%08x at pc 0x%"PRIxVADDR,
174                                   __func__, "bad COP1 branch", inst.word, instpc);
175                     break;
176 
177           default:
178                     if (!allowNonBranch)
179                               panic("%s: %s instruction 0x%08x at pc 0x%"PRIxVADDR,
180                                   __func__, "non-branch", inst.word, instpc);
181                     nextpc = instpc + 4;
182           }
183           KASSERT((nextpc & 0x3) == 0);
184           return nextpc;
185 #undef    BRANCHTARGET
186 }
187 
188 /*
189  * Emulate instructions (including floating-point instructions)
190  */
191 void
mips_emul_inst(uint32_t status,uint32_t cause,vaddr_t opc,struct trapframe * tf)192 mips_emul_inst(uint32_t status, uint32_t cause, vaddr_t opc,
193     struct trapframe *tf)
194 {
195           uint32_t inst;
196           ksiginfo_t ksi;
197           int code = ILL_ILLOPC;
198 
199           /*
200            *  Fetch the instruction.
201            */
202           if (cause & MIPS_CR_BR_DELAY)
203                     inst = mips_ufetch32((uint32_t *)opc+1);
204           else
205                     inst = mips_ufetch32((uint32_t *)opc);
206 
207           switch (((InstFmt)inst).FRType.op) {
208           case OP_LL:
209                     mips_emul_ll(inst, tf, cause);
210                     break;
211           case OP_SC:
212                     mips_emul_sc(inst, tf, cause);
213                     break;
214           case OP_SPECIAL:
215                     mips_emul_special(inst, tf, cause);
216                     break;
217           case OP_SPECIAL3:
218                     mips_emul_special3(inst, tf, cause);
219                     break;
220           case OP_COP1:
221 #if defined(FPEMUL)
222                     mips_emul_fp(inst, tf, cause);
223                     break;
224 #endif
225           case OP_LWC1:
226 #if defined(FPEMUL)
227                     mips_emul_lwc1(inst, tf, cause);
228                     break;
229 #endif
230           case OP_LDC1:
231 #if defined(FPEMUL)
232                     mips_emul_ldc1(inst, tf, cause);
233                     break;
234 #endif
235           case OP_SWC1:
236 #if defined(FPEMUL)
237                     mips_emul_swc1(inst, tf, cause);
238                     break;
239 #endif
240           case OP_SDC1:
241 #if defined(FPEMUL)
242                     mips_emul_sdc1(inst, tf, cause);
243                     break;
244 #else
245                     code = ILL_COPROC;
246                     /* FALLTHROUGH */
247 #endif
248           default:
249 #ifdef DEBUG
250                     printf("pid %d (%s): trap: bad insn @ %#"PRIxVADDR
251                         " cause %#x status %#"PRIxREGISTER" insn %#x code %d\n",
252                         curproc->p_pid, curproc->p_comm, opc,
253                         cause, tf->tf_regs[_R_SR], inst, code);
254 #endif
255                     tf->tf_regs[_R_CAUSE] = cause;
256                     tf->tf_regs[_R_BADVADDR] = opc;
257                     KSI_INIT_TRAP(&ksi);
258                     ksi.ksi_signo = SIGILL;
259                     ksi.ksi_trap = cause; /* XXX */
260                     ksi.ksi_code = code;
261                     ksi.ksi_addr = (void *)opc;
262                     (*curproc->p_emul->e_trapsignal)(curlwp, &ksi);
263                     break;
264           }
265 }
266 
267 static inline void
send_sigsegv(intptr_t vaddr,uint32_t exccode,struct trapframe * tf,uint32_t cause)268 send_sigsegv(intptr_t vaddr, uint32_t exccode, struct trapframe *tf,
269     uint32_t cause)
270 {
271           ksiginfo_t ksi;
272           cause = (cause & ~0xFF) | (exccode << MIPS_CR_EXC_CODE_SHIFT);
273           tf->tf_regs[_R_CAUSE] = cause;
274           tf->tf_regs[_R_BADVADDR] = vaddr;
275           KSI_INIT_TRAP(&ksi);
276           ksi.ksi_signo = SIGSEGV;
277           ksi.ksi_trap = cause;
278           ksi.ksi_code = SEGV_MAPERR;
279           ksi.ksi_addr = (void *)vaddr;
280           (*curproc->p_emul->e_trapsignal)(curlwp, &ksi);
281 }
282 
283 static inline void
update_pc(struct trapframe * tf,uint32_t cause)284 update_pc(struct trapframe *tf, uint32_t cause)
285 {
286 
287           if (cause & MIPS_CR_BR_DELAY)
288                     tf->tf_regs[_R_PC] =
289                         mips_emul_branch(tf, tf->tf_regs[_R_PC],
290                               PCB_FSR(curpcb), 0);
291           else
292                     tf->tf_regs[_R_PC] += 4;
293 }
294 
295 /*
296  * MIPS2 LL instruction
297  */
298 void
mips_emul_ll(uint32_t inst,struct trapframe * tf,uint32_t cause)299 mips_emul_ll(uint32_t inst, struct trapframe *tf, uint32_t cause)
300 {
301           intptr_t  vaddr;
302           int16_t             offset;
303           void                *t;
304 
305           offset = inst & 0xFFFF;
306           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
307 
308           /* segment and alignment check */
309           if (vaddr < 0 || (vaddr & 3)) {
310                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
311                     return;
312           }
313 
314           t = &(tf->tf_regs[(inst>>16)&0x1F]);
315 
316           if (copyin((void *)vaddr, t, 4) != 0) {
317                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
318                     return;
319           }
320 
321           llstate.lwp = curlwp;
322           llstate.addr = vaddr;
323           llstate.value = *((uint32_t *)t);
324 
325           update_pc(tf, cause);
326 }
327 
328 /*
329  * MIPS2 SC instruction
330  */
331 void
mips_emul_sc(uint32_t inst,struct trapframe * tf,uint32_t cause)332 mips_emul_sc(uint32_t inst, struct trapframe *tf, uint32_t cause)
333 {
334           intptr_t  vaddr;
335           uint32_t  value;
336           int16_t             offset;
337           mips_reg_t          *t;
338 
339           offset = inst & 0xFFFF;
340           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
341 
342           /* segment and alignment check */
343           if (vaddr < 0 || (vaddr & 3)) {
344                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
345                     return;
346           }
347 
348           t = (mips_reg_t *)&(tf->tf_regs[(inst>>16)&0x1F]);
349 
350           /*
351            * Check that the process and address match the last
352            * LL instruction.
353            */
354           if (curlwp == llstate.lwp && vaddr == llstate.addr) {
355                     llstate.lwp = NULL;
356                     /*
357                      * Check that the data at the address hasn't changed
358                      * since the LL instruction.
359                      */
360                     if (copyin((void *)vaddr, &value, 4) != 0) {
361                               send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
362                               return;
363                     }
364                     if (value == llstate.value) {
365                               /* SC successful */
366                               if (copyout(t, (void *)vaddr, 4) != 0) {
367                                         send_sigsegv(vaddr, T_TLB_ST_MISS,
368                                             tf, cause);
369                                         return;
370                               }
371                               *t = 1;
372                               update_pc(tf, cause);
373                               return;
374                     }
375           }
376 
377           /* SC failed */
378           *t = 0;
379           update_pc(tf, cause);
380 }
381 
382 void
mips_emul_special(uint32_t inst,struct trapframe * tf,uint32_t cause)383 mips_emul_special(uint32_t inst, struct trapframe *tf, uint32_t cause)
384 {
385           ksiginfo_t ksi;
386           const InstFmt instfmt = { .word = inst };
387 
388           switch (instfmt.RType.func) {
389           case OP_SYNC:
390                     /* nothing */
391                     break;
392           default:
393                     tf->tf_regs[_R_CAUSE] = cause;
394                     tf->tf_regs[_R_BADVADDR] = tf->tf_regs[_R_PC];
395                     KSI_INIT_TRAP(&ksi);
396                     ksi.ksi_signo = SIGILL;
397                     ksi.ksi_trap = cause;
398                     ksi.ksi_code = ILL_ILLOPC;
399                     ksi.ksi_addr = (void *)(intptr_t)tf->tf_regs[_R_PC];
400                     (*curproc->p_emul->e_trapsignal)(curlwp, &ksi);
401                     break;
402           }
403 
404           update_pc(tf, cause);
405 }
406 
407 void
mips_emul_special3(uint32_t inst,struct trapframe * tf,uint32_t cause)408 mips_emul_special3(uint32_t inst, struct trapframe *tf, uint32_t cause)
409 {
410           ksiginfo_t ksi;
411           const InstFmt instfmt = { .word = inst };
412           switch (instfmt.RType.func) {
413           case OP_LX: {
414                     const intptr_t vaddr = tf->tf_regs[instfmt.RType.rs]
415                         + tf->tf_regs[instfmt.RType.rt];
416                     mips_reg_t r;
417                     int error = EFAULT;
418                     if (vaddr < 0) {
419                         addr_err:
420                               send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
421                               return;
422                     }
423                     switch (instfmt.RType.shamt) {
424 #if !defined(__mips_o32)
425                     case OP_LX_LDX: {
426                               uint64_t tmp64;
427                               if (vaddr & 7)
428                                         goto addr_err;
429                               error = copyin((void *)vaddr, &tmp64, sizeof(tmp64));
430                               r = tmp64;
431                               break;
432                     }
433 #endif
434                     case OP_LX_LWX: {
435                               int32_t tmp32;
436                               if (vaddr & 3)
437                                         goto addr_err;
438                               error = copyin((void *)vaddr, &tmp32, sizeof(tmp32));
439                               r = tmp32;
440                               break;
441                     }
442                     case OP_LX_LHX: {
443                               int16_t tmp16;
444                               if (vaddr & 1)
445                                         goto addr_err;
446                               error = copyin((void *)vaddr, &tmp16, sizeof(tmp16));
447                               r = tmp16;
448                               break;
449                     }
450                     case OP_LX_LBUX: {
451                               uint8_t tmp8;
452                               error = copyin((void *)vaddr, &tmp8, sizeof(tmp8));
453                               r = tmp8;
454                               break;
455                     }
456                     default:
457                               goto illopc;
458                     }
459                     if (error) {
460                               send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
461                               return;
462                     }
463                     tf->tf_regs[instfmt.RType.rd] = r;
464                     break;
465           }
466           case OP_RDHWR:
467                     switch (instfmt.RType.rd) {
468                     case MIPS_HWR_ULR:
469                               tf->tf_regs[instfmt.RType.rt] =
470                                   (mips_reg_t)(intptr_t)curlwp->l_private;
471                               goto done;
472                     }
473                     /* FALLTHROUGH */
474           illopc:
475           default:
476                     tf->tf_regs[_R_CAUSE] = cause;
477                     tf->tf_regs[_R_BADVADDR] = tf->tf_regs[_R_PC];
478                     KSI_INIT_TRAP(&ksi);
479                     ksi.ksi_signo = SIGILL;
480                     ksi.ksi_trap = cause;
481                     ksi.ksi_code = ILL_ILLOPC;
482                     ksi.ksi_addr = (void *)(intptr_t)tf->tf_regs[_R_PC];
483                     (*curproc->p_emul->e_trapsignal)(curlwp, &ksi);
484                     return;
485           }
486 done:
487           update_pc(tf, cause);
488 }
489 
490 #if defined(FPEMUL)
491 
492 #define LWSWC1_MAXLOOP        12
493 
494 void
mips_emul_lwc1(uint32_t inst,struct trapframe * tf,uint32_t cause)495 mips_emul_lwc1(uint32_t inst, struct trapframe *tf, uint32_t cause)
496 {
497           intptr_t  vaddr;
498           int16_t             offset;
499           void                *t;
500           mips_reg_t          pc;
501           int                 i;
502 
503           offset = inst & 0xFFFF;
504           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
505 
506           /* segment and alignment check */
507           if (vaddr < 0 || (vaddr & 3)) {
508                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
509                     return;
510           }
511 
512           /* NewABI FIXME */
513           t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]);
514 
515           if (copyin((void *)vaddr, t, 4) != 0) {
516                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
517                     return;
518           }
519 
520           pc = tf->tf_regs[_R_PC];
521           update_pc(tf, cause);
522 
523           if (cause & MIPS_CR_BR_DELAY)
524                     return;
525 
526           for (i = 1; i < LWSWC1_MAXLOOP; i++) {
527                     if (mips_btop(tf->tf_regs[_R_PC]) != mips_btop(pc))
528                               return;
529 
530                     vaddr = tf->tf_regs[_R_PC];   /* XXX truncates to 32 bits */
531                     inst = mips_ufetch32((uint32_t *)vaddr);
532                     if (((InstFmt)inst).FRType.op != OP_LWC1)
533                               return;
534 
535                     offset = inst & 0xFFFF;
536                     vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
537 
538                     /* segment and alignment check */
539                     if (vaddr < 0 || (vaddr & 3)) {
540                               send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
541                               return;
542                     }
543 
544                     /* NewABI FIXME */
545                     t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]);
546 
547                     if (copyin((void *)vaddr, t, 4) != 0) {
548                               send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
549                               return;
550                     }
551 
552                     pc = tf->tf_regs[_R_PC];
553                     update_pc(tf, cause);
554           }
555 }
556 
557 void
mips_emul_ldc1(uint32_t inst,struct trapframe * tf,uint32_t cause)558 mips_emul_ldc1(uint32_t inst, struct trapframe *tf, uint32_t cause)
559 {
560           intptr_t  vaddr;
561           int16_t             offset;
562           void                *t;
563 
564           offset = inst & 0xFFFF;
565           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
566 
567           /* segment and alignment check */
568           if (vaddr < 0  || (vaddr & 7)) {
569                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
570                     return;
571           }
572 
573           /* NewABI FIXME */
574           t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1E]);
575 
576           if (copyin((void *)vaddr, t, 8) != 0) {
577                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
578                     return;
579           }
580 
581           update_pc(tf, cause);
582 }
583 
584 void
mips_emul_swc1(uint32_t inst,struct trapframe * tf,uint32_t cause)585 mips_emul_swc1(uint32_t inst, struct trapframe *tf, uint32_t cause)
586 {
587           intptr_t  vaddr;
588           int16_t             offset;
589           void                *t;
590           mips_reg_t          pc;
591           int                 i;
592 
593           offset = inst & 0xFFFF;
594           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
595 
596           /* segment and alignment check */
597           if (vaddr < 0 || (vaddr & 3)) {
598                     send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
599                     return;
600           }
601 
602           /* NewABI FIXME */
603           t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]);
604 
605           if (copyout(t, (void *)vaddr, 4) != 0) {
606                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
607                     return;
608           }
609 
610           pc = tf->tf_regs[_R_PC];
611           update_pc(tf, cause);
612 
613           if (cause & MIPS_CR_BR_DELAY)
614                     return;
615 
616           for (i = 1; i < LWSWC1_MAXLOOP; i++) {
617                     if (mips_btop(tf->tf_regs[_R_PC]) != mips_btop(pc))
618                               return;
619 
620                     vaddr = tf->tf_regs[_R_PC];   /* XXX truncates to 32 bits */
621                     inst = mips_ufetch32((uint32_t *)vaddr);
622                     if (((InstFmt)inst).FRType.op != OP_SWC1)
623                               return;
624 
625                     offset = inst & 0xFFFF;
626                     vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
627 
628                     /* segment and alignment check */
629                     if (vaddr < 0 || (vaddr & 3)) {
630                               send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
631                               return;
632                     }
633 
634                     /* NewABI FIXME */
635                     t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]);
636 
637                     if (copyout(t, (void *)vaddr, 4) != 0) {
638                               send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
639                               return;
640                     }
641 
642                     pc = tf->tf_regs[_R_PC];
643                     update_pc(tf, cause);
644           }
645 }
646 
647 void
mips_emul_sdc1(uint32_t inst,struct trapframe * tf,uint32_t cause)648 mips_emul_sdc1(uint32_t inst, struct trapframe *tf, uint32_t cause)
649 {
650           intptr_t  vaddr;
651           int16_t             offset;
652           void                *t;
653 
654           offset = inst & 0xFFFF;
655           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
656 
657           /* segment and alignment check */
658           if (vaddr < 0 || (vaddr & 7)) {
659                     send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
660                     return;
661           }
662 
663           /* NewABI FIXME */
664           t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1E]);
665 
666           if (copyout(t, (void *)vaddr, 8) != 0) {
667                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
668                     return;
669           }
670 
671           update_pc(tf, cause);
672 }
673 
674 void
mips_emul_lb(uint32_t inst,struct trapframe * tf,uint32_t cause)675 mips_emul_lb(uint32_t inst, struct trapframe *tf, uint32_t cause)
676 {
677           intptr_t  vaddr;
678           int16_t             offset;
679           int8_t              x;
680 
681           offset = inst & 0xFFFF;
682           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
683 
684           /* segment check */
685           if (vaddr < 0) {
686                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
687                     return;
688           }
689 
690           if (copyin((void *)vaddr, &x, 1) != 0) {
691                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
692                     return;
693           }
694 
695           tf->tf_regs[(inst>>16)&0x1F] = x;
696 
697           update_pc(tf, cause);
698 }
699 
700 void
mips_emul_lbu(uint32_t inst,struct trapframe * tf,uint32_t cause)701 mips_emul_lbu(uint32_t inst, struct trapframe *tf, uint32_t cause)
702 {
703           intptr_t  vaddr;
704           int16_t             offset;
705           uint8_t             x;
706 
707           offset = inst & 0xFFFF;
708           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
709 
710           /* segment check */
711           if (vaddr < 0) {
712                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
713                     return;
714           }
715 
716           if (copyin((void *)vaddr, &x, 1) != 0) {
717                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
718                     return;
719           }
720 
721           tf->tf_regs[(inst>>16)&0x1F] = x;
722 
723           update_pc(tf, cause);
724 }
725 
726 void
mips_emul_lh(uint32_t inst,struct trapframe * tf,uint32_t cause)727 mips_emul_lh(uint32_t inst, struct trapframe *tf, uint32_t cause)
728 {
729           intptr_t  vaddr;
730           int16_t             offset;
731           int16_t             x;
732 
733           offset = inst & 0xFFFF;
734           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
735 
736           /* segment and alignment check */
737           if (vaddr < 0 || (vaddr & 1)) {
738                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
739                     return;
740           }
741 
742           if (copyin((void *)vaddr, &x, 2) != 0) {
743                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
744                     return;
745           }
746 
747           tf->tf_regs[(inst>>16)&0x1F] = x;
748 
749           update_pc(tf, cause);
750 }
751 
752 void
mips_emul_lhu(uint32_t inst,struct trapframe * tf,uint32_t cause)753 mips_emul_lhu(uint32_t inst, struct trapframe *tf, uint32_t cause)
754 {
755           intptr_t  vaddr;
756           int16_t             offset;
757           uint16_t  x;
758 
759           offset = inst & 0xFFFF;
760           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
761 
762           /* segment and alignment check */
763           if (vaddr < 0 || (vaddr & 1)) {
764                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
765                     return;
766           }
767 
768           if (copyin((void *)vaddr, &x, 2) != 0) {
769                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
770                     return;
771           }
772 
773           tf->tf_regs[(inst>>16)&0x1F] = (mips_ureg_t)x;
774 
775           update_pc(tf, cause);
776 }
777 
778 void
mips_emul_lw(uint32_t inst,struct trapframe * tf,uint32_t cause)779 mips_emul_lw(uint32_t inst, struct trapframe *tf, uint32_t cause)
780 {
781           intptr_t  vaddr;
782           int16_t             offset;
783           int32_t             x;
784 
785           offset = inst & 0xFFFF;
786           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
787 
788           /* segment and alignment check */
789           if (vaddr < 0 || (vaddr & 3)) {
790                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
791                     return;
792           }
793 
794           if (copyin((void *)vaddr, &x, 4) != 0) {
795                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
796                     return;
797           }
798 
799           tf->tf_regs[(inst>>16)&0x1F] = x;
800 
801           update_pc(tf, cause);
802 }
803 
804 void
mips_emul_lwl(uint32_t inst,struct trapframe * tf,uint32_t cause)805 mips_emul_lwl(uint32_t inst, struct trapframe *tf, uint32_t cause)
806 {
807           intptr_t  vaddr;
808           uint32_t  a, x, shift;
809           int16_t             offset;
810 
811           offset = inst & 0xFFFF;
812           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
813 
814           /* segment check */
815           if (vaddr < 0) {
816                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
817                     return;
818           }
819 
820           if (copyin((void *)(vaddr & ~3), &a, 4) != 0) {
821                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
822                     return;
823           }
824 
825           x = tf->tf_regs[(inst>>16)&0x1F];
826 
827           shift = (3 - (vaddr & 0x00000003)) * 8;
828           a <<= shift;
829           x &= ~(0xFFFFFFFFUL << shift);
830           x |= a;
831 
832           tf->tf_regs[(inst>>16)&0x1F] = x;
833 
834           update_pc(tf, cause);
835 }
836 
837 void
mips_emul_lwr(uint32_t inst,struct trapframe * tf,uint32_t cause)838 mips_emul_lwr(uint32_t inst, struct trapframe *tf, uint32_t cause)
839 {
840           intptr_t  vaddr;
841           uint32_t  a, x, shift;
842           int16_t             offset;
843 
844           offset = inst & 0xFFFF;
845           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
846 
847           /* segment check */
848           if (vaddr & 0x80000000) {
849                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
850                     return;
851           }
852 
853           if (copyin((void *)(vaddr & ~3), &a, 4) != 0) {
854                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
855                     return;
856           }
857 
858           x = tf->tf_regs[(inst>>16)&0x1F];
859 
860           shift = (vaddr & 0x00000003) * 8;
861           a >>= shift;
862           x &= ~(0xFFFFFFFFUL >> shift);
863           x |= a;
864 
865           tf->tf_regs[(inst>>16)&0x1F] = x;
866 
867           update_pc(tf, cause);
868 }
869 
870 #if defined(__mips_n32) || defined(__mips_n64) || defined(__mips_o64)
871 void
mips_emul_lwu(uint32_t inst,struct trapframe * tf,uint32_t cause)872 mips_emul_lwu(uint32_t inst, struct trapframe *tf, uint32_t cause)
873 {
874           intptr_t  vaddr;
875           int16_t             offset;
876           uint32_t  x;
877 
878           offset = inst & 0xFFFF;
879           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
880 
881           /* segment and alignment check */
882           if (vaddr > VM_MAX_ADDRESS || vaddr & 0x3) {
883                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
884                     return;
885           }
886 
887           if (copyin((void *)vaddr, &x, 4) != 0) {
888                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
889                     return;
890           }
891 
892           tf->tf_regs[(inst>>16)&0x1F] = x;
893 
894           update_pc(tf, cause);
895 }
896 
897 void
mips_emul_ld(uint32_t inst,struct trapframe * tf,uint32_t cause)898 mips_emul_ld(uint32_t inst, struct trapframe *tf, uint32_t cause)
899 {
900           intptr_t  vaddr;
901           int16_t             offset;
902 
903           offset = inst & 0xFFFF;
904           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
905 
906           /* segment and alignment check */
907           if (vaddr > VM_MAX_ADDRESS || vaddr & 0x7) {
908                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
909                     return;
910           }
911 
912           if (copyin((void *)vaddr, &(tf->tf_regs[(inst>>16)&0x1F]), 8) != 0) {
913                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
914                     return;
915           }
916 
917           update_pc(tf, cause);
918 }
919 
920 void
mips_emul_ldl(uint32_t inst,struct trapframe * tf,uint32_t cause)921 mips_emul_ldl(uint32_t inst, struct trapframe *tf, uint32_t cause)
922 {
923           intptr_t  vaddr;
924           uint64_t  a, x;
925           uint32_t  shift;
926           int16_t             offset;
927 
928           offset = inst & 0xFFFF;
929           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
930 
931           /* segment check */
932           if (vaddr & 0x80000000) {
933                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
934                     return;
935           }
936 
937           if (copyin((void *)(vaddr & ~0x7), &a, 8) != 0) {
938                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
939                     return;
940           }
941 
942           x = tf->tf_regs[(inst>>16)&0x1F];
943 
944           shift = (7 - (vaddr & 0x7)) * 8;
945           a <<= shift;
946           x &= ~(~(uint64_t)0UL << shift);
947           x |= a;
948 
949           tf->tf_regs[(inst>>16)&0x1F] = x;
950 
951           update_pc(tf, cause);
952 }
953 
954 void
mips_emul_ldr(uint32_t inst,struct trapframe * tf,uint32_t cause)955 mips_emul_ldr(uint32_t inst, struct trapframe *tf, uint32_t cause)
956 {
957           intptr_t  vaddr;
958           uint64_t  a, x;
959           uint32_t  shift;
960           int16_t             offset;
961 
962           offset = inst & 0xFFFF;
963           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
964 
965           /* segment check */
966           if (vaddr < 0) {
967                     send_sigsegv(vaddr, T_ADDR_ERR_LD, tf, cause);
968                     return;
969           }
970 
971           if (copyin((void *)(vaddr & ~0x7), &a, 8) != 0) {
972                     send_sigsegv(vaddr, T_TLB_LD_MISS, tf, cause);
973                     return;
974           }
975 
976           x = tf->tf_regs[(inst>>16)&0x1F];
977 
978           shift = (vaddr & 0x7) * 8;
979           a >>= shift;
980           x &= ~(~(uint64_t)0UL >> shift);
981           x |= a;
982 
983           tf->tf_regs[(inst>>16)&0x1F] = x;
984 
985           update_pc(tf, cause);
986 }
987 #endif /* defined(__mips_n32) || defined(__mips_n64) || defined(__mips_o64) */
988 
989 void
mips_emul_sb(uint32_t inst,struct trapframe * tf,uint32_t cause)990 mips_emul_sb(uint32_t inst, struct trapframe *tf, uint32_t cause)
991 {
992           intptr_t  vaddr;
993           int16_t             offset;
994 
995           offset = inst & 0xFFFF;
996           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
997 
998           /* segment check */
999           if (vaddr < 0) {
1000                     send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
1001                     return;
1002           }
1003 
1004           if (ustore_8((void *)vaddr, tf->tf_regs[(inst>>16)&0x1F]) != 0) {
1005                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1006                     return;
1007           }
1008 
1009           update_pc(tf, cause);
1010 }
1011 
1012 void
mips_emul_sh(uint32_t inst,struct trapframe * tf,uint32_t cause)1013 mips_emul_sh(uint32_t inst, struct trapframe *tf, uint32_t cause)
1014 {
1015           intptr_t  vaddr;
1016           int16_t             offset;
1017 
1018           offset = inst & 0xFFFF;
1019           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
1020 
1021           /* segment and alignment check */
1022           if (vaddr < 0 || vaddr & 1) {
1023                     send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
1024                     return;
1025           }
1026 
1027           if (ustore_16((void *)vaddr, tf->tf_regs[(inst>>16)&0x1F]) != 0) {
1028                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1029                     return;
1030           }
1031 
1032           update_pc(tf, cause);
1033 }
1034 
1035 void
mips_emul_sw(uint32_t inst,struct trapframe * tf,uint32_t cause)1036 mips_emul_sw(uint32_t inst, struct trapframe *tf, uint32_t cause)
1037 {
1038           intptr_t  vaddr;
1039           int16_t             offset;
1040 
1041           offset = inst & 0xFFFF;
1042           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
1043 
1044           /* segment and alignment check */
1045           if (vaddr < 0 || (vaddr & 3)) {
1046                     send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
1047                     return;
1048           }
1049 
1050           if (ustore_32((void *)vaddr, tf->tf_regs[(inst>>16)&0x1F]) != 0) {
1051                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1052                     return;
1053           }
1054 
1055           update_pc(tf, cause);
1056 }
1057 
1058 void
mips_emul_swl(uint32_t inst,struct trapframe * tf,uint32_t cause)1059 mips_emul_swl(uint32_t inst, struct trapframe *tf, uint32_t cause)
1060 {
1061           intptr_t  vaddr;
1062           uint32_t  a, x, shift;
1063           int16_t             offset;
1064 
1065           offset = inst & 0xFFFF;
1066           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
1067 
1068           /* segment check */
1069           if (vaddr < 0) {
1070                     send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
1071                     return;
1072           }
1073 
1074           if (copyin((void *)(vaddr & ~3), &a, 4) != 0) {
1075                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1076                     return;
1077           }
1078 
1079           x = tf->tf_regs[(inst>>16)&0x1F];
1080 
1081           shift = (3 - (vaddr & 3)) * 8;
1082           x >>= shift;
1083           a &= ~(0xFFFFFFFFUL >> shift);
1084           a |= x;
1085 
1086           if (ustore_32((void *)vaddr, a) != 0) {
1087                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1088                     return;
1089           }
1090 
1091           update_pc(tf, cause);
1092 }
1093 
1094 void
mips_emul_swr(uint32_t inst,struct trapframe * tf,uint32_t cause)1095 mips_emul_swr(uint32_t inst, struct trapframe *tf, uint32_t cause)
1096 {
1097           intptr_t  vaddr;
1098           uint32_t  a, x, shift;
1099           int16_t             offset;
1100 
1101           offset = inst & 0xFFFF;
1102           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
1103 
1104           /* segment check */
1105           if (vaddr < 0) {
1106                     send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
1107                     return;
1108           }
1109 
1110           if (copyin((void *)(vaddr & ~3), &a, 4) != 0) {
1111                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1112                     return;
1113           }
1114 
1115           x = tf->tf_regs[(inst>>16)&0x1F];
1116 
1117           shift = (vaddr & 3) * 8;
1118           x <<= shift;
1119           a &= ~(0xFFFFFFFFUL << shift);
1120           a |= x;
1121 
1122           if (ustore_32((void *)vaddr, a) != 0) {
1123                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1124                     return;
1125           }
1126 
1127           update_pc(tf, cause);
1128 }
1129 
1130 #if defined(__mips_n32) || defined(__mips_n64) || defined(__mips_o64)
1131 void
mips_emul_sd(uint32_t inst,struct trapframe * tf,uint32_t cause)1132 mips_emul_sd(uint32_t inst, struct trapframe *tf, uint32_t cause)
1133 {
1134           intptr_t  vaddr;
1135           int16_t             offset;
1136 
1137           offset = inst & 0xFFFF;
1138           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
1139 
1140           /* segment and alignment check */
1141           if (vaddr < 0 || vaddr & 0x7) {
1142                     send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
1143                     return;
1144           }
1145 
1146           if (copyout((void *)vaddr, &tf->tf_regs[(inst>>16)&0x1F], 8) < 0) {
1147                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1148                     return;
1149           }
1150 
1151           update_pc(tf, cause);
1152 }
1153 
1154 void
mips_emul_sdl(uint32_t inst,struct trapframe * tf,uint32_t cause)1155 mips_emul_sdl(uint32_t inst, struct trapframe *tf, uint32_t cause)
1156 {
1157           intptr_t  vaddr;
1158           uint64_t  a, x;
1159           uint32_t  shift;
1160           int16_t             offset;
1161 
1162           offset = inst & 0xFFFF;
1163           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
1164 
1165           /* segment check */
1166           if (vaddr < 0) {
1167                     send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
1168                     return;
1169           }
1170 
1171           if (copyin((void *)(vaddr & ~0x7), &a, 8) != 0) {
1172                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1173                     return;
1174           }
1175 
1176           x = tf->tf_regs[(inst>>16)&0x1F];
1177 
1178           shift = (7 - (vaddr & 7)) * 8;
1179           x >>= shift;
1180           a &= ~(~(uint64_t)0U >> shift);
1181           a |= x;
1182 
1183           if (copyout((void *)(vaddr & ~0x7), &a, 8) != 0) {
1184                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1185                     return;
1186           }
1187 
1188           update_pc(tf, cause);
1189 }
1190 
1191 void
mips_emul_sdr(uint32_t inst,struct trapframe * tf,uint32_t cause)1192 mips_emul_sdr(uint32_t inst, struct trapframe *tf, uint32_t cause)
1193 {
1194           intptr_t  vaddr;
1195           uint64_t  a, x;
1196           uint32_t  shift;
1197           int16_t             offset;
1198 
1199           offset = inst & 0xFFFF;
1200           vaddr = tf->tf_regs[(inst>>21)&0x1F] + offset;
1201 
1202           /* segment check */
1203           if (vaddr < 0) {
1204                     send_sigsegv(vaddr, T_ADDR_ERR_ST, tf, cause);
1205                     return;
1206           }
1207 
1208           if (copyin((void *)(vaddr & ~0x7), &a, 8) != 0) {
1209                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1210                     return;
1211           }
1212 
1213           x = tf->tf_regs[(inst>>16)&0x1F];
1214 
1215           shift = (vaddr & 7) * 8;
1216           x <<= shift;
1217           a &= ~(~(uint64_t)0U << shift);
1218           a |= x;
1219 
1220           if (copyout((void *)(vaddr & ~0x7), &a, 8) != 0) {
1221                     send_sigsegv(vaddr, T_TLB_ST_MISS, tf, cause);
1222                     return;
1223           }
1224 
1225           update_pc(tf, cause);
1226 }
1227 #endif /* defined(__mips_n32) || defined(__mips_n64) || defined(__mips_o64) */
1228 #endif /* defined(FPEMUL) */
1229