s390x updates:

- rework interrupt handling for tcg, smp is now considered non-experimental
 - some general improvements in the flic
 - improvements in the pci code, and wiring it up in tcg
 - add PTFF subfunctions for multiple-epoch to the cpu model
 - maintainership updates
 - various other fixes and improvements
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEEw9DWbcNiT/aowBjO3s9rk8bwL68FAlp9ZCISHGNvaHVja0By
 ZWRoYXQuY29tAAoJEN7Pa5PG8C+vuhwQAKS4IepY5sEA0eUScpA+xDBLiXYkwTIP
 Nq8Jm553voTvKgadBmYp8zC4U0jxfbsKd6NFxFisRiFtxsrT91L/akQTolTRU2F4
 i44K6LCcfqWIclSWQrZVGoxYpg7h2tZYBsq9stZOT+8eeoNeseZLMQGzjif9DbmS
 jTwGLcWUAmzvemk/rygzRHHhpNS3Ed5OHCeMunpG+RuKlRBkleVU9yft2QbztNtG
 LOBlLtlKX+6r8A0Xv3yKh/q9q+lxEOebONt8A1MhpcLlqMP3XHesS+T7v/rh7Bld
 CXCYWXy6QHm4xDUV+9rflawaNisYnZDJfovKk7k6UPHi+EXhivPBjT0teS/CZ0D+
 O+iDiXwlZc58/5A7fsUoXFzj/ZlLu5fJXvmGz0iYyDh+UthjqzrDqdcPp5h788gH
 8TsjapTc9X6g+D8lgPVh93/ejnCcVqaeSrjEbmjzksnBprTMEb94ew8nnjcWlppR
 zii1fQfpstoaBUWDPMpHImnL46oSEyKJ1XF/NA4X/gRIQsxfuUbtQAfwv+rgamQb
 T3nVyvdu0vlO+SSrkCJ/siZ+Nyf4A+ksNfIBSfUPgzVF6J0nzJ/uD3jOkxm/M5pa
 ScFrEop41jYcrZsOJqrb2HifzJpETssd4+s9lD44vUimiORpEPoeG7zF5sqbrlvc
 fO9X3ufY8n2i
 =/u33
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20180209' into staging

s390x updates:
- rework interrupt handling for tcg, smp is now considered non-experimental
- some general improvements in the flic
- improvements in the pci code, and wiring it up in tcg
- add PTFF subfunctions for multiple-epoch to the cpu model
- maintainership updates
- various other fixes and improvements

# gpg: Signature made Fri 09 Feb 2018 09:04:34 GMT
# gpg:                using RSA key DECF6B93C6F02FAF
# gpg: Good signature from "Cornelia Huck <conny@cornelia-huck.de>"
# gpg:                 aka "Cornelia Huck <huckc@linux.vnet.ibm.com>"
# gpg:                 aka "Cornelia Huck <cornelia.huck@de.ibm.com>"
# gpg:                 aka "Cornelia Huck <cohuck@kernel.org>"
# gpg:                 aka "Cornelia Huck <cohuck@redhat.com>"
# Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0  18CE DECF 6B93 C6F0 2FAF

* remotes/cohuck/tags/s390x-20180209: (29 commits)
  MAINTAINERS: add David as additional tcg/s390 maintainer
  MAINTAINERS: reorganize s390-ccw bios maintainership
  MAINTAINERS: add myself as overall s390x maintainer
  s390x/pci: use the right pal and pba in reg_ioat()
  s390x/pci: fixup global refresh
  s390x/pci: fixup the code walking IOMMU tables
  s390x/cpumodel: model PTFF subfunctions for Multiple-epoch facility
  s390x/cpumodel: allow zpci features in qemu model
  s390x/tcg: wire up pci instructions
  s390x/sclp: fix event mask handling
  s390x/flic: cache the common flic class in a central function
  s390x/kvm: cache the kvm flic in a central function
  s390x/tcg: cache the qemu flic in a central function
  configure: s390x supports mttcg now
  s390x/tcg: remove SMP warning
  s390x/tcg: STSI overhaul
  s390x: fix size + content of STSI blocks
  s390x/flic: optimize CPU wakeup for TCG
  s390x/flic: implement qemu_s390_clear_io_flic()
  s390x/tcg: implement TEST PENDING INTERRUPTION
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-02-09 11:46:32 +00:00
commit fdcbebe451
28 changed files with 1241 additions and 549 deletions

View file

