1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
|
/*
* qemu user cpu loop
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu.h"
#include "cpu_loop-common.h"
static void gen_sigill_reg(CPUTLGState *env)
{
target_siginfo_t info;
info.si_signo = TARGET_SIGILL;
info.si_errno = 0;
info.si_code = TARGET_ILL_PRVREG;
info._sifields._sigfault._addr = env->pc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
static void do_signal(CPUTLGState *env, int signo, int sigcode)
{
target_siginfo_t info;
info.si_signo = signo;
info.si_errno = 0;
info._sifields._sigfault._addr = env->pc;
if (signo == TARGET_SIGSEGV) {
/* The passed in sigcode is a dummy; check for a page mapping
and pass either MAPERR or ACCERR. */
target_ulong addr = env->excaddr;
info._sifields._sigfault._addr = addr;
if (page_check_range(addr, 1, PAGE_VALID) < 0) {
sigcode = TARGET_SEGV_MAPERR;
} else {
sigcode = TARGET_SEGV_ACCERR;
}
}
info.si_code = sigcode;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
static void gen_sigsegv_maperr(CPUTLGState *env, target_ulong addr)
{
env->excaddr = addr;
do_signal(env, TARGET_SIGSEGV, 0);
}
static void set_regval(CPUTLGState *env, uint8_t reg, uint64_t val)
{
if (unlikely(reg >= TILEGX_R_COUNT)) {
switch (reg) {
case TILEGX_R_SN:
case TILEGX_R_ZERO:
return;
case TILEGX_R_IDN0:
case TILEGX_R_IDN1:
case TILEGX_R_UDN0:
case TILEGX_R_UDN1:
case TILEGX_R_UDN2:
case TILEGX_R_UDN3:
gen_sigill_reg(env);
return;
default:
g_assert_not_reached();
}
}
env->regs[reg] = val;
}
/*
* Compare the 8-byte contents of the CmpValue SPR with the 8-byte value in
* memory at the address held in the first source register. If the values are
* not equal, then no memory operation is performed. If the values are equal,
* the 8-byte quantity from the second source register is written into memory
* at the address held in the first source register. In either case, the result
* of the instruction is the value read from memory. The compare and write to
* memory are atomic and thus can be used for synchronization purposes. This
* instruction only operates for addresses aligned to a 8-byte boundary.
* Unaligned memory access causes an Unaligned Data Reference interrupt.
*
* Functional Description (64-bit)
* uint64_t memVal = memoryReadDoubleWord (rf[SrcA]);
* rf[Dest] = memVal;
* if (memVal == SPR[CmpValueSPR])
* memoryWriteDoubleWord (rf[SrcA], rf[SrcB]);
*
* Functional Description (32-bit)
* uint64_t memVal = signExtend32 (memoryReadWord (rf[SrcA]));
* rf[Dest] = memVal;
* if (memVal == signExtend32 (SPR[CmpValueSPR]))
* memoryWriteWord (rf[SrcA], rf[SrcB]);
*
*
* This function also processes exch and exch4 which need not process SPR.
*/
static void do_exch(CPUTLGState *env, bool quad, bool cmp)
{
target_ulong addr;
target_long val, sprval;
start_exclusive();
addr = env->atomic_srca;
if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) {
goto sigsegv_maperr;
}
if (cmp) {
if (quad) {
sprval = env->spregs[TILEGX_SPR_CMPEXCH];
} else {
sprval = sextract64(env->spregs[TILEGX_SPR_CMPEXCH], 0, 32);
}
}
if (!cmp || val == sprval) {
target_long valb = env->atomic_srcb;
if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) {
goto sigsegv_maperr;
}
}
set_regval(env, env->atomic_dstr, val);
end_exclusive();
return;
sigsegv_maperr:
end_exclusive();
gen_sigsegv_maperr(env, addr);
}
static void do_fetch(CPUTLGState *env, int trapnr, bool quad)
{
int8_t write = 1;
target_ulong addr;
target_long val, valb;
start_exclusive();
addr = env->atomic_srca;
valb = env->atomic_srcb;
if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) {
goto sigsegv_maperr;
}
switch (trapnr) {
case TILEGX_EXCP_OPCODE_FETCHADD:
case TILEGX_EXCP_OPCODE_FETCHADD4:
valb += val;
break;
case TILEGX_EXCP_OPCODE_FETCHADDGEZ:
valb += val;
if (valb < 0) {
write = 0;
}
break;
case TILEGX_EXCP_OPCODE_FETCHADDGEZ4:
valb += val;
if ((int32_t)valb < 0) {
write = 0;
}
break;
case TILEGX_EXCP_OPCODE_FETCHAND:
case TILEGX_EXCP_OPCODE_FETCHAND4:
valb &= val;
break;
case TILEGX_EXCP_OPCODE_FETCHOR:
case TILEGX_EXCP_OPCODE_FETCHOR4:
valb |= val;
break;
default:
g_assert_not_reached();
}
if (write) {
if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) {
goto sigsegv_maperr;
}
}
set_regval(env, env->atomic_dstr, val);
end_exclusive();
return;
sigsegv_maperr:
end_exclusive();
gen_sigsegv_maperr(env, addr);
}
void cpu_loop(CPUTLGState *env)
{
CPUState *cs = env_cpu(env);
int trapnr;
while (1) {
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
switch (trapnr) {
case TILEGX_EXCP_SYSCALL:
{
abi_ulong ret = do_syscall(env, env->regs[TILEGX_R_NR],
env->regs[0], env->regs[1],
env->regs[2], env->regs[3],
env->regs[4], env->regs[5],
env->regs[6], env->regs[7]);
if (ret == -TARGET_ERESTARTSYS) {
env->pc -= 8;
} else if (ret != -TARGET_QEMU_ESIGRETURN) {
env->regs[TILEGX_R_RE] = ret;
env->regs[TILEGX_R_ERR] = TILEGX_IS_ERRNO(ret) ? -ret : 0;
}
break;
}
case TILEGX_EXCP_OPCODE_EXCH:
do_exch(env, true, false);
break;
case TILEGX_EXCP_OPCODE_EXCH4:
do_exch(env, false, false);
break;
case TILEGX_EXCP_OPCODE_CMPEXCH:
do_exch(env, true, true);
break;
case TILEGX_EXCP_OPCODE_CMPEXCH4:
do_exch(env, false, true);
break;
case TILEGX_EXCP_OPCODE_FETCHADD:
case TILEGX_EXCP_OPCODE_FETCHADDGEZ:
case TILEGX_EXCP_OPCODE_FETCHAND:
case TILEGX_EXCP_OPCODE_FETCHOR:
do_fetch(env, trapnr, true);
break;
case TILEGX_EXCP_OPCODE_FETCHADD4:
case TILEGX_EXCP_OPCODE_FETCHADDGEZ4:
case TILEGX_EXCP_OPCODE_FETCHAND4:
case TILEGX_EXCP_OPCODE_FETCHOR4:
do_fetch(env, trapnr, false);
break;
case TILEGX_EXCP_SIGNAL:
do_signal(env, env->signo, env->sigcode);
break;
case TILEGX_EXCP_REG_IDN_ACCESS:
case TILEGX_EXCP_REG_UDN_ACCESS:
gen_sigill_reg(env);
break;
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
break;
default:
fprintf(stderr, "trapnr is %d[0x%x].\n", trapnr, trapnr);
g_assert_not_reached();
}
process_pending_signals(env);
}
}
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
{
int i;
for (i = 0; i < TILEGX_R_COUNT; i++) {
env->regs[i] = regs->regs[i];
}
for (i = 0; i < TILEGX_SPR_COUNT; i++) {
env->spregs[i] = 0;
}
env->pc = regs->pc;
}
|