qemu/linux-user/sparc/cpu_loop.c
<<
>>
Prefs
   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-common.h"
  22#include "qemu.h"
  23#include "cpu_loop-common.h"
  24
  25#define SPARC64_STACK_BIAS 2047
  26
  27//#define DEBUG_WIN
  28
  29/* WARNING: dealing with register windows _is_ complicated. More info
  30   can be found at http://www.sics.se/~psm/sparcstack.html */
  31static inline int get_reg_index(CPUSPARCState *env, int cwp, int index)
  32{
  33    index = (index + cwp * 16) % (16 * env->nwindows);
  34    /* wrap handling : if cwp is on the last window, then we use the
  35       registers 'after' the end */
  36    if (index < 8 && env->cwp == env->nwindows - 1)
  37        index += 16 * env->nwindows;
  38    return index;
  39}
  40
  41/* save the register window 'cwp1' */
  42static inline void save_window_offset(CPUSPARCState *env, int cwp1)
  43{
  44    unsigned int i;
  45    abi_ulong sp_ptr;
  46
  47    sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
  48#ifdef TARGET_SPARC64
  49    if (sp_ptr & 3)
  50        sp_ptr += SPARC64_STACK_BIAS;
  51#endif
  52#if defined(DEBUG_WIN)
  53    printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n",
  54           sp_ptr, cwp1);
  55#endif
  56    for(i = 0; i < 16; i++) {
  57        /* FIXME - what to do if put_user() fails? */
  58        put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
  59        sp_ptr += sizeof(abi_ulong);
  60    }
  61}
  62
  63static void save_window(CPUSPARCState *env)
  64{
  65#ifndef TARGET_SPARC64
  66    unsigned int new_wim;
  67    new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) &
  68        ((1LL << env->nwindows) - 1);
  69    save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
  70    env->wim = new_wim;
  71#else
  72    save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
  73    env->cansave++;
  74    env->canrestore--;
  75#endif
  76}
  77
  78static void restore_window(CPUSPARCState *env)
  79{
  80#ifndef TARGET_SPARC64
  81    unsigned int new_wim;
  82#endif
  83    unsigned int i, cwp1;
  84    abi_ulong sp_ptr;
  85
  86#ifndef TARGET_SPARC64
  87    new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) &
  88        ((1LL << env->nwindows) - 1);
  89#endif
  90
  91    /* restore the invalid window */
  92    cwp1 = cpu_cwp_inc(env, env->cwp + 1);
  93    sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
  94#ifdef TARGET_SPARC64
  95    if (sp_ptr & 3)
  96        sp_ptr += SPARC64_STACK_BIAS;
  97#endif
  98#if defined(DEBUG_WIN)
  99    printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n",
 100           sp_ptr, cwp1);
 101#endif
 102    for(i = 0; i < 16; i++) {
 103        /* FIXME - what to do if get_user() fails? */
 104        get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
 105        sp_ptr += sizeof(abi_ulong);
 106    }
 107#ifdef TARGET_SPARC64
 108    env->canrestore++;
 109    if (env->cleanwin < env->nwindows - 1)
 110        env->cleanwin++;
 111    env->cansave--;
 112#else
 113    env->wim = new_wim;
 114#endif
 115}
 116
 117static void flush_windows(CPUSPARCState *env)
 118{
 119    int offset, cwp1;
 120
 121    offset = 1;
 122    for(;;) {
 123        /* if restore would invoke restore_window(), then we can stop */
 124        cwp1 = cpu_cwp_inc(env, env->cwp + offset);
 125#ifndef TARGET_SPARC64
 126        if (env->wim & (1 << cwp1))
 127            break;
 128#else
 129        if (env->canrestore == 0)
 130            break;
 131        env->cansave++;
 132        env->canrestore--;
 133#endif
 134        save_window_offset(env, cwp1);
 135        offset++;
 136    }
 137    cwp1 = cpu_cwp_inc(env, env->cwp + 1);
 138#ifndef TARGET_SPARC64
 139    /* set wim so that restore will reload the registers */
 140    env->wim = 1 << cwp1;
 141#endif
 142#if defined(DEBUG_WIN)
 143    printf("flush_windows: nb=%d\n", offset - 1);
 144#endif
 145}
 146
 147void cpu_loop (CPUSPARCState *env)
 148{
 149    CPUState *cs = env_cpu(env);
 150    int trapnr;
 151    abi_long ret;
 152    target_siginfo_t info;
 153
 154    while (1) {
 155        cpu_exec_start(cs);
 156        trapnr = cpu_exec(cs);
 157        cpu_exec_end(cs);
 158        process_queued_cpu_work(cs);
 159
 160        /* Compute PSR before exposing state.  */
 161        if (env->cc_op != CC_OP_FLAGS) {
 162            cpu_get_psr(env);
 163        }
 164
 165        switch (trapnr) {
 166#ifndef TARGET_SPARC64
 167        case 0x88:
 168        case 0x90:
 169#else
 170        case 0x110:
 171        case 0x16d:
 172#endif
 173            ret = do_syscall (env, env->gregs[1],
 174                              env->regwptr[0], env->regwptr[1],
 175                              env->regwptr[2], env->regwptr[3],
 176                              env->regwptr[4], env->regwptr[5],
 177                              0, 0);
 178            if (ret == -TARGET_ERESTARTSYS || ret == -TARGET_QEMU_ESIGRETURN) {
 179                break;
 180            }
 181            if ((abi_ulong)ret >= (abi_ulong)(-515)) {
 182#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
 183                env->xcc |= PSR_CARRY;
 184#else
 185                env->psr |= PSR_CARRY;
 186#endif
 187                ret = -ret;
 188            } else {
 189#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
 190                env->xcc &= ~PSR_CARRY;
 191#else
 192                env->psr &= ~PSR_CARRY;
 193#endif
 194            }
 195            env->regwptr[0] = ret;
 196            /* next instruction */
 197            env->pc = env->npc;
 198            env->npc = env->npc + 4;
 199            break;
 200        case 0x83: /* flush windows */
 201#ifdef TARGET_ABI32
 202        case 0x103:
 203#endif
 204            flush_windows(env);
 205            /* next instruction */
 206            env->pc = env->npc;
 207            env->npc = env->npc + 4;
 208            break;
 209#ifndef TARGET_SPARC64
 210        case TT_WIN_OVF: /* window overflow */
 211            save_window(env);
 212            break;
 213        case TT_WIN_UNF: /* window underflow */
 214            restore_window(env);
 215            break;
 216        case TT_TFAULT:
 217        case TT_DFAULT:
 218            {
 219                info.si_signo = TARGET_SIGSEGV;
 220                info.si_errno = 0;
 221                /* XXX: check env->error_code */
 222                info.si_code = TARGET_SEGV_MAPERR;
 223                info._sifields._sigfault._addr = env->mmuregs[4];
 224                queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
 225            }
 226            break;
 227#else
 228        case TT_SPILL: /* window overflow */
 229            save_window(env);
 230            break;
 231        case TT_FILL: /* window underflow */
 232            restore_window(env);
 233            break;
 234        case TT_TFAULT:
 235        case TT_DFAULT:
 236            {
 237                info.si_signo = TARGET_SIGSEGV;
 238                info.si_errno = 0;
 239                /* XXX: check env->error_code */
 240                info.si_code = TARGET_SEGV_MAPERR;
 241                if (trapnr == TT_DFAULT)
 242                    info._sifields._sigfault._addr = env->dmmu.mmuregs[4];
 243                else
 244                    info._sifields._sigfault._addr = cpu_tsptr(env)->tpc;
 245                queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
 246            }
 247            break;
 248#ifndef TARGET_ABI32
 249        case 0x16e:
 250            flush_windows(env);
 251            sparc64_get_context(env);
 252            break;
 253        case 0x16f:
 254            flush_windows(env);
 255            sparc64_set_context(env);
 256            break;
 257#endif
 258#endif
 259        case EXCP_INTERRUPT:
 260            /* just indicate that signals should be handled asap */
 261            break;
 262        case TT_ILL_INSN:
 263            {
 264                info.si_signo = TARGET_SIGILL;
 265                info.si_errno = 0;
 266                info.si_code = TARGET_ILL_ILLOPC;
 267                info._sifields._sigfault._addr = env->pc;
 268                queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
 269            }
 270            break;
 271        case EXCP_DEBUG:
 272            info.si_signo = TARGET_SIGTRAP;
 273            info.si_errno = 0;
 274            info.si_code = TARGET_TRAP_BRKPT;
 275            queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
 276            break;
 277        case EXCP_ATOMIC:
 278            cpu_exec_step_atomic(cs);
 279            break;
 280        default:
 281            fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr);
 282            cpu_dump_state(cs, stderr, 0);
 283            exit(EXIT_FAILURE);
 284        }
 285        process_pending_signals (env);
 286    }
 287}
 288
 289void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
 290{
 291    int i;
 292    env->pc = regs->pc;
 293    env->npc = regs->npc;
 294    env->y = regs->y;
 295    for(i = 0; i < 8; i++)
 296        env->gregs[i] = regs->u_regs[i];
 297    for(i = 0; i < 8; i++)
 298        env->regwptr[i] = regs->u_regs[i + 8];
 299}
 300