@ -100,7 +100,6 @@ static void s390_cpu_initial_reset(CPUState *s)
{
S390CPU *cpu = S390_CPU(s);
CPUS390XState *env = &cpu->env;
int i;
s390_cpu_reset(s);
/* initial reset does not clear everything! */
@ -116,10 +115,6 @@ static void s390_cpu_initial_reset(CPUState *s)
env->gbea = 1;
env->pfault_token = -1UL;
for (i = 0; i < ARRAY_SIZE(env->io_index); i++) {
env->io_index[i] = -1;
}
env->mchk_index = -1;
/* tininess for underflow is detected before rounding */
set_float_detect_tininess(float_tininess_before_rounding,
@ -137,7 +132,6 @@ static void s390_cpu_full_reset(CPUState *s)
S390CPU *cpu = S390_CPU(s);
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
CPUS390XState *env = &cpu->env;
int i;
scc->parent_reset(s);
cpu->env.sigp_order = 0;
@ -153,10 +147,6 @@ static void s390_cpu_full_reset(CPUState *s)
env->gbea = 1;
env->pfault_token = -1UL;
for (i = 0; i < ARRAY_SIZE(env->io_index); i++) {
env->io_index[i] = -1;
}
env->mchk_index = -1;
/* tininess for underflow is detected before rounding */
set_float_detect_tininess(float_tininess_before_rounding,

View file

@ -53,12 +53,6 @@
#define MMU_USER_IDX 0
#define MAX_IO_QUEUE 16
#define MAX_MCHK_QUEUE 16
#define PSW_MCHK_MASK 0x0004000000000000
#define PSW_IO_MASK 0x0200000000000000
#define S390_MAX_CPUS 248
typedef struct PSW {
@ -66,17 +60,6 @@ typedef struct PSW {
uint64_t addr;
} PSW;
typedef struct IOIntQueue {
uint16_t id;
uint16_t nr;
uint32_t parm;
uint32_t word;
} IOIntQueue;
typedef struct MchkQueue {
uint16_t type;
} MchkQueue;
struct CPUS390XState {
uint64_t regs[16]; /* GP registers */
/*
@ -122,15 +105,9 @@ struct CPUS390XState {
uint64_t cregs[16]; /* control registers */
IOIntQueue io_queue[MAX_IO_QUEUE][8];
MchkQueue mchk_queue[MAX_MCHK_QUEUE];
int pending_int;
uint32_t service_param;
uint16_t external_call_addr;
DECLARE_BITMAP(emergency_signals, S390_MAX_CPUS);
int io_index[8];
int mchk_index;
uint64_t ckc;
uint64_t cputm;
@ -409,9 +386,6 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc,
#define EXCP_IO 7 /* I/O interrupt */
#define EXCP_MCHK 8 /* machine check */
#define INTERRUPT_IO (1 << 0)
#define INTERRUPT_MCHK (1 << 1)
#define INTERRUPT_EXT_SERVICE (1 << 2)
#define INTERRUPT_EXT_CPU_TIMER (1 << 3)
#define INTERRUPT_EXT_CLOCK_COMPARATOR (1 << 4)
#define INTERRUPT_EXTERNAL_CALL (1 << 5)
@ -452,62 +426,66 @@ static inline void setcc(S390CPU *cpu, uint64_t cc)
}
/* STSI */
#define STSI_LEVEL_MASK 0x00000000f0000000ULL
#define STSI_LEVEL_CURRENT 0x0000000000000000ULL
#define STSI_LEVEL_1 0x0000000010000000ULL
#define STSI_LEVEL_2 0x0000000020000000ULL
#define STSI_LEVEL_3 0x0000000030000000ULL
#define STSI_R0_FC_MASK 0x00000000f0000000ULL
#define STSI_R0_FC_CURRENT 0x0000000000000000ULL
#define STSI_R0_FC_LEVEL_1 0x0000000010000000ULL
#define STSI_R0_FC_LEVEL_2 0x0000000020000000ULL
#define STSI_R0_FC_LEVEL_3 0x0000000030000000ULL
#define STSI_R0_RESERVED_MASK 0x000000000fffff00ULL
#define STSI_R0_SEL1_MASK 0x00000000000000ffULL
#define STSI_R1_RESERVED_MASK 0x00000000ffff0000ULL
#define STSI_R1_SEL2_MASK 0x000000000000ffffULL
/* Basic Machine Configuration */
struct sysib_111 {
uint32_t res1[8];
typedef struct SysIB_111 {
uint8_t res1[32];
uint8_t manuf[16];
uint8_t type[4];
uint8_t res2[12];
uint8_t model[16];
uint8_t sequence[16];
uint8_t plant[4];
uint8_t res3[156];
};
uint8_t res3[3996];
} SysIB_111;
QEMU_BUILD_BUG_ON(sizeof(SysIB_111) != 4096);
/* Basic Machine CPU */
struct sysib_121 {
uint32_t res1[80];
typedef struct SysIB_121 {
uint8_t res1[80];
uint8_t sequence[16];
uint8_t plant[4];
uint8_t res2[2];
uint16_t cpu_addr;
uint8_t res3[152];
};
uint8_t res3[3992];
} SysIB_121;
QEMU_BUILD_BUG_ON(sizeof(SysIB_121) != 4096);
/* Basic Machine CPUs */
struct sysib_122 {
typedef struct SysIB_122 {
uint8_t res1[32];
uint32_t capability;
uint16_t total_cpus;
uint16_t active_cpus;
uint16_t conf_cpus;
uint16_t standby_cpus;
uint16_t reserved_cpus;
uint16_t adjustments[2026];
};
} SysIB_122;
QEMU_BUILD_BUG_ON(sizeof(SysIB_122) != 4096);
/* LPAR CPU */
struct sysib_221 {
uint32_t res1[80];
typedef struct SysIB_221 {
uint8_t res1[80];
uint8_t sequence[16];
uint8_t plant[4];
uint16_t cpu_id;
uint16_t cpu_addr;
uint8_t res3[152];
};
uint8_t res3[3992];
} SysIB_221;
QEMU_BUILD_BUG_ON(sizeof(SysIB_221) != 4096);
/* LPAR CPUs */
struct sysib_222 {
uint32_t res1[32];
typedef struct SysIB_222 {
uint8_t res1[32];
uint16_t lpar_num;
uint8_t res2;
uint8_t lcpuc;
@ -520,11 +498,12 @@ struct sysib_222 {
uint8_t res3[16];
uint16_t dedicated_cpus;
uint16_t shared_cpus;
uint8_t res4[180];
};
uint8_t res4[4020];
} SysIB_222;
QEMU_BUILD_BUG_ON(sizeof(SysIB_222) != 4096);
/* VM CPUs */
struct sysib_322 {
typedef struct SysIB_322 {
uint8_t res1[31];
uint8_t count;
struct {
@ -543,7 +522,18 @@ struct sysib_322 {
} vm[8];
uint8_t res4[1504];
uint8_t ext_names[8][256];
};
} SysIB_322;
QEMU_BUILD_BUG_ON(sizeof(SysIB_322) != 4096);
typedef union SysIB {
SysIB_111 sysib_111;
SysIB_121 sysib_121;
SysIB_122 sysib_122;
SysIB_221 sysib_221;
SysIB_222 sysib_222;
SysIB_322 sysib_322;
} SysIB;
QEMU_BUILD_BUG_ON(sizeof(SysIB) != 4096);
/* MMU defines */
#define _ASCE_ORIGIN ~0xfffULL /* segment table origin */
@ -718,6 +708,10 @@ static inline unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
return 0;
}
#endif /* CONFIG_USER_ONLY */
static inline uint8_t s390_cpu_get_state(S390CPU *cpu)
{
return cpu->env.cpu_state;
}
/* cpu_models.c */
@ -752,7 +746,6 @@ void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen,
/* service interrupts are floating therefore we must not pass an cpustate */
void s390_sclp_extint(uint32_t parm);
/* mmu_helper.c */
int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf,
int len, bool is_write);

View file

@ -156,8 +156,12 @@ static const S390FeatDef s390_features[] = {
FEAT_INIT("ptff-qpc", S390_FEAT_TYPE_PTFF, 3, "PTFF Query Physical Clock"),
FEAT_INIT("ptff-qui", S390_FEAT_TYPE_PTFF, 4, "PTFF Query UTC Information"),
FEAT_INIT("ptff-qtou", S390_FEAT_TYPE_PTFF, 5, "PTFF Query TOD Offset User"),
FEAT_INIT("ptff-qsie", S390_FEAT_TYPE_PTFF, 10, "PTFF Query Steering Information Extended"),
FEAT_INIT("ptff-qtoue", S390_FEAT_TYPE_PTFF, 13, "PTFF Query TOD Offset User Extended"),
FEAT_INIT("ptff-sto", S390_FEAT_TYPE_PTFF, 65, "PTFF Set TOD Offset"),
FEAT_INIT("ptff-stou", S390_FEAT_TYPE_PTFF, 69, "PTFF Set TOD Offset User"),
FEAT_INIT("ptff-stoe", S390_FEAT_TYPE_PTFF, 73, "PTFF Set TOD Offset Extended"),
FEAT_INIT("ptff-stoue", S390_FEAT_TYPE_PTFF, 77, "PTFF Set TOD Offset User Extended"),
FEAT_INIT("kmac-dea", S390_FEAT_TYPE_KMAC, 1, "KMAC DEA"),
FEAT_INIT("kmac-tdea-128", S390_FEAT_TYPE_KMAC, 2, "KMAC TDEA-128"),
@ -445,6 +449,7 @@ static S390FeatGroupDef s390_feature_groups[] = {
FEAT_GROUP_INIT("plo", PLO, "Perform-locked-operation facility"),
FEAT_GROUP_INIT("tods", TOD_CLOCK_STEERING, "Tod-clock-steering facility"),
FEAT_GROUP_INIT("gen13ptff", GEN13_PTFF, "PTFF enhancements introduced with z13"),
FEAT_GROUP_INIT("mepochptff", MULTIPLE_EPOCH_PTFF, "PTFF enhancements introduced with Multiple-epoch facility"),
FEAT_GROUP_INIT("msa", MSA, "Message-security-assist facility"),
FEAT_GROUP_INIT("msa1", MSA_EXT_1, "Message-security-assist-extension 1 facility"),
FEAT_GROUP_INIT("msa2", MSA_EXT_2, "Message-security-assist-extension 2 facility"),

View file

@ -151,8 +151,12 @@ typedef enum {
S390_FEAT_PTFF_QPT,
S390_FEAT_PTFF_QUI,
S390_FEAT_PTFF_QTOU,
S390_FEAT_PTFF_QSIE,
S390_FEAT_PTFF_QTOUE,
S390_FEAT_PTFF_STO,
S390_FEAT_PTFF_STOU,
S390_FEAT_PTFF_STOE,
S390_FEAT_PTFF_STOUE,
/* KMAC */
S390_FEAT_KMAC_DEA,

View file

@ -23,6 +23,7 @@
#include "qapi/qmp/qbool.h"
#ifndef CONFIG_USER_ONLY
#include "sysemu/arch_init.h"
#include "hw/pci/pci.h"
#endif
#define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \
@ -1271,6 +1272,11 @@ static void register_types(void)
/* init all bitmaps from gnerated data initially */
s390_init_feat_bitmap(qemu_max_cpu_feat_init, qemu_max_cpu_feat);
#ifndef CONFIG_USER_ONLY
if (!pci_available) {
clear_bit(S390_FEAT_ZPCI, qemu_max_cpu_feat);
}
#endif
for (i = 0; i < ARRAY_SIZE(s390_cpu_defs); i++) {
s390_init_feat_bitmap(s390_cpu_defs[i].base_init,
s390_cpu_defs[i].base_feat);

View file

@ -29,6 +29,7 @@
#include "exec/address-spaces.h"
#ifndef CONFIG_USER_ONLY
#include "sysemu/sysemu.h"
#include "hw/s390x/s390_flic.h"
#endif
/* #define DEBUG_S390 */
@ -237,6 +238,7 @@ static void do_svc_interrupt(CPUS390XState *env)
static void do_ext_interrupt(CPUS390XState *env)
{
QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic());
S390CPU *cpu = s390_env_get_cpu(env);
uint64_t mask, addr;
uint16_t cpu_addr;
@ -273,17 +275,14 @@ static void do_ext_interrupt(CPUS390XState *env)
lowcore->ext_int_code = cpu_to_be16(EXT_CPU_TIMER);
lowcore->cpu_addr = 0;
env->pending_int &= ~INTERRUPT_EXT_CPU_TIMER;
} else if ((env->pending_int & INTERRUPT_EXT_SERVICE) &&
} else if (qemu_s390_flic_has_service(flic) &&
(env->cregs[0] & CR0_SERVICE_SC)) {
/*
* FIXME: floating IRQs should be considered by all CPUs and
* shuld not get cleared by CPU reset.
*/
uint32_t param;
param = qemu_s390_flic_dequeue_service(flic);
lowcore->ext_int_code = cpu_to_be16(EXT_SERVICE);
lowcore->ext_params = cpu_to_be32(env->service_param);
lowcore->ext_params = cpu_to_be32(param);
lowcore->cpu_addr = 0;
env->service_param = 0;
env->pending_int &= ~INTERRUPT_EXT_SERVICE;
} else {
g_assert_not_reached();
}
@ -303,95 +302,46 @@ static void do_ext_interrupt(CPUS390XState *env)
static void do_io_interrupt(CPUS390XState *env)
{
S390CPU *cpu = s390_env_get_cpu(env);
QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic());
uint64_t mask, addr;
QEMUS390FlicIO *io;
LowCore *lowcore;
IOIntQueue *q;
uint8_t isc;
int disable = 1;
int found = 0;
if (!(env->psw.mask & PSW_MASK_IO)) {
cpu_abort(CPU(cpu), "I/O int w/o I/O mask\n");
}
g_assert(env->psw.mask & PSW_MASK_IO);
io = qemu_s390_flic_dequeue_io(flic, env->cregs[6]);
g_assert(io);
for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) {
uint64_t isc_bits;
lowcore = cpu_map_lowcore(env);
if (env->io_index[isc] < 0) {
continue;
}
if (env->io_index[isc] >= MAX_IO_QUEUE) {
cpu_abort(CPU(cpu), "I/O queue overrun for isc %d: %d\n",
isc, env->io_index[isc]);
}
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);
lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env));
lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr);
mask = be64_to_cpu(lowcore->io_new_psw.mask);
addr = be64_to_cpu(lowcore->io_new_psw.addr);
q = &env->io_queue[env->io_index[isc]][isc];
isc_bits = ISC_TO_ISC_BITS(IO_INT_WORD_ISC(q->word));
if (!(env->cregs[6] & isc_bits)) {
disable = 0;
continue;
}
if (!found) {
uint64_t mask, addr;
found = 1;
lowcore = cpu_map_lowcore(env);
lowcore->subchannel_id = cpu_to_be16(q->id);
lowcore->subchannel_nr = cpu_to_be16(q->nr);
lowcore->io_int_parm = cpu_to_be32(q->parm);
lowcore->io_int_word = cpu_to_be32(q->word);
lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env));
lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr);
mask = be64_to_cpu(lowcore->io_new_psw.mask);
addr = be64_to_cpu(lowcore->io_new_psw.addr);
cpu_unmap_lowcore(lowcore);
env->io_index[isc]--;
DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
env->psw.mask, env->psw.addr);
load_psw(env, mask, addr);
}
if (env->io_index[isc] >= 0) {
disable = 0;
}
continue;
}
if (disable) {
env->pending_int &= ~INTERRUPT_IO;
}
cpu_unmap_lowcore(lowcore);
g_free(io);
DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, env->psw.mask,
env->psw.addr);
load_psw(env, mask, addr);
}
static void do_mchk_interrupt(CPUS390XState *env)
{
S390CPU *cpu = s390_env_get_cpu(env);
QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic());
uint64_t mask, addr;
LowCore *lowcore;
MchkQueue *q;
int i;
if (!(env->psw.mask & PSW_MASK_MCHECK)) {
cpu_abort(CPU(cpu), "Machine check w/o mchk mask\n");
}
/* for now we only support channel report machine checks (floating) */
g_assert(env->psw.mask & PSW_MASK_MCHECK);
g_assert(env->cregs[14] & CR14_CHANNEL_REPORT_SC);
if (env->mchk_index < 0 || env->mchk_index >= MAX_MCHK_QUEUE) {
cpu_abort(CPU(cpu), "Mchk queue overrun: %d\n", env->mchk_index);
}
q = &env->mchk_queue[env->mchk_index];
if (q->type != 1) {
/* Don't know how to handle this... */
cpu_abort(CPU(cpu), "Unknown machine check type %d\n", q->type);
}
if (!(env->cregs[14] & (1 << 28))) {
/* CRW machine checks disabled */
return;
}
qemu_s390_flic_dequeue_crw_mchk(flic);
lowcore = cpu_map_lowcore(env);
@ -418,11 +368,6 @@ static void do_mchk_interrupt(CPUS390XState *env)
cpu_unmap_lowcore(lowcore);
env->mchk_index--;
if (env->mchk_index == -1) {
env->pending_int &= ~INTERRUPT_MCHK;
}
DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
env->psw.mask, env->psw.addr);
@ -431,12 +376,15 @@ static void do_mchk_interrupt(CPUS390XState *env)
void s390_cpu_do_interrupt(CPUState *cs)
{
QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic());
S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env;
bool stopped = false;
qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n",
__func__, cs->exception_index, env->psw.addr);
try_deliver:
/* handle machine checks */
if (cs->exception_index == -1 && s390_cpu_has_mcck_int(cpu)) {
cs->exception_index = EXCP_MCHK;
@ -479,20 +427,30 @@ void s390_cpu_do_interrupt(CPUState *cs)
break;
case EXCP_STOP:
do_stop_interrupt(env);
stopped = true;
break;
}
/* WAIT PSW during interrupt injection or STOP interrupt */
if (cs->exception_index == EXCP_HLT) {
/* don't trigger a cpu_loop_exit(), use an interrupt instead */
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HALT);
if (cs->exception_index != -1 && !stopped) {
/* check if there are more pending interrupts to deliver */
cs->exception_index = -1;
goto try_deliver;
}
cs->exception_index = -1;
/* we might still have pending interrupts, but not deliverable */
if (!env->pending_int) {
if (!env->pending_int && !qemu_s390_flic_has_any(flic)) {
cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
}
/* WAIT PSW during interrupt injection or STOP interrupt */
if ((env->psw.mask & PSW_MASK_WAIT) || stopped) {
/* don't trigger a cpu_loop_exit(), use an interrupt instead */
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HALT);
} else if (cs->halted) {
/* unhalt if we had a WAIT PSW somehwere in our injection chain */
s390_cpu_unhalt(cpu);
}
}
bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
@ -510,6 +468,11 @@ bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
s390_cpu_do_interrupt(cs);
return true;
}
if (env->psw.mask & PSW_MASK_WAIT) {
/* Woken up because of a floating interrupt but it has already
* been delivered. Go back to sleep. */
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HALT);
}
}
return false;
}

