summaryrefslogtreecommitdiff
path: root/hw/s390x
diff options
context:
space:
mode:
Diffstat (limited to 'hw/s390x')
-rw-r--r--hw/s390x/css.c10
-rw-r--r--hw/s390x/css.h2
-rw-r--r--hw/s390x/event-facility.c93
-rw-r--r--hw/s390x/ipl.c25
-rw-r--r--hw/s390x/s390-virtio-ccw.c8
-rw-r--r--hw/s390x/s390-virtio-hcall.c14
-rw-r--r--hw/s390x/s390-virtio.c2
-rw-r--r--hw/s390x/sclp.c69
-rw-r--r--hw/s390x/virtio-ccw.c102
-rw-r--r--hw/s390x/virtio-ccw.h4
10 files changed, 220 insertions, 109 deletions
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index 75b04b45af..7074d2b3d5 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -116,6 +116,15 @@ void css_conditional_io_interrupt(SubchDev *sch)
}
}
+void css_adapter_interrupt(uint8_t isc)
+{
+ S390CPU *cpu = s390_cpu_addr2state(0);
+ uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI;
+
+ trace_css_adapter_interrupt(isc);
+ s390_io_interrupt(cpu, 0, 0, 0, io_int_word);
+}
+
static void sch_handle_clear_func(SubchDev *sch)
{
PMCW *p = &sch->curr_status.pmcw;
@@ -1259,6 +1268,7 @@ void css_reset_sch(SubchDev *sch)
sch->channel_prog = 0x0;
sch->last_cmd_valid = false;
sch->orb = NULL;
+ sch->thinint_active = false;
}
void css_reset(void)
diff --git a/hw/s390x/css.h b/hw/s390x/css.h
index b536ab5957..e9b440540d 100644
--- a/hw/s390x/css.h
+++ b/hw/s390x/css.h
@@ -77,6 +77,7 @@ struct SubchDev {
CCW1 last_cmd;
bool last_cmd_valid;
ORB *orb;
+ bool thinint_active;
/* transport-provided data: */
int (*ccw_cb) (SubchDev *, CCW1);
SenseId id;
@@ -97,4 +98,5 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid);
void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
int hotplugged, int add);
void css_generate_chp_crws(uint8_t cssid, uint8_t chpid);
+void css_adapter_interrupt(uint8_t isc);
#endif
diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c
index a73c0b924a..0777a93916 100644
--- a/hw/s390x/event-facility.c
+++ b/hw/s390x/event-facility.c
@@ -21,13 +21,13 @@
#include "hw/s390x/sclp.h"
#include "hw/s390x/event-facility.h"
-typedef struct EventTypesBus {
+typedef struct SCLPEventsBus {
BusState qbus;
-} EventTypesBus;
+} SCLPEventsBus;
struct SCLPEventFacility {
- EventTypesBus sbus;
- DeviceState *qdev;
+ SysBusDevice parent_obj;
+ SCLPEventsBus sbus;
/* guest' receive mask */
unsigned int receive_mask;
};
@@ -291,7 +291,7 @@ static void sclp_events_bus_class_init(ObjectClass *klass, void *data)
{
}
-static const TypeInfo s390_sclp_events_bus_info = {
+static const TypeInfo sclp_events_bus_info = {
.name = TYPE_SCLP_EVENTS_BUS,
.parent = TYPE_BUS,
.class_init = sclp_events_bus_class_init,
@@ -299,7 +299,7 @@ static const TypeInfo s390_sclp_events_bus_info = {
static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code)
{
- switch (code) {
+ switch (code & SCLP_CMD_CODE_MASK) {
case SCLP_CMD_READ_EVENT_DATA:
read_event_data(ef, sccb);
break;
@@ -315,21 +315,26 @@ static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code)
}
}
-static int init_event_facility(S390SCLPDevice *sdev)
+static const VMStateDescription vmstate_event_facility = {
+ .name = "vmstate-event-facility",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(receive_mask, SCLPEventFacility),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int init_event_facility(SCLPEventFacility *event_facility)
{
- SCLPEventFacility *event_facility;
+ DeviceState *sdev = DEVICE(event_facility);
DeviceState *quiesce;
- event_facility = g_malloc0(sizeof(SCLPEventFacility));
- sdev->ef = event_facility;
- sdev->sclp_command_handler = command_handler;
- sdev->event_pending = event_pending;
-
- /* Spawn a new sclp-events facility */
+ /* Spawn a new bus for SCLP events */
qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus),
- TYPE_SCLP_EVENTS_BUS, DEVICE(sdev), NULL);
+ TYPE_SCLP_EVENTS_BUS, sdev, NULL);
event_facility->sbus.qbus.allow_hotplug = 0;
- event_facility->qdev = (DeviceState *) sdev;
quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce");
if (!quiesce) {
@@ -346,43 +351,57 @@ static int init_event_facility(S390SCLPDevice *sdev)
static void reset_event_facility(DeviceState *dev)
{
- S390SCLPDevice *sdev = SCLP_S390_DEVICE(dev);
+ SCLPEventFacility *sdev = EVENT_FACILITY(dev);
- sdev->ef->receive_mask = 0;
+ sdev->receive_mask = 0;
}
static void init_event_facility_class(ObjectClass *klass, void *data)
{
- DeviceClass *dc = DEVICE_CLASS(klass);
- S390SCLPDeviceClass *k = SCLP_S390_DEVICE_CLASS(klass);
+ SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(sbdc);
+ SCLPEventFacilityClass *k = EVENT_FACILITY_CLASS(dc);
dc->reset = reset_event_facility;
+ dc->vmsd = &vmstate_event_facility;
k->init = init_event_facility;
+ k->command_handler = command_handler;
+ k->event_pending = event_pending;
}
-static const TypeInfo s390_sclp_event_facility_info = {
- .name = "s390-sclp-event-facility",
- .parent = TYPE_DEVICE_S390_SCLP,
- .instance_size = sizeof(S390SCLPDevice),
+static const TypeInfo sclp_event_facility_info = {
+ .name = TYPE_SCLP_EVENT_FACILITY,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SCLPEventFacility),
.class_init = init_event_facility_class,
+ .class_size = sizeof(SCLPEventFacilityClass),
};
-static int event_qdev_init(DeviceState *qdev)
+static void event_realize(DeviceState *qdev, Error **errp)
{
- SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev);
+ SCLPEvent *event = SCLP_EVENT(qdev);
SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event);
- return child->init(event);
+ if (child->init) {
+ int rc = child->init(event);
+ if (rc < 0) {
+ error_setg(errp, "SCLP event initialization failed.");
+ return;
+ }
+ }
}
-static int event_qdev_exit(DeviceState *qdev)
+static void event_unrealize(DeviceState *qdev, Error **errp)
{
- SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev);
+ SCLPEvent *event = SCLP_EVENT(qdev);
SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event);
if (child->exit) {
- child->exit(event);
+ int rc = child->exit(event);
+ if (rc < 0) {
+ error_setg(errp, "SCLP event exit failed.");
+ return;
+ }
}
- return 0;
}
static void event_class_init(ObjectClass *klass, void *data)
@@ -391,11 +410,11 @@ static void event_class_init(ObjectClass *klass, void *data)
dc->bus_type = TYPE_SCLP_EVENTS_BUS;
dc->unplug = qdev_simple_unplug_cb;
- dc->init = event_qdev_init;
- dc->exit = event_qdev_exit;
+ dc->realize = event_realize;
+ dc->unrealize = event_unrealize;
}
-static const TypeInfo s390_sclp_event_type_info = {
+static const TypeInfo sclp_event_type_info = {
.name = TYPE_SCLP_EVENT,
.parent = TYPE_DEVICE,
.instance_size = sizeof(SCLPEvent),
@@ -406,9 +425,9 @@ static const TypeInfo s390_sclp_event_type_info = {
static void register_types(void)
{
- type_register_static(&s390_sclp_events_bus_info);
- type_register_static(&s390_sclp_event_facility_info);
- type_register_static(&s390_sclp_event_type_info);
+ type_register_static(&sclp_events_bus_info);
+ type_register_static(&sclp_event_facility_info);
+ type_register_static(&sclp_event_type_info);
}
type_init(register_types)
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 1a6397b88e..32d38a08f6 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -95,24 +95,29 @@ static int s390_ipl_init(SysBusDevice *dev)
}
return 0;
} else {
- kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, NULL,
+ uint64_t pentry = KERN_IMAGE_START;
+ kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL,
NULL, 1, ELF_MACHINE, 0);
- if (kernel_size == -1) {
+ if (kernel_size < 0) {
kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
}
- if (kernel_size == -1) {
+ if (kernel_size < 0) {
fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel);
return -1;
}
- /* we have to overwrite values in the kernel image, which are "rom" */
- strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline);
-
/*
- * we can not rely on the ELF entry point, since up to 3.2 this
- * value was 0x800 (the SALIPL loader) and it wont work. For
- * all (Linux) cases 0x10000 (KERN_IMAGE_START) should be fine.
+ * Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the
+ * kernel parameters here as well. Note: For old kernels (up to 3.2)
+ * we can not rely on the ELF entry point - it was 0x800 (the SALIPL
+ * loader) and it won't work. For this case we force it to 0x10000, too.
*/
- ipl->start_addr = KERN_IMAGE_START;
+ if (pentry == KERN_IMAGE_START || pentry == 0x800) {
+ ipl->start_addr = KERN_IMAGE_START;
+ /* Overwrite parameters in the kernel image, which are "rom" */
+ strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline);
+ } else {
+ ipl->start_addr = pentry;
+ }
}
if (ipl->initrd) {
ram_addr_t initrd_offset;
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 733d988871..0d4f6ae2f3 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -13,13 +13,14 @@
#include "exec/address-spaces.h"
#include "s390-virtio.h"
#include "hw/s390x/sclp.h"
+#include "hw/s390x/s390_flic.h"
#include "ioinst.h"
#include "css.h"
#include "virtio-ccw.h"
void io_subsystem_reset(void)
{
- DeviceState *css, *sclp;
+ DeviceState *css, *sclp, *flic;
css = DEVICE(object_resolve_path_type("", "virtual-css-bridge", NULL));
if (css) {
@@ -30,6 +31,10 @@ void io_subsystem_reset(void)
if (sclp) {
qdev_reset_all(sclp);
}
+ flic = DEVICE(object_resolve_path_type("", "s390-flic", NULL));
+ if (flic) {
+ qdev_reset_all(flic);
+ }
}
static int virtio_ccw_hcall_notify(const uint64_t *args)
@@ -99,6 +104,7 @@ static void ccw_init(QEMUMachineInitArgs *args)
s390_sclp_init();
s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline,
args->initrd_filename, "s390-ccw.img");
+ s390_flic_init();
/* register hypercalls */
virtio_ccw_register_hcalls();
diff --git a/hw/s390x/s390-virtio-hcall.c b/hw/s390x/s390-virtio-hcall.c
index ee626493c6..c7bdc2005d 100644
--- a/hw/s390x/s390-virtio-hcall.c
+++ b/hw/s390x/s390-virtio-hcall.c
@@ -26,11 +26,15 @@ void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn)
int s390_virtio_hypercall(CPUS390XState *env)
{
- s390_virtio_fn fn = s390_diag500_table[env->regs[1]];
-
- if (!fn) {
- return -EINVAL;
+ s390_virtio_fn fn;
+
+ if (env->regs[1] < MAX_DIAG_SUBCODES) {
+ fn = s390_diag500_table[env->regs[1]];
+ if (fn) {
+ env->regs[2] = fn(&env->regs[2]);
+ return 0;
+ }
}
- return fn(&env->regs[2]);
+ return -EINVAL;
}
diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c
index 9eeda97920..0f03fd18b9 100644
--- a/hw/s390x/s390-virtio.c
+++ b/hw/s390x/s390-virtio.c
@@ -36,6 +36,7 @@
#include "hw/s390x/s390-virtio-bus.h"
#include "hw/s390x/sclp.h"
+#include "hw/s390x/s390_flic.h"
#include "hw/s390x/s390-virtio.h"
//#define DEBUG_S390
@@ -251,6 +252,7 @@ static void s390_init(QEMUMachineInitArgs *args)
s390_sclp_init();
s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline,
args->initrd_filename, ZIPL_FILENAME);
+ s390_flic_init();
/* register hypercalls */
s390_virtio_register_hcalls();
diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c
index 4e0c564c5c..d8ddf35e58 100644
--- a/hw/s390x/sclp.c
+++ b/hw/s390x/sclp.c
@@ -18,11 +18,12 @@
#include "sysemu/sysemu.h"
#include "hw/s390x/sclp.h"
+#include "hw/s390x/event-facility.h"
-static inline S390SCLPDevice *get_event_facility(void)
+static inline SCLPEventFacility *get_event_facility(void)
{
ObjectProperty *op = object_property_find(qdev_get_machine(),
- "s390-sclp-event-facility",
+ TYPE_SCLP_EVENT_FACILITY,
NULL);
assert(op);
return op->opaque;
@@ -89,9 +90,10 @@ static void sclp_read_cpu_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
}
-static void sclp_execute(SCCB *sccb, uint64_t code)
+static void sclp_execute(SCCB *sccb, uint32_t code)
{
- S390SCLPDevice *sdev = get_event_facility();
+ SCLPEventFacility *ef = get_event_facility();
+ SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
switch (code & SCLP_CMD_CODE_MASK) {
case SCLP_CMDW_READ_SCP_INFO:
@@ -102,12 +104,12 @@ static void sclp_execute(SCCB *sccb, uint64_t code)
sclp_read_cpu_info(sccb);
break;
default:
- sdev->sclp_command_handler(sdev->ef, sccb, code);
+ efc->command_handler(ef, sccb, code);
break;
}
}
-int sclp_service_call(uint32_t sccb, uint64_t code)
+int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
{
int r = 0;
SCCB work_sccb;
@@ -115,11 +117,16 @@ int sclp_service_call(uint32_t sccb, uint64_t code)
hwaddr sccb_len = sizeof(SCCB);
/* first some basic checks on program checks */
+ if (env->psw.mask & PSW_MASK_PSTATE) {
+ r = -PGM_PRIVILEGED;
+ goto out;
+ }
if (cpu_physical_memory_is_io(sccb)) {
r = -PGM_ADDRESSING;
goto out;
}
- if (sccb & ~0x7ffffff8ul) {
+ if ((sccb & ~0x1fffUL) == 0 || (sccb & ~0x1fffUL) == env->psa
+ || (sccb & ~0x7ffffff8UL) != 0) {
r = -PGM_SPECIFICATION;
goto out;
}
@@ -151,11 +158,13 @@ out:
void sclp_service_interrupt(uint32_t sccb)
{
- S390SCLPDevice *sdev = get_event_facility();
+ SCLPEventFacility *ef = get_event_facility();
+ SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
+
uint32_t param = sccb & ~3;
/* Indicate whether an event is still pending */
- param |= sdev->event_pending(sdev->ef) ? 1 : 0;
+ param |= efc->event_pending(ef) ? 1 : 0;
if (!param) {
/* No need to send an interrupt, there's nothing to be notified about */
@@ -168,47 +177,9 @@ void sclp_service_interrupt(uint32_t sccb)
void s390_sclp_init(void)
{
- DeviceState *dev = qdev_create(NULL, "s390-sclp-event-facility");
+ DeviceState *dev = qdev_create(NULL, TYPE_SCLP_EVENT_FACILITY);
- object_property_add_child(qdev_get_machine(), "s390-sclp-event-facility",
+ object_property_add_child(qdev_get_machine(), TYPE_SCLP_EVENT_FACILITY,
OBJECT(dev), NULL);
qdev_init_nofail(dev);
}
-
-static int s390_sclp_dev_init(SysBusDevice *dev)
-{
- int r;
- S390SCLPDevice *sdev = (S390SCLPDevice *)dev;
- S390SCLPDeviceClass *sclp = SCLP_S390_DEVICE_GET_CLASS(dev);
-
- r = sclp->init(sdev);
- if (!r) {
- assert(sdev->event_pending);
- assert(sdev->sclp_command_handler);
- }
-
- return r;
-}
-
-static void s390_sclp_device_class_init(ObjectClass *klass, void *data)
-{
- SysBusDeviceClass *dc = SYS_BUS_DEVICE_CLASS(klass);
-
- dc->init = s390_sclp_dev_init;
-}
-
-static const TypeInfo s390_sclp_device_info = {
- .name = TYPE_DEVICE_S390_SCLP,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(S390SCLPDevice),
- .class_init = s390_sclp_device_class_init,
- .class_size = sizeof(S390SCLPDeviceClass),
- .abstract = true,
-};
-
-static void s390_sclp_register_types(void)
-{
- type_register_static(&s390_sclp_device_info);
-}
-
-type_init(s390_sclp_register_types)
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index f6e0e3e4ae..a01801e342 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -1,7 +1,7 @@
/*
* virtio ccw target implementation
*
- * Copyright 2012 IBM Corp.
+ * Copyright 2012,2014 IBM Corp.
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
@@ -188,6 +188,13 @@ typedef struct VirtioFeatDesc {
uint8_t index;
} QEMU_PACKED VirtioFeatDesc;
+typedef struct VirtioThinintInfo {
+ hwaddr summary_indicator;
+ hwaddr device_indicator;
+ uint64_t ind_bit;
+ uint8_t isc;
+} QEMU_PACKED VirtioThinintInfo;
+
/* Specify where the virtqueues for the subchannel are in guest memory. */
static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
uint16_t index, uint16_t num)
@@ -237,6 +244,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
bool check_len;
int len;
hwaddr hw_len;
+ VirtioThinintInfo *thinint;
if (!dev) {
return -EINVAL;
@@ -428,6 +436,11 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
ret = -EINVAL;
break;
}
+ if (sch->thinint_active) {
+ /* Trigger a command reject. */
+ ret = -ENOSYS;
+ break;
+ }
if (!ccw.cda) {
ret = -EFAULT;
} else {
@@ -480,6 +493,42 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
ret = 0;
}
break;
+ case CCW_CMD_SET_IND_ADAPTER:
+ if (check_len) {
+ if (ccw.count != sizeof(*thinint)) {
+ ret = -EINVAL;
+ break;
+ }
+ } else if (ccw.count < sizeof(*thinint)) {
+ /* Can't execute command. */
+ ret = -EINVAL;
+ break;
+ }
+ len = sizeof(*thinint);
+ hw_len = len;
+ if (!ccw.cda) {
+ ret = -EFAULT;
+ } else if (dev->indicators && !sch->thinint_active) {
+ /* Trigger a command reject. */
+ ret = -ENOSYS;
+ } else {
+ thinint = cpu_physical_memory_map(ccw.cda, &hw_len, 0);
+ if (!thinint) {
+ ret = -EFAULT;
+ } else {
+ len = hw_len;
+ dev->summary_indicator = thinint->summary_indicator;
+ dev->indicators = thinint->device_indicator;
+ dev->thinint_isc = thinint->isc;
+ dev->ind_bit = thinint->ind_bit;
+ cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len);
+ sch->thinint_active = ((dev->indicators != 0) &&
+ (dev->summary_indicator != 0));
+ sch->curr_status.scsw.count = ccw.count - len;
+ ret = 0;
+ }
+ }
+ break;
default:
ret = -ENOSYS;
break;
@@ -511,6 +560,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev)
sch->channel_prog = 0x0;
sch->last_cmd_valid = false;
sch->orb = NULL;
+ sch->thinint_active = false;
/*
* Use a device number if provided. Otherwise, fall back to subchannel
* number.
@@ -858,6 +908,28 @@ static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d)
return container_of(d, VirtioCcwDevice, parent_obj);
}
+static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc,
+ uint8_t to_be_set)
+{
+ uint8_t ind_old, ind_new;
+ hwaddr len = 1;
+ uint8_t *ind_addr;
+
+ ind_addr = cpu_physical_memory_map(ind_loc, &len, 1);
+ if (!ind_addr) {
+ error_report("%s(%x.%x.%04x): unable to access indicator",
+ __func__, sch->cssid, sch->ssid, sch->schid);
+ return -1;
+ }
+ do {
+ ind_old = *ind_addr;
+ ind_new = ind_old | to_be_set;
+ } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old);
+ cpu_physical_memory_unmap(ind_addr, len, 1, len);
+
+ return ind_old;
+}
+
static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
{
VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d);
@@ -872,9 +944,26 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
if (!dev->indicators) {
return;
}
- indicators = ldq_phys(&address_space_memory, dev->indicators);
- indicators |= 1ULL << vector;
- stq_phys(&address_space_memory, dev->indicators, indicators);
+ if (sch->thinint_active) {
+ /*
+ * In the adapter interrupt case, indicators points to a
+ * memory area that may be (way) larger than 64 bit and
+ * ind_bit indicates the start of the indicators in a big
+ * endian notation.
+ */
+ virtio_set_ind_atomic(sch, dev->indicators +
+ (dev->ind_bit + vector) / 8,
+ 0x80 >> ((dev->ind_bit + vector) % 8));
+ if (!virtio_set_ind_atomic(sch, dev->summary_indicator,
+ 0x01)) {
+ css_adapter_interrupt(dev->thinint_isc);
+ }
+ } else {
+ indicators = ldq_phys(&address_space_memory, dev->indicators);
+ indicators |= 1ULL << vector;
+ stq_phys(&address_space_memory, dev->indicators, indicators);
+ css_conditional_io_interrupt(sch);
+ }
} else {
if (!dev->indicators2) {
return;
@@ -883,10 +972,8 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
indicators = ldq_phys(&address_space_memory, dev->indicators2);
indicators |= 1ULL << vector;
stq_phys(&address_space_memory, dev->indicators2, indicators);
+ css_conditional_io_interrupt(sch);
}
-
- css_conditional_io_interrupt(sch);
-
}
static unsigned virtio_ccw_get_features(DeviceState *d)
@@ -907,6 +994,7 @@ static void virtio_ccw_reset(DeviceState *d)
css_reset_sch(dev->sch);
dev->indicators = 0;
dev->indicators2 = 0;
+ dev->summary_indicator = 0;
}
static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
index 00932c746d..4393e44814 100644
--- a/hw/s390x/virtio-ccw.h
+++ b/hw/s390x/virtio-ccw.h
@@ -38,6 +38,7 @@
#define CCW_CMD_SET_IND 0x43
#define CCW_CMD_SET_CONF_IND 0x53
#define CCW_CMD_READ_VQ_CONF 0x32
+#define CCW_CMD_SET_IND_ADAPTER 0x73
#define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device"
#define VIRTIO_CCW_DEVICE(obj) \
@@ -83,9 +84,12 @@ struct VirtioCcwDevice {
bool ioeventfd_started;
bool ioeventfd_disabled;
uint32_t flags;
+ uint8_t thinint_isc;
/* Guest provided values: */
hwaddr indicators;
hwaddr indicators2;
+ hwaddr summary_indicator;
+ uint64_t ind_bit;
};
/* virtual css bus type */