qemu/target/ppc/misc_helper.c
<<
>>
Prefs
   1/*
   2 * Miscellaneous PowerPC emulation helpers for QEMU.
   3 *
   4 *  Copyright (c) 2003-2007 Jocelyn Mayer
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2.1 of the License, or (at your option) any later version.
  10 *
  11 * This library 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 GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "qemu/log.h"
  22#include "cpu.h"
  23#include "exec/exec-all.h"
  24#include "exec/helper-proto.h"
  25#include "qemu/error-report.h"
  26#include "qemu/main-loop.h"
  27#include "mmu-book3s-v3.h"
  28
  29#include "helper_regs.h"
  30
  31/*****************************************************************************/
  32/* SPR accesses */
  33void helper_load_dump_spr(CPUPPCState *env, uint32_t sprn)
  34{
  35    qemu_log("Read SPR %d %03x => " TARGET_FMT_lx "\n", sprn, sprn,
  36             env->spr[sprn]);
  37}
  38
  39void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn)
  40{
  41    qemu_log("Write SPR %d %03x <= " TARGET_FMT_lx "\n", sprn, sprn,
  42             env->spr[sprn]);
  43}
  44
  45#ifdef TARGET_PPC64
  46static void raise_hv_fu_exception(CPUPPCState *env, uint32_t bit,
  47                                  const char *caller, uint32_t cause,
  48                                  uintptr_t raddr)
  49{
  50    qemu_log_mask(CPU_LOG_INT, "HV Facility %d is unavailable (%s)\n",
  51                  bit, caller);
  52
  53    env->spr[SPR_HFSCR] &= ~((target_ulong)FSCR_IC_MASK << FSCR_IC_POS);
  54
  55    raise_exception_err_ra(env, POWERPC_EXCP_HV_FU, cause, raddr);
  56}
  57
  58static void raise_fu_exception(CPUPPCState *env, uint32_t bit,
  59                               uint32_t sprn, uint32_t cause,
  60                               uintptr_t raddr)
  61{
  62    qemu_log("Facility SPR %d is unavailable (SPR FSCR:%d)\n", sprn, bit);
  63
  64    env->spr[SPR_FSCR] &= ~((target_ulong)FSCR_IC_MASK << FSCR_IC_POS);
  65    cause &= FSCR_IC_MASK;
  66    env->spr[SPR_FSCR] |= (target_ulong)cause << FSCR_IC_POS;
  67
  68    raise_exception_err_ra(env, POWERPC_EXCP_FU, 0, raddr);
  69}
  70#endif
  71
  72void helper_hfscr_facility_check(CPUPPCState *env, uint32_t bit,
  73                                 const char *caller, uint32_t cause)
  74{
  75#ifdef TARGET_PPC64
  76    if ((env->msr_mask & MSR_HVB) && !FIELD_EX64(env->msr, MSR, HV) &&
  77                                     !(env->spr[SPR_HFSCR] & (1UL << bit))) {
  78        raise_hv_fu_exception(env, bit, caller, cause, GETPC());
  79    }
  80#endif
  81}
  82
  83void helper_fscr_facility_check(CPUPPCState *env, uint32_t bit,
  84                                uint32_t sprn, uint32_t cause)
  85{
  86#ifdef TARGET_PPC64
  87    if (env->spr[SPR_FSCR] & (1ULL << bit)) {
  88        /* Facility is enabled, continue */
  89        return;
  90    }
  91    raise_fu_exception(env, bit, sprn, cause, GETPC());
  92#endif
  93}
  94
  95void helper_msr_facility_check(CPUPPCState *env, uint32_t bit,
  96                               uint32_t sprn, uint32_t cause)
  97{
  98#ifdef TARGET_PPC64
  99    if (env->msr & (1ULL << bit)) {
 100        /* Facility is enabled, continue */
 101        return;
 102    }
 103    raise_fu_exception(env, bit, sprn, cause, GETPC());
 104#endif
 105}
 106
 107#if !defined(CONFIG_USER_ONLY)
 108
 109void helper_store_sdr1(CPUPPCState *env, target_ulong val)
 110{
 111    if (env->spr[SPR_SDR1] != val) {
 112        ppc_store_sdr1(env, val);
 113        tlb_flush(env_cpu(env));
 114    }
 115}
 116
 117#if defined(TARGET_PPC64)
 118void helper_store_ptcr(CPUPPCState *env, target_ulong val)
 119{
 120    if (env->spr[SPR_PTCR] != val) {
 121        PowerPCCPU *cpu = env_archcpu(env);
 122        target_ulong ptcr_mask = PTCR_PATB | PTCR_PATS;
 123        target_ulong patbsize = val & PTCR_PATS;
 124
 125        qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, val);
 126
 127        assert(!cpu->vhyp);
 128        assert(env->mmu_model & POWERPC_MMU_3_00);
 129
 130        if (val & ~ptcr_mask) {
 131            error_report("Invalid bits 0x"TARGET_FMT_lx" set in PTCR",
 132                         val & ~ptcr_mask);
 133            val &= ptcr_mask;
 134        }
 135
 136        if (patbsize > 24) {
 137            error_report("Invalid Partition Table size 0x" TARGET_FMT_lx
 138                         " stored in PTCR", patbsize);
 139            return;
 140        }
 141
 142        env->spr[SPR_PTCR] = val;
 143        tlb_flush(env_cpu(env));
 144    }
 145}
 146
 147void helper_store_pcr(CPUPPCState *env, target_ulong value)
 148{
 149    PowerPCCPU *cpu = env_archcpu(env);
 150    PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
 151
 152    env->spr[SPR_PCR] = value & pcc->pcr_mask;
 153}
 154
 155/*
 156 * DPDES register is shared. Each bit reflects the state of the
 157 * doorbell interrupt of a thread of the same core.
 158 */
 159target_ulong helper_load_dpdes(CPUPPCState *env)
 160{
 161    target_ulong dpdes = 0;
 162
 163    helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP);
 164
 165    /* TODO: TCG supports only one thread */
 166    if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
 167        dpdes = 1;
 168    }
 169
 170    return dpdes;
 171}
 172
 173void helper_store_dpdes(CPUPPCState *env, target_ulong val)
 174{
 175    PowerPCCPU *cpu = env_archcpu(env);
 176    CPUState *cs = CPU(cpu);
 177
 178    helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP);
 179
 180    /* TODO: TCG supports only one thread */
 181    if (val & ~0x1) {
 182        qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value "
 183                      TARGET_FMT_lx"\n", val);
 184        return;
 185    }
 186
 187    if (val & 0x1) {
 188        env->pending_interrupts |= 1 << PPC_INTERRUPT_DOORBELL;
 189        cpu_interrupt(cs, CPU_INTERRUPT_HARD);
 190    } else {
 191        env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
 192    }
 193}
 194#endif /* defined(TARGET_PPC64) */
 195
 196void helper_store_pidr(CPUPPCState *env, target_ulong val)
 197{
 198    env->spr[SPR_BOOKS_PID] = val;
 199    tlb_flush(env_cpu(env));
 200}
 201
 202void helper_store_lpidr(CPUPPCState *env, target_ulong val)
 203{
 204    env->spr[SPR_LPIDR] = val;
 205
 206    /*
 207     * We need to flush the TLB on LPID changes as we only tag HV vs
 208     * guest in TCG TLB. Also the quadrants means the HV will
 209     * potentially access and cache entries for the current LPID as
 210     * well.
 211     */
 212    tlb_flush(env_cpu(env));
 213}
 214
 215void helper_store_40x_dbcr0(CPUPPCState *env, target_ulong val)
 216{
 217    /* Bits 26 & 27 affect single-stepping. */
 218    hreg_compute_hflags(env);
 219    /* Bits 28 & 29 affect reset or shutdown. */
 220    store_40x_dbcr0(env, val);
 221}
 222
 223void helper_store_40x_sler(CPUPPCState *env, target_ulong val)
 224{
 225    store_40x_sler(env, val);
 226}
 227#endif
 228
 229/*****************************************************************************/
 230/* Special registers manipulation */
 231
 232/*
 233 * This code is lifted from MacOnLinux. It is called whenever THRM1,2
 234 * or 3 is read an fixes up the values in such a way that will make
 235 * MacOS not hang. These registers exist on some 75x and 74xx
 236 * processors.
 237 */
 238void helper_fixup_thrm(CPUPPCState *env)
 239{
 240    target_ulong v, t;
 241    int i;
 242
 243#define THRM1_TIN       (1 << 31)
 244#define THRM1_TIV       (1 << 30)
 245#define THRM1_THRES(x)  (((x) & 0x7f) << 23)
 246#define THRM1_TID       (1 << 2)
 247#define THRM1_TIE       (1 << 1)
 248#define THRM1_V         (1 << 0)
 249#define THRM3_E         (1 << 0)
 250
 251    if (!(env->spr[SPR_THRM3] & THRM3_E)) {
 252        return;
 253    }
 254
 255    /* Note: Thermal interrupts are unimplemented */
 256    for (i = SPR_THRM1; i <= SPR_THRM2; i++) {
 257        v = env->spr[i];
 258        if (!(v & THRM1_V)) {
 259            continue;
 260        }
 261        v |= THRM1_TIV;
 262        v &= ~THRM1_TIN;
 263        t = v & THRM1_THRES(127);
 264        if ((v & THRM1_TID) && t < THRM1_THRES(24)) {
 265            v |= THRM1_TIN;
 266        }
 267        if (!(v & THRM1_TID) && t > THRM1_THRES(24)) {
 268            v |= THRM1_TIN;
 269        }
 270        env->spr[i] = v;
 271    }
 272}
 273