View file

@ -59,6 +59,12 @@
S390_FEAT_PTFF_QTOU, \
S390_FEAT_PTFF_STOU
#define S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF \
S390_FEAT_PTFF_QSIE, \
S390_FEAT_PTFF_QTOUE, \
S390_FEAT_PTFF_STOE, \
S390_FEAT_PTFF_STOUE
#define S390_FEAT_GROUP_MSA \
S390_FEAT_MSA, \
S390_FEAT_KMAC_DEA, \
@ -219,6 +225,9 @@ static uint16_t group_TOD_CLOCK_STEERING[] = {
static uint16_t group_GEN13_PTFF[] = {
S390_FEAT_GROUP_GEN13_PTFF,
};
static uint16_t group_MULTIPLE_EPOCH_PTFF[] = {
S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF,
};
static uint16_t group_MSA[] = {
S390_FEAT_GROUP_MSA,
};
@ -466,6 +475,7 @@ static uint16_t full_GEN14_GA1[] = {
S390_FEAT_CMM_NT,
S390_FEAT_HPMA2,
S390_FEAT_SIE_KSS,
S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF,
};
/* Default features (in order of release)
@ -572,8 +582,10 @@ static uint16_t qemu_LATEST[] = {
S390_FEAT_STFLE_49,
S390_FEAT_LOCAL_TLB_CLEARING,
S390_FEAT_INTERLOCKED_ACCESS_2,
S390_FEAT_MSA_EXT_4,
S390_FEAT_ADAPTER_EVENT_NOTIFICATION,
S390_FEAT_ADAPTER_INT_SUPPRESSION,
S390_FEAT_MSA_EXT_3,
S390_FEAT_MSA_EXT_4,
};
/* add all new definitions before this point */
@ -582,6 +594,8 @@ static uint16_t qemu_MAX[] = {
S390_FEAT_STFLE_53,
/* generates a dependency warning, leave it out for now */
S390_FEAT_MSA_EXT_5,
/* only with CONFIG_PCI */
S390_FEAT_ZPCI,
};
/****** END FEATURE DEFS ******/
@ -664,6 +678,7 @@ static FeatGroupDefSpec FeatGroupDef[] = {
FEAT_GROUP_INITIALIZER(PLO),
FEAT_GROUP_INITIALIZER(TOD_CLOCK_STEERING),
FEAT_GROUP_INITIALIZER(GEN13_PTFF),
FEAT_GROUP_INITIALIZER(MULTIPLE_EPOCH_PTFF),
FEAT_GROUP_INITIALIZER(MSA),
FEAT_GROUP_INITIALIZER(MSA_EXT_1),
FEAT_GROUP_INITIALIZER(MSA_EXT_2),

View file

@ -170,6 +170,16 @@ DEF_HELPER_4(schm, void, env, i64, i64, i64)
DEF_HELPER_3(ssch, void, env, i64, i64)
DEF_HELPER_2(stcrw, void, env, i64)
DEF_HELPER_3(stsch, void, env, i64, i64)
DEF_HELPER_2(tpi, i32, env, i64)
DEF_HELPER_3(tsch, void, env, i64, i64)
DEF_HELPER_2(chsc, void, env, i64)
DEF_HELPER_2(clp, void, env, i32)
DEF_HELPER_3(pcilg, void, env, i32, i32)
DEF_HELPER_3(pcistg, void, env, i32, i32)
DEF_HELPER_4(stpcifc, void, env, i32, i64, i32)
DEF_HELPER_3(sic, void, env, i64, i64)
DEF_HELPER_3(rpcit, void, env, i32, i32)
DEF_HELPER_5(pcistb, void, env, i32, i32, i64, i32)
DEF_HELPER_4(mpcifc, void, env, i32, i64, i32)
#endif

View file

@ -1063,8 +1063,22 @@
C(0xb233, SSCH, S, Z, 0, insn, 0, 0, ssch, 0)
C(0xb239, STCRW, S, Z, 0, insn, 0, 0, stcrw, 0)
C(0xb234, STSCH, S, Z, 0, insn, 0, 0, stsch, 0)
C(0xb236, TPI , S, Z, la2, 0, 0, 0, tpi, 0)
C(0xb235, TSCH, S, Z, 0, insn, 0, 0, tsch, 0)
/* ??? Not listed in PoO ninth edition, but there's a linux driver that
uses it: "A CHSC subchannel is usually present on LPAR only." */
C(0xb25f, CHSC, RRE, Z, 0, insn, 0, 0, chsc, 0)
/* zPCI Instructions */
/* None of these instructions are documented in the PoP, so this is all
based upon target/s390x/kvm.c and Linux code and likely incomplete */
C(0xebd0, PCISTB, RSY_a, PCI, la2, 0, 0, 0, pcistb, 0)
C(0xebd1, SIC, RSY_a, AIS, r1, r3, 0, 0, sic, 0)
C(0xb9a0, CLP, RRF_c, PCI, 0, 0, 0, 0, clp, 0)
C(0xb9d0, PCISTG, RRE, PCI, 0, 0, 0, 0, pcistg, 0)
C(0xb9d2, PCILG, RRE, PCI, 0, 0, 0, 0, pcilg, 0)
C(0xb9d3, RPCIT, RRE, PCI, 0, 0, 0, 0, rpcit, 0)
C(0xe3d0, MPCIFC, RXY_a, PCI, la2, 0, 0, 0, mpcifc, 0)
C(0xe3d4, STPCIFC, RXY_a, PCI, la2, 0, 0, 0, stpcifc, 0)
#endif /* CONFIG_USER_ONLY */

View file

@ -278,11 +278,6 @@ static inline void s390_do_cpu_full_reset(CPUState *cs, run_on_cpu_data arg)
cpu_reset(cs);
}
static inline uint8_t s390_cpu_get_state(S390CPU *cpu)
{
return cpu->env.cpu_state;
}
/* arch_dump.c */
int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,

View file

@ -15,6 +15,9 @@
#include "exec/exec-all.h"
#include "sysemu/kvm.h"
#include "hw/s390x/ioinst.h"
#if !defined(CONFIG_USER_ONLY)
#include "hw/s390x/s390_flic.h"
#endif
/* Ensure to exit the TB after this call! */
void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen)
@ -55,17 +58,6 @@ void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen,
}
#if !defined(CONFIG_USER_ONLY)
static void cpu_inject_service(S390CPU *cpu, uint32_t param)
{
CPUS390XState *env = &cpu->env;
/* multiplexing is good enough for sclp - kvm does it internally as well*/
env->service_param |= param;
env->pending_int |= INTERRUPT_EXT_SERVICE;
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}
void cpu_inject_clock_comparator(S390CPU *cpu)
{
CPUS390XState *env = &cpu->env;
@ -134,48 +126,6 @@ void cpu_inject_stop(S390CPU *cpu)
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}
static void cpu_inject_io(S390CPU *cpu, uint16_t subchannel_id,
uint16_t subchannel_number,
uint32_t io_int_parm, uint32_t io_int_word)
{
CPUS390XState *env = &cpu->env;
int isc = IO_INT_WORD_ISC(io_int_word);
if (env->io_index[isc] == MAX_IO_QUEUE - 1) {
/* ugh - can't queue anymore. Let's drop. */
return;
}
env->io_index[isc]++;
assert(env->io_index[isc] < MAX_IO_QUEUE);
env->io_queue[env->io_index[isc]][isc].id = subchannel_id;
env->io_queue[env->io_index[isc]][isc].nr = subchannel_number;
env->io_queue[env->io_index[isc]][isc].parm = io_int_parm;
env->io_queue[env->io_index[isc]][isc].word = io_int_word;
env->pending_int |= INTERRUPT_IO;
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}
static void cpu_inject_crw_mchk(S390CPU *cpu)
{
CPUS390XState *env = &cpu->env;
if (env->mchk_index == MAX_MCHK_QUEUE - 1) {
/* ugh - can't queue anymore. Let's drop. */
return;
}
env->mchk_index++;
assert(env->mchk_index < MAX_MCHK_QUEUE);
env->mchk_queue[env->mchk_index].type = 1;
env->pending_int |= INTERRUPT_MCHK;
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}
/*
* All of the following interrupts are floating, i.e. not per-vcpu.
* We just need a dummy cpustate in order to be able to inject in the
@ -183,53 +133,50 @@ static void cpu_inject_crw_mchk(S390CPU *cpu)
*/
void s390_sclp_extint(uint32_t parm)
{
if (kvm_enabled()) {
kvm_s390_service_interrupt(parm);
} else {
S390CPU *dummy_cpu = s390_cpu_addr2state(0);
S390FLICState *fs = s390_get_flic();
S390FLICStateClass *fsc = s390_get_flic_class(fs);
cpu_inject_service(dummy_cpu, parm);
}
fsc->inject_service(fs, parm);
}
void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr,
uint32_t io_int_parm, uint32_t io_int_word)
{
if (kvm_enabled()) {
kvm_s390_io_interrupt(subchannel_id, subchannel_nr, io_int_parm,
io_int_word);
} else {
S390CPU *dummy_cpu = s390_cpu_addr2state(0);
S390FLICState *fs = s390_get_flic();
S390FLICStateClass *fsc = s390_get_flic_class(fs);
cpu_inject_io(dummy_cpu, subchannel_id, subchannel_nr, io_int_parm,
io_int_word);
}
fsc->inject_io(fs, subchannel_id, subchannel_nr, io_int_parm, io_int_word);
}
void s390_crw_mchk(void)
{
if (kvm_enabled()) {
kvm_s390_crw_mchk();
} else {
S390CPU *dummy_cpu = s390_cpu_addr2state(0);
S390FLICState *fs = s390_get_flic();
S390FLICStateClass *fsc = s390_get_flic_class(fs);
cpu_inject_crw_mchk(dummy_cpu);
}
fsc->inject_crw_mchk(fs);
}
bool s390_cpu_has_mcck_int(S390CPU *cpu)
{
QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
CPUS390XState *env = &cpu->env;
if (!(env->psw.mask & PSW_MASK_MCHECK)) {
return false;
}
return env->pending_int & INTERRUPT_MCHK;
/* for now we only support channel report machine checks (floating) */
if (qemu_s390_flic_has_crw_mchk(flic) &&
(env->cregs[14] & CR14_CHANNEL_REPORT_SC)) {
return true;
}
return false;
}
bool s390_cpu_has_ext_int(S390CPU *cpu)
{
QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
CPUS390XState *env = &cpu->env;
if (!(env->psw.mask & PSW_MASK_EXT)) {
@ -261,7 +208,7 @@ bool s390_cpu_has_ext_int(S390CPU *cpu)
return true;
}
if ((env->pending_int & INTERRUPT_EXT_SERVICE) &&
if (qemu_s390_flic_has_service(flic) &&
(env->cregs[0] & CR0_SERVICE_SC)) {
return true;
}
@ -271,13 +218,14 @@ bool s390_cpu_has_ext_int(S390CPU *cpu)
bool s390_cpu_has_io_int(S390CPU *cpu)
{
QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
CPUS390XState *env = &cpu->env;
if (!(env->psw.mask & PSW_MASK_IO)) {
return false;
}
return env->pending_int & INTERRUPT_IO;
return qemu_s390_flic_has_io(flic, env->cregs[6]);
}
bool s390_cpu_has_restart_int(S390CPU *cpu)

View file

@ -12,10 +12,6 @@
#include "cpu.h"
#include "kvm_s390x.h"
void kvm_s390_service_interrupt(uint32_t parm)
{
}
void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, uint64_t te_code)
{
}
@ -30,15 +26,6 @@ void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code)
{
}
void kvm_s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr,
uint32_t io_int_parm, uint32_t io_int_word)
{
}
void kvm_s390_crw_mchk(void)
{
}
int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state)
{
return -ENOSYS;

View file

@ -1034,7 +1034,7 @@ void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq)
inject_vcpu_irq_legacy(cs, irq);
}
static void __kvm_s390_floating_interrupt(struct kvm_s390_irq *irq)
void kvm_s390_floating_interrupt_legacy(struct kvm_s390_irq *irq)
{
struct kvm_s390_interrupt kvmint = {};
int r;
@ -1052,33 +1052,6 @@ static void __kvm_s390_floating_interrupt(struct kvm_s390_irq *irq)
}
}
void kvm_s390_floating_interrupt(struct kvm_s390_irq *irq)
{
static bool use_flic = true;
int r;
if (use_flic) {
r = kvm_s390_inject_flic(irq);
if (r == -ENOSYS) {
use_flic = false;
}
if (!r) {
return;
}
}
__kvm_s390_floating_interrupt(irq);
}
void kvm_s390_service_interrupt(uint32_t parm)
{
struct kvm_s390_irq irq = {
.type = KVM_S390_INT_SERVICE,
.u.ext.ext_params = parm,
};
kvm_s390_floating_interrupt(&irq);
}
void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code)
{
struct kvm_s390_irq irq = {
@ -1690,10 +1663,10 @@ static int handle_tsch(S390CPU *cpu)
* If an I/O interrupt had been dequeued, we have to reinject it.
*/
if (run->s390_tsch.dequeued) {
kvm_s390_io_interrupt(run->s390_tsch.subchannel_id,
run->s390_tsch.subchannel_nr,
run->s390_tsch.io_int_parm,
run->s390_tsch.io_int_word);
s390_io_interrupt(run->s390_tsch.subchannel_id,
run->s390_tsch.subchannel_nr,
run->s390_tsch.io_int_parm,
run->s390_tsch.io_int_word);
}
ret = 0;
}
@ -1702,7 +1675,7 @@ static int handle_tsch(S390CPU *cpu)
static void insert_stsi_3_2_2(S390CPU *cpu, __u64 addr, uint8_t ar)
{
struct sysib_322 sysib;
SysIB_322 sysib;
int del;
if (s390_cpu_virt_mem_read(cpu, addr, ar, &sysib, sizeof(sysib))) {
@ -1840,37 +1813,6 @@ bool kvm_arch_stop_on_emulation_error(CPUState *cpu)
return true;
}
void kvm_s390_io_interrupt(uint16_t subchannel_id,
uint16_t subchannel_nr, uint32_t io_int_parm,
uint32_t io_int_word)
{
struct kvm_s390_irq irq = {
.u.io.subchannel_id = subchannel_id,
.u.io.subchannel_nr = subchannel_nr,
.u.io.io_int_parm = io_int_parm,
.u.io.io_int_word = io_int_word,
};
if (io_int_word & IO_INT_WORD_AI) {
irq.type = KVM_S390_INT_IO(1, 0, 0, 0);
} else {
irq.type = KVM_S390_INT_IO(0, (subchannel_id & 0xff00) >> 8,
(subchannel_id & 0x0006),
subchannel_nr);
}
kvm_s390_floating_interrupt(&irq);
}
void kvm_s390_crw_mchk(void)
{
struct kvm_s390_irq irq = {
.type = KVM_S390_MCHK,
.u.mchk.cr14 = CR14_CHANNEL_REPORT_SC,
.u.mchk.mcic = s390_build_validity_mcic() | MCIC_SC_CP,
};
kvm_s390_floating_interrupt(&irq);
}
void kvm_s390_enable_css_support(S390CPU *cpu)
{
int r;
@ -2279,6 +2221,14 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
return;
}
/* PTFF subfunctions might be indicated although kernel support missing */
if (!test_bit(S390_FEAT_MULTIPLE_EPOCH, model->features)) {
clear_bit(S390_FEAT_PTFF_QSIE, model->features);
clear_bit(S390_FEAT_PTFF_QTOUE, model->features);
clear_bit(S390_FEAT_PTFF_STOE, model->features);
clear_bit(S390_FEAT_PTFF_STOUE, model->features);
}
/* with cpu model support, CMM is only indicated if really available */
if (kvm_s390_cmma_available()) {
set_bit(S390_FEAT_CMM, model->features);

View file

@ -12,17 +12,12 @@
struct kvm_s390_irq;
void kvm_s390_floating_interrupt(struct kvm_s390_irq *irq);
void kvm_s390_service_interrupt(uint32_t parm);
void kvm_s390_floating_interrupt_legacy(struct kvm_s390_irq *irq);
void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq);
void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, uint64_t te_code);
int kvm_s390_mem_op(S390CPU *cpu, vaddr addr, uint8_t ar, void *hostbuf,
int len, bool is_write);
void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code);
void kvm_s390_io_interrupt(uint16_t subchannel_id,
uint16_t subchannel_nr, uint32_t io_int_parm,
uint32_t io_int_word);
void kvm_s390_crw_mchk(void);
int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state);
void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu);
int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu);
@ -44,7 +39,4 @@ void kvm_s390_crypto_reset(void);
void kvm_s390_restart_interrupt(S390CPU *cpu);
void kvm_s390_stop_interrupt(S390CPU *cpu);
/* implemented outside of target/s390x/ */
int kvm_s390_inject_flic(struct kvm_s390_irq *irq);
#endif /* KVM_S390X_H */

