mirror of https://github.com/xemu-project/xemu.git
Various changes for the s390x code:
- updates for cpu model handling - fix compilation with --disable-tcg - fixes in vfio-ccw and I/O instruction handling -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJZdww5AAoJEN7Pa5PG8C+vzlwP/iQBy81J4aS8HvsOqRrPO8SZ ympZW9hxux52Igr1yXbCJn2jYGmF2Yl4mS+Q/4JvZ6cs38bSMLOE/FWEIvrjgpKG +IAnbv7icn8/tJFgZSdQpmvb5meeX6T3HNsyerjnGtz3c84DDBycJp4jRWTkDWB+ zE0CyAPfqborCK7kMR788lXIZVo5DnwMGq+rOriIPPKrPxeuhCO11xNTSBnPS5Ae 2P/eKfgqd99hjBK4vqR+zo0ldo3uL7xDZnVK54ucp+xLmtQnKMeqrT73+LE/OHXR kL9mrWLVYawJUuMFPR6J20E5IMFjLKHkVg177a78Zukf2lwCkn/xegzDZnhCPX4e bTFlJBSB4C/7KMFi1QFtyqUbw5VQ7CeAaCJskwLE6aJ89hmKkuGdOcgGOanJtv4f IniOBANWie/IfJVGnHQ0amupMK9UKCGjJ3+k6SLQQl3UV8q4EE/0QvcKvMwJa8gE 2wnmPue5gFUhFKZBR6ugbVC+C5aMBF/LvY8sPRLgioN3rKi0gzXO5ChCwqLV0J7J 3q3VN1yltAcgmkvUkkNkNg9XI/9sft9ycCZhQdPQvHhAa8dSQj503m1TknhTjVnl os7RXnMMhJfKayJEXbmAFQU9TldCtZKNGAnWPwXbEednHZHOBGPz9msYOZgFMwXk ttg9AHhAzY/ynnOIisO2 =8RaH -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20170725' into staging Various changes for the s390x code: - updates for cpu model handling - fix compilation with --disable-tcg - fixes in vfio-ccw and I/O instruction handling # gpg: Signature made Tue 25 Jul 2017 10:15:37 BST # gpg: using RSA key 0xDECF6B93C6F02FAF # 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>" # Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0 18CE DECF 6B93 C6F0 2FAF * remotes/cohuck/tags/s390x-20170725: s390x/css: fix ilen in IO instruction handlers target/s390x: Add remaining switches to compile with --disable-tcg target/s390x: Move exception-related functions to a new excp_helper.c file target/s390x: Rework program_interrupt() and related functions target/s390x: Move diag helpers to a separate file target/s390x: Move s390_cpu_dump_state() to helper.c target/s390x: improve baselining if certain base features are missing s390x/kvm: better comment regarding zPCI feature availability target/s390x: introduce (test|set)_be_bit target/s390x: indicate query subfunction in s390_fill_feat_block target/s390x: drop BE_BIT() s390/cpumodel: remove KSS from the default model of z14 vfio/ccw: fix initialization of the Object DeviceState pointer in the common base-device vfio/ccw: allocate irq info with the right size Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
5fb203a199
|
@ -168,7 +168,7 @@ static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
argsz = sizeof(*irq_set);
|
||||
argsz = sizeof(*irq_info);
|
||||
irq_info = g_malloc0(argsz);
|
||||
irq_info->index = VFIO_CCW_IO_IRQ_INDEX;
|
||||
irq_info->argsz = argsz;
|
||||
|
@ -338,6 +338,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp)
|
|||
vcdev->vdev.type = VFIO_DEVICE_TYPE_CCW;
|
||||
vcdev->vdev.name = g_strdup_printf("%x.%x.%04x", cdev->hostid.cssid,
|
||||
cdev->hostid.ssid, cdev->hostid.devid);
|
||||
vcdev->vdev.dev = dev;
|
||||
QLIST_FOREACH(vbasedev, &group->device_list, next) {
|
||||
if (strcmp(vbasedev->name, vcdev->vdev.name) == 0) {
|
||||
error_setg(&err, "vfio: subchannel %s has already been attached",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
obj-y += translate.o helper.o cpu.o interrupt.o
|
||||
obj-y += int_helper.o fpu_helper.o cc_helper.o mem_helper.o misc_helper.o
|
||||
obj-y += gdbstub.o cpu_models.o cpu_features.o
|
||||
obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o arch_dump.o mmu_helper.o
|
||||
obj-y += cpu.o cpu_models.o cpu_features.o gdbstub.o interrupt.o helper.o
|
||||
obj-$(CONFIG_TCG) += translate.o cc_helper.o excp_helper.o fpu_helper.o
|
||||
obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o
|
||||
obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o arch_dump.o mmu_helper.o diag.o
|
||||
obj-$(CONFIG_KVM) += kvm.o
|
||||
|
||||
# build and run feature list generator
|
||||
|
|
|
@ -417,7 +417,9 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data)
|
|||
cc->reset = s390_cpu_full_reset;
|
||||
cc->class_by_name = s390_cpu_class_by_name,
|
||||
cc->has_work = s390_cpu_has_work;
|
||||
#ifdef CONFIG_TCG
|
||||
cc->do_interrupt = s390_cpu_do_interrupt;
|
||||
#endif
|
||||
cc->dump_state = s390_cpu_dump_state;
|
||||
cc->set_pc = s390_cpu_set_pc;
|
||||
cc->gdb_read_register = s390_cpu_gdb_read_register;
|
||||
|
@ -428,9 +430,11 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data)
|
|||
cc->get_phys_page_debug = s390_cpu_get_phys_page_debug;
|
||||
cc->vmsd = &vmstate_s390_cpu;
|
||||
cc->write_elf64_note = s390_cpu_write_elf64_note;
|
||||
#ifdef CONFIG_TCG
|
||||
cc->cpu_exec_interrupt = s390_cpu_exec_interrupt;
|
||||
cc->debug_excp_handler = s390x_cpu_debug_excp_handler;
|
||||
cc->do_unaligned_access = s390x_cpu_do_unaligned_access;
|
||||
#endif
|
||||
#endif
|
||||
cc->disas_set_info = s390_cpu_disas_set_info;
|
||||
|
||||
|
|
|
@ -474,10 +474,6 @@ static inline bool get_per_in_range(CPUS390XState *env, uint64_t addr)
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen);
|
||||
#endif
|
||||
|
||||
S390CPU *cpu_s390x_init(const char *cpu_model);
|
||||
S390CPU *s390x_new_cpu(const char *cpu_model, int64_t id, Error **errp);
|
||||
S390CPU *cpu_s390x_create(const char *cpu_model, Error **errp);
|
||||
|
@ -805,6 +801,8 @@ static inline void setcc(S390CPU *cpu, uint64_t cc)
|
|||
env->cc_op = cc;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
typedef struct LowCore
|
||||
{
|
||||
/* prefix area: defined by architecture */
|
||||
|
@ -922,6 +920,11 @@ typedef struct LowCore
|
|||
uint8_t pad18[0x2000-0x1400]; /* 0x1400 */
|
||||
} QEMU_PACKED LowCore;
|
||||
|
||||
LowCore *cpu_map_lowcore(CPUS390XState *env);
|
||||
void cpu_unmap_lowcore(LowCore *lowcore);
|
||||
|
||||
#endif
|
||||
|
||||
/* STSI */
|
||||
#define STSI_LEVEL_MASK 0x00000000f0000000ULL
|
||||
#define STSI_LEVEL_CURRENT 0x0000000000000000ULL
|
||||
|
@ -1098,6 +1101,7 @@ struct sysib_322 {
|
|||
#define SIGP_ORDER_MASK 0x000000ff
|
||||
|
||||
void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr);
|
||||
uint64_t get_psw_mask(CPUS390XState *env);
|
||||
target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr);
|
||||
int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
|
||||
target_ulong *raddr, int *flags, bool exc);
|
||||
|
@ -1146,10 +1150,12 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3);
|
|||
/* automatically detect the instruction length */
|
||||
#define ILEN_AUTO 0xff
|
||||
void program_interrupt(CPUS390XState *env, uint32_t code, int ilen);
|
||||
void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen);
|
||||
void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp,
|
||||
uintptr_t retaddr);
|
||||
|
||||
#ifdef CONFIG_KVM
|
||||
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);
|
||||
|
@ -1170,6 +1176,9 @@ int kvm_s390_get_ri(void);
|
|||
int kvm_s390_get_gs(void);
|
||||
void kvm_s390_crypto_reset(void);
|
||||
#else
|
||||
static inline void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code)
|
||||
{
|
||||
}
|
||||
static inline void kvm_s390_io_interrupt(uint16_t subchannel_id,
|
||||
uint16_t subchannel_nr,
|
||||
uint32_t io_int_parm,
|
||||
|
|
|
@ -336,18 +336,39 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type,
|
|||
S390Feat feat;
|
||||
int bit_nr;
|
||||
|
||||
if (type == S390_FEAT_TYPE_STFL && test_bit(S390_FEAT_ZARCH, features)) {
|
||||
/* Features that are always active */
|
||||
data[0] |= 0x20; /* z/Architecture */
|
||||
data[17] |= 0x20; /* Configuration-z-architectural-mode */
|
||||
}
|
||||
switch (type) {
|
||||
case S390_FEAT_TYPE_STFL:
|
||||
if (test_bit(S390_FEAT_ZARCH, features)) {
|
||||
/* Features that are always active */
|
||||
set_be_bit(2, data); /* z/Architecture */
|
||||
set_be_bit(138, data); /* Configuration-z-architectural-mode */
|
||||
}
|
||||
break;
|
||||
case S390_FEAT_TYPE_PTFF:
|
||||
case S390_FEAT_TYPE_KMAC:
|
||||
case S390_FEAT_TYPE_KMC:
|
||||
case S390_FEAT_TYPE_KM:
|
||||
case S390_FEAT_TYPE_KIMD:
|
||||
case S390_FEAT_TYPE_KLMD:
|
||||
case S390_FEAT_TYPE_PCKMO:
|
||||
case S390_FEAT_TYPE_KMCTR:
|
||||
case S390_FEAT_TYPE_KMF:
|
||||
case S390_FEAT_TYPE_KMO:
|
||||
case S390_FEAT_TYPE_PCC:
|
||||
case S390_FEAT_TYPE_PPNO:
|
||||
case S390_FEAT_TYPE_KMA:
|
||||
set_be_bit(0, data); /* query is always available */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
feat = find_first_bit(features, S390_FEAT_MAX);
|
||||
while (feat < S390_FEAT_MAX) {
|
||||
if (s390_features[feat].type == type) {
|
||||
bit_nr = s390_features[feat].bit;
|
||||
/* big endian on uint8_t array */
|
||||
data[bit_nr / 8] |= 0x80 >> (bit_nr % 8);
|
||||
set_be_bit(bit_nr, data);
|
||||
}
|
||||
feat = find_next_bit(features, S390_FEAT_MAX, feat + 1);
|
||||
}
|
||||
|
|
|
@ -92,6 +92,13 @@ typedef struct {
|
|||
const S390FeatGroupDef *s390_feat_group_def(S390FeatGroup group);
|
||||
|
||||
#define BE_BIT_NR(BIT) (BIT ^ (BITS_PER_LONG - 1))
|
||||
#define BE_BIT(BIT) (1ULL < BE_BIT_NR(BIT))
|
||||
|
||||
static inline void set_be_bit(unsigned int bit_nr, uint8_t *array)
|
||||
{
|
||||
array[bit_nr / 8] |= 0x80 >> (bit_nr % 8);
|
||||
}
|
||||
static inline bool test_be_bit(unsigned int bit_nr, const uint8_t *array)
|
||||
{
|
||||
return array[bit_nr / 8] & (0x80 >> (bit_nr % 8));
|
||||
}
|
||||
#endif /* TARGET_S390X_CPU_FEATURES_H */
|
||||
|
|
|
@ -78,6 +78,9 @@ static S390CPUDef s390_cpu_defs[] = {
|
|||
CPUDEF_INIT(0x3906, 14, 1, 47, 0x08000000U, "z14", "IBM z14 GA1"),
|
||||
};
|
||||
|
||||
/* features part of a base model but not relevant for finding a base model */
|
||||
S390FeatBitmap ignored_base_feat;
|
||||
|
||||
void s390_cpudef_featoff(uint8_t gen, uint8_t ec_ga, S390Feat feat)
|
||||
{
|
||||
const S390CPUDef *def;
|
||||
|
@ -237,6 +240,11 @@ const S390CPUDef *s390_find_cpu_def(uint16_t type, uint8_t gen, uint8_t ec_ga,
|
|||
if (features) {
|
||||
/* see if the model satisfies the minimum features */
|
||||
bitmap_andnot(missing, def->base_feat, features, S390_FEAT_MAX);
|
||||
/*
|
||||
* Ignore certain features that are in the base model, but not
|
||||
* relevant for the search (esp. MSA subfunctions).
|
||||
*/
|
||||
bitmap_andnot(missing, missing, ignored_base_feat, S390_FEAT_MAX);
|
||||
if (!bitmap_empty(missing, S390_FEAT_MAX)) {
|
||||
break;
|
||||
}
|
||||
|
@ -723,6 +731,7 @@ static void check_consistency(const S390CPUModel *model)
|
|||
{ S390_FEAT_KLMD_SHAKE_256, S390_FEAT_MSA },
|
||||
{ S390_FEAT_PRNO_TRNG_QRTCR, S390_FEAT_MSA_EXT_5 },
|
||||
{ S390_FEAT_PRNO_TRNG, S390_FEAT_MSA_EXT_5 },
|
||||
{ S390_FEAT_SIE_KSS, S390_FEAT_SIE_F2 },
|
||||
};
|
||||
int i;
|
||||
|
||||
|
@ -1209,10 +1218,35 @@ static const TypeInfo host_s390_cpu_type_info = {
|
|||
};
|
||||
#endif
|
||||
|
||||
static void init_ignored_base_feat(void)
|
||||
{
|
||||
static const int feats[] = {
|
||||
/* MSA subfunctions that could not be available on certain machines */
|
||||
S390_FEAT_KMAC_DEA,
|
||||
S390_FEAT_KMAC_TDEA_128,
|
||||
S390_FEAT_KMAC_TDEA_192,
|
||||
S390_FEAT_KMC_DEA,
|
||||
S390_FEAT_KMC_TDEA_128,
|
||||
S390_FEAT_KMC_TDEA_192,
|
||||
S390_FEAT_KM_DEA,
|
||||
S390_FEAT_KM_TDEA_128,
|
||||
S390_FEAT_KM_TDEA_192,
|
||||
S390_FEAT_KIMD_SHA_1,
|
||||
S390_FEAT_KLMD_SHA_1,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(feats); i++) {
|
||||
set_bit(feats[i], ignored_base_feat);
|
||||
}
|
||||
}
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
init_ignored_base_feat();
|
||||
|
||||
/* init all bitmaps from gnerated data initially */
|
||||
for (i = 0; i < ARRAY_SIZE(s390_cpu_defs); i++) {
|
||||
s390_init_feat_bitmap(s390_cpu_defs[i].base_init,
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* S390x DIAG instruction helper functions
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "hw/watchdog/wdt_diag288.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "hw/s390x/ipl.h"
|
||||
|
||||
static int modified_clear_reset(S390CPU *cpu)
|
||||
{
|
||||
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
|
||||
CPUState *t;
|
||||
|
||||
pause_all_vcpus();
|
||||
cpu_synchronize_all_states();
|
||||
CPU_FOREACH(t) {
|
||||
run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL);
|
||||
}
|
||||
s390_cmma_reset();
|
||||
subsystem_reset();
|
||||
s390_crypto_reset();
|
||||
scc->load_normal(CPU(cpu));
|
||||
cpu_synchronize_all_post_reset();
|
||||
resume_all_vcpus();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_normal_reset(S390CPU *cpu)
|
||||
{
|
||||
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
|
||||
CPUState *t;
|
||||
|
||||
pause_all_vcpus();
|
||||
cpu_synchronize_all_states();
|
||||
CPU_FOREACH(t) {
|
||||
run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL);
|
||||
}
|
||||
s390_cmma_reset();
|
||||
subsystem_reset();
|
||||
scc->initial_cpu_reset(CPU(cpu));
|
||||
scc->load_normal(CPU(cpu));
|
||||
cpu_synchronize_all_post_reset();
|
||||
resume_all_vcpus();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
|
||||
{
|
||||
uint64_t func = env->regs[r1];
|
||||
uint64_t timeout = env->regs[r1 + 1];
|
||||
uint64_t action = env->regs[r3];
|
||||
Object *obj;
|
||||
DIAG288State *diag288;
|
||||
DIAG288Class *diag288_class;
|
||||
|
||||
if (r1 % 2 || action != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Timeout must be more than 15 seconds except for timer deletion */
|
||||
if (func != WDT_DIAG288_CANCEL && timeout < 15) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL);
|
||||
if (!obj) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
diag288 = DIAG288(obj);
|
||||
diag288_class = DIAG288_GET_CLASS(diag288);
|
||||
return diag288_class->handle_timer(diag288, func, timeout);
|
||||
}
|
||||
|
||||
#define DIAG_308_RC_OK 0x0001
|
||||
#define DIAG_308_RC_NO_CONF 0x0102
|
||||
#define DIAG_308_RC_INVALID 0x0402
|
||||
|
||||
void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3)
|
||||
{
|
||||
uint64_t addr = env->regs[r1];
|
||||
uint64_t subcode = env->regs[r3];
|
||||
IplParameterBlock *iplb;
|
||||
|
||||
if (env->psw.mask & PSW_MASK_PSTATE) {
|
||||
program_interrupt(env, PGM_PRIVILEGED, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((subcode & ~0x0ffffULL) || (subcode > 6)) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (subcode) {
|
||||
case 0:
|
||||
modified_clear_reset(s390_env_get_cpu(env));
|
||||
if (tcg_enabled()) {
|
||||
cpu_loop_exit(CPU(s390_env_get_cpu(env)));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
load_normal_reset(s390_env_get_cpu(env));
|
||||
if (tcg_enabled()) {
|
||||
cpu_loop_exit(CPU(s390_env_get_cpu(env)));
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
s390_reipl_request();
|
||||
if (tcg_enabled()) {
|
||||
cpu_loop_exit(CPU(s390_env_get_cpu(env)));
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if ((r1 & 1) || (addr & 0x0fffULL)) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
if (!address_space_access_valid(&address_space_memory, addr,
|
||||
sizeof(IplParameterBlock), false)) {
|
||||
program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
iplb = g_malloc0(sizeof(IplParameterBlock));
|
||||
cpu_physical_memory_read(addr, iplb, sizeof(iplb->len));
|
||||
if (!iplb_valid_len(iplb)) {
|
||||
env->regs[r1 + 1] = DIAG_308_RC_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len));
|
||||
|
||||
if (!iplb_valid_ccw(iplb) && !iplb_valid_fcp(iplb)) {
|
||||
env->regs[r1 + 1] = DIAG_308_RC_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
s390_ipl_update_diag308(iplb);
|
||||
env->regs[r1 + 1] = DIAG_308_RC_OK;
|
||||
out:
|
||||
g_free(iplb);
|
||||
return;
|
||||
case 6:
|
||||
if ((r1 & 1) || (addr & 0x0fffULL)) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
if (!address_space_access_valid(&address_space_memory, addr,
|
||||
sizeof(IplParameterBlock), true)) {
|
||||
program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
iplb = s390_ipl_get_iplb();
|
||||
if (iplb) {
|
||||
cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len));
|
||||
env->regs[r1 + 1] = DIAG_308_RC_OK;
|
||||
} else {
|
||||
env->regs[r1 + 1] = DIAG_308_RC_NO_CONF;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
hw_error("Unhandled diag308 subcode %" PRIx64, subcode);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,515 @@
|
|||
/*
|
||||
* s390x exception / interrupt helpers
|
||||
*
|
||||
* Copyright (c) 2009 Ulrich Hecht
|
||||
* Copyright (c) 2011 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
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "cpu.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "hw/s390x/ioinst.h"
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
#include "sysemu/sysemu.h"
|
||||
#endif
|
||||
|
||||
/* #define DEBUG_S390 */
|
||||
/* #define DEBUG_S390_STDOUT */
|
||||
|
||||
#ifdef DEBUG_S390
|
||||
#ifdef DEBUG_S390_STDOUT
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { fprintf(stderr, fmt, ## __VA_ARGS__); \
|
||||
if (qemu_log_separate()) { qemu_log(fmt, ##__VA_ARGS__); } } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { qemu_log(fmt, ## __VA_ARGS__); } while (0)
|
||||
#endif
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { } while (0)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
|
||||
void s390_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
cs->exception_index = -1;
|
||||
}
|
||||
|
||||
int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
|
||||
int rw, int mmu_idx)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
|
||||
cs->exception_index = EXCP_PGM;
|
||||
cpu->env.int_pgm_code = PGM_ADDRESSING;
|
||||
/* On real machines this value is dropped into LowMem. Since this
|
||||
is userland, simply put this someplace that cpu_loop can find it. */
|
||||
cpu->env.__excp_addr = address;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_USER_ONLY */
|
||||
|
||||
int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr,
|
||||
int rw, int mmu_idx)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
uint64_t asc = cpu_mmu_idx_to_asc(mmu_idx);
|
||||
target_ulong vaddr, raddr;
|
||||
int prot;
|
||||
|
||||
DPRINTF("%s: address 0x%" VADDR_PRIx " rw %d mmu_idx %d\n",
|
||||
__func__, orig_vaddr, rw, mmu_idx);
|
||||
|
||||
orig_vaddr &= TARGET_PAGE_MASK;
|
||||
vaddr = orig_vaddr;
|
||||
|
||||
/* 31-Bit mode */
|
||||
if (!(env->psw.mask & PSW_MASK_64)) {
|
||||
vaddr &= 0x7fffffff;
|
||||
}
|
||||
|
||||
if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot, true)) {
|
||||
/* Translation ended in exception */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* check out of RAM access */
|
||||
if (raddr > ram_size) {
|
||||
DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__,
|
||||
(uint64_t)raddr, (uint64_t)ram_size);
|
||||
trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_AUTO);
|
||||
return 1;
|
||||
}
|
||||
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n",
|
||||
__func__, (uint64_t)vaddr, (uint64_t)raddr, prot);
|
||||
|
||||
tlb_set_page(cs, orig_vaddr, raddr, prot,
|
||||
mmu_idx, TARGET_PAGE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void do_program_interrupt(CPUS390XState *env)
|
||||
{
|
||||
uint64_t mask, addr;
|
||||
LowCore *lowcore;
|
||||
int ilen = env->int_pgm_ilen;
|
||||
|
||||
if (ilen == ILEN_AUTO) {
|
||||
ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
|
||||
}
|
||||
assert(ilen == 2 || ilen == 4 || ilen == 6);
|
||||
|
||||
switch (env->int_pgm_code) {
|
||||
case PGM_PER:
|
||||
if (env->per_perc_atmid & PER_CODE_EVENT_NULLIFICATION) {
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
case PGM_OPERATION:
|
||||
case PGM_PRIVILEGED:
|
||||
case PGM_EXECUTE:
|
||||
case PGM_PROTECTION:
|
||||
case PGM_ADDRESSING:
|
||||
case PGM_SPECIFICATION:
|
||||
case PGM_DATA:
|
||||
case PGM_FIXPT_OVERFLOW:
|
||||
case PGM_FIXPT_DIVIDE:
|
||||
case PGM_DEC_OVERFLOW:
|
||||
case PGM_DEC_DIVIDE:
|
||||
case PGM_HFP_EXP_OVERFLOW:
|
||||
case PGM_HFP_EXP_UNDERFLOW:
|
||||
case PGM_HFP_SIGNIFICANCE:
|
||||
case PGM_HFP_DIVIDE:
|
||||
case PGM_TRANS_SPEC:
|
||||
case PGM_SPECIAL_OP:
|
||||
case PGM_OPERAND:
|
||||
case PGM_HFP_SQRT:
|
||||
case PGM_PC_TRANS_SPEC:
|
||||
case PGM_ALET_SPEC:
|
||||
case PGM_MONITOR:
|
||||
/* advance the PSW if our exception is not nullifying */
|
||||
env->psw.addr += ilen;
|
||||
break;
|
||||
}
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n",
|
||||
__func__, env->int_pgm_code, ilen);
|
||||
|
||||
lowcore = cpu_map_lowcore(env);
|
||||
|
||||
/* Signal PER events with the exception. */
|
||||
if (env->per_perc_atmid) {
|
||||
env->int_pgm_code |= PGM_PER;
|
||||
lowcore->per_address = cpu_to_be64(env->per_address);
|
||||
lowcore->per_perc_atmid = cpu_to_be16(env->per_perc_atmid);
|
||||
env->per_perc_atmid = 0;
|
||||
}
|
||||
|
||||
lowcore->pgm_ilen = cpu_to_be16(ilen);
|
||||
lowcore->pgm_code = cpu_to_be16(env->int_pgm_code);
|
||||
lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env));
|
||||
lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
|
||||
mask = be64_to_cpu(lowcore->program_new_psw.mask);
|
||||
addr = be64_to_cpu(lowcore->program_new_psw.addr);
|
||||
lowcore->per_breaking_event_addr = cpu_to_be64(env->gbea);
|
||||
|
||||
cpu_unmap_lowcore(lowcore);
|
||||
|
||||
DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__,
|
||||
env->int_pgm_code, ilen, env->psw.mask,
|
||||
env->psw.addr);
|
||||
|
||||
load_psw(env, mask, addr);
|
||||
}
|
||||
|
||||
static void do_svc_interrupt(CPUS390XState *env)
|
||||
{
|
||||
uint64_t mask, addr;
|
||||
LowCore *lowcore;
|
||||
|
||||
lowcore = cpu_map_lowcore(env);
|
||||
|
||||
lowcore->svc_code = cpu_to_be16(env->int_svc_code);
|
||||
lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen);
|
||||
lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
|
||||
lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen);
|
||||
mask = be64_to_cpu(lowcore->svc_new_psw.mask);
|
||||
addr = be64_to_cpu(lowcore->svc_new_psw.addr);
|
||||
|
||||
cpu_unmap_lowcore(lowcore);
|
||||
|
||||
load_psw(env, mask, addr);
|
||||
|
||||
/* When a PER event is pending, the PER exception has to happen
|
||||
immediately after the SERVICE CALL one. */
|
||||
if (env->per_perc_atmid) {
|
||||
env->int_pgm_code = PGM_PER;
|
||||
env->int_pgm_ilen = env->int_svc_ilen;
|
||||
do_program_interrupt(env);
|
||||
}
|
||||
}
|
||||
|
||||
#define VIRTIO_SUBCODE_64 0x0D00
|
||||
|
||||
static void do_ext_interrupt(CPUS390XState *env)
|
||||
{
|
||||
S390CPU *cpu = s390_env_get_cpu(env);
|
||||
uint64_t mask, addr;
|
||||
LowCore *lowcore;
|
||||
ExtQueue *q;
|
||||
|
||||
if (!(env->psw.mask & PSW_MASK_EXT)) {
|
||||
cpu_abort(CPU(cpu), "Ext int w/o ext mask\n");
|
||||
}
|
||||
|
||||
if (env->ext_index < 0 || env->ext_index >= MAX_EXT_QUEUE) {
|
||||
cpu_abort(CPU(cpu), "Ext queue overrun: %d\n", env->ext_index);
|
||||
}
|
||||
|
||||
q = &env->ext_queue[env->ext_index];
|
||||
lowcore = cpu_map_lowcore(env);
|
||||
|
||||
lowcore->ext_int_code = cpu_to_be16(q->code);
|
||||
lowcore->ext_params = cpu_to_be32(q->param);
|
||||
lowcore->ext_params2 = cpu_to_be64(q->param64);
|
||||
lowcore->external_old_psw.mask = cpu_to_be64(get_psw_mask(env));
|
||||
lowcore->external_old_psw.addr = cpu_to_be64(env->psw.addr);
|
||||
lowcore->cpu_addr = cpu_to_be16(env->cpu_num | VIRTIO_SUBCODE_64);
|
||||
mask = be64_to_cpu(lowcore->external_new_psw.mask);
|
||||
addr = be64_to_cpu(lowcore->external_new_psw.addr);
|
||||
|
||||
cpu_unmap_lowcore(lowcore);
|
||||
|
||||
env->ext_index--;
|
||||
if (env->ext_index == -1) {
|
||||
env->pending_int &= ~INTERRUPT_EXT;
|
||||
}
|
||||
|
||||
DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
|
||||
env->psw.mask, env->psw.addr);
|
||||
|
||||
load_psw(env, mask, addr);
|
||||
}
|
||||
|
||||
static void do_io_interrupt(CPUS390XState *env)
|
||||
{
|
||||
S390CPU *cpu = s390_env_get_cpu(env);
|
||||
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");
|
||||
}
|
||||
|
||||
for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) {
|
||||
uint64_t isc_bits;
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void do_mchk_interrupt(CPUS390XState *env)
|
||||
{
|
||||
S390CPU *cpu = s390_env_get_cpu(env);
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
lowcore = cpu_map_lowcore(env);
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
lowcore->floating_pt_save_area[i] = cpu_to_be64(get_freg(env, i)->ll);
|
||||
lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]);
|
||||
lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]);
|
||||
lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]);
|
||||
}
|
||||
lowcore->prefixreg_save_area = cpu_to_be32(env->psa);
|
||||
lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc);
|
||||
lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr);
|
||||
lowcore->cpu_timer_save_area[0] = cpu_to_be32(env->cputm >> 32);
|
||||
lowcore->cpu_timer_save_area[1] = cpu_to_be32((uint32_t)env->cputm);
|
||||
lowcore->clock_comp_save_area[0] = cpu_to_be32(env->ckc >> 32);
|
||||
lowcore->clock_comp_save_area[1] = cpu_to_be32((uint32_t)env->ckc);
|
||||
|
||||
lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d);
|
||||
lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000);
|
||||
lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env));
|
||||
lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr);
|
||||
mask = be64_to_cpu(lowcore->mcck_new_psw.mask);
|
||||
addr = be64_to_cpu(lowcore->mcck_new_psw.addr);
|
||||
|
||||
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);
|
||||
|
||||
load_psw(env, mask, addr);
|
||||
}
|
||||
|
||||
void s390_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n",
|
||||
__func__, cs->exception_index, env->psw.addr);
|
||||
|
||||
s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
|
||||
/* handle machine checks */
|
||||
if ((env->psw.mask & PSW_MASK_MCHECK) &&
|
||||
(cs->exception_index == -1)) {
|
||||
if (env->pending_int & INTERRUPT_MCHK) {
|
||||
cs->exception_index = EXCP_MCHK;
|
||||
}
|
||||
}
|
||||
/* handle external interrupts */
|
||||
if ((env->psw.mask & PSW_MASK_EXT) &&
|
||||
cs->exception_index == -1) {
|
||||
if (env->pending_int & INTERRUPT_EXT) {
|
||||
/* code is already in env */
|
||||
cs->exception_index = EXCP_EXT;
|
||||
} else if (env->pending_int & INTERRUPT_TOD) {
|
||||
cpu_inject_ext(cpu, 0x1004, 0, 0);
|
||||
cs->exception_index = EXCP_EXT;
|
||||
env->pending_int &= ~INTERRUPT_EXT;
|
||||
env->pending_int &= ~INTERRUPT_TOD;
|
||||
} else if (env->pending_int & INTERRUPT_CPUTIMER) {
|
||||
cpu_inject_ext(cpu, 0x1005, 0, 0);
|
||||
cs->exception_index = EXCP_EXT;
|
||||
env->pending_int &= ~INTERRUPT_EXT;
|
||||
env->pending_int &= ~INTERRUPT_TOD;
|
||||
}
|
||||
}
|
||||
/* handle I/O interrupts */
|
||||
if ((env->psw.mask & PSW_MASK_IO) &&
|
||||
(cs->exception_index == -1)) {
|
||||
if (env->pending_int & INTERRUPT_IO) {
|
||||
cs->exception_index = EXCP_IO;
|
||||
}
|
||||
}
|
||||
|
||||
switch (cs->exception_index) {
|
||||
case EXCP_PGM:
|
||||
do_program_interrupt(env);
|
||||
break;
|
||||
case EXCP_SVC:
|
||||
do_svc_interrupt(env);
|
||||
break;
|
||||
case EXCP_EXT:
|
||||
do_ext_interrupt(env);
|
||||
break;
|
||||
case EXCP_IO:
|
||||
do_io_interrupt(env);
|
||||
break;
|
||||
case EXCP_MCHK:
|
||||
do_mchk_interrupt(env);
|
||||
break;
|
||||
}
|
||||
cs->exception_index = -1;
|
||||
|
||||
if (!env->pending_int) {
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
|
||||
}
|
||||
}
|
||||
|
||||
bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
|
||||
if (env->ex_value) {
|
||||
/* Execution of the target insn is indivisible from
|
||||
the parent EXECUTE insn. */
|
||||
return false;
|
||||
}
|
||||
if (env->psw.mask & PSW_MASK_EXT) {
|
||||
s390_cpu_do_interrupt(cs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void s390x_cpu_debug_excp_handler(CPUState *cs)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
CPUWatchpoint *wp_hit = cs->watchpoint_hit;
|
||||
|
||||
if (wp_hit && wp_hit->flags & BP_CPU) {
|
||||
/* FIXME: When the storage-alteration-space control bit is set,
|
||||
the exception should only be triggered if the memory access
|
||||
is done using an address space with the storage-alteration-event
|
||||
bit set. We have no way to detect that with the current
|
||||
watchpoint code. */
|
||||
cs->watchpoint_hit = NULL;
|
||||
|
||||
env->per_address = env->psw.addr;
|
||||
env->per_perc_atmid |= PER_CODE_EVENT_STORE | get_per_atmid(env);
|
||||
/* FIXME: We currently no way to detect the address space used
|
||||
to trigger the watchpoint. For now just consider it is the
|
||||
current default ASC. This turn to be true except when MVCP
|
||||
and MVCS instrutions are not used. */
|
||||
env->per_perc_atmid |= env->psw.mask & (PSW_MASK_ASC) >> 46;
|
||||
|
||||
/* Remove all watchpoints to re-execute the code. A PER exception
|
||||
will be triggered, it will call load_psw which will recompute
|
||||
the watchpoints. */
|
||||
cpu_watchpoint_remove_all(cs, BP_CPU);
|
||||
cpu_loop_exit_noexc(cs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Unaligned accesses are only diagnosed with MO_ALIGN. At the moment,
|
||||
this is only for the atomic operations, for which we want to raise a
|
||||
specification exception. */
|
||||
void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
|
||||
MMUAccessType access_type,
|
||||
int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
|
||||
if (retaddr) {
|
||||
cpu_restore_state(cs, retaddr);
|
||||
}
|
||||
program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USER_ONLY */
|
|
@ -535,7 +535,6 @@ static uint16_t default_GEN14_GA1[] = {
|
|||
S390_FEAT_GROUP_MSA_EXT_6,
|
||||
S390_FEAT_GROUP_MSA_EXT_7,
|
||||
S390_FEAT_GROUP_MSA_EXT_8,
|
||||
S390_FEAT_SIE_KSS,
|
||||
};
|
||||
|
||||
/****** END FEATURE DEFS ******/
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "exec/gdbstub.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "hw/s390x/ioinst.h"
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
#include "sysemu/sysemu.h"
|
||||
|
@ -143,79 +142,7 @@ S390CPU *cpu_s390x_init(const char *cpu_model)
|
|||
return cpu;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
|
||||
void s390_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
cs->exception_index = -1;
|
||||
}
|
||||
|
||||
int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
|
||||
int rw, int mmu_idx)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
|
||||
cs->exception_index = EXCP_PGM;
|
||||
cpu->env.int_pgm_code = PGM_ADDRESSING;
|
||||
/* On real machines this value is dropped into LowMem. Since this
|
||||
is userland, simply put this someplace that cpu_loop can find it. */
|
||||
cpu->env.__excp_addr = address;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_USER_ONLY */
|
||||
|
||||
/* Ensure to exit the TB after this call! */
|
||||
void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen)
|
||||
{
|
||||
CPUState *cs = CPU(s390_env_get_cpu(env));
|
||||
|
||||
cs->exception_index = EXCP_PGM;
|
||||
env->int_pgm_code = code;
|
||||
env->int_pgm_ilen = ilen;
|
||||
}
|
||||
|
||||
int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr,
|
||||
int rw, int mmu_idx)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
uint64_t asc = cpu_mmu_idx_to_asc(mmu_idx);
|
||||
target_ulong vaddr, raddr;
|
||||
int prot;
|
||||
|
||||
DPRINTF("%s: address 0x%" VADDR_PRIx " rw %d mmu_idx %d\n",
|
||||
__func__, orig_vaddr, rw, mmu_idx);
|
||||
|
||||
orig_vaddr &= TARGET_PAGE_MASK;
|
||||
vaddr = orig_vaddr;
|
||||
|
||||
/* 31-Bit mode */
|
||||
if (!(env->psw.mask & PSW_MASK_64)) {
|
||||
vaddr &= 0x7fffffff;
|
||||
}
|
||||
|
||||
if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot, true)) {
|
||||
/* Translation ended in exception */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* check out of RAM access */
|
||||
if (raddr > ram_size) {
|
||||
DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__,
|
||||
(uint64_t)raddr, (uint64_t)ram_size);
|
||||
trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_AUTO);
|
||||
return 1;
|
||||
}
|
||||
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n",
|
||||
__func__, (uint64_t)vaddr, (uint64_t)raddr, prot);
|
||||
|
||||
tlb_set_page(cs, orig_vaddr, raddr, prot,
|
||||
mmu_idx, TARGET_PAGE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr)
|
||||
{
|
||||
|
@ -272,7 +199,7 @@ void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr)
|
|||
}
|
||||
}
|
||||
|
||||
static uint64_t get_psw_mask(CPUS390XState *env)
|
||||
uint64_t get_psw_mask(CPUS390XState *env)
|
||||
{
|
||||
uint64_t r = env->psw.mask;
|
||||
|
||||
|
@ -288,7 +215,7 @@ static uint64_t get_psw_mask(CPUS390XState *env)
|
|||
return r;
|
||||
}
|
||||
|
||||
static LowCore *cpu_map_lowcore(CPUS390XState *env)
|
||||
LowCore *cpu_map_lowcore(CPUS390XState *env)
|
||||
{
|
||||
S390CPU *cpu = s390_env_get_cpu(env);
|
||||
LowCore *lowcore;
|
||||
|
@ -303,7 +230,7 @@ static LowCore *cpu_map_lowcore(CPUS390XState *env)
|
|||
return lowcore;
|
||||
}
|
||||
|
||||
static void cpu_unmap_lowcore(LowCore *lowcore)
|
||||
void cpu_unmap_lowcore(LowCore *lowcore)
|
||||
{
|
||||
cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore));
|
||||
}
|
||||
|
@ -325,362 +252,6 @@ void do_restart_interrupt(CPUS390XState *env)
|
|||
load_psw(env, mask, addr);
|
||||
}
|
||||
|
||||
static void do_program_interrupt(CPUS390XState *env)
|
||||
{
|
||||
uint64_t mask, addr;
|
||||
LowCore *lowcore;
|
||||
int ilen = env->int_pgm_ilen;
|
||||
|
||||
if (ilen == ILEN_AUTO) {
|
||||
ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
|
||||
}
|
||||
assert(ilen == 2 || ilen == 4 || ilen == 6);
|
||||
|
||||
switch (env->int_pgm_code) {
|
||||
case PGM_PER:
|
||||
if (env->per_perc_atmid & PER_CODE_EVENT_NULLIFICATION) {
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
case PGM_OPERATION:
|
||||
case PGM_PRIVILEGED:
|
||||
case PGM_EXECUTE:
|
||||
case PGM_PROTECTION:
|
||||
case PGM_ADDRESSING:
|
||||
case PGM_SPECIFICATION:
|
||||
case PGM_DATA:
|
||||
case PGM_FIXPT_OVERFLOW:
|
||||
case PGM_FIXPT_DIVIDE:
|
||||
case PGM_DEC_OVERFLOW:
|
||||
case PGM_DEC_DIVIDE:
|
||||
case PGM_HFP_EXP_OVERFLOW:
|
||||
case PGM_HFP_EXP_UNDERFLOW:
|
||||
case PGM_HFP_SIGNIFICANCE:
|
||||
case PGM_HFP_DIVIDE:
|
||||
case PGM_TRANS_SPEC:
|
||||
case PGM_SPECIAL_OP:
|
||||
case PGM_OPERAND:
|
||||
case PGM_HFP_SQRT:
|
||||
case PGM_PC_TRANS_SPEC:
|
||||
case PGM_ALET_SPEC:
|
||||
case PGM_MONITOR:
|
||||
/* advance the PSW if our exception is not nullifying */
|
||||
env->psw.addr += ilen;
|
||||
break;
|
||||
}
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n",
|
||||
__func__, env->int_pgm_code, ilen);
|
||||
|
||||
lowcore = cpu_map_lowcore(env);
|
||||
|
||||
/* Signal PER events with the exception. */
|
||||
if (env->per_perc_atmid) {
|
||||
env->int_pgm_code |= PGM_PER;
|
||||
lowcore->per_address = cpu_to_be64(env->per_address);
|
||||
lowcore->per_perc_atmid = cpu_to_be16(env->per_perc_atmid);
|
||||
env->per_perc_atmid = 0;
|
||||
}
|
||||
|
||||
lowcore->pgm_ilen = cpu_to_be16(ilen);
|
||||
lowcore->pgm_code = cpu_to_be16(env->int_pgm_code);
|
||||
lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env));
|
||||
lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
|
||||
mask = be64_to_cpu(lowcore->program_new_psw.mask);
|
||||
addr = be64_to_cpu(lowcore->program_new_psw.addr);
|
||||
lowcore->per_breaking_event_addr = cpu_to_be64(env->gbea);
|
||||
|
||||
cpu_unmap_lowcore(lowcore);
|
||||
|
||||
DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__,
|
||||
env->int_pgm_code, ilen, env->psw.mask,
|
||||
env->psw.addr);
|
||||
|
||||
load_psw(env, mask, addr);
|
||||
}
|
||||
|
||||
static void do_svc_interrupt(CPUS390XState *env)
|
||||
{
|
||||
uint64_t mask, addr;
|
||||
LowCore *lowcore;
|
||||
|
||||
lowcore = cpu_map_lowcore(env);
|
||||
|
||||
lowcore->svc_code = cpu_to_be16(env->int_svc_code);
|
||||
lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen);
|
||||
lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
|
||||
lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen);
|
||||
mask = be64_to_cpu(lowcore->svc_new_psw.mask);
|
||||
addr = be64_to_cpu(lowcore->svc_new_psw.addr);
|
||||
|
||||
cpu_unmap_lowcore(lowcore);
|
||||
|
||||
load_psw(env, mask, addr);
|
||||
|
||||
/* When a PER event is pending, the PER exception has to happen
|
||||
immediately after the SERVICE CALL one. */
|
||||
if (env->per_perc_atmid) {
|
||||
env->int_pgm_code = PGM_PER;
|
||||
env->int_pgm_ilen = env->int_svc_ilen;
|
||||
do_program_interrupt(env);
|
||||
}
|
||||
}
|
||||
|
||||
#define VIRTIO_SUBCODE_64 0x0D00
|
||||
|
||||
static void do_ext_interrupt(CPUS390XState *env)
|
||||
{
|
||||
S390CPU *cpu = s390_env_get_cpu(env);
|
||||
uint64_t mask, addr;
|
||||
LowCore *lowcore;
|
||||
ExtQueue *q;
|
||||
|
||||
if (!(env->psw.mask & PSW_MASK_EXT)) {
|
||||
cpu_abort(CPU(cpu), "Ext int w/o ext mask\n");
|
||||
}
|
||||
|
||||
if (env->ext_index < 0 || env->ext_index >= MAX_EXT_QUEUE) {
|
||||
cpu_abort(CPU(cpu), "Ext queue overrun: %d\n", env->ext_index);
|
||||
}
|
||||
|
||||
q = &env->ext_queue[env->ext_index];
|
||||
lowcore = cpu_map_lowcore(env);
|
||||
|
||||
lowcore->ext_int_code = cpu_to_be16(q->code);
|
||||
lowcore->ext_params = cpu_to_be32(q->param);
|
||||
lowcore->ext_params2 = cpu_to_be64(q->param64);
|
||||
lowcore->external_old_psw.mask = cpu_to_be64(get_psw_mask(env));
|
||||
lowcore->external_old_psw.addr = cpu_to_be64(env->psw.addr);
|
||||
lowcore->cpu_addr = cpu_to_be16(env->cpu_num | VIRTIO_SUBCODE_64);
|
||||
mask = be64_to_cpu(lowcore->external_new_psw.mask);
|
||||
addr = be64_to_cpu(lowcore->external_new_psw.addr);
|
||||
|
||||
cpu_unmap_lowcore(lowcore);
|
||||
|
||||
env->ext_index--;
|
||||
if (env->ext_index == -1) {
|
||||
env->pending_int &= ~INTERRUPT_EXT;
|
||||
}
|
||||
|
||||
DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
|
||||
env->psw.mask, env->psw.addr);
|
||||
|
||||
load_psw(env, mask, addr);
|
||||
}
|
||||
|
||||
static void do_io_interrupt(CPUS390XState *env)
|
||||
{
|
||||
S390CPU *cpu = s390_env_get_cpu(env);
|
||||
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");
|
||||
}
|
||||
|
||||
for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) {
|
||||
uint64_t isc_bits;
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void do_mchk_interrupt(CPUS390XState *env)
|
||||
{
|
||||
S390CPU *cpu = s390_env_get_cpu(env);
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
lowcore = cpu_map_lowcore(env);
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
lowcore->floating_pt_save_area[i] = cpu_to_be64(get_freg(env, i)->ll);
|
||||
lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]);
|
||||
lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]);
|
||||
lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]);
|
||||
}
|
||||
lowcore->prefixreg_save_area = cpu_to_be32(env->psa);
|
||||
lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc);
|
||||
lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr);
|
||||
lowcore->cpu_timer_save_area[0] = cpu_to_be32(env->cputm >> 32);
|
||||
lowcore->cpu_timer_save_area[1] = cpu_to_be32((uint32_t)env->cputm);
|
||||
lowcore->clock_comp_save_area[0] = cpu_to_be32(env->ckc >> 32);
|
||||
lowcore->clock_comp_save_area[1] = cpu_to_be32((uint32_t)env->ckc);
|
||||
|
||||
lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d);
|
||||
lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000);
|
||||
lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env));
|
||||
lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr);
|
||||
mask = be64_to_cpu(lowcore->mcck_new_psw.mask);
|
||||
addr = be64_to_cpu(lowcore->mcck_new_psw.addr);
|
||||
|
||||
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);
|
||||
|
||||
load_psw(env, mask, addr);
|
||||
}
|
||||
|
||||
void s390_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n",
|
||||
__func__, cs->exception_index, env->psw.addr);
|
||||
|
||||
s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
|
||||
/* handle machine checks */
|
||||
if ((env->psw.mask & PSW_MASK_MCHECK) &&
|
||||
(cs->exception_index == -1)) {
|
||||
if (env->pending_int & INTERRUPT_MCHK) {
|
||||
cs->exception_index = EXCP_MCHK;
|
||||
}
|
||||
}
|
||||
/* handle external interrupts */
|
||||
if ((env->psw.mask & PSW_MASK_EXT) &&
|
||||
cs->exception_index == -1) {
|
||||
if (env->pending_int & INTERRUPT_EXT) {
|
||||
/* code is already in env */
|
||||
cs->exception_index = EXCP_EXT;
|
||||
} else if (env->pending_int & INTERRUPT_TOD) {
|
||||
cpu_inject_ext(cpu, 0x1004, 0, 0);
|
||||
cs->exception_index = EXCP_EXT;
|
||||
env->pending_int &= ~INTERRUPT_EXT;
|
||||
env->pending_int &= ~INTERRUPT_TOD;
|
||||
} else if (env->pending_int & INTERRUPT_CPUTIMER) {
|
||||
cpu_inject_ext(cpu, 0x1005, 0, 0);
|
||||
cs->exception_index = EXCP_EXT;
|
||||
env->pending_int &= ~INTERRUPT_EXT;
|
||||
env->pending_int &= ~INTERRUPT_TOD;
|
||||
}
|
||||
}
|
||||
/* handle I/O interrupts */
|
||||
if ((env->psw.mask & PSW_MASK_IO) &&
|
||||
(cs->exception_index == -1)) {
|
||||
if (env->pending_int & INTERRUPT_IO) {
|
||||
cs->exception_index = EXCP_IO;
|
||||
}
|
||||
}
|
||||
|
||||
switch (cs->exception_index) {
|
||||
case EXCP_PGM:
|
||||
do_program_interrupt(env);
|
||||
break;
|
||||
case EXCP_SVC:
|
||||
do_svc_interrupt(env);
|
||||
break;
|
||||
case EXCP_EXT:
|
||||
do_ext_interrupt(env);
|
||||
break;
|
||||
case EXCP_IO:
|
||||
do_io_interrupt(env);
|
||||
break;
|
||||
case EXCP_MCHK:
|
||||
do_mchk_interrupt(env);
|
||||
break;
|
||||
}
|
||||
cs->exception_index = -1;
|
||||
|
||||
if (!env->pending_int) {
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
|
||||
}
|
||||
}
|
||||
|
||||
bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
|
||||
if (env->ex_value) {
|
||||
/* Execution of the target insn is indivisible from
|
||||
the parent EXECUTE insn. */
|
||||
return false;
|
||||
}
|
||||
if (env->psw.mask & PSW_MASK_EXT) {
|
||||
s390_cpu_do_interrupt(cs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void s390_cpu_recompute_watchpoints(CPUState *cs)
|
||||
{
|
||||
const int wp_flags = BP_CPU | BP_MEM_WRITE | BP_STOP_BEFORE_ACCESS;
|
||||
|
@ -720,49 +291,64 @@ void s390_cpu_recompute_watchpoints(CPUState *cs)
|
|||
}
|
||||
}
|
||||
|
||||
void s390x_cpu_debug_excp_handler(CPUState *cs)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
CPUWatchpoint *wp_hit = cs->watchpoint_hit;
|
||||
|
||||
if (wp_hit && wp_hit->flags & BP_CPU) {
|
||||
/* FIXME: When the storage-alteration-space control bit is set,
|
||||
the exception should only be triggered if the memory access
|
||||
is done using an address space with the storage-alteration-event
|
||||
bit set. We have no way to detect that with the current
|
||||
watchpoint code. */
|
||||
cs->watchpoint_hit = NULL;
|
||||
|
||||
env->per_address = env->psw.addr;
|
||||
env->per_perc_atmid |= PER_CODE_EVENT_STORE | get_per_atmid(env);
|
||||
/* FIXME: We currently no way to detect the address space used
|
||||
to trigger the watchpoint. For now just consider it is the
|
||||
current default ASC. This turn to be true except when MVCP
|
||||
and MVCS instrutions are not used. */
|
||||
env->per_perc_atmid |= env->psw.mask & (PSW_MASK_ASC) >> 46;
|
||||
|
||||
/* Remove all watchpoints to re-execute the code. A PER exception
|
||||
will be triggered, it will call load_psw which will recompute
|
||||
the watchpoints. */
|
||||
cpu_watchpoint_remove_all(cs, BP_CPU);
|
||||
cpu_loop_exit_noexc(cs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Unaligned accesses are only diagnosed with MO_ALIGN. At the moment,
|
||||
this is only for the atomic operations, for which we want to raise a
|
||||
specification exception. */
|
||||
void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
|
||||
MMUAccessType access_type,
|
||||
int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
|
||||
if (retaddr) {
|
||||
cpu_restore_state(cs, retaddr);
|
||||
}
|
||||
program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO);
|
||||
}
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
void s390_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
|
||||
int flags)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
int i;
|
||||
|
||||
if (env->cc_op > 3) {
|
||||
cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %15s\n",
|
||||
env->psw.mask, env->psw.addr, cc_name(env->cc_op));
|
||||
} else {
|
||||
cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %02x\n",
|
||||
env->psw.mask, env->psw.addr, env->cc_op);
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
cpu_fprintf(f, "R%02d=%016" PRIx64, i, env->regs[i]);
|
||||
if ((i % 4) == 3) {
|
||||
cpu_fprintf(f, "\n");
|
||||
} else {
|
||||
cpu_fprintf(f, " ");
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
cpu_fprintf(f, "F%02d=%016" PRIx64, i, get_freg(env, i)->ll);
|
||||
if ((i % 4) == 3) {
|
||||
cpu_fprintf(f, "\n");
|
||||
} else {
|
||||
cpu_fprintf(f, " ");
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
cpu_fprintf(f, "V%02d=%016" PRIx64 "%016" PRIx64, i,
|
||||
env->vregs[i][0].ll, env->vregs[i][1].ll);
|
||||
cpu_fprintf(f, (i % 2) ? "\n" : " ");
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
for (i = 0; i < 16; i++) {
|
||||
cpu_fprintf(f, "C%02d=%016" PRIx64, i, env->cregs[i]);
|
||||
if ((i % 4) == 3) {
|
||||
cpu_fprintf(f, "\n");
|
||||
} else {
|
||||
cpu_fprintf(f, " ");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_INLINE_BRANCHES
|
||||
for (i = 0; i < CC_OP_MAX; i++) {
|
||||
cpu_fprintf(f, " %15s = %10ld\t%10ld\n", cc_name(i),
|
||||
inline_branch_miss[i], inline_branch_hit[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
cpu_fprintf(f, "\n");
|
||||
}
|
||||
|
|
|
@ -8,10 +8,49 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "hw/s390x/ioinst.h"
|
||||
|
||||
/* Ensure to exit the TB after this call! */
|
||||
void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen)
|
||||
{
|
||||
CPUState *cs = CPU(s390_env_get_cpu(env));
|
||||
|
||||
cs->exception_index = EXCP_PGM;
|
||||
env->int_pgm_code = code;
|
||||
env->int_pgm_ilen = ilen;
|
||||
}
|
||||
|
||||
static void tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code,
|
||||
int ilen)
|
||||
{
|
||||
#ifdef CONFIG_TCG
|
||||
trigger_pgm_exception(env, code, ilen);
|
||||
cpu_loop_exit(CPU(s390_env_get_cpu(env)));
|
||||
#else
|
||||
g_assert_not_reached();
|
||||
#endif
|
||||
}
|
||||
|
||||
void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
|
||||
{
|
||||
S390CPU *cpu = s390_env_get_cpu(env);
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
|
||||
env->psw.addr);
|
||||
|
||||
if (kvm_enabled()) {
|
||||
kvm_s390_program_interrupt(cpu, code);
|
||||
} else if (tcg_enabled()) {
|
||||
tcg_s390_program_interrupt(env, code, ilen);
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void cpu_inject_ext(S390CPU *cpu, uint32_t code, uint32_t param,
|
||||
uint64_t param64)
|
||||
|
|
|
@ -45,7 +45,7 @@ void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1)
|
|||
int cc;
|
||||
|
||||
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
|
||||
program_interrupt(&cpu->env, PGM_OPERAND, 2);
|
||||
program_interrupt(&cpu->env, PGM_OPERAND, 4);
|
||||
return;
|
||||
}
|
||||
trace_ioinst_sch_id("xsch", cssid, ssid, schid);
|
||||
|
@ -78,7 +78,7 @@ void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1)
|
|||
int cc;
|
||||
|
||||
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
|
||||
program_interrupt(&cpu->env, PGM_OPERAND, 2);
|
||||
program_interrupt(&cpu->env, PGM_OPERAND, 4);
|
||||
return;
|
||||
}
|
||||
trace_ioinst_sch_id("csch", cssid, ssid, schid);
|
||||
|
@ -102,7 +102,7 @@ void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1)
|
|||
int cc;
|
||||
|
||||
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
|
||||
program_interrupt(&cpu->env, PGM_OPERAND, 2);
|
||||
program_interrupt(&cpu->env, PGM_OPERAND, 4);
|
||||
return;
|
||||
}
|
||||
trace_ioinst_sch_id("hsch", cssid, ssid, schid);
|
||||
|
@ -153,7 +153,7 @@ void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
|
|||
|
||||
addr = decode_basedisp_s(env, ipb, &ar);
|
||||
if (addr & 3) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, 2);
|
||||
program_interrupt(env, PGM_SPECIFICATION, 4);
|
||||
return;
|
||||
}
|
||||
if (s390_cpu_virt_mem_read(cpu, addr, ar, &schib, sizeof(schib))) {
|
||||
|
@ -161,7 +161,7 @@ void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
|
|||
}
|
||||
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) ||
|
||||
!ioinst_schib_valid(&schib)) {
|
||||
program_interrupt(env, PGM_OPERAND, 2);
|
||||
program_interrupt(env, PGM_OPERAND, 4);
|
||||
return;
|
||||
}
|
||||
trace_ioinst_sch_id("msch", cssid, ssid, schid);
|
||||
|
@ -224,7 +224,7 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
|
|||
|
||||
addr = decode_basedisp_s(env, ipb, &ar);
|
||||
if (addr & 3) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, 2);
|
||||
program_interrupt(env, PGM_SPECIFICATION, 4);
|
||||
return;
|
||||
}
|
||||
if (s390_cpu_virt_mem_read(cpu, addr, ar, &orig_orb, sizeof(orb))) {
|
||||
|
@ -233,7 +233,7 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
|
|||
copy_orb_from_guest(&orb, &orig_orb);
|
||||
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) ||
|
||||
!ioinst_orb_valid(&orb)) {
|
||||
program_interrupt(env, PGM_OPERAND, 2);
|
||||
program_interrupt(env, PGM_OPERAND, 4);
|
||||
return;
|
||||
}
|
||||
trace_ioinst_sch_id("ssch", cssid, ssid, schid);
|
||||
|
@ -277,7 +277,7 @@ void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb)
|
|||
|
||||
addr = decode_basedisp_s(env, ipb, &ar);
|
||||
if (addr & 3) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, 2);
|
||||
program_interrupt(env, PGM_SPECIFICATION, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -304,7 +304,7 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
|
|||
|
||||
addr = decode_basedisp_s(env, ipb, &ar);
|
||||
if (addr & 3) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, 2);
|
||||
program_interrupt(env, PGM_SPECIFICATION, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -315,7 +315,7 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
|
|||
* access execption if it is not) first.
|
||||
*/
|
||||
if (!s390_cpu_virt_mem_check_write(cpu, addr, ar, sizeof(schib))) {
|
||||
program_interrupt(env, PGM_OPERAND, 2);
|
||||
program_interrupt(env, PGM_OPERAND, 4);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -363,13 +363,13 @@ int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
|
|||
uint8_t ar;
|
||||
|
||||
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
|
||||
program_interrupt(env, PGM_OPERAND, 2);
|
||||
program_interrupt(env, PGM_OPERAND, 4);
|
||||
return -EIO;
|
||||
}
|
||||
trace_ioinst_sch_id("tsch", cssid, ssid, schid);
|
||||
addr = decode_basedisp_s(env, ipb, &ar);
|
||||
if (addr & 3) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, 2);
|
||||
program_interrupt(env, PGM_SPECIFICATION, 4);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -659,7 +659,7 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb)
|
|||
addr = env->regs[reg];
|
||||
/* Page boundary? */
|
||||
if (addr & 0xfff) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, 2);
|
||||
program_interrupt(env, PGM_SPECIFICATION, 4);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
|
@ -674,7 +674,7 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb)
|
|||
len = be16_to_cpu(req->len);
|
||||
/* Length field valid? */
|
||||
if ((len < 16) || (len > 4088) || (len & 7)) {
|
||||
program_interrupt(env, PGM_OPERAND, 2);
|
||||
program_interrupt(env, PGM_OPERAND, 4);
|
||||
return;
|
||||
}
|
||||
memset((char *)req + len, 0, TARGET_PAGE_SIZE - len);
|
||||
|
@ -718,7 +718,7 @@ int ioinst_handle_tpi(S390CPU *cpu, uint32_t ipb)
|
|||
trace_ioinst("tpi");
|
||||
addr = decode_basedisp_s(env, ipb, &ar);
|
||||
if (addr & 3) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, 2);
|
||||
program_interrupt(env, PGM_SPECIFICATION, 4);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -747,7 +747,7 @@ void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2,
|
|||
trace_ioinst("schm");
|
||||
|
||||
if (SCHM_REG1_RES(reg1)) {
|
||||
program_interrupt(env, PGM_OPERAND, 2);
|
||||
program_interrupt(env, PGM_OPERAND, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -756,7 +756,7 @@ void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2,
|
|||
dct = SCHM_REG1_DCT(reg1);
|
||||
|
||||
if (update && (reg2 & 0x000000000000001f)) {
|
||||
program_interrupt(env, PGM_OPERAND, 2);
|
||||
program_interrupt(env, PGM_OPERAND, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -771,7 +771,7 @@ void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1)
|
|||
int cc;
|
||||
|
||||
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
|
||||
program_interrupt(&cpu->env, PGM_OPERAND, 2);
|
||||
program_interrupt(&cpu->env, PGM_OPERAND, 4);
|
||||
return;
|
||||
}
|
||||
trace_ioinst_sch_id("rsch", cssid, ssid, schid);
|
||||
|
@ -808,7 +808,7 @@ void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1)
|
|||
CPUS390XState *env = &cpu->env;
|
||||
|
||||
if (RCHP_REG1_RES(reg1)) {
|
||||
program_interrupt(env, PGM_OPERAND, 2);
|
||||
program_interrupt(env, PGM_OPERAND, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -831,7 +831,7 @@ void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1)
|
|||
break;
|
||||
default:
|
||||
/* Invalid channel subsystem. */
|
||||
program_interrupt(env, PGM_OPERAND, 2);
|
||||
program_interrupt(env, PGM_OPERAND, 4);
|
||||
return;
|
||||
}
|
||||
setcc(cpu, cc);
|
||||
|
@ -842,6 +842,6 @@ void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1)
|
|||
{
|
||||
/* We do not provide address limit checking, so let's suppress it. */
|
||||
if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) {
|
||||
program_interrupt(&cpu->env, PGM_OPERAND, 2);
|
||||
program_interrupt(&cpu->env, PGM_OPERAND, 4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1032,7 +1032,7 @@ void kvm_s390_service_interrupt(uint32_t parm)
|
|||
kvm_s390_floating_interrupt(&irq);
|
||||
}
|
||||
|
||||
static void enter_pgmcheck(S390CPU *cpu, uint16_t code)
|
||||
void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code)
|
||||
{
|
||||
struct kvm_s390_irq irq = {
|
||||
.type = KVM_S390_PROGRAM_INT,
|
||||
|
@ -1068,7 +1068,7 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run,
|
|||
|
||||
r = sclp_service_call(env, sccb, code);
|
||||
if (r < 0) {
|
||||
enter_pgmcheck(cpu, -r);
|
||||
kvm_s390_program_interrupt(cpu, -r);
|
||||
} else {
|
||||
setcc(cpu, r);
|
||||
}
|
||||
|
@ -1236,7 +1236,7 @@ static int kvm_sic_service_call(S390CPU *cpu, struct kvm_run *run)
|
|||
isc = (env->regs[r3] >> 27) & 0x7;
|
||||
r = css_do_sic(env, isc, mode);
|
||||
if (r) {
|
||||
enter_pgmcheck(cpu, -r);
|
||||
kvm_s390_program_interrupt(cpu, -r);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1357,7 +1357,7 @@ static int handle_hypercall(S390CPU *cpu, struct kvm_run *run)
|
|||
cpu_synchronize_state(CPU(cpu));
|
||||
ret = s390_virtio_hypercall(env);
|
||||
if (ret == -EINVAL) {
|
||||
enter_pgmcheck(cpu, PGM_SPECIFICATION);
|
||||
kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1374,7 +1374,7 @@ static void kvm_handle_diag_288(S390CPU *cpu, struct kvm_run *run)
|
|||
r3 = run->s390_sieic.ipa & 0x000f;
|
||||
rc = handle_diag_288(&cpu->env, r1, r3);
|
||||
if (rc) {
|
||||
enter_pgmcheck(cpu, PGM_SPECIFICATION);
|
||||
kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1431,7 +1431,7 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
|
|||
break;
|
||||
default:
|
||||
DPRINTF("KVM: unknown DIAG: 0x%x\n", func_code);
|
||||
enter_pgmcheck(cpu, PGM_SPECIFICATION);
|
||||
kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1899,7 +1899,7 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run)
|
|||
|
||||
if (r < 0) {
|
||||
r = 0;
|
||||
enter_pgmcheck(cpu, 0x0001);
|
||||
kvm_s390_program_interrupt(cpu, PGM_OPERATION);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
@ -2009,7 +2009,7 @@ static int handle_intercept(S390CPU *cpu)
|
|||
/* Then check for potential pgm check loops */
|
||||
r = handle_oper_loop(cpu, run);
|
||||
if (r == 0) {
|
||||
enter_pgmcheck(cpu, PGM_OPERATION);
|
||||
kvm_s390_program_interrupt(cpu, PGM_OPERATION);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -2430,16 +2430,6 @@ int kvm_arch_msi_data_to_gsi(uint32_t data)
|
|||
abort();
|
||||
}
|
||||
|
||||
static inline int test_bit_inv(long nr, const unsigned long *addr)
|
||||
{
|
||||
return test_bit(BE_BIT_NR(nr), addr);
|
||||
}
|
||||
|
||||
static inline void set_bit_inv(long nr, unsigned long *addr)
|
||||
{
|
||||
set_bit(BE_BIT_NR(nr), addr);
|
||||
}
|
||||
|
||||
static int query_cpu_subfunc(S390FeatBitmap features)
|
||||
{
|
||||
struct kvm_s390_vm_cpu_subfunc prop;
|
||||
|
@ -2506,41 +2496,28 @@ static int configure_cpu_subfunc(const S390FeatBitmap features)
|
|||
s390_fill_feat_block(features, S390_FEAT_TYPE_PLO, prop.plo);
|
||||
if (test_bit(S390_FEAT_TOD_CLOCK_STEERING, features)) {
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_PTFF, prop.ptff);
|
||||
prop.ptff[0] |= 0x80; /* query is always available */
|
||||
}
|
||||
if (test_bit(S390_FEAT_MSA, features)) {
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_KMAC, prop.kmac);
|
||||
prop.kmac[0] |= 0x80; /* query is always available */
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_KMC, prop.kmc);
|
||||
prop.kmc[0] |= 0x80; /* query is always available */
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_KM, prop.km);
|
||||
prop.km[0] |= 0x80; /* query is always available */
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_KIMD, prop.kimd);
|
||||
prop.kimd[0] |= 0x80; /* query is always available */
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_KLMD, prop.klmd);
|
||||
prop.klmd[0] |= 0x80; /* query is always available */
|
||||
}
|
||||
if (test_bit(S390_FEAT_MSA_EXT_3, features)) {
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_PCKMO, prop.pckmo);
|
||||
prop.pckmo[0] |= 0x80; /* query is always available */
|
||||
}
|
||||
if (test_bit(S390_FEAT_MSA_EXT_4, features)) {
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_KMCTR, prop.kmctr);
|
||||
prop.kmctr[0] |= 0x80; /* query is always available */
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_KMF, prop.kmf);
|
||||
prop.kmf[0] |= 0x80; /* query is always available */
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_KMO, prop.kmo);
|
||||
prop.kmo[0] |= 0x80; /* query is always available */
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_PCC, prop.pcc);
|
||||
prop.pcc[0] |= 0x80; /* query is always available */
|
||||
}
|
||||
if (test_bit(S390_FEAT_MSA_EXT_5, features)) {
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_PPNO, prop.ppno);
|
||||
prop.ppno[0] |= 0x80; /* query is always available */
|
||||
}
|
||||
if (test_bit(S390_FEAT_MSA_EXT_8, features)) {
|
||||
s390_fill_feat_block(features, S390_FEAT_TYPE_KMA, prop.kma);
|
||||
prop.kma[0] |= 0x80; /* query is always available */
|
||||
}
|
||||
return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr);
|
||||
}
|
||||
|
@ -2579,7 +2556,7 @@ static int query_cpu_feat(S390FeatBitmap features)
|
|||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kvm_to_feat); i++) {
|
||||
if (test_bit_inv(kvm_to_feat[i][0], (unsigned long *)prop.feat)) {
|
||||
if (test_be_bit(kvm_to_feat[i][0], (uint8_t *) prop.feat)) {
|
||||
set_bit(kvm_to_feat[i][1], features);
|
||||
}
|
||||
}
|
||||
|
@ -2598,7 +2575,7 @@ static int configure_cpu_feat(const S390FeatBitmap features)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(kvm_to_feat); i++) {
|
||||
if (test_bit(kvm_to_feat[i][1], features)) {
|
||||
set_bit_inv(kvm_to_feat[i][0], (unsigned long *)prop.feat);
|
||||
set_be_bit(kvm_to_feat[i][0], (uint8_t *) prop.feat);
|
||||
}
|
||||
}
|
||||
return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
@ -2684,7 +2661,7 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
|
|||
clear_bit(S390_FEAT_CMM_NT, model->features);
|
||||
}
|
||||
|
||||
/* set zpci and aen facilities */
|
||||
/* We emulate a zPCI bus and AEN, therefore we don't need HW support */
|
||||
set_bit(S390_FEAT_ZPCI, model->features);
|
||||
set_bit(S390_FEAT_ADAPTER_EVENT_NOTIFICATION, model->features);
|
||||
|
||||
|
|
|
@ -24,22 +24,15 @@
|
|||
#include "exec/memory.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#ifdef CONFIG_KVM
|
||||
#include <linux/kvm.h>
|
||||
#endif
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
#include "hw/watchdog/wdt_diag288.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/s390x/ebcdic.h"
|
||||
#include "hw/s390x/ipl.h"
|
||||
#endif
|
||||
|
||||
/* #define DEBUG_HELPER */
|
||||
|
@ -75,32 +68,6 @@ void HELPER(exception)(CPUS390XState *env, uint32_t excp)
|
|||
cpu_loop_exit(cs);
|
||||
}
|
||||
|
||||
void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
|
||||
{
|
||||
S390CPU *cpu = s390_env_get_cpu(env);
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
|
||||
env->psw.addr);
|
||||
|
||||
if (kvm_enabled()) {
|
||||
#ifdef CONFIG_KVM
|
||||
struct kvm_s390_irq irq = {
|
||||
.type = KVM_S390_PROGRAM_INT,
|
||||
.u.pgm.code = code,
|
||||
};
|
||||
|
||||
kvm_s390_vcpu_interrupt(cpu, &irq);
|
||||
#endif
|
||||
} else {
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
env->int_pgm_code = code;
|
||||
env->int_pgm_ilen = ilen;
|
||||
cs->exception_index = EXCP_PGM;
|
||||
cpu_loop_exit(cs);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
/* SCLP service call */
|
||||
|
@ -116,166 +83,6 @@ uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
|
|||
return r;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static int modified_clear_reset(S390CPU *cpu)
|
||||
{
|
||||
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
|
||||
CPUState *t;
|
||||
|
||||
pause_all_vcpus();
|
||||
cpu_synchronize_all_states();
|
||||
CPU_FOREACH(t) {
|
||||
run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL);
|
||||
}
|
||||
s390_cmma_reset();
|
||||
subsystem_reset();
|
||||
s390_crypto_reset();
|
||||
scc->load_normal(CPU(cpu));
|
||||
cpu_synchronize_all_post_reset();
|
||||
resume_all_vcpus();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_normal_reset(S390CPU *cpu)
|
||||
{
|
||||
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
|
||||
CPUState *t;
|
||||
|
||||
pause_all_vcpus();
|
||||
cpu_synchronize_all_states();
|
||||
CPU_FOREACH(t) {
|
||||
run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL);
|
||||
}
|
||||
s390_cmma_reset();
|
||||
subsystem_reset();
|
||||
scc->initial_cpu_reset(CPU(cpu));
|
||||
scc->load_normal(CPU(cpu));
|
||||
cpu_synchronize_all_post_reset();
|
||||
resume_all_vcpus();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
|
||||
{
|
||||
uint64_t func = env->regs[r1];
|
||||
uint64_t timeout = env->regs[r1 + 1];
|
||||
uint64_t action = env->regs[r3];
|
||||
Object *obj;
|
||||
DIAG288State *diag288;
|
||||
DIAG288Class *diag288_class;
|
||||
|
||||
if (r1 % 2 || action != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Timeout must be more than 15 seconds except for timer deletion */
|
||||
if (func != WDT_DIAG288_CANCEL && timeout < 15) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL);
|
||||
if (!obj) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
diag288 = DIAG288(obj);
|
||||
diag288_class = DIAG288_GET_CLASS(diag288);
|
||||
return diag288_class->handle_timer(diag288, func, timeout);
|
||||
}
|
||||
|
||||
#define DIAG_308_RC_OK 0x0001
|
||||
#define DIAG_308_RC_NO_CONF 0x0102
|
||||
#define DIAG_308_RC_INVALID 0x0402
|
||||
|
||||
void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3)
|
||||
{
|
||||
uint64_t addr = env->regs[r1];
|
||||
uint64_t subcode = env->regs[r3];
|
||||
IplParameterBlock *iplb;
|
||||
|
||||
if (env->psw.mask & PSW_MASK_PSTATE) {
|
||||
program_interrupt(env, PGM_PRIVILEGED, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((subcode & ~0x0ffffULL) || (subcode > 6)) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (subcode) {
|
||||
case 0:
|
||||
modified_clear_reset(s390_env_get_cpu(env));
|
||||
if (tcg_enabled()) {
|
||||
cpu_loop_exit(CPU(s390_env_get_cpu(env)));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
load_normal_reset(s390_env_get_cpu(env));
|
||||
if (tcg_enabled()) {
|
||||
cpu_loop_exit(CPU(s390_env_get_cpu(env)));
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
s390_reipl_request();
|
||||
if (tcg_enabled()) {
|
||||
cpu_loop_exit(CPU(s390_env_get_cpu(env)));
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if ((r1 & 1) || (addr & 0x0fffULL)) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
if (!address_space_access_valid(&address_space_memory, addr,
|
||||
sizeof(IplParameterBlock), false)) {
|
||||
program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
iplb = g_malloc0(sizeof(IplParameterBlock));
|
||||
cpu_physical_memory_read(addr, iplb, sizeof(iplb->len));
|
||||
if (!iplb_valid_len(iplb)) {
|
||||
env->regs[r1 + 1] = DIAG_308_RC_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len));
|
||||
|
||||
if (!iplb_valid_ccw(iplb) && !iplb_valid_fcp(iplb)) {
|
||||
env->regs[r1 + 1] = DIAG_308_RC_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
s390_ipl_update_diag308(iplb);
|
||||
env->regs[r1 + 1] = DIAG_308_RC_OK;
|
||||
out:
|
||||
g_free(iplb);
|
||||
return;
|
||||
case 6:
|
||||
if ((r1 & 1) || (addr & 0x0fffULL)) {
|
||||
program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
if (!address_space_access_valid(&address_space_memory, addr,
|
||||
sizeof(IplParameterBlock), true)) {
|
||||
program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO);
|
||||
return;
|
||||
}
|
||||
iplb = s390_ipl_get_iplb();
|
||||
if (iplb) {
|
||||
cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len));
|
||||
env->regs[r1 + 1] = DIAG_308_RC_OK;
|
||||
} else {
|
||||
env->regs[r1 + 1] = DIAG_308_RC_NO_CONF;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
hw_error("Unhandled diag308 subcode %" PRIx64, subcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num)
|
||||
{
|
||||
uint64_t r;
|
||||
|
|
|
@ -93,66 +93,6 @@ static uint64_t pc_to_link_info(DisasContext *s, uint64_t pc)
|
|||
return pc;
|
||||
}
|
||||
|
||||
void s390_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
|
||||
int flags)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
CPUS390XState *env = &cpu->env;
|
||||
int i;
|
||||
|
||||
if (env->cc_op > 3) {
|
||||
cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %15s\n",
|
||||
env->psw.mask, env->psw.addr, cc_name(env->cc_op));
|
||||
} else {
|
||||
cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %02x\n",
|
||||
env->psw.mask, env->psw.addr, env->cc_op);
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
cpu_fprintf(f, "R%02d=%016" PRIx64, i, env->regs[i]);
|
||||
if ((i % 4) == 3) {
|
||||
cpu_fprintf(f, "\n");
|
||||
} else {
|
||||
cpu_fprintf(f, " ");
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
cpu_fprintf(f, "F%02d=%016" PRIx64, i, get_freg(env, i)->ll);
|
||||
if ((i % 4) == 3) {
|
||||
cpu_fprintf(f, "\n");
|
||||
} else {
|
||||
cpu_fprintf(f, " ");
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
cpu_fprintf(f, "V%02d=%016" PRIx64 "%016" PRIx64, i,
|
||||
env->vregs[i][0].ll, env->vregs[i][1].ll);
|
||||
cpu_fprintf(f, (i % 2) ? "\n" : " ");
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
for (i = 0; i < 16; i++) {
|
||||
cpu_fprintf(f, "C%02d=%016" PRIx64, i, env->cregs[i]);
|
||||
if ((i % 4) == 3) {
|
||||
cpu_fprintf(f, "\n");
|
||||
} else {
|
||||
cpu_fprintf(f, " ");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_INLINE_BRANCHES
|
||||
for (i = 0; i < CC_OP_MAX; i++) {
|
||||
cpu_fprintf(f, " %15s = %10ld\t%10ld\n", cc_name(i),
|
||||
inline_branch_miss[i], inline_branch_hit[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
cpu_fprintf(f, "\n");
|
||||
}
|
||||
|
||||
static TCGv_i64 psw_addr;
|
||||
static TCGv_i64 psw_mask;
|
||||
static TCGv_i64 gbea;
|
||||
|
|
Loading…
Reference in New Issue