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