View file

@ -36,6 +36,10 @@
#include "hw/s390x/ebcdic.h"
#include "hw/s390x/s390-virtio-hcall.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/s390_flic.h"
#include "hw/s390x/ioinst.h"
#include "hw/s390x/s390-pci-inst.h"
#include "hw/boards.h"
#endif
/* #define DEBUG_HELPER */
@ -194,132 +198,148 @@ void HELPER(spt)(CPUS390XState *env, uint64_t time)
}
/* Store System Information */
uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0,
uint64_t r0, uint64_t r1)
uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint64_t r0, uint64_t r1)
{
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;
S390CPU *cpu = s390_env_get_cpu(env);
int cc = 0;
int sel1, sel2;
SysIB sysib = { 0 };
int i, cc = 0;
if ((r0 & STSI_LEVEL_MASK) <= STSI_LEVEL_3 &&
((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK))) {
/* valid function code, invalid reserved bits */
s390_program_interrupt(env, PGM_SPECIFICATION, 4, GETPC());
if ((r0 & STSI_R0_FC_MASK) > STSI_R0_FC_LEVEL_3) {
/* invalid function code: no other checks are performed */
return 3;
}
sel1 = r0 & STSI_R0_SEL1_MASK;
sel2 = r1 & STSI_R1_SEL2_MASK;
if ((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK)) {
s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
}
/* XXX: spec exception if sysib is not 4k-aligned */
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;
}
switch (r0 & STSI_LEVEL_MASK) {
case STSI_LEVEL_1:
if (a0 & ~TARGET_PAGE_MASK) {
s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
}
/* 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++;
}
}
/*
* 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:
if ((sel1 == 1) && (sel2 == 1)) {
/* Basic Machine Configuration */
struct sysib_111 sysib;
char type[5] = {};
memset(&sysib, 0, sizeof(sysib));
ebcdic_put(sysib.manuf, "QEMU ", 16);
ebcdic_put(sysib.sysib_111.manuf, "QEMU ", 16);
/* same as machine type number in STORE CPU ID, but in EBCDIC */
snprintf(type, ARRAY_SIZE(type), "%X", cpu->model->def->type);
ebcdic_put(sysib.type, type, 4);
ebcdic_put(sysib.sysib_111.type, type, 4);
/* model number (not stored in STORE CPU ID for z/Architecure) */
ebcdic_put(sysib.model, "QEMU ", 16);
ebcdic_put(sysib.sequence, "QEMU ", 16);
ebcdic_put(sysib.plant, "QEMU", 4);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
ebcdic_put(sysib.sysib_111.model, "QEMU ", 16);
ebcdic_put(sysib.sysib_111.sequence, "QEMU ", 16);
ebcdic_put(sysib.sysib_111.plant, "QEMU", 4);
} else if ((sel1 == 2) && (sel2 == 1)) {
/* Basic Machine CPU */
struct sysib_121 sysib;
memset(&sysib, 0, sizeof(sysib));
/* XXX make different for different CPUs? */
ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16);
ebcdic_put(sysib.plant, "QEMU", 4);
stw_p(&sysib.cpu_addr, env->core_id);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
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);
} else if ((sel1 == 2) && (sel2 == 2)) {
/* Basic Machine CPUs */
struct sysib_122 sysib;
memset(&sysib, 0, sizeof(sysib));
stl_p(&sysib.capability, 0x443afc29);
/* XXX change when SMP comes */
stw_p(&sysib.total_cpus, 1);
stw_p(&sysib.active_cpus, 1);
stw_p(&sysib.standby_cpus, 0);
stw_p(&sysib.reserved_cpus, 0);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
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);
} else {
cc = 3;
}
break;
case STSI_LEVEL_2:
{
if ((sel1 == 2) && (sel2 == 1)) {
/* LPAR CPU */
struct sysib_221 sysib;
memset(&sysib, 0, sizeof(sysib));
/* XXX make different for different CPUs? */
ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16);
ebcdic_put(sysib.plant, "QEMU", 4);
stw_p(&sysib.cpu_addr, env->core_id);
stw_p(&sysib.cpu_id, 0);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
} else if ((sel1 == 2) && (sel2 == 2)) {
/* LPAR CPUs */
struct sysib_222 sysib;
memset(&sysib, 0, sizeof(sysib));
stw_p(&sysib.lpar_num, 0);
sysib.lcpuc = 0;
/* XXX change when SMP comes */
stw_p(&sysib.total_cpus, 1);
stw_p(&sysib.conf_cpus, 1);
stw_p(&sysib.standby_cpus, 0);
stw_p(&sysib.reserved_cpus, 0);
ebcdic_put(sysib.name, "QEMU ", 8);
stl_p(&sysib.caf, 1000);
stw_p(&sysib.dedicated_cpus, 0);
stw_p(&sysib.shared_cpus, 0);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
} else {
cc = 3;
}
break;
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;
}
case STSI_LEVEL_3:
{
if ((sel1 == 2) && (sel2 == 2)) {
/* VM CPUs */
struct sysib_322 sysib;
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 */
memset(&sysib, 0, sizeof(sysib));
sysib.count = 1;
/* XXX change when SMP comes */
stw_p(&sysib.vm[0].total_cpus, 1);
stw_p(&sysib.vm[0].conf_cpus, 1);
stw_p(&sysib.vm[0].standby_cpus, 0);
stw_p(&sysib.vm[0].reserved_cpus, 0);
ebcdic_put(sysib.vm[0].name, "KVMguest", 8);
stl_p(&sysib.vm[0].caf, 1000);
ebcdic_put(sysib.vm[0].cpi, "KVM/Linux ", 16);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
/* 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)));
strncpy((char *)sysib.sysib_322.ext_names[0], qemu_name,
sizeof(sysib.sysib_322.ext_names[0]));
} else {
cc = 3;
ebcdic_put(sysib.sysib_322.vm[0].name, "TCGguest", 8);
strcpy((char *)sysib.sysib_322.ext_names[0], "TCGguest");
}
break;
/* add the uuid */
memcpy(sysib.sysib_322.vm[0].uuid, &qemu_uuid,
sizeof(sysib.sysib_322.vm[0].uuid));
} else {
cc = 3;
}
case STSI_LEVEL_CURRENT:
env->regs[0] = STSI_LEVEL_3;
break;
default:
cc = 3;
break;
}
if (cc == 0) {
if (s390_cpu_virt_mem_write(cpu, a0, 0, &sysib, sizeof(sysib))) {
s390_cpu_virt_mem_handle_exc(cpu, ra);
}
}
return cc;
}
@ -429,6 +449,59 @@ void HELPER(stsch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
qemu_mutex_unlock_iothread();
}
uint32_t HELPER(tpi)(CPUS390XState *env, uint64_t addr)
{
const uintptr_t ra = GETPC();
S390CPU *cpu = s390_env_get_cpu(env);
QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
QEMUS390FlicIO *io = NULL;
LowCore *lowcore;
if (addr & 0x3) {
s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
}
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;
}
void HELPER(tsch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
{
S390CPU *cpu = s390_env_get_cpu(env);
@ -560,3 +633,91 @@ uint32_t HELPER(stfle)(CPUS390XState *env, uint64_t addr)
env->regs[0] = deposit64(env->regs[0], 0, 8, (max_bytes / 8) - 1);
return count_bytes >= max_bytes ? 0 : 3;
}
#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)
{
S390CPU *cpu = s390_env_get_cpu(env);
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)
{
S390CPU *cpu = s390_env_get_cpu(env);
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)
{
S390CPU *cpu = s390_env_get_cpu(env);
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)
{
S390CPU *cpu = s390_env_get_cpu(env);
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) {
s390_program_interrupt(env, -r, 4, GETPC());
}
}
void HELPER(rpcit)(CPUS390XState *env, uint32_t r1, uint32_t r2)
{
S390CPU *cpu = s390_env_get_cpu(env);
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)
{
S390CPU *cpu = s390_env_get_cpu(env);
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)
{
S390CPU *cpu = s390_env_get_cpu(env);
qemu_mutex_lock_iothread();
mpcifc_service_call(cpu, r1, fiba, ar, GETPC());
qemu_mutex_unlock_iothread();
}
#endif

