root / target-s390x / mem_helper.c @ a8170e5e
History | View | Annotate | Download (29.8 kB)
1 |
/*
|
---|---|
2 |
* S/390 memory access helper routines
|
3 |
*
|
4 |
* Copyright (c) 2009 Ulrich Hecht
|
5 |
* Copyright (c) 2009 Alexander Graf
|
6 |
*
|
7 |
* This library is free software; you can redistribute it and/or
|
8 |
* modify it under the terms of the GNU Lesser General Public
|
9 |
* License as published by the Free Software Foundation; either
|
10 |
* version 2 of the License, or (at your option) any later version.
|
11 |
*
|
12 |
* This library is distributed in the hope that it will be useful,
|
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15 |
* Lesser General Public License for more details.
|
16 |
*
|
17 |
* You should have received a copy of the GNU Lesser General Public
|
18 |
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
19 |
*/
|
20 |
|
21 |
#include "cpu.h" |
22 |
#include "helper.h" |
23 |
|
24 |
/*****************************************************************************/
|
25 |
/* Softmmu support */
|
26 |
#if !defined(CONFIG_USER_ONLY)
|
27 |
#include "softmmu_exec.h" |
28 |
|
29 |
#define MMUSUFFIX _mmu
|
30 |
|
31 |
#define SHIFT 0 |
32 |
#include "softmmu_template.h" |
33 |
|
34 |
#define SHIFT 1 |
35 |
#include "softmmu_template.h" |
36 |
|
37 |
#define SHIFT 2 |
38 |
#include "softmmu_template.h" |
39 |
|
40 |
#define SHIFT 3 |
41 |
#include "softmmu_template.h" |
42 |
|
43 |
/* try to fill the TLB and return an exception if error. If retaddr is
|
44 |
NULL, it means that the function was called in C code (i.e. not
|
45 |
from generated code or from helper.c) */
|
46 |
/* XXX: fix it to restore all registers */
|
47 |
void tlb_fill(CPUS390XState *env, target_ulong addr, int is_write, int mmu_idx, |
48 |
uintptr_t retaddr) |
49 |
{ |
50 |
TranslationBlock *tb; |
51 |
int ret;
|
52 |
|
53 |
ret = cpu_s390x_handle_mmu_fault(env, addr, is_write, mmu_idx); |
54 |
if (unlikely(ret != 0)) { |
55 |
if (likely(retaddr)) {
|
56 |
/* now we have a real cpu fault */
|
57 |
tb = tb_find_pc(retaddr); |
58 |
if (likely(tb)) {
|
59 |
/* the PC is inside the translated code. It means that we have
|
60 |
a virtual CPU fault */
|
61 |
cpu_restore_state(tb, env, retaddr); |
62 |
} |
63 |
} |
64 |
cpu_loop_exit(env); |
65 |
} |
66 |
} |
67 |
|
68 |
#endif
|
69 |
|
70 |
/* #define DEBUG_HELPER */
|
71 |
#ifdef DEBUG_HELPER
|
72 |
#define HELPER_LOG(x...) qemu_log(x)
|
73 |
#else
|
74 |
#define HELPER_LOG(x...)
|
75 |
#endif
|
76 |
|
77 |
#ifndef CONFIG_USER_ONLY
|
78 |
static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest, |
79 |
uint8_t byte) |
80 |
{ |
81 |
hwaddr dest_phys; |
82 |
hwaddr len = l; |
83 |
void *dest_p;
|
84 |
uint64_t asc = env->psw.mask & PSW_MASK_ASC; |
85 |
int flags;
|
86 |
|
87 |
if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) { |
88 |
cpu_stb_data(env, dest, byte); |
89 |
cpu_abort(env, "should never reach here");
|
90 |
} |
91 |
dest_phys |= dest & ~TARGET_PAGE_MASK; |
92 |
|
93 |
dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
|
94 |
|
95 |
memset(dest_p, byte, len); |
96 |
|
97 |
cpu_physical_memory_unmap(dest_p, 1, len, len);
|
98 |
} |
99 |
|
100 |
static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest, |
101 |
uint64_t src) |
102 |
{ |
103 |
hwaddr dest_phys; |
104 |
hwaddr src_phys; |
105 |
hwaddr len = l; |
106 |
void *dest_p;
|
107 |
void *src_p;
|
108 |
uint64_t asc = env->psw.mask & PSW_MASK_ASC; |
109 |
int flags;
|
110 |
|
111 |
if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) { |
112 |
cpu_stb_data(env, dest, 0);
|
113 |
cpu_abort(env, "should never reach here");
|
114 |
} |
115 |
dest_phys |= dest & ~TARGET_PAGE_MASK; |
116 |
|
117 |
if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) { |
118 |
cpu_ldub_data(env, src); |
119 |
cpu_abort(env, "should never reach here");
|
120 |
} |
121 |
src_phys |= src & ~TARGET_PAGE_MASK; |
122 |
|
123 |
dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
|
124 |
src_p = cpu_physical_memory_map(src_phys, &len, 0);
|
125 |
|
126 |
memmove(dest_p, src_p, len); |
127 |
|
128 |
cpu_physical_memory_unmap(dest_p, 1, len, len);
|
129 |
cpu_physical_memory_unmap(src_p, 0, len, len);
|
130 |
} |
131 |
#endif
|
132 |
|
133 |
/* and on array */
|
134 |
uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest, |
135 |
uint64_t src) |
136 |
{ |
137 |
int i;
|
138 |
unsigned char x; |
139 |
uint32_t cc = 0;
|
140 |
|
141 |
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", |
142 |
__func__, l, dest, src); |
143 |
for (i = 0; i <= l; i++) { |
144 |
x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i); |
145 |
if (x) {
|
146 |
cc = 1;
|
147 |
} |
148 |
cpu_stb_data(env, dest + i, x); |
149 |
} |
150 |
return cc;
|
151 |
} |
152 |
|
153 |
/* xor on array */
|
154 |
uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest, |
155 |
uint64_t src) |
156 |
{ |
157 |
int i;
|
158 |
unsigned char x; |
159 |
uint32_t cc = 0;
|
160 |
|
161 |
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", |
162 |
__func__, l, dest, src); |
163 |
|
164 |
#ifndef CONFIG_USER_ONLY
|
165 |
/* xor with itself is the same as memset(0) */
|
166 |
if ((l > 32) && (src == dest) && |
167 |
(src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) { |
168 |
mvc_fast_memset(env, l + 1, dest, 0); |
169 |
return 0; |
170 |
} |
171 |
#else
|
172 |
if (src == dest) {
|
173 |
memset(g2h(dest), 0, l + 1); |
174 |
return 0; |
175 |
} |
176 |
#endif
|
177 |
|
178 |
for (i = 0; i <= l; i++) { |
179 |
x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i); |
180 |
if (x) {
|
181 |
cc = 1;
|
182 |
} |
183 |
cpu_stb_data(env, dest + i, x); |
184 |
} |
185 |
return cc;
|
186 |
} |
187 |
|
188 |
/* or on array */
|
189 |
uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest, |
190 |
uint64_t src) |
191 |
{ |
192 |
int i;
|
193 |
unsigned char x; |
194 |
uint32_t cc = 0;
|
195 |
|
196 |
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", |
197 |
__func__, l, dest, src); |
198 |
for (i = 0; i <= l; i++) { |
199 |
x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i); |
200 |
if (x) {
|
201 |
cc = 1;
|
202 |
} |
203 |
cpu_stb_data(env, dest + i, x); |
204 |
} |
205 |
return cc;
|
206 |
} |
207 |
|
208 |
/* memmove */
|
209 |
void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
|
210 |
{ |
211 |
int i = 0; |
212 |
int x = 0; |
213 |
uint32_t l_64 = (l + 1) / 8; |
214 |
|
215 |
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", |
216 |
__func__, l, dest, src); |
217 |
|
218 |
#ifndef CONFIG_USER_ONLY
|
219 |
if ((l > 32) && |
220 |
(src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) && |
221 |
(dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) { |
222 |
if (dest == (src + 1)) { |
223 |
mvc_fast_memset(env, l + 1, dest, cpu_ldub_data(env, src));
|
224 |
return;
|
225 |
} else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) { |
226 |
mvc_fast_memmove(env, l + 1, dest, src);
|
227 |
return;
|
228 |
} |
229 |
} |
230 |
#else
|
231 |
if (dest == (src + 1)) { |
232 |
memset(g2h(dest), cpu_ldub_data(env, src), l + 1);
|
233 |
return;
|
234 |
} else {
|
235 |
memmove(g2h(dest), g2h(src), l + 1);
|
236 |
return;
|
237 |
} |
238 |
#endif
|
239 |
|
240 |
/* handle the parts that fit into 8-byte loads/stores */
|
241 |
if (dest != (src + 1)) { |
242 |
for (i = 0; i < l_64; i++) { |
243 |
cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x)); |
244 |
x += 8;
|
245 |
} |
246 |
} |
247 |
|
248 |
/* slow version crossing pages with byte accesses */
|
249 |
for (i = x; i <= l; i++) {
|
250 |
cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i)); |
251 |
} |
252 |
} |
253 |
|
254 |
/* compare unsigned byte arrays */
|
255 |
uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2) |
256 |
{ |
257 |
int i;
|
258 |
unsigned char x, y; |
259 |
uint32_t cc; |
260 |
|
261 |
HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n", |
262 |
__func__, l, s1, s2); |
263 |
for (i = 0; i <= l; i++) { |
264 |
x = cpu_ldub_data(env, s1 + i); |
265 |
y = cpu_ldub_data(env, s2 + i); |
266 |
HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
|
267 |
if (x < y) {
|
268 |
cc = 1;
|
269 |
goto done;
|
270 |
} else if (x > y) { |
271 |
cc = 2;
|
272 |
goto done;
|
273 |
} |
274 |
} |
275 |
cc = 0;
|
276 |
done:
|
277 |
HELPER_LOG("\n");
|
278 |
return cc;
|
279 |
} |
280 |
|
281 |
/* compare logical under mask */
|
282 |
uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask, |
283 |
uint64_t addr) |
284 |
{ |
285 |
uint8_t r, d; |
286 |
uint32_t cc; |
287 |
|
288 |
HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1, |
289 |
mask, addr); |
290 |
cc = 0;
|
291 |
while (mask) {
|
292 |
if (mask & 8) { |
293 |
d = cpu_ldub_data(env, addr); |
294 |
r = (r1 & 0xff000000UL) >> 24; |
295 |
HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d, |
296 |
addr); |
297 |
if (r < d) {
|
298 |
cc = 1;
|
299 |
break;
|
300 |
} else if (r > d) { |
301 |
cc = 2;
|
302 |
break;
|
303 |
} |
304 |
addr++; |
305 |
} |
306 |
mask = (mask << 1) & 0xf; |
307 |
r1 <<= 8;
|
308 |
} |
309 |
HELPER_LOG("\n");
|
310 |
return cc;
|
311 |
} |
312 |
|
313 |
/* store character under mask */
|
314 |
void HELPER(stcm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
|
315 |
uint64_t addr) |
316 |
{ |
317 |
uint8_t r; |
318 |
|
319 |
HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%lx\n", __func__, r1, mask,
|
320 |
addr); |
321 |
while (mask) {
|
322 |
if (mask & 8) { |
323 |
r = (r1 & 0xff000000UL) >> 24; |
324 |
cpu_stb_data(env, addr, r); |
325 |
HELPER_LOG("mask 0x%x %02x (0x%lx) ", mask, r, addr);
|
326 |
addr++; |
327 |
} |
328 |
mask = (mask << 1) & 0xf; |
329 |
r1 <<= 8;
|
330 |
} |
331 |
HELPER_LOG("\n");
|
332 |
} |
333 |
|
334 |
static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2) |
335 |
{ |
336 |
uint64_t r = d2; |
337 |
|
338 |
if (x2) {
|
339 |
r += env->regs[x2]; |
340 |
} |
341 |
|
342 |
if (b2) {
|
343 |
r += env->regs[b2]; |
344 |
} |
345 |
|
346 |
/* 31-Bit mode */
|
347 |
if (!(env->psw.mask & PSW_MASK_64)) {
|
348 |
r &= 0x7fffffff;
|
349 |
} |
350 |
|
351 |
return r;
|
352 |
} |
353 |
|
354 |
static inline uint64_t get_address_31fix(CPUS390XState *env, int reg) |
355 |
{ |
356 |
uint64_t r = env->regs[reg]; |
357 |
|
358 |
/* 31-Bit mode */
|
359 |
if (!(env->psw.mask & PSW_MASK_64)) {
|
360 |
r &= 0x7fffffff;
|
361 |
} |
362 |
|
363 |
return r;
|
364 |
} |
365 |
|
366 |
/* search string (c is byte to search, r2 is string, r1 end of string) */
|
367 |
uint32_t HELPER(srst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2) |
368 |
{ |
369 |
uint64_t i; |
370 |
uint32_t cc = 2;
|
371 |
uint64_t str = get_address_31fix(env, r2); |
372 |
uint64_t end = get_address_31fix(env, r1); |
373 |
|
374 |
HELPER_LOG("%s: c %d *r1 0x%" PRIx64 " *r2 0x%" PRIx64 "\n", __func__, |
375 |
c, env->regs[r1], env->regs[r2]); |
376 |
|
377 |
for (i = str; i != end; i++) {
|
378 |
if (cpu_ldub_data(env, i) == c) {
|
379 |
env->regs[r1] = i; |
380 |
cc = 1;
|
381 |
break;
|
382 |
} |
383 |
} |
384 |
|
385 |
return cc;
|
386 |
} |
387 |
|
388 |
/* unsigned string compare (c is string terminator) */
|
389 |
uint32_t HELPER(clst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2) |
390 |
{ |
391 |
uint64_t s1 = get_address_31fix(env, r1); |
392 |
uint64_t s2 = get_address_31fix(env, r2); |
393 |
uint8_t v1, v2; |
394 |
uint32_t cc; |
395 |
|
396 |
c = c & 0xff;
|
397 |
#ifdef CONFIG_USER_ONLY
|
398 |
if (!c) {
|
399 |
HELPER_LOG("%s: comparing '%s' and '%s'\n",
|
400 |
__func__, (char *)g2h(s1), (char *)g2h(s2)); |
401 |
} |
402 |
#endif
|
403 |
for (;;) {
|
404 |
v1 = cpu_ldub_data(env, s1); |
405 |
v2 = cpu_ldub_data(env, s2); |
406 |
if ((v1 == c || v2 == c) || (v1 != v2)) {
|
407 |
break;
|
408 |
} |
409 |
s1++; |
410 |
s2++; |
411 |
} |
412 |
|
413 |
if (v1 == v2) {
|
414 |
cc = 0;
|
415 |
} else {
|
416 |
cc = (v1 < v2) ? 1 : 2; |
417 |
/* FIXME: 31-bit mode! */
|
418 |
env->regs[r1] = s1; |
419 |
env->regs[r2] = s2; |
420 |
} |
421 |
return cc;
|
422 |
} |
423 |
|
424 |
/* move page */
|
425 |
void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
|
426 |
{ |
427 |
/* XXX missing r0 handling */
|
428 |
#ifdef CONFIG_USER_ONLY
|
429 |
int i;
|
430 |
|
431 |
for (i = 0; i < TARGET_PAGE_SIZE; i++) { |
432 |
cpu_stb_data(env, r1 + i, cpu_ldub_data(env, r2 + i)); |
433 |
} |
434 |
#else
|
435 |
mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2); |
436 |
#endif
|
437 |
} |
438 |
|
439 |
/* string copy (c is string terminator) */
|
440 |
void HELPER(mvst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
|
441 |
{ |
442 |
uint64_t dest = get_address_31fix(env, r1); |
443 |
uint64_t src = get_address_31fix(env, r2); |
444 |
uint8_t v; |
445 |
|
446 |
c = c & 0xff;
|
447 |
#ifdef CONFIG_USER_ONLY
|
448 |
if (!c) {
|
449 |
HELPER_LOG("%s: copy '%s' to 0x%lx\n", __func__, (char *)g2h(src), |
450 |
dest); |
451 |
} |
452 |
#endif
|
453 |
for (;;) {
|
454 |
v = cpu_ldub_data(env, src); |
455 |
cpu_stb_data(env, dest, v); |
456 |
if (v == c) {
|
457 |
break;
|
458 |
} |
459 |
src++; |
460 |
dest++; |
461 |
} |
462 |
env->regs[r1] = dest; /* FIXME: 31-bit mode! */
|
463 |
} |
464 |
|
465 |
/* compare and swap 64-bit */
|
466 |
uint32_t HELPER(csg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) |
467 |
{ |
468 |
/* FIXME: locking? */
|
469 |
uint32_t cc; |
470 |
uint64_t v2 = cpu_ldq_data(env, a2); |
471 |
|
472 |
if (env->regs[r1] == v2) {
|
473 |
cc = 0;
|
474 |
cpu_stq_data(env, a2, env->regs[r3]); |
475 |
} else {
|
476 |
cc = 1;
|
477 |
env->regs[r1] = v2; |
478 |
} |
479 |
return cc;
|
480 |
} |
481 |
|
482 |
/* compare double and swap 64-bit */
|
483 |
uint32_t HELPER(cdsg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) |
484 |
{ |
485 |
/* FIXME: locking? */
|
486 |
uint32_t cc; |
487 |
uint64_t v2_hi = cpu_ldq_data(env, a2); |
488 |
uint64_t v2_lo = cpu_ldq_data(env, a2 + 8);
|
489 |
uint64_t v1_hi = env->regs[r1]; |
490 |
uint64_t v1_lo = env->regs[r1 + 1];
|
491 |
|
492 |
if ((v1_hi == v2_hi) && (v1_lo == v2_lo)) {
|
493 |
cc = 0;
|
494 |
cpu_stq_data(env, a2, env->regs[r3]); |
495 |
cpu_stq_data(env, a2 + 8, env->regs[r3 + 1]); |
496 |
} else {
|
497 |
cc = 1;
|
498 |
env->regs[r1] = v2_hi; |
499 |
env->regs[r1 + 1] = v2_lo;
|
500 |
} |
501 |
|
502 |
return cc;
|
503 |
} |
504 |
|
505 |
/* compare and swap 32-bit */
|
506 |
uint32_t HELPER(cs)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) |
507 |
{ |
508 |
/* FIXME: locking? */
|
509 |
uint32_t cc; |
510 |
uint32_t v2 = cpu_ldl_data(env, a2); |
511 |
|
512 |
HELPER_LOG("%s: r1 %d a2 0x%lx r3 %d\n", __func__, r1, a2, r3);
|
513 |
if (((uint32_t)env->regs[r1]) == v2) {
|
514 |
cc = 0;
|
515 |
cpu_stl_data(env, a2, (uint32_t)env->regs[r3]); |
516 |
} else {
|
517 |
cc = 1;
|
518 |
env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | v2;
|
519 |
} |
520 |
return cc;
|
521 |
} |
522 |
|
523 |
static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
|
524 |
uint32_t mask) |
525 |
{ |
526 |
int pos = 24; /* top of the lower half of r1 */ |
527 |
uint64_t rmask = 0xff000000ULL;
|
528 |
uint8_t val = 0;
|
529 |
int ccd = 0; |
530 |
uint32_t cc = 0;
|
531 |
|
532 |
while (mask) {
|
533 |
if (mask & 8) { |
534 |
env->regs[r1] &= ~rmask; |
535 |
val = cpu_ldub_data(env, address); |
536 |
if ((val & 0x80) && !ccd) { |
537 |
cc = 1;
|
538 |
} |
539 |
ccd = 1;
|
540 |
if (val && cc == 0) { |
541 |
cc = 2;
|
542 |
} |
543 |
env->regs[r1] |= (uint64_t)val << pos; |
544 |
address++; |
545 |
} |
546 |
mask = (mask << 1) & 0xf; |
547 |
pos -= 8;
|
548 |
rmask >>= 8;
|
549 |
} |
550 |
|
551 |
return cc;
|
552 |
} |
553 |
|
554 |
/* execute instruction
|
555 |
this instruction executes an insn modified with the contents of r1
|
556 |
it does not change the executed instruction in memory
|
557 |
it does not change the program counter
|
558 |
in other words: tricky...
|
559 |
currently implemented by interpreting the cases it is most commonly used in
|
560 |
*/
|
561 |
uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1, |
562 |
uint64_t addr, uint64_t ret) |
563 |
{ |
564 |
uint16_t insn = cpu_lduw_code(env, addr); |
565 |
|
566 |
HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__, v1, addr,
|
567 |
insn); |
568 |
if ((insn & 0xf0ff) == 0xd000) { |
569 |
uint32_t l, insn2, b1, b2, d1, d2; |
570 |
|
571 |
l = v1 & 0xff;
|
572 |
insn2 = cpu_ldl_code(env, addr + 2);
|
573 |
b1 = (insn2 >> 28) & 0xf; |
574 |
b2 = (insn2 >> 12) & 0xf; |
575 |
d1 = (insn2 >> 16) & 0xfff; |
576 |
d2 = insn2 & 0xfff;
|
577 |
switch (insn & 0xf00) { |
578 |
case 0x200: |
579 |
helper_mvc(env, l, get_address(env, 0, b1, d1),
|
580 |
get_address(env, 0, b2, d2));
|
581 |
break;
|
582 |
case 0x500: |
583 |
cc = helper_clc(env, l, get_address(env, 0, b1, d1),
|
584 |
get_address(env, 0, b2, d2));
|
585 |
break;
|
586 |
case 0x700: |
587 |
cc = helper_xc(env, l, get_address(env, 0, b1, d1),
|
588 |
get_address(env, 0, b2, d2));
|
589 |
break;
|
590 |
case 0xc00: |
591 |
helper_tr(env, l, get_address(env, 0, b1, d1),
|
592 |
get_address(env, 0, b2, d2));
|
593 |
break;
|
594 |
default:
|
595 |
goto abort;
|
596 |
break;
|
597 |
} |
598 |
} else if ((insn & 0xff00) == 0x0a00) { |
599 |
/* supervisor call */
|
600 |
HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff); |
601 |
env->psw.addr = ret - 4;
|
602 |
env->int_svc_code = (insn | v1) & 0xff;
|
603 |
env->int_svc_ilc = 4;
|
604 |
helper_exception(env, EXCP_SVC); |
605 |
} else if ((insn & 0xff00) == 0xbf00) { |
606 |
uint32_t insn2, r1, r3, b2, d2; |
607 |
|
608 |
insn2 = cpu_ldl_code(env, addr + 2);
|
609 |
r1 = (insn2 >> 20) & 0xf; |
610 |
r3 = (insn2 >> 16) & 0xf; |
611 |
b2 = (insn2 >> 12) & 0xf; |
612 |
d2 = insn2 & 0xfff;
|
613 |
cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
|
614 |
} else {
|
615 |
abort:
|
616 |
cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n",
|
617 |
insn); |
618 |
} |
619 |
return cc;
|
620 |
} |
621 |
|
622 |
/* store character under mask high operates on the upper half of r1 */
|
623 |
void HELPER(stcmh)(CPUS390XState *env, uint32_t r1, uint64_t address,
|
624 |
uint32_t mask) |
625 |
{ |
626 |
int pos = 56; /* top of the upper half of r1 */ |
627 |
|
628 |
while (mask) {
|
629 |
if (mask & 8) { |
630 |
cpu_stb_data(env, address, (env->regs[r1] >> pos) & 0xff);
|
631 |
address++; |
632 |
} |
633 |
mask = (mask << 1) & 0xf; |
634 |
pos -= 8;
|
635 |
} |
636 |
} |
637 |
|
638 |
/* insert character under mask high; same as icm, but operates on the
|
639 |
upper half of r1 */
|
640 |
uint32_t HELPER(icmh)(CPUS390XState *env, uint32_t r1, uint64_t address, |
641 |
uint32_t mask) |
642 |
{ |
643 |
int pos = 56; /* top of the upper half of r1 */ |
644 |
uint64_t rmask = 0xff00000000000000ULL;
|
645 |
uint8_t val = 0;
|
646 |
int ccd = 0; |
647 |
uint32_t cc = 0;
|
648 |
|
649 |
while (mask) {
|
650 |
if (mask & 8) { |
651 |
env->regs[r1] &= ~rmask; |
652 |
val = cpu_ldub_data(env, address); |
653 |
if ((val & 0x80) && !ccd) { |
654 |
cc = 1;
|
655 |
} |
656 |
ccd = 1;
|
657 |
if (val && cc == 0) { |
658 |
cc = 2;
|
659 |
} |
660 |
env->regs[r1] |= (uint64_t)val << pos; |
661 |
address++; |
662 |
} |
663 |
mask = (mask << 1) & 0xf; |
664 |
pos -= 8;
|
665 |
rmask >>= 8;
|
666 |
} |
667 |
|
668 |
return cc;
|
669 |
} |
670 |
|
671 |
/* load access registers r1 to r3 from memory at a2 */
|
672 |
void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
673 |
{ |
674 |
int i;
|
675 |
|
676 |
for (i = r1;; i = (i + 1) % 16) { |
677 |
env->aregs[i] = cpu_ldl_data(env, a2); |
678 |
a2 += 4;
|
679 |
|
680 |
if (i == r3) {
|
681 |
break;
|
682 |
} |
683 |
} |
684 |
} |
685 |
|
686 |
/* store access registers r1 to r3 in memory at a2 */
|
687 |
void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
688 |
{ |
689 |
int i;
|
690 |
|
691 |
for (i = r1;; i = (i + 1) % 16) { |
692 |
cpu_stl_data(env, a2, env->aregs[i]); |
693 |
a2 += 4;
|
694 |
|
695 |
if (i == r3) {
|
696 |
break;
|
697 |
} |
698 |
} |
699 |
} |
700 |
|
701 |
/* move long */
|
702 |
uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2) |
703 |
{ |
704 |
uint64_t destlen = env->regs[r1 + 1] & 0xffffff; |
705 |
uint64_t dest = get_address_31fix(env, r1); |
706 |
uint64_t srclen = env->regs[r2 + 1] & 0xffffff; |
707 |
uint64_t src = get_address_31fix(env, r2); |
708 |
uint8_t pad = src >> 24;
|
709 |
uint8_t v; |
710 |
uint32_t cc; |
711 |
|
712 |
if (destlen == srclen) {
|
713 |
cc = 0;
|
714 |
} else if (destlen < srclen) { |
715 |
cc = 1;
|
716 |
} else {
|
717 |
cc = 2;
|
718 |
} |
719 |
|
720 |
if (srclen > destlen) {
|
721 |
srclen = destlen; |
722 |
} |
723 |
|
724 |
for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
|
725 |
v = cpu_ldub_data(env, src); |
726 |
cpu_stb_data(env, dest, v); |
727 |
} |
728 |
|
729 |
for (; destlen; dest++, destlen--) {
|
730 |
cpu_stb_data(env, dest, pad); |
731 |
} |
732 |
|
733 |
env->regs[r1 + 1] = destlen;
|
734 |
/* can't use srclen here, we trunc'ed it */
|
735 |
env->regs[r2 + 1] -= src - env->regs[r2];
|
736 |
env->regs[r1] = dest; |
737 |
env->regs[r2] = src; |
738 |
|
739 |
return cc;
|
740 |
} |
741 |
|
742 |
/* move long extended another memcopy insn with more bells and whistles */
|
743 |
uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2, |
744 |
uint32_t r3) |
745 |
{ |
746 |
uint64_t destlen = env->regs[r1 + 1];
|
747 |
uint64_t dest = env->regs[r1]; |
748 |
uint64_t srclen = env->regs[r3 + 1];
|
749 |
uint64_t src = env->regs[r3]; |
750 |
uint8_t pad = a2 & 0xff;
|
751 |
uint8_t v; |
752 |
uint32_t cc; |
753 |
|
754 |
if (!(env->psw.mask & PSW_MASK_64)) {
|
755 |
destlen = (uint32_t)destlen; |
756 |
srclen = (uint32_t)srclen; |
757 |
dest &= 0x7fffffff;
|
758 |
src &= 0x7fffffff;
|
759 |
} |
760 |
|
761 |
if (destlen == srclen) {
|
762 |
cc = 0;
|
763 |
} else if (destlen < srclen) { |
764 |
cc = 1;
|
765 |
} else {
|
766 |
cc = 2;
|
767 |
} |
768 |
|
769 |
if (srclen > destlen) {
|
770 |
srclen = destlen; |
771 |
} |
772 |
|
773 |
for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
|
774 |
v = cpu_ldub_data(env, src); |
775 |
cpu_stb_data(env, dest, v); |
776 |
} |
777 |
|
778 |
for (; destlen; dest++, destlen--) {
|
779 |
cpu_stb_data(env, dest, pad); |
780 |
} |
781 |
|
782 |
env->regs[r1 + 1] = destlen;
|
783 |
/* can't use srclen here, we trunc'ed it */
|
784 |
/* FIXME: 31-bit mode! */
|
785 |
env->regs[r3 + 1] -= src - env->regs[r3];
|
786 |
env->regs[r1] = dest; |
787 |
env->regs[r3] = src; |
788 |
|
789 |
return cc;
|
790 |
} |
791 |
|
792 |
/* compare logical long extended memcompare insn with padding */
|
793 |
uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2, |
794 |
uint32_t r3) |
795 |
{ |
796 |
uint64_t destlen = env->regs[r1 + 1];
|
797 |
uint64_t dest = get_address_31fix(env, r1); |
798 |
uint64_t srclen = env->regs[r3 + 1];
|
799 |
uint64_t src = get_address_31fix(env, r3); |
800 |
uint8_t pad = a2 & 0xff;
|
801 |
uint8_t v1 = 0, v2 = 0; |
802 |
uint32_t cc = 0;
|
803 |
|
804 |
if (!(destlen || srclen)) {
|
805 |
return cc;
|
806 |
} |
807 |
|
808 |
if (srclen > destlen) {
|
809 |
srclen = destlen; |
810 |
} |
811 |
|
812 |
for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
|
813 |
v1 = srclen ? cpu_ldub_data(env, src) : pad; |
814 |
v2 = destlen ? cpu_ldub_data(env, dest) : pad; |
815 |
if (v1 != v2) {
|
816 |
cc = (v1 < v2) ? 1 : 2; |
817 |
break;
|
818 |
} |
819 |
} |
820 |
|
821 |
env->regs[r1 + 1] = destlen;
|
822 |
/* can't use srclen here, we trunc'ed it */
|
823 |
env->regs[r3 + 1] -= src - env->regs[r3];
|
824 |
env->regs[r1] = dest; |
825 |
env->regs[r3] = src; |
826 |
|
827 |
return cc;
|
828 |
} |
829 |
|
830 |
/* checksum */
|
831 |
void HELPER(cksm)(CPUS390XState *env, uint32_t r1, uint32_t r2)
|
832 |
{ |
833 |
uint64_t src = get_address_31fix(env, r2); |
834 |
uint64_t src_len = env->regs[(r2 + 1) & 15]; |
835 |
uint64_t cksm = (uint32_t)env->regs[r1]; |
836 |
|
837 |
while (src_len >= 4) { |
838 |
cksm += cpu_ldl_data(env, src); |
839 |
|
840 |
/* move to next word */
|
841 |
src_len -= 4;
|
842 |
src += 4;
|
843 |
} |
844 |
|
845 |
switch (src_len) {
|
846 |
case 0: |
847 |
break;
|
848 |
case 1: |
849 |
cksm += cpu_ldub_data(env, src) << 24;
|
850 |
break;
|
851 |
case 2: |
852 |
cksm += cpu_lduw_data(env, src) << 16;
|
853 |
break;
|
854 |
case 3: |
855 |
cksm += cpu_lduw_data(env, src) << 16;
|
856 |
cksm += cpu_ldub_data(env, src + 2) << 8; |
857 |
break;
|
858 |
} |
859 |
|
860 |
/* indicate we've processed everything */
|
861 |
env->regs[r2] = src + src_len; |
862 |
env->regs[(r2 + 1) & 15] = 0; |
863 |
|
864 |
/* store result */
|
865 |
env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
|
866 |
((uint32_t)cksm + (cksm >> 32));
|
867 |
} |
868 |
|
869 |
void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
|
870 |
uint64_t src) |
871 |
{ |
872 |
int len_dest = len >> 4; |
873 |
int len_src = len & 0xf; |
874 |
uint8_t b; |
875 |
int second_nibble = 0; |
876 |
|
877 |
dest += len_dest; |
878 |
src += len_src; |
879 |
|
880 |
/* last byte is special, it only flips the nibbles */
|
881 |
b = cpu_ldub_data(env, src); |
882 |
cpu_stb_data(env, dest, (b << 4) | (b >> 4)); |
883 |
src--; |
884 |
len_src--; |
885 |
|
886 |
/* now pad every nibble with 0xf0 */
|
887 |
|
888 |
while (len_dest > 0) { |
889 |
uint8_t cur_byte = 0;
|
890 |
|
891 |
if (len_src > 0) { |
892 |
cur_byte = cpu_ldub_data(env, src); |
893 |
} |
894 |
|
895 |
len_dest--; |
896 |
dest--; |
897 |
|
898 |
/* only advance one nibble at a time */
|
899 |
if (second_nibble) {
|
900 |
cur_byte >>= 4;
|
901 |
len_src--; |
902 |
src--; |
903 |
} |
904 |
second_nibble = !second_nibble; |
905 |
|
906 |
/* digit */
|
907 |
cur_byte = (cur_byte & 0xf);
|
908 |
/* zone bits */
|
909 |
cur_byte |= 0xf0;
|
910 |
|
911 |
cpu_stb_data(env, dest, cur_byte); |
912 |
} |
913 |
} |
914 |
|
915 |
void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
|
916 |
uint64_t trans) |
917 |
{ |
918 |
int i;
|
919 |
|
920 |
for (i = 0; i <= len; i++) { |
921 |
uint8_t byte = cpu_ldub_data(env, array + i); |
922 |
uint8_t new_byte = cpu_ldub_data(env, trans + byte); |
923 |
|
924 |
cpu_stb_data(env, array + i, new_byte); |
925 |
} |
926 |
} |
927 |
|
928 |
#if !defined(CONFIG_USER_ONLY)
|
929 |
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
930 |
{ |
931 |
int i;
|
932 |
uint64_t src = a2; |
933 |
|
934 |
for (i = r1;; i = (i + 1) % 16) { |
935 |
env->cregs[i] = cpu_ldq_data(env, src); |
936 |
HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n", |
937 |
i, src, env->cregs[i]); |
938 |
src += sizeof(uint64_t);
|
939 |
|
940 |
if (i == r3) {
|
941 |
break;
|
942 |
} |
943 |
} |
944 |
|
945 |
tlb_flush(env, 1);
|
946 |
} |
947 |
|
948 |
void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
949 |
{ |
950 |
int i;
|
951 |
uint64_t src = a2; |
952 |
|
953 |
for (i = r1;; i = (i + 1) % 16) { |
954 |
env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
|
955 |
cpu_ldl_data(env, src); |
956 |
src += sizeof(uint32_t);
|
957 |
|
958 |
if (i == r3) {
|
959 |
break;
|
960 |
} |
961 |
} |
962 |
|
963 |
tlb_flush(env, 1);
|
964 |
} |
965 |
|
966 |
void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
967 |
{ |
968 |
int i;
|
969 |
uint64_t dest = a2; |
970 |
|
971 |
for (i = r1;; i = (i + 1) % 16) { |
972 |
cpu_stq_data(env, dest, env->cregs[i]); |
973 |
dest += sizeof(uint64_t);
|
974 |
|
975 |
if (i == r3) {
|
976 |
break;
|
977 |
} |
978 |
} |
979 |
} |
980 |
|
981 |
void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
982 |
{ |
983 |
int i;
|
984 |
uint64_t dest = a2; |
985 |
|
986 |
for (i = r1;; i = (i + 1) % 16) { |
987 |
cpu_stl_data(env, dest, env->cregs[i]); |
988 |
dest += sizeof(uint32_t);
|
989 |
|
990 |
if (i == r3) {
|
991 |
break;
|
992 |
} |
993 |
} |
994 |
} |
995 |
|
996 |
uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2) |
997 |
{ |
998 |
/* XXX implement */
|
999 |
|
1000 |
return 0; |
1001 |
} |
1002 |
|
1003 |
/* insert storage key extended */
|
1004 |
uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2) |
1005 |
{ |
1006 |
uint64_t addr = get_address(env, 0, 0, r2); |
1007 |
|
1008 |
if (addr > ram_size) {
|
1009 |
return 0; |
1010 |
} |
1011 |
|
1012 |
return env->storage_keys[addr / TARGET_PAGE_SIZE];
|
1013 |
} |
1014 |
|
1015 |
/* set storage key extended */
|
1016 |
void HELPER(sske)(CPUS390XState *env, uint32_t r1, uint64_t r2)
|
1017 |
{ |
1018 |
uint64_t addr = get_address(env, 0, 0, r2); |
1019 |
|
1020 |
if (addr > ram_size) {
|
1021 |
return;
|
1022 |
} |
1023 |
|
1024 |
env->storage_keys[addr / TARGET_PAGE_SIZE] = r1; |
1025 |
} |
1026 |
|
1027 |
/* reset reference bit extended */
|
1028 |
uint32_t HELPER(rrbe)(CPUS390XState *env, uint32_t r1, uint64_t r2) |
1029 |
{ |
1030 |
uint8_t re; |
1031 |
uint8_t key; |
1032 |
|
1033 |
if (r2 > ram_size) {
|
1034 |
return 0; |
1035 |
} |
1036 |
|
1037 |
key = env->storage_keys[r2 / TARGET_PAGE_SIZE]; |
1038 |
re = key & (SK_R | SK_C); |
1039 |
env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R); |
1040 |
|
1041 |
/*
|
1042 |
* cc
|
1043 |
*
|
1044 |
* 0 Reference bit zero; change bit zero
|
1045 |
* 1 Reference bit zero; change bit one
|
1046 |
* 2 Reference bit one; change bit zero
|
1047 |
* 3 Reference bit one; change bit one
|
1048 |
*/
|
1049 |
|
1050 |
return re >> 1; |
1051 |
} |
1052 |
|
1053 |
/* compare and swap and purge */
|
1054 |
uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint32_t r2) |
1055 |
{ |
1056 |
uint32_t cc; |
1057 |
uint32_t o1 = env->regs[r1]; |
1058 |
uint64_t a2 = get_address_31fix(env, r2) & ~3ULL;
|
1059 |
uint32_t o2 = cpu_ldl_data(env, a2); |
1060 |
|
1061 |
if (o1 == o2) {
|
1062 |
cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]); |
1063 |
if (env->regs[r2] & 0x3) { |
1064 |
/* flush TLB / ALB */
|
1065 |
tlb_flush(env, 1);
|
1066 |
} |
1067 |
cc = 0;
|
1068 |
} else {
|
1069 |
env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
|
1070 |
cc = 1;
|
1071 |
} |
1072 |
|
1073 |
return cc;
|
1074 |
} |
1075 |
|
1076 |
static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
|
1077 |
uint64_t mode1, uint64_t a2, uint64_t mode2) |
1078 |
{ |
1079 |
target_ulong src, dest; |
1080 |
int flags, cc = 0, i; |
1081 |
|
1082 |
if (!l) {
|
1083 |
return 0; |
1084 |
} else if (l > 256) { |
1085 |
/* max 256 */
|
1086 |
l = 256;
|
1087 |
cc = 3;
|
1088 |
} |
1089 |
|
1090 |
if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) { |
1091 |
cpu_loop_exit(env); |
1092 |
} |
1093 |
dest |= a1 & ~TARGET_PAGE_MASK; |
1094 |
|
1095 |
if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) { |
1096 |
cpu_loop_exit(env); |
1097 |
} |
1098 |
src |= a2 & ~TARGET_PAGE_MASK; |
1099 |
|
1100 |
/* XXX replace w/ memcpy */
|
1101 |
for (i = 0; i < l; i++) { |
1102 |
/* XXX be more clever */
|
1103 |
if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
|
1104 |
(((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) { |
1105 |
mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2); |
1106 |
break;
|
1107 |
} |
1108 |
stb_phys(dest + i, ldub_phys(src + i)); |
1109 |
} |
1110 |
|
1111 |
return cc;
|
1112 |
} |
1113 |
|
1114 |
uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) |
1115 |
{ |
1116 |
HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", |
1117 |
__func__, l, a1, a2); |
1118 |
|
1119 |
return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
|
1120 |
} |
1121 |
|
1122 |
uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) |
1123 |
{ |
1124 |
HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", |
1125 |
__func__, l, a1, a2); |
1126 |
|
1127 |
return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
|
1128 |
} |
1129 |
|
1130 |
/* invalidate pte */
|
1131 |
void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
|
1132 |
{ |
1133 |
uint64_t page = vaddr & TARGET_PAGE_MASK; |
1134 |
uint64_t pte = 0;
|
1135 |
|
1136 |
/* XXX broadcast to other CPUs */
|
1137 |
|
1138 |
/* XXX Linux is nice enough to give us the exact pte address.
|
1139 |
According to spec we'd have to find it out ourselves */
|
1140 |
/* XXX Linux is fine with overwriting the pte, the spec requires
|
1141 |
us to only set the invalid bit */
|
1142 |
stq_phys(pte_addr, pte | _PAGE_INVALID); |
1143 |
|
1144 |
/* XXX we exploit the fact that Linux passes the exact virtual
|
1145 |
address here - it's not obliged to! */
|
1146 |
tlb_flush_page(env, page); |
1147 |
|
1148 |
/* XXX 31-bit hack */
|
1149 |
if (page & 0x80000000) { |
1150 |
tlb_flush_page(env, page & ~0x80000000);
|
1151 |
} else {
|
1152 |
tlb_flush_page(env, page | 0x80000000);
|
1153 |
} |
1154 |
} |
1155 |
|
1156 |
/* flush local tlb */
|
1157 |
void HELPER(ptlb)(CPUS390XState *env)
|
1158 |
{ |
1159 |
tlb_flush(env, 1);
|
1160 |
} |
1161 |
|
1162 |
/* store using real address */
|
1163 |
void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint32_t v1)
|
1164 |
{ |
1165 |
stw_phys(get_address(env, 0, 0, addr), v1); |
1166 |
} |
1167 |
|
1168 |
/* load real address */
|
1169 |
uint32_t HELPER(lra)(CPUS390XState *env, uint64_t addr, uint32_t r1) |
1170 |
{ |
1171 |
uint32_t cc = 0;
|
1172 |
int old_exc = env->exception_index;
|
1173 |
uint64_t asc = env->psw.mask & PSW_MASK_ASC; |
1174 |
uint64_t ret; |
1175 |
int flags;
|
1176 |
|
1177 |
/* XXX incomplete - has more corner cases */
|
1178 |
if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) { |
1179 |
program_interrupt(env, PGM_SPECIAL_OP, 2);
|
1180 |
} |
1181 |
|
1182 |
env->exception_index = old_exc; |
1183 |
if (mmu_translate(env, addr, 0, asc, &ret, &flags)) { |
1184 |
cc = 3;
|
1185 |
} |
1186 |
if (env->exception_index == EXCP_PGM) {
|
1187 |
ret = env->int_pgm_code | 0x80000000;
|
1188 |
} else {
|
1189 |
ret |= addr & ~TARGET_PAGE_MASK; |
1190 |
} |
1191 |
env->exception_index = old_exc; |
1192 |
|
1193 |
if (!(env->psw.mask & PSW_MASK_64)) {
|
1194 |
env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
|
1195 |
(ret & 0xffffffffULL);
|
1196 |
} else {
|
1197 |
env->regs[r1] = ret; |
1198 |
} |
1199 |
|
1200 |
return cc;
|
1201 |
} |
1202 |
|
1203 |
#endif
|