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