View file

@ -4199,6 +4199,14 @@ static ExitStatus op_stcrw(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
static ExitStatus op_tpi(DisasContext *s, DisasOps *o)
{
check_privileged(s);
gen_helper_tpi(cc_op, cpu_env, o->addr1);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_tsch(DisasContext *s, DisasOps *o)
{
check_privileged(s);
@ -4777,6 +4785,106 @@ static ExitStatus op_zero2(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
#ifndef CONFIG_USER_ONLY
static ExitStatus op_clp(DisasContext *s, DisasOps *o)
{
TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
check_privileged(s);
gen_helper_clp(cpu_env, r2);
tcg_temp_free_i32(r2);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_pcilg(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
check_privileged(s);
gen_helper_pcilg(cpu_env, r1, r2);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r2);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_pcistg(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
check_privileged(s);
gen_helper_pcistg(cpu_env, r1, r2);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r2);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_stpcifc(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2));
check_privileged(s);
gen_helper_stpcifc(cpu_env, r1, o->addr1, ar);
tcg_temp_free_i32(ar);
tcg_temp_free_i32(r1);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_sic(DisasContext *s, DisasOps *o)
{
check_privileged(s);
gen_helper_sic(cpu_env, o->in1, o->in2);
return NO_EXIT;
}
static ExitStatus op_rpcit(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
check_privileged(s);
gen_helper_rpcit(cpu_env, r1, r2);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r2);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_pcistb(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2));
check_privileged(s);
gen_helper_pcistb(cpu_env, r1, r3, o->addr1, ar);
tcg_temp_free_i32(ar);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r3);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_mpcifc(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2));
check_privileged(s);
gen_helper_mpcifc(cpu_env, r1, o->addr1, ar);
tcg_temp_free_i32(ar);
tcg_temp_free_i32(r1);
set_cc_static(s);
return NO_EXIT;
}
#endif
/* ====================================================================== */
/* The "Cc OUTput" generators. Given the generated output (and in some cases
the original inputs), update the various cc data structures in order to
@ -5708,6 +5816,8 @@ enum DisasInsnEnum {
#define FAC_MSA4 S390_FEAT_MSA_EXT_4 /* msa-extension-4 facility */
#define FAC_MSA5 S390_FEAT_MSA_EXT_5 /* msa-extension-5 facility */
#define FAC_ECT S390_FEAT_EXTRACT_CPU_TIME
#define FAC_PCI S390_FEAT_ZPCI /* z/PCI facility */
#define FAC_AIS S390_FEAT_ADAPTER_INT_SUPPRESSION
static const DisasInsn insn_info[] = {
#include "insn-data.def"