2009-12-05 14:44:21 +03:00
|
|
|
/*
|
2012-09-02 11:33:35 +04:00
|
|
|
* S/390 misc helper routines
|
2009-12-05 14:44:21 +03:00
|
|
|
*
|
2011-03-23 12:58:07 +03:00
|
|
|
* Copyright (c) 2009 Ulrich Hecht
|
2009-12-05 14:44:21 +03:00
|
|
|
* Copyright (c) 2009 Alexander Graf
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
2019-01-29 16:37:47 +03:00
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2009-12-05 14:44:21 +03:00
|
|
|
*
|
|
|
|
* This library 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
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2010-03-07 18:48:43 +03:00
|
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
2009-12-05 14:44:21 +03:00
|
|
|
*/
|
|
|
|
|
2016-01-26 21:17:00 +03:00
|
|
|
#include "qemu/osdep.h"
|
2021-01-14 10:07:36 +03:00
|
|
|
#include "qemu/cutils.h"
|
2011-07-13 16:44:15 +04:00
|
|
|
#include "cpu.h"
|
2021-07-07 13:53:16 +03:00
|
|
|
#include "s390x-internal.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/host-utils.h"
|
2014-04-08 09:31:41 +04:00
|
|
|
#include "exec/helper-proto.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/timer.h"
|
2016-03-15 15:18:37 +03:00
|
|
|
#include "exec/exec-all.h"
|
2014-03-28 22:42:10 +04:00
|
|
|
#include "exec/cpu_ldst.h"
|
2018-06-27 16:44:06 +03:00
|
|
|
#include "qapi/error.h"
|
|
|
|
#include "tcg_s390x.h"
|
2019-02-12 08:30:44 +03:00
|
|
|
#include "s390-tod.h"
|
2009-12-05 14:44:21 +03:00
|
|
|
|
2012-09-02 11:33:30 +04:00
|
|
|
#if !defined(CONFIG_USER_ONLY)
|
2013-07-25 18:57:45 +04:00
|
|
|
#include "sysemu/cpus.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/sysemu.h"
|
2013-05-21 19:04:58 +04:00
|
|
|
#include "hw/s390x/ebcdic.h"
|
2017-09-13 16:24:00 +03:00
|
|
|
#include "hw/s390x/s390-virtio-hcall.h"
|
2017-09-13 16:24:03 +03:00
|
|
|
#include "hw/s390x/sclp.h"
|
2018-01-29 15:56:14 +03:00
|
|
|
#include "hw/s390x/s390_flic.h"
|
2018-01-29 15:56:18 +03:00
|
|
|
#include "hw/s390x/ioinst.h"
|
2017-11-16 12:09:53 +03:00
|
|
|
#include "hw/s390x/s390-pci-inst.h"
|
2018-01-29 15:56:18 +03:00
|
|
|
#include "hw/boards.h"
|
2018-06-27 16:44:06 +03:00
|
|
|
#include "hw/s390x/tod.h"
|
2009-12-05 14:44:21 +03:00
|
|
|
#endif
|
2011-03-23 12:58:07 +03:00
|
|
|
|
2011-03-23 12:58:07 +03:00
|
|
|
/* #define DEBUG_HELPER */
|
|
|
|
#ifdef DEBUG_HELPER
|
|
|
|
#define HELPER_LOG(x...) qemu_log(x)
|
|
|
|
#else
|
|
|
|
#define HELPER_LOG(x...)
|
|
|
|
#endif
|
|
|
|
|
2012-09-15 06:31:57 +04:00
|
|
|
/* Raise an exception statically from a TB. */
|
2012-09-02 11:33:39 +04:00
|
|
|
void HELPER(exception)(CPUS390XState *env, uint32_t excp)
|
2011-03-23 12:58:07 +03:00
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
CPUState *cs = env_cpu(env);
|
2013-08-26 10:31:06 +04:00
|
|
|
|
2012-09-02 11:33:30 +04:00
|
|
|
HELPER_LOG("%s: exception %d\n", __func__, excp);
|
2013-08-26 10:31:06 +04:00
|
|
|
cs->exception_index = excp;
|
2013-08-27 19:52:12 +04:00
|
|
|
cpu_loop_exit(cs);
|
2011-03-23 12:58:07 +03:00
|
|
|
}
|
|
|
|
|
2017-12-08 19:02:05 +03:00
|
|
|
/* Store CPU Timer (also used for EXTRACT CPU TIME) */
|
|
|
|
uint64_t HELPER(stpt)(CPUS390XState *env)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_USER_ONLY)
|
|
|
|
/*
|
|
|
|
* Fake a descending CPU timer. We could get negative values here,
|
|
|
|
* but we don't care as it is up to the OS when to process that
|
|
|
|
* interrupt and reset to > 0.
|
|
|
|
*/
|
|
|
|
return UINT64_MAX - (uint64_t)cpu_get_host_ticks();
|
|
|
|
#else
|
|
|
|
return time2tod(env->cputm - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-02-12 08:30:44 +03:00
|
|
|
/* Store Clock */
|
|
|
|
uint64_t HELPER(stck)(CPUS390XState *env)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_USER_ONLY
|
|
|
|
struct timespec ts;
|
|
|
|
uint64_t ns;
|
|
|
|
|
|
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
|
|
ns = ts.tv_sec * NANOSECONDS_PER_SECOND + ts.tv_nsec;
|
|
|
|
|
|
|
|
return TOD_UNIX_EPOCH + time2tod(ns);
|
|
|
|
#else
|
|
|
|
S390TODState *td = s390_get_todstate();
|
|
|
|
S390TODClass *tdc = S390_TOD_GET_CLASS(td);
|
|
|
|
S390TOD tod;
|
2017-06-01 01:01:20 +03:00
|
|
|
|
2019-02-12 08:30:44 +03:00
|
|
|
tdc->get(td, &tod, &error_abort);
|
|
|
|
return tod.low;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
2011-03-23 12:58:07 +03:00
|
|
|
/* SCLP service call */
|
2012-08-27 22:12:40 +04:00
|
|
|
uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
|
2011-03-23 12:58:07 +03:00
|
|
|
{
|
tcg: drop global lock during TCG code execution
This finally allows TCG to benefit from the iothread introduction: Drop
the global mutex while running pure TCG CPU code. Reacquire the lock
when entering MMIO or PIO emulation, or when leaving the TCG loop.
We have to revert a few optimization for the current TCG threading
model, namely kicking the TCG thread in qemu_mutex_lock_iothread and not
kicking it in qemu_cpu_kick. We also need to disable RAM block
reordering until we have a more efficient locking mechanism at hand.
Still, a Linux x86 UP guest and my Musicpal ARM model boot fine here.
These numbers demonstrate where we gain something:
20338 jan 20 0 331m 75m 6904 R 99 0.9 0:50.95 qemu-system-arm
20337 jan 20 0 331m 75m 6904 S 20 0.9 0:26.50 qemu-system-arm
The guest CPU was fully loaded, but the iothread could still run mostly
independent on a second core. Without the patch we don't get beyond
32206 jan 20 0 330m 73m 7036 R 82 0.9 1:06.00 qemu-system-arm
32204 jan 20 0 330m 73m 7036 S 21 0.9 0:17.03 qemu-system-arm
We don't benefit significantly, though, when the guest is not fully
loading a host CPU.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Message-Id: <1439220437-23957-10-git-send-email-fred.konrad@greensocs.com>
[FK: Rebase, fix qemu_devices_reset deadlock, rm address_space_* mutex]
Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
[EGC: fixed iothread lock for cpu-exec IRQ handling]
Signed-off-by: Emilio G. Cota <cota@braap.org>
[AJB: -smp single-threaded fix, clean commit msg, BQL fixes]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Reviewed-by: Pranith Kumar <bobby.prani@gmail.com>
[PM: target-arm changes]
Acked-by: Peter Maydell <peter.maydell@linaro.org>
2017-02-23 21:29:11 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2014-01-13 15:55:55 +04:00
|
|
|
int r = sclp_service_call(env, r1, r2);
|
2017-11-30 19:27:38 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2012-07-24 01:37:04 +04:00
|
|
|
if (r < 0) {
|
2019-10-01 20:16:00 +03:00
|
|
|
tcg_s390_program_interrupt(env, -r, GETPC());
|
2012-07-24 01:37:04 +04:00
|
|
|
}
|
|
|
|
return r;
|
2011-03-23 12:58:07 +03:00
|
|
|
}
|
|
|
|
|
2015-06-15 18:57:07 +03:00
|
|
|
void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num)
|
2011-03-23 12:58:07 +03:00
|
|
|
{
|
|
|
|
uint64_t r;
|
|
|
|
|
|
|
|
switch (num) {
|
|
|
|
case 0x500:
|
|
|
|
/* KVM hypercall */
|
2017-04-24 01:32:40 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2013-01-17 08:23:46 +04:00
|
|
|
r = s390_virtio_hypercall(env);
|
2017-04-24 01:32:40 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2011-03-23 12:58:07 +03:00
|
|
|
break;
|
|
|
|
case 0x44:
|
|
|
|
/* yield */
|
|
|
|
r = 0;
|
|
|
|
break;
|
|
|
|
case 0x308:
|
|
|
|
/* ipl */
|
2017-11-16 20:05:26 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2017-11-30 19:27:34 +03:00
|
|
|
handle_diag_308(env, r1, r3, GETPC());
|
2017-11-16 20:05:26 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2011-03-23 12:58:07 +03:00
|
|
|
r = 0;
|
|
|
|
break;
|
2017-08-16 18:30:36 +03:00
|
|
|
case 0x288:
|
|
|
|
/* time bomb (watchdog) */
|
|
|
|
r = handle_diag_288(env, r1, r3);
|
|
|
|
break;
|
2011-03-23 12:58:07 +03:00
|
|
|
default:
|
|
|
|
r = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r) {
|
2019-10-01 20:16:00 +03:00
|
|
|
tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
|
2011-03-23 12:58:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set Prefix */
|
2012-09-02 11:33:39 +04:00
|
|
|
void HELPER(spx)(CPUS390XState *env, uint64_t a1)
|
2011-03-23 12:58:07 +03:00
|
|
|
{
|
2021-08-05 15:59:38 +03:00
|
|
|
const uint32_t prefix = a1 & 0x7fffe000;
|
|
|
|
const uint32_t old_prefix = env->psa;
|
2019-03-23 05:21:48 +03:00
|
|
|
CPUState *cs = env_cpu(env);
|
2021-08-05 15:59:38 +03:00
|
|
|
|
|
|
|
if (prefix == old_prefix) {
|
|
|
|
return;
|
|
|
|
}
|
2022-06-30 12:43:40 +03:00
|
|
|
/*
|
|
|
|
* Since prefix got aligned to 8k and memory increments are a multiple of
|
|
|
|
* 8k checking the first page is sufficient
|
|
|
|
*/
|
|
|
|
if (!mmu_absolute_addr_valid(prefix, true)) {
|
|
|
|
tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC());
|
|
|
|
}
|
2013-09-04 03:29:02 +04:00
|
|
|
|
2012-08-25 03:11:32 +04:00
|
|
|
env->psa = prefix;
|
2015-11-13 15:25:21 +03:00
|
|
|
HELPER_LOG("prefix: %#x\n", prefix);
|
2013-09-04 03:29:02 +04:00
|
|
|
tlb_flush_page(cs, 0);
|
|
|
|
tlb_flush_page(cs, TARGET_PAGE_SIZE);
|
2021-08-05 15:59:38 +03:00
|
|
|
if (prefix != 0) {
|
|
|
|
tlb_flush_page(cs, prefix);
|
|
|
|
tlb_flush_page(cs, prefix + TARGET_PAGE_SIZE);
|
|
|
|
}
|
|
|
|
if (old_prefix != 0) {
|
|
|
|
tlb_flush_page(cs, old_prefix);
|
|
|
|
tlb_flush_page(cs, old_prefix + TARGET_PAGE_SIZE);
|
|
|
|
}
|
2011-03-23 12:58:07 +03:00
|
|
|
}
|
|
|
|
|
2018-06-29 20:05:20 +03:00
|
|
|
static void update_ckc_timer(CPUS390XState *env)
|
2011-03-23 12:58:07 +03:00
|
|
|
{
|
2018-06-27 16:44:06 +03:00
|
|
|
S390TODState *td = s390_get_todstate();
|
2018-06-29 20:05:20 +03:00
|
|
|
uint64_t time;
|
2018-06-27 16:44:06 +03:00
|
|
|
|
2018-06-27 16:44:07 +03:00
|
|
|
/* stop the timer and remove pending CKC IRQs */
|
|
|
|
timer_del(env->tod_timer);
|
2018-06-29 20:05:20 +03:00
|
|
|
g_assert(qemu_mutex_iothread_locked());
|
2018-06-27 16:44:07 +03:00
|
|
|
env->pending_int &= ~INTERRUPT_EXT_CLOCK_COMPARATOR;
|
|
|
|
|
|
|
|
/* the tod has to exceed the ckc, this can never happen if ckc is all 1's */
|
2018-06-29 20:05:20 +03:00
|
|
|
if (env->ckc == -1ULL) {
|
2011-03-23 12:58:07 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-19 00:42:26 +03:00
|
|
|
/* difference between origins */
|
2018-06-29 20:05:20 +03:00
|
|
|
time = env->ckc - td->base.low;
|
2015-05-19 00:42:26 +03:00
|
|
|
|
2011-03-23 12:58:07 +03:00
|
|
|
/* nanoseconds */
|
2015-05-19 00:42:25 +03:00
|
|
|
time = tod2time(time);
|
2011-03-23 12:58:07 +03:00
|
|
|
|
2018-06-27 16:44:05 +03:00
|
|
|
timer_mod(env->tod_timer, time);
|
2011-03-23 12:58:07 +03:00
|
|
|
}
|
|
|
|
|
2018-06-29 20:05:20 +03:00
|
|
|
/* Set Clock Comparator */
|
|
|
|
void HELPER(sckc)(CPUS390XState *env, uint64_t ckc)
|
|
|
|
{
|
|
|
|
env->ckc = ckc;
|
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
update_ckc_timer(env);
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
}
|
|
|
|
|
2018-06-27 16:44:06 +03:00
|
|
|
void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque)
|
|
|
|
{
|
|
|
|
S390CPU *cpu = S390_CPU(cs);
|
|
|
|
|
2018-06-29 20:05:20 +03:00
|
|
|
update_ckc_timer(&cpu->env);
|
2018-06-27 16:44:06 +03:00
|
|
|
}
|
|
|
|
|
2018-06-27 16:44:08 +03:00
|
|
|
/* Set Clock */
|
|
|
|
uint32_t HELPER(sck)(CPUS390XState *env, uint64_t tod_low)
|
|
|
|
{
|
|
|
|
S390TODState *td = s390_get_todstate();
|
|
|
|
S390TODClass *tdc = S390_TOD_GET_CLASS(td);
|
|
|
|
S390TOD tod = {
|
|
|
|
.high = 0,
|
|
|
|
.low = tod_low,
|
|
|
|
};
|
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
tdc->set(td, &tod, &error_abort);
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-08 19:01:56 +03:00
|
|
|
/* Set Tod Programmable Field */
|
|
|
|
void HELPER(sckpf)(CPUS390XState *env, uint64_t r0)
|
|
|
|
{
|
|
|
|
uint32_t val = r0;
|
|
|
|
|
|
|
|
if (val & 0xffff0000) {
|
2019-10-01 20:16:00 +03:00
|
|
|
tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
|
2017-12-08 19:01:56 +03:00
|
|
|
}
|
|
|
|
env->todpr = val;
|
|
|
|
}
|
|
|
|
|
2011-03-23 12:58:07 +03:00
|
|
|
/* Store Clock Comparator */
|
2012-08-25 02:36:58 +04:00
|
|
|
uint64_t HELPER(stckc)(CPUS390XState *env)
|
2011-03-23 12:58:07 +03:00
|
|
|
{
|
2015-05-19 00:42:28 +03:00
|
|
|
return env->ckc;
|
2011-03-23 12:58:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set CPU Timer */
|
2012-08-25 02:47:26 +04:00
|
|
|
void HELPER(spt)(CPUS390XState *env, uint64_t time)
|
2011-03-23 12:58:07 +03:00
|
|
|
{
|
|
|
|
if (time == -1ULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* nanoseconds */
|
2015-05-19 00:42:25 +03:00
|
|
|
time = tod2time(time);
|
2011-03-23 12:58:07 +03:00
|
|
|
|
2015-05-19 00:42:29 +03:00
|
|
|
env->cputm = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time;
|
|
|
|
|
|
|
|
timer_mod(env->cpu_timer, env->cputm);
|
2011-03-23 12:58:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Store System Information */
|
2018-01-29 15:56:18 +03:00
|
|
|
uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint64_t r0, uint64_t r1)
|
2011-03-23 12:58:07 +03:00
|
|
|
{
|
2018-01-29 15:56:18 +03:00
|
|
|
const uintptr_t ra = GETPC();
|
|
|
|
const uint32_t sel1 = r0 & STSI_R0_SEL1_MASK;
|
|
|
|
const uint32_t sel2 = r1 & STSI_R1_SEL2_MASK;
|
|
|
|
const MachineState *ms = MACHINE(qdev_get_machine());
|
|
|
|
uint16_t total_cpus = 0, conf_cpus = 0, reserved_cpus = 0;
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2018-05-12 07:59:43 +03:00
|
|
|
SysIB sysib = { };
|
2018-01-29 15:56:18 +03:00
|
|
|
int i, cc = 0;
|
2011-03-23 12:58:07 +03:00
|
|
|
|
2018-01-29 15:56:18 +03:00
|
|
|
if ((r0 & STSI_R0_FC_MASK) > STSI_R0_FC_LEVEL_3) {
|
|
|
|
/* invalid function code: no other checks are performed */
|
|
|
|
return 3;
|
2011-03-23 12:58:07 +03:00
|
|
|
}
|
|
|
|
|
2018-01-29 15:56:18 +03:00
|
|
|
if ((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK)) {
|
2019-10-01 20:16:00 +03:00
|
|
|
tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
|
2018-01-29 15:56:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((r0 & STSI_R0_FC_MASK) == STSI_R0_FC_CURRENT) {
|
|
|
|
/* query the current level: no further checks are performed */
|
|
|
|
env->regs[0] = STSI_R0_FC_LEVEL_3;
|
|
|
|
return 0;
|
|
|
|
}
|
2011-03-23 12:58:07 +03:00
|
|
|
|
2018-01-29 15:56:18 +03:00
|
|
|
if (a0 & ~TARGET_PAGE_MASK) {
|
2019-10-01 20:16:00 +03:00
|
|
|
tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
|
2018-01-29 15:56:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* count the cpus and split them into configured and reserved ones */
|
|
|
|
for (i = 0; i < ms->possible_cpus->len; i++) {
|
|
|
|
total_cpus++;
|
|
|
|
if (ms->possible_cpus->cpus[i].cpu) {
|
|
|
|
conf_cpus++;
|
|
|
|
} else {
|
|
|
|
reserved_cpus++;
|
|
|
|
}
|
|
|
|
}
|
2011-03-23 12:58:07 +03:00
|
|
|
|
2018-01-29 15:56:18 +03:00
|
|
|
/*
|
|
|
|
* In theory, we could report Level 1 / Level 2 as current. However,
|
|
|
|
* the Linux kernel will detect this as running under LPAR and assume
|
|
|
|
* that we have a sclp linemode console (which is always present on
|
|
|
|
* LPAR, but not the default for QEMU), therefore not displaying boot
|
|
|
|
* messages and making booting a Linux kernel under TCG harder.
|
|
|
|
*
|
|
|
|
* For now we fake the same SMP configuration on all levels.
|
|
|
|
*
|
|
|
|
* TODO: We could later make the level configurable via the machine
|
|
|
|
* and change defaults (linemode console) based on machine type
|
|
|
|
* and accelerator.
|
|
|
|
*/
|
|
|
|
switch (r0 & STSI_R0_FC_MASK) {
|
|
|
|
case STSI_R0_FC_LEVEL_1:
|
2011-03-23 12:58:07 +03:00
|
|
|
if ((sel1 == 1) && (sel2 == 1)) {
|
|
|
|
/* Basic Machine Configuration */
|
2017-06-09 16:34:26 +03:00
|
|
|
char type[5] = {};
|
2011-03-23 12:58:07 +03:00
|
|
|
|
2018-01-29 15:56:18 +03:00
|
|
|
ebcdic_put(sysib.sysib_111.manuf, "QEMU ", 16);
|
2017-06-09 16:34:26 +03:00
|
|
|
/* same as machine type number in STORE CPU ID, but in EBCDIC */
|
|
|
|
snprintf(type, ARRAY_SIZE(type), "%X", cpu->model->def->type);
|
2018-01-29 15:56:18 +03:00
|
|
|
ebcdic_put(sysib.sysib_111.type, type, 4);
|
2022-11-11 21:17:33 +03:00
|
|
|
/* model number (not stored in STORE CPU ID for z/Architecture) */
|
2018-01-29 15:56:18 +03:00
|
|
|
ebcdic_put(sysib.sysib_111.model, "QEMU ", 16);
|
|
|
|
ebcdic_put(sysib.sysib_111.sequence, "QEMU ", 16);
|
|
|
|
ebcdic_put(sysib.sysib_111.plant, "QEMU", 4);
|
2011-03-23 12:58:07 +03:00
|
|
|
} else if ((sel1 == 2) && (sel2 == 1)) {
|
|
|
|
/* Basic Machine CPU */
|
2018-01-29 15:56:18 +03:00
|
|
|
ebcdic_put(sysib.sysib_121.sequence, "QEMUQEMUQEMUQEMU", 16);
|
|
|
|
ebcdic_put(sysib.sysib_121.plant, "QEMU", 4);
|
|
|
|
sysib.sysib_121.cpu_addr = cpu_to_be16(env->core_id);
|
2011-03-23 12:58:07 +03:00
|
|
|
} else if ((sel1 == 2) && (sel2 == 2)) {
|
|
|
|
/* Basic Machine CPUs */
|
2018-01-29 15:56:18 +03:00
|
|
|
sysib.sysib_122.capability = cpu_to_be32(0x443afc29);
|
|
|
|
sysib.sysib_122.total_cpus = cpu_to_be16(total_cpus);
|
|
|
|
sysib.sysib_122.conf_cpus = cpu_to_be16(conf_cpus);
|
|
|
|
sysib.sysib_122.reserved_cpus = cpu_to_be16(reserved_cpus);
|
2011-03-23 12:58:07 +03:00
|
|
|
} else {
|
|
|
|
cc = 3;
|
|
|
|
}
|
|
|
|
break;
|
2018-01-29 15:56:18 +03:00
|
|
|
case STSI_R0_FC_LEVEL_2:
|
|
|
|
if ((sel1 == 2) && (sel2 == 1)) {
|
|
|
|
/* LPAR CPU */
|
|
|
|
ebcdic_put(sysib.sysib_221.sequence, "QEMUQEMUQEMUQEMU", 16);
|
|
|
|
ebcdic_put(sysib.sysib_221.plant, "QEMU", 4);
|
|
|
|
sysib.sysib_221.cpu_addr = cpu_to_be16(env->core_id);
|
|
|
|
} else if ((sel1 == 2) && (sel2 == 2)) {
|
|
|
|
/* LPAR CPUs */
|
|
|
|
sysib.sysib_222.lcpuc = 0x80; /* dedicated */
|
|
|
|
sysib.sysib_222.total_cpus = cpu_to_be16(total_cpus);
|
|
|
|
sysib.sysib_222.conf_cpus = cpu_to_be16(conf_cpus);
|
|
|
|
sysib.sysib_222.reserved_cpus = cpu_to_be16(reserved_cpus);
|
|
|
|
ebcdic_put(sysib.sysib_222.name, "QEMU ", 8);
|
|
|
|
sysib.sysib_222.caf = cpu_to_be32(1000);
|
|
|
|
sysib.sysib_222.dedicated_cpus = cpu_to_be16(conf_cpus);
|
|
|
|
} else {
|
|
|
|
cc = 3;
|
2011-03-23 12:58:07 +03:00
|
|
|
}
|
2018-01-29 15:56:18 +03:00
|
|
|
break;
|
|
|
|
case STSI_R0_FC_LEVEL_3:
|
|
|
|
if ((sel1 == 2) && (sel2 == 2)) {
|
|
|
|
/* VM CPUs */
|
|
|
|
sysib.sysib_322.count = 1;
|
|
|
|
sysib.sysib_322.vm[0].total_cpus = cpu_to_be16(total_cpus);
|
|
|
|
sysib.sysib_322.vm[0].conf_cpus = cpu_to_be16(conf_cpus);
|
|
|
|
sysib.sysib_322.vm[0].reserved_cpus = cpu_to_be16(reserved_cpus);
|
|
|
|
sysib.sysib_322.vm[0].caf = cpu_to_be32(1000);
|
|
|
|
/* Linux kernel uses this to distinguish us from z/VM */
|
|
|
|
ebcdic_put(sysib.sysib_322.vm[0].cpi, "KVM/Linux ", 16);
|
|
|
|
sysib.sysib_322.vm[0].ext_name_encoding = 2; /* UTF-8 */
|
|
|
|
|
|
|
|
/* If our VM has a name, use the real name */
|
|
|
|
if (qemu_name) {
|
|
|
|
memset(sysib.sysib_322.vm[0].name, 0x40,
|
|
|
|
sizeof(sysib.sysib_322.vm[0].name));
|
|
|
|
ebcdic_put(sysib.sysib_322.vm[0].name, qemu_name,
|
|
|
|
MIN(sizeof(sysib.sysib_322.vm[0].name),
|
|
|
|
strlen(qemu_name)));
|
2021-01-14 10:07:36 +03:00
|
|
|
strpadcpy((char *)sysib.sysib_322.ext_names[0],
|
|
|
|
sizeof(sysib.sysib_322.ext_names[0]),
|
|
|
|
qemu_name, '\0');
|
|
|
|
|
2012-09-02 11:33:30 +04:00
|
|
|
} else {
|
2018-01-29 15:56:18 +03:00
|
|
|
ebcdic_put(sysib.sysib_322.vm[0].name, "TCGguest", 8);
|
|
|
|
strcpy((char *)sysib.sysib_322.ext_names[0], "TCGguest");
|
2012-09-02 11:33:30 +04:00
|
|
|
}
|
2018-01-29 15:56:18 +03:00
|
|
|
|
|
|
|
/* add the uuid */
|
|
|
|
memcpy(sysib.sysib_322.vm[0].uuid, &qemu_uuid,
|
|
|
|
sizeof(sysib.sysib_322.vm[0].uuid));
|
|
|
|
} else {
|
|
|
|
cc = 3;
|
2011-03-23 12:58:07 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-01-29 15:56:18 +03:00
|
|
|
if (cc == 0) {
|
|
|
|
if (s390_cpu_virt_mem_write(cpu, a0, 0, &sysib, sizeof(sysib))) {
|
|
|
|
s390_cpu_virt_mem_handle_exc(cpu, ra);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-23 12:58:07 +03:00
|
|
|
return cc;
|
|
|
|
}
|
|
|
|
|
2012-09-02 11:33:39 +04:00
|
|
|
uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1,
|
2017-09-28 23:37:04 +03:00
|
|
|
uint32_t r3)
|
2011-03-23 12:58:07 +03:00
|
|
|
{
|
2017-09-28 23:37:04 +03:00
|
|
|
int cc;
|
2011-03-23 12:58:07 +03:00
|
|
|
|
2017-09-28 23:37:04 +03:00
|
|
|
/* TODO: needed to inject interrupts - push further down */
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
cc = handle_sigp(env, order_code & SIGP_ORDER_MASK, r1, r3);
|
|
|
|
qemu_mutex_unlock_iothread();
|
2011-03-23 12:58:07 +03:00
|
|
|
|
|
|
|
return cc;
|
|
|
|
}
|
|
|
|
#endif
|
2015-06-15 18:57:09 +03:00
|
|
|
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
|
|
void HELPER(xsch)(CPUS390XState *env, uint64_t r1)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2017-11-30 19:27:32 +03:00
|
|
|
ioinst_handle_xsch(cpu, r1, GETPC());
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2015-06-15 18:57:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(csch)(CPUS390XState *env, uint64_t r1)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2017-11-30 19:27:32 +03:00
|
|
|
ioinst_handle_csch(cpu, r1, GETPC());
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2015-06-15 18:57:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(hsch)(CPUS390XState *env, uint64_t r1)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2017-11-30 19:27:32 +03:00
|
|
|
ioinst_handle_hsch(cpu, r1, GETPC());
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2015-06-15 18:57:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(msch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2017-11-30 19:27:32 +03:00
|
|
|
ioinst_handle_msch(cpu, r1, inst >> 16, GETPC());
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2015-06-15 18:57:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(rchp)(CPUS390XState *env, uint64_t r1)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2017-11-30 19:27:32 +03:00
|
|
|
ioinst_handle_rchp(cpu, r1, GETPC());
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2015-06-15 18:57:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(rsch)(CPUS390XState *env, uint64_t r1)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2017-11-30 19:27:32 +03:00
|
|
|
ioinst_handle_rsch(cpu, r1, GETPC());
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2015-06-15 18:57:09 +03:00
|
|
|
}
|
|
|
|
|
2017-12-08 19:02:01 +03:00
|
|
|
void HELPER(sal)(CPUS390XState *env, uint64_t r1)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-12-08 19:02:01 +03:00
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
ioinst_handle_sal(cpu, r1, GETPC());
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
}
|
|
|
|
|
2017-12-08 19:02:02 +03:00
|
|
|
void HELPER(schm)(CPUS390XState *env, uint64_t r1, uint64_t r2, uint64_t inst)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-12-08 19:02:02 +03:00
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
ioinst_handle_schm(cpu, r1, r2, inst >> 16, GETPC());
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
}
|
|
|
|
|
2015-06-15 18:57:09 +03:00
|
|
|
void HELPER(ssch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2017-11-30 19:27:32 +03:00
|
|
|
ioinst_handle_ssch(cpu, r1, inst >> 16, GETPC());
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2017-12-08 19:01:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(stcrw)(CPUS390XState *env, uint64_t inst)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-12-08 19:01:58 +03:00
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
ioinst_handle_stcrw(cpu, inst >> 16, GETPC());
|
|
|
|
qemu_mutex_unlock_iothread();
|
2015-06-15 18:57:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(stsch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2017-11-30 19:27:32 +03:00
|
|
|
ioinst_handle_stsch(cpu, r1, inst >> 16, GETPC());
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2015-06-15 18:57:09 +03:00
|
|
|
}
|
|
|
|
|
2018-01-29 15:56:14 +03:00
|
|
|
uint32_t HELPER(tpi)(CPUS390XState *env, uint64_t addr)
|
|
|
|
{
|
|
|
|
const uintptr_t ra = GETPC();
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2018-01-29 15:56:21 +03:00
|
|
|
QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
|
2018-01-29 15:56:14 +03:00
|
|
|
QEMUS390FlicIO *io = NULL;
|
|
|
|
LowCore *lowcore;
|
|
|
|
|
|
|
|
if (addr & 0x3) {
|
2019-10-01 20:16:00 +03:00
|
|
|
tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
|
2018-01-29 15:56:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
io = qemu_s390_flic_dequeue_io(flic, env->cregs[6]);
|
|
|
|
if (!io) {
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr) {
|
|
|
|
struct {
|
|
|
|
uint16_t id;
|
|
|
|
uint16_t nr;
|
|
|
|
uint32_t parm;
|
|
|
|
} intc = {
|
|
|
|
.id = cpu_to_be16(io->id),
|
|
|
|
.nr = cpu_to_be16(io->nr),
|
|
|
|
.parm = cpu_to_be32(io->parm),
|
|
|
|
};
|
|
|
|
|
|
|
|
if (s390_cpu_virt_mem_write(cpu, addr, 0, &intc, sizeof(intc))) {
|
|
|
|
/* writing failed, reinject and properly clean up */
|
|
|
|
s390_io_interrupt(io->id, io->nr, io->parm, io->word);
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
g_free(io);
|
|
|
|
s390_cpu_virt_mem_handle_exc(cpu, ra);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* no protection applies */
|
|
|
|
lowcore = cpu_map_lowcore(env);
|
|
|
|
lowcore->subchannel_id = cpu_to_be16(io->id);
|
|
|
|
lowcore->subchannel_nr = cpu_to_be16(io->nr);
|
|
|
|
lowcore->io_int_parm = cpu_to_be32(io->parm);
|
|
|
|
lowcore->io_int_word = cpu_to_be32(io->word);
|
|
|
|
cpu_unmap_lowcore(lowcore);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free(io);
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-06-15 18:57:09 +03:00
|
|
|
void HELPER(tsch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2017-11-30 19:27:32 +03:00
|
|
|
ioinst_handle_tsch(cpu, r1, inst >> 16, GETPC());
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2015-06-15 18:57:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(chsc)(CPUS390XState *env, uint64_t inst)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_lock_iothread();
|
2017-11-30 19:27:32 +03:00
|
|
|
ioinst_handle_chsc(cpu, inst >> 16, GETPC());
|
2017-03-01 19:22:40 +03:00
|
|
|
qemu_mutex_unlock_iothread();
|
2015-06-15 18:57:09 +03:00
|
|
|
}
|
|
|
|
#endif
|
2015-06-13 01:45:56 +03:00
|
|
|
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
|
|
void HELPER(per_check_exception)(CPUS390XState *env)
|
|
|
|
{
|
|
|
|
if (env->per_perc_atmid) {
|
2019-10-01 20:16:00 +03:00
|
|
|
tcg_s390_program_interrupt(env, PGM_PER, GETPC());
|
2015-06-13 01:45:56 +03:00
|
|
|
}
|
|
|
|
}
|
2015-06-13 01:45:57 +03:00
|
|
|
|
2017-08-18 14:43:48 +03:00
|
|
|
/* Check if an address is within the PER starting address and the PER
|
|
|
|
ending address. The address range might loop. */
|
|
|
|
static inline bool get_per_in_range(CPUS390XState *env, uint64_t addr)
|
|
|
|
{
|
|
|
|
if (env->cregs[10] <= env->cregs[11]) {
|
|
|
|
return env->cregs[10] <= addr && addr <= env->cregs[11];
|
|
|
|
} else {
|
|
|
|
return env->cregs[10] <= addr || addr <= env->cregs[11];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-13 01:45:57 +03:00
|
|
|
void HELPER(per_branch)(CPUS390XState *env, uint64_t from, uint64_t to)
|
|
|
|
{
|
|
|
|
if ((env->cregs[9] & PER_CR9_EVENT_BRANCH)) {
|
|
|
|
if (!(env->cregs[9] & PER_CR9_CONTROL_BRANCH_ADDRESS)
|
|
|
|
|| get_per_in_range(env, to)) {
|
|
|
|
env->per_address = from;
|
|
|
|
env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-06-13 01:45:58 +03:00
|
|
|
|
|
|
|
void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr)
|
|
|
|
{
|
|
|
|
if ((env->cregs[9] & PER_CR9_EVENT_IFETCH) && get_per_in_range(env, addr)) {
|
|
|
|
env->per_address = addr;
|
|
|
|
env->per_perc_atmid = PER_CODE_EVENT_IFETCH | get_per_atmid(env);
|
2015-06-13 01:46:02 +03:00
|
|
|
|
|
|
|
/* If the instruction has to be nullified, trigger the
|
|
|
|
exception immediately. */
|
|
|
|
if (env->cregs[9] & PER_CR9_EVENT_NULLIFICATION) {
|
2019-03-23 05:21:48 +03:00
|
|
|
CPUState *cs = env_cpu(env);
|
2015-06-13 01:46:02 +03:00
|
|
|
|
2017-06-09 17:21:55 +03:00
|
|
|
env->per_perc_atmid |= PER_CODE_EVENT_NULLIFICATION;
|
2015-06-13 01:46:02 +03:00
|
|
|
env->int_pgm_code = PGM_PER;
|
|
|
|
env->int_pgm_ilen = get_ilen(cpu_ldub_code(env, addr));
|
|
|
|
|
|
|
|
cs->exception_index = EXCP_PGM;
|
|
|
|
cpu_loop_exit(cs);
|
|
|
|
}
|
2015-06-13 01:45:58 +03:00
|
|
|
}
|
|
|
|
}
|
2019-12-11 23:36:13 +03:00
|
|
|
|
|
|
|
void HELPER(per_store_real)(CPUS390XState *env)
|
|
|
|
{
|
|
|
|
if ((env->cregs[9] & PER_CR9_EVENT_STORE) &&
|
|
|
|
(env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) {
|
|
|
|
/* PSW is saved just before calling the helper. */
|
|
|
|
env->per_address = env->psw.addr;
|
|
|
|
env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env);
|
|
|
|
}
|
|
|
|
}
|
2015-06-13 01:45:56 +03:00
|
|
|
#endif
|
2013-03-26 02:10:06 +04:00
|
|
|
|
2017-09-28 23:37:07 +03:00
|
|
|
static uint8_t stfl_bytes[2048];
|
|
|
|
static unsigned int used_stfl_bytes;
|
2013-03-26 02:10:06 +04:00
|
|
|
|
2017-09-28 23:37:07 +03:00
|
|
|
static void prepare_stfl(void)
|
2013-03-26 02:10:06 +04:00
|
|
|
{
|
2017-09-28 23:37:07 +03:00
|
|
|
static bool initialized;
|
|
|
|
int i;
|
2013-03-26 02:10:06 +04:00
|
|
|
|
2017-09-28 23:37:07 +03:00
|
|
|
/* racy, but we don't care, the same values are always written */
|
|
|
|
if (initialized) {
|
|
|
|
return;
|
2013-03-26 02:10:06 +04:00
|
|
|
}
|
|
|
|
|
2017-09-28 23:37:07 +03:00
|
|
|
s390_get_feat_block(S390_FEAT_TYPE_STFL, stfl_bytes);
|
|
|
|
for (i = 0; i < sizeof(stfl_bytes); i++) {
|
|
|
|
if (stfl_bytes[i]) {
|
|
|
|
used_stfl_bytes = i + 1;
|
2013-03-26 02:10:06 +04:00
|
|
|
}
|
|
|
|
}
|
2017-09-28 23:37:07 +03:00
|
|
|
initialized = true;
|
2013-03-26 02:10:06 +04:00
|
|
|
}
|
|
|
|
|
2017-09-27 20:00:27 +03:00
|
|
|
#ifndef CONFIG_USER_ONLY
|
2013-03-26 02:10:06 +04:00
|
|
|
void HELPER(stfl)(CPUS390XState *env)
|
|
|
|
{
|
2017-09-27 20:00:27 +03:00
|
|
|
LowCore *lowcore;
|
2013-03-26 02:10:06 +04:00
|
|
|
|
2017-09-27 20:00:27 +03:00
|
|
|
lowcore = cpu_map_lowcore(env);
|
2017-09-28 23:37:07 +03:00
|
|
|
prepare_stfl();
|
|
|
|
memcpy(&lowcore->stfl_fac_list, stfl_bytes, sizeof(lowcore->stfl_fac_list));
|
2017-09-27 20:00:27 +03:00
|
|
|
cpu_unmap_lowcore(lowcore);
|
2013-03-26 02:10:06 +04:00
|
|
|
}
|
2017-09-27 20:00:27 +03:00
|
|
|
#endif
|
2013-03-26 02:10:06 +04:00
|
|
|
|
|
|
|
uint32_t HELPER(stfle)(CPUS390XState *env, uint64_t addr)
|
|
|
|
{
|
2017-09-28 23:37:07 +03:00
|
|
|
const uintptr_t ra = GETPC();
|
|
|
|
const int count_bytes = ((env->regs[0] & 0xff) + 1) * 8;
|
2019-05-31 17:31:44 +03:00
|
|
|
int max_bytes;
|
2017-09-28 23:37:07 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (addr & 0x7) {
|
2019-10-01 20:16:00 +03:00
|
|
|
tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
|
2017-09-28 23:37:07 +03:00
|
|
|
}
|
2013-03-26 02:10:06 +04:00
|
|
|
|
2017-09-28 23:37:07 +03:00
|
|
|
prepare_stfl();
|
2019-05-31 17:31:44 +03:00
|
|
|
max_bytes = ROUND_UP(used_stfl_bytes, 8);
|
s390x/tcg: Store only the necessary amount of doublewords for STFLE
The PoP (z14, 7-382) says:
Doublewords to the right of the doubleword in which the
highest-numbered facility bit is assigned for a model
may or may not be stored.
However, stack protection in certain binaries can't deal with that.
"gzip" example code:
f1b4: a7 08 00 03 lhi %r0,3
f1b8: b2 b0 f0 a0 stfle 160(%r15)
f1bc: e3 20 f0 b2 00 90 llgc %r2,178(%r15)
f1c2: c0 2b 00 00 00 01 nilf %r2,1
f1c8: b2 4f 00 10 ear %r1,%a0
f1cc: b9 14 00 22 lgfr %r2,%r2
f1d0: eb 11 00 20 00 0d sllg %r1,%r1,32
f1d6: b2 4f 00 11 ear %r1,%a1
f1da: d5 07 f0 b8 10 28 clc 184(8,%r15),40(%r1)
f1e0: a7 74 00 06 jne f1ec <file_read@@Base+0x1bc>
f1e4: eb ef f1 30 00 04 lmg %r14,%r15,304(%r15)
f1ea: 07 fe br %r14
f1ec: c0 e5 ff ff 9d 6e brasl %r14,2cc8 <__stack_chk_fail@plt>
In QEMU, we currently have:
max_bytes = 24
the code asks for (3 + 1) doublewords == 32 bytes.
If we write 32 bytes instead of only 24, and return "2 + 1" doublewords
("one less than the number of doulewords needed to contain all of the
facility bits"), the example code detects a stack corruption.
In my opinion, the code is wrong. However, it seems to work fine on
real machines. So let's limit storing to the minimum of the requested
and the maximum doublewords.
Cc: Stefan Liebler <stli@linux.ibm.com>
Cc: Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: David Hildenbrand <david@redhat.com>
2019-05-31 17:33:38 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The PoP says that doublewords beyond the highest-numbered facility
|
|
|
|
* bit may or may not be stored. However, existing hardware appears to
|
|
|
|
* not store the words, and existing software depend on that.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < MIN(count_bytes, max_bytes); ++i) {
|
2017-09-28 23:37:07 +03:00
|
|
|
cpu_stb_data_ra(env, addr + i, stfl_bytes[i], ra);
|
2013-03-26 02:10:06 +04:00
|
|
|
}
|
|
|
|
|
2017-09-28 23:37:07 +03:00
|
|
|
env->regs[0] = deposit64(env->regs[0], 0, 8, (max_bytes / 8) - 1);
|
|
|
|
return count_bytes >= max_bytes ? 0 : 3;
|
2013-03-26 02:10:06 +04:00
|
|
|
}
|
2017-11-16 12:09:53 +03:00
|
|
|
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
|
|
/*
|
|
|
|
* Note: we ignore any return code of the functions called for the pci
|
|
|
|
* instructions, as the only time they return !0 is when the stub is
|
|
|
|
* called, and in that case we didn't even offer the zpci facility.
|
|
|
|
* The only exception is SIC, where program checks need to be handled
|
|
|
|
* by the caller.
|
|
|
|
*/
|
|
|
|
void HELPER(clp)(CPUS390XState *env, uint32_t r2)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-11-16 12:09:53 +03:00
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
clp_service_call(cpu, r2, GETPC());
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(pcilg)(CPUS390XState *env, uint32_t r1, uint32_t r2)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-11-16 12:09:53 +03:00
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
pcilg_service_call(cpu, r1, r2, GETPC());
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(pcistg)(CPUS390XState *env, uint32_t r1, uint32_t r2)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-11-16 12:09:53 +03:00
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
pcistg_service_call(cpu, r1, r2, GETPC());
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(stpcifc)(CPUS390XState *env, uint32_t r1, uint64_t fiba,
|
|
|
|
uint32_t ar)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-11-16 12:09:53 +03:00
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
stpcifc_service_call(cpu, r1, fiba, ar, GETPC());
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(sic)(CPUS390XState *env, uint64_t r1, uint64_t r3)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
r = css_do_sic(env, (r3 >> 27) & 0x7, r1 & 0xffff);
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
/* css_do_sic() may actually return a PGM_xxx value to inject */
|
|
|
|
if (r) {
|
2019-10-01 20:16:00 +03:00
|
|
|
tcg_s390_program_interrupt(env, -r, GETPC());
|
2017-11-16 12:09:53 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(rpcit)(CPUS390XState *env, uint32_t r1, uint32_t r2)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-11-16 12:09:53 +03:00
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
rpcit_service_call(cpu, r1, r2, GETPC());
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(pcistb)(CPUS390XState *env, uint32_t r1, uint32_t r3,
|
|
|
|
uint64_t gaddr, uint32_t ar)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-11-16 12:09:53 +03:00
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
pcistb_service_call(cpu, r1, r3, gaddr, ar, GETPC());
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HELPER(mpcifc)(CPUS390XState *env, uint32_t r1, uint64_t fiba,
|
|
|
|
uint32_t ar)
|
|
|
|
{
|
2019-03-23 05:21:48 +03:00
|
|
|
S390CPU *cpu = env_archcpu(env);
|
2017-11-16 12:09:53 +03:00
|
|
|
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
mpcifc_service_call(cpu, r1, fiba, ar, GETPC());
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
}
|
|
|
|
#endif
|