qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

cpu_loop.c (11092B)


      1 /*
      2  *  qemu user cpu loop
      3  *
      4  *  Copyright (c) 2003-2008 Fabrice Bellard
      5  *
      6  *  This program is free software; you can redistribute it and/or modify
      7  *  it under the terms of the GNU General Public License as published by
      8  *  the Free Software Foundation; either version 2 of the License, or
      9  *  (at your option) any later version.
     10  *
     11  *  This program is distributed in the hope that it will be useful,
     12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *  GNU General Public License for more details.
     15  *
     16  *  You should have received a copy of the GNU General Public License
     17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
     18  */
     19 
     20 #include "qemu/osdep.h"
     21 #include "qemu.h"
     22 #include "user-internals.h"
     23 #include "cpu_loop-common.h"
     24 #include "signal-common.h"
     25 #include "elf.h"
     26 #include "internal.h"
     27 #include "fpu_helper.h"
     28 
     29 # ifdef TARGET_ABI_MIPSO32
     30 #  define MIPS_SYSCALL_NUMBER_UNUSED -1
     31 static const int8_t mips_syscall_args[] = {
     32 #include "syscall-args-o32.c.inc"
     33 };
     34 # endif /* O32 */
     35 
     36 /* Break codes */
     37 enum {
     38     BRK_OVERFLOW = 6,
     39     BRK_DIVZERO = 7
     40 };
     41 
     42 static void do_tr_or_bp(CPUMIPSState *env, unsigned int code, bool trap)
     43 {
     44     target_ulong pc = env->active_tc.PC;
     45 
     46     switch (code) {
     47     case BRK_OVERFLOW:
     48         force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTOVF, pc);
     49         break;
     50     case BRK_DIVZERO:
     51         force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, pc);
     52         break;
     53     default:
     54         if (trap) {
     55             force_sig(TARGET_SIGTRAP);
     56         } else {
     57             force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, pc);
     58         }
     59         break;
     60     }
     61 }
     62 
     63 void cpu_loop(CPUMIPSState *env)
     64 {
     65     CPUState *cs = env_cpu(env);
     66     int trapnr, si_code;
     67     unsigned int code;
     68     abi_long ret;
     69 # ifdef TARGET_ABI_MIPSO32
     70     unsigned int syscall_num;
     71 # endif
     72 
     73     for(;;) {
     74         cpu_exec_start(cs);
     75         trapnr = cpu_exec(cs);
     76         cpu_exec_end(cs);
     77         process_queued_cpu_work(cs);
     78 
     79         switch(trapnr) {
     80         case EXCP_SYSCALL:
     81             env->active_tc.PC += 4;
     82 # ifdef TARGET_ABI_MIPSO32
     83             syscall_num = env->active_tc.gpr[2] - 4000;
     84             if (syscall_num >= sizeof(mips_syscall_args)) {
     85                 /* syscall_num is larger that any defined for MIPS O32 */
     86                 ret = -TARGET_ENOSYS;
     87             } else if (mips_syscall_args[syscall_num] ==
     88                        MIPS_SYSCALL_NUMBER_UNUSED) {
     89                 /* syscall_num belongs to the range not defined for MIPS O32 */
     90                 ret = -TARGET_ENOSYS;
     91             } else {
     92                 /* syscall_num is valid */
     93                 int nb_args;
     94                 abi_ulong sp_reg;
     95                 abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0;
     96 
     97                 nb_args = mips_syscall_args[syscall_num];
     98                 sp_reg = env->active_tc.gpr[29];
     99                 switch (nb_args) {
    100                 /* these arguments are taken from the stack */
    101                 case 8:
    102                     if ((ret = get_user_ual(arg8, sp_reg + 28)) != 0) {
    103                         goto done_syscall;
    104                     }
    105                     /* fall through */
    106                 case 7:
    107                     if ((ret = get_user_ual(arg7, sp_reg + 24)) != 0) {
    108                         goto done_syscall;
    109                     }
    110                     /* fall through */
    111                 case 6:
    112                     if ((ret = get_user_ual(arg6, sp_reg + 20)) != 0) {
    113                         goto done_syscall;
    114                     }
    115                     /* fall through */
    116                 case 5:
    117                     if ((ret = get_user_ual(arg5, sp_reg + 16)) != 0) {
    118                         goto done_syscall;
    119                     }
    120                     /* fall through */
    121                 default:
    122                     break;
    123                 }
    124                 ret = do_syscall(env, env->active_tc.gpr[2],
    125                                  env->active_tc.gpr[4],
    126                                  env->active_tc.gpr[5],
    127                                  env->active_tc.gpr[6],
    128                                  env->active_tc.gpr[7],
    129                                  arg5, arg6, arg7, arg8);
    130             }
    131 done_syscall:
    132 # else
    133             ret = do_syscall(env, env->active_tc.gpr[2],
    134                              env->active_tc.gpr[4], env->active_tc.gpr[5],
    135                              env->active_tc.gpr[6], env->active_tc.gpr[7],
    136                              env->active_tc.gpr[8], env->active_tc.gpr[9],
    137                              env->active_tc.gpr[10], env->active_tc.gpr[11]);
    138 # endif /* O32 */
    139             if (ret == -QEMU_ERESTARTSYS) {
    140                 env->active_tc.PC -= 4;
    141                 break;
    142             }
    143             if (ret == -QEMU_ESIGRETURN) {
    144                 /* Returning from a successful sigreturn syscall.
    145                    Avoid clobbering register state.  */
    146                 break;
    147             }
    148             if ((abi_ulong)ret >= (abi_ulong)-1133) {
    149                 env->active_tc.gpr[7] = 1; /* error flag */
    150                 ret = -ret;
    151             } else {
    152                 env->active_tc.gpr[7] = 0; /* error flag */
    153             }
    154             env->active_tc.gpr[2] = ret;
    155             break;
    156         case EXCP_CpU:
    157         case EXCP_RI:
    158         case EXCP_DSPDIS:
    159             force_sig(TARGET_SIGILL);
    160             break;
    161         case EXCP_INTERRUPT:
    162             /* just indicate that signals should be handled asap */
    163             break;
    164         case EXCP_DEBUG:
    165             force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT,
    166                             env->active_tc.PC);
    167             break;
    168         case EXCP_FPE:
    169             si_code = TARGET_FPE_FLTUNK;
    170             if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INVALID) {
    171                 si_code = TARGET_FPE_FLTINV;
    172             } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_DIV0) {
    173                 si_code = TARGET_FPE_FLTDIV;
    174             } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_OVERFLOW) {
    175                 si_code = TARGET_FPE_FLTOVF;
    176             } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_UNDERFLOW) {
    177                 si_code = TARGET_FPE_FLTUND;
    178             } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INEXACT) {
    179                 si_code = TARGET_FPE_FLTRES;
    180             }
    181             force_sig_fault(TARGET_SIGFPE, si_code, env->active_tc.PC);
    182             break;
    183 
    184         /* The code below was inspired by the MIPS Linux kernel trap
    185          * handling code in arch/mips/kernel/traps.c.
    186          */
    187         case EXCP_BREAK:
    188             /*
    189              * As described in the original Linux kernel code, the below
    190              * checks on 'code' are to work around an old assembly bug.
    191              */
    192             code = env->error_code;
    193             if (code >= (1 << 10)) {
    194                 code >>= 10;
    195             }
    196             do_tr_or_bp(env, code, false);
    197             break;
    198         case EXCP_TRAP:
    199             do_tr_or_bp(env, env->error_code, true);
    200             break;
    201         case EXCP_ATOMIC:
    202             cpu_exec_step_atomic(cs);
    203             break;
    204         default:
    205             EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr);
    206             abort();
    207         }
    208         process_pending_signals(env);
    209     }
    210 }
    211 
    212 void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
    213 {
    214     CPUState *cpu = env_cpu(env);
    215     TaskState *ts = cpu->opaque;
    216     struct image_info *info = ts->info;
    217     int i;
    218 
    219     struct mode_req {
    220         bool single;
    221         bool soft;
    222         bool fr1;
    223         bool frdefault;
    224         bool fre;
    225     };
    226 
    227     static const struct mode_req fpu_reqs[] = {
    228         [MIPS_ABI_FP_ANY]    = { true,  true,  true,  true,  true  },
    229         [MIPS_ABI_FP_DOUBLE] = { false, false, false, true,  true  },
    230         [MIPS_ABI_FP_SINGLE] = { true,  false, false, false, false },
    231         [MIPS_ABI_FP_SOFT]   = { false, true,  false, false, false },
    232         [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false },
    233         [MIPS_ABI_FP_XX]     = { false, false, true,  true,  true  },
    234         [MIPS_ABI_FP_64]     = { false, false, true,  false, false },
    235         [MIPS_ABI_FP_64A]    = { false, false, true,  false, true  }
    236     };
    237 
    238     /*
    239      * Mode requirements when .MIPS.abiflags is not present in the ELF.
    240      * Not present means that everything is acceptable except FR1.
    241      */
    242     static struct mode_req none_req = { true, true, false, true, true };
    243 
    244     struct mode_req prog_req;
    245     struct mode_req interp_req;
    246 
    247     for(i = 0; i < 32; i++) {
    248         env->active_tc.gpr[i] = regs->regs[i];
    249     }
    250     env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1;
    251     if (regs->cp0_epc & 1) {
    252         env->hflags |= MIPS_HFLAG_M16;
    253     }
    254 
    255 #ifdef TARGET_ABI_MIPSO32
    256 # define MAX_FP_ABI MIPS_ABI_FP_64A
    257 #else
    258 # define MAX_FP_ABI MIPS_ABI_FP_SOFT
    259 #endif
    260      if ((info->fp_abi > MAX_FP_ABI && info->fp_abi != MIPS_ABI_FP_UNKNOWN)
    261         || (info->interp_fp_abi > MAX_FP_ABI &&
    262             info->interp_fp_abi != MIPS_ABI_FP_UNKNOWN)) {
    263         fprintf(stderr, "qemu: Unexpected FPU mode\n");
    264         exit(1);
    265     }
    266 
    267     prog_req = (info->fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req
    268                                             : fpu_reqs[info->fp_abi];
    269     interp_req = (info->interp_fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req
    270                                             : fpu_reqs[info->interp_fp_abi];
    271 
    272     prog_req.single &= interp_req.single;
    273     prog_req.soft &= interp_req.soft;
    274     prog_req.fr1 &= interp_req.fr1;
    275     prog_req.frdefault &= interp_req.frdefault;
    276     prog_req.fre &= interp_req.fre;
    277 
    278     bool cpu_has_mips_r2_r6 = env->insn_flags & ISA_MIPS_R2 ||
    279                               env->insn_flags & ISA_MIPS_R6;
    280 
    281     if (prog_req.fre && !prog_req.frdefault && !prog_req.fr1) {
    282         env->CP0_Config5 |= (1 << CP0C5_FRE);
    283         if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) {
    284             env->hflags |= MIPS_HFLAG_FRE;
    285         }
    286     } else if ((prog_req.fr1 && prog_req.frdefault) ||
    287          (prog_req.single && !prog_req.frdefault)) {
    288         if ((env->active_fpu.fcr0 & (1 << FCR0_F64)
    289             && cpu_has_mips_r2_r6) || prog_req.fr1) {
    290             env->CP0_Status |= (1 << CP0St_FR);
    291             env->hflags |= MIPS_HFLAG_F64;
    292         }
    293     } else  if (!prog_req.fre && !prog_req.frdefault &&
    294           !prog_req.fr1 && !prog_req.single && !prog_req.soft) {
    295         fprintf(stderr, "qemu: Can't find a matching FPU mode\n");
    296         exit(1);
    297     }
    298 
    299     if (env->insn_flags & ISA_NANOMIPS32) {
    300         return;
    301     }
    302     if (((info->elf_flags & EF_MIPS_NAN2008) != 0) !=
    303         ((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) != 0)) {
    304         if ((env->active_fpu.fcr31_rw_bitmask &
    305               (1 << FCR31_NAN2008)) == 0) {
    306             fprintf(stderr, "ELF binary's NaN mode not supported by CPU\n");
    307             exit(1);
    308         }
    309         if ((info->elf_flags & EF_MIPS_NAN2008) != 0) {
    310             env->active_fpu.fcr31 |= (1 << FCR31_NAN2008);
    311         } else {
    312             env->active_fpu.fcr31 &= ~(1 << FCR31_NAN2008);
    313         }
    314         restore_snan_bit_mode(env);
    315     }
    316 }