summaryrefslogtreecommitdiffstats
path: root/qemu/hw/s390x
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/hw/s390x')
-rw-r--r--qemu/hw/s390x/Makefile.objs4
-rw-r--r--qemu/hw/s390x/css.c282
-rw-r--r--qemu/hw/s390x/css.h15
-rw-r--r--qemu/hw/s390x/event-facility.c55
-rw-r--r--qemu/hw/s390x/ipl.c129
-rw-r--r--qemu/hw/s390x/ipl.h30
-rw-r--r--qemu/hw/s390x/s390-pci-bus.c49
-rw-r--r--qemu/hw/s390x/s390-pci-bus.h5
-rw-r--r--qemu/hw/s390x/s390-pci-inst.c53
-rw-r--r--qemu/hw/s390x/s390-skeys-kvm.c76
-rw-r--r--qemu/hw/s390x/s390-skeys.c415
-rw-r--r--qemu/hw/s390x/s390-virtio-bus.c763
-rw-r--r--qemu/hw/s390x/s390-virtio-bus.h186
-rw-r--r--qemu/hw/s390x/s390-virtio-ccw.c286
-rw-r--r--qemu/hw/s390x/s390-virtio-hcall.c1
-rw-r--r--qemu/hw/s390x/s390-virtio.c238
-rw-r--r--qemu/hw/s390x/s390-virtio.h4
-rw-r--r--qemu/hw/s390x/sclp.c273
-rw-r--r--qemu/hw/s390x/sclpcpu.c29
-rw-r--r--qemu/hw/s390x/sclpquiesce.c5
-rw-r--r--qemu/hw/s390x/virtio-ccw.c144
-rw-r--r--qemu/hw/s390x/virtio-ccw.h19
22 files changed, 1391 insertions, 1670 deletions
diff --git a/qemu/hw/s390x/Makefile.objs b/qemu/hw/s390x/Makefile.objs
index 27cd75a93..220361782 100644
--- a/qemu/hw/s390x/Makefile.objs
+++ b/qemu/hw/s390x/Makefile.objs
@@ -1,4 +1,4 @@
-obj-y = s390-virtio-bus.o s390-virtio.o
+obj-y += s390-virtio.o
obj-y += s390-virtio-hcall.o
obj-y += sclp.o
obj-y += event-facility.o
@@ -9,3 +9,5 @@ obj-y += css.o
obj-y += s390-virtio-ccw.o
obj-y += virtio-ccw.o
obj-y += s390-pci-bus.o s390-pci-inst.o
+obj-y += s390-skeys.o
+obj-$(CONFIG_KVM) += s390-skeys-kvm.o
diff --git a/qemu/hw/s390x/css.c b/qemu/hw/s390x/css.c
index 5df450e00..3a1d91958 100644
--- a/qemu/hw/s390x/css.c
+++ b/qemu/hw/s390x/css.c
@@ -9,6 +9,7 @@
* directory.
*/
+#include "qemu/osdep.h"
#include <hw/qdev.h>
#include "qemu/bitops.h"
#include "exec/address-spaces.h"
@@ -49,6 +50,7 @@ typedef struct IoAdapter {
typedef struct ChannelSubSys {
QTAILQ_HEAD(, CrwContainer) pending_crws;
+ bool sei_pending;
bool do_crw_mchk;
bool crws_lost;
uint8_t max_cssid;
@@ -58,9 +60,81 @@ typedef struct ChannelSubSys {
CssImage *css[MAX_CSSID + 1];
uint8_t default_cssid;
QTAILQ_HEAD(, IoAdapter) io_adapters;
+ QTAILQ_HEAD(, IndAddr) indicator_addresses;
} ChannelSubSys;
-static ChannelSubSys *channel_subsys;
+static ChannelSubSys channel_subsys = {
+ .pending_crws = QTAILQ_HEAD_INITIALIZER(channel_subsys.pending_crws),
+ .do_crw_mchk = true,
+ .sei_pending = false,
+ .do_crw_mchk = true,
+ .crws_lost = false,
+ .chnmon_active = false,
+ .io_adapters = QTAILQ_HEAD_INITIALIZER(channel_subsys.io_adapters),
+ .indicator_addresses =
+ QTAILQ_HEAD_INITIALIZER(channel_subsys.indicator_addresses),
+};
+
+IndAddr *get_indicator(hwaddr ind_addr, int len)
+{
+ IndAddr *indicator;
+
+ QTAILQ_FOREACH(indicator, &channel_subsys.indicator_addresses, sibling) {
+ if (indicator->addr == ind_addr) {
+ indicator->refcnt++;
+ return indicator;
+ }
+ }
+ indicator = g_new0(IndAddr, 1);
+ indicator->addr = ind_addr;
+ indicator->len = len;
+ indicator->refcnt = 1;
+ QTAILQ_INSERT_TAIL(&channel_subsys.indicator_addresses,
+ indicator, sibling);
+ return indicator;
+}
+
+static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr,
+ bool do_map)
+{
+ S390FLICState *fs = s390_get_flic();
+ S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
+
+ return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map);
+}
+
+void release_indicator(AdapterInfo *adapter, IndAddr *indicator)
+{
+ assert(indicator->refcnt > 0);
+ indicator->refcnt--;
+ if (indicator->refcnt > 0) {
+ return;
+ }
+ QTAILQ_REMOVE(&channel_subsys.indicator_addresses, indicator, sibling);
+ if (indicator->map) {
+ s390_io_adapter_map(adapter, indicator->map, false);
+ }
+ g_free(indicator);
+}
+
+int map_indicator(AdapterInfo *adapter, IndAddr *indicator)
+{
+ int ret;
+
+ if (indicator->map) {
+ return 0; /* already mapped is not an error */
+ }
+ indicator->map = indicator->addr;
+ ret = s390_io_adapter_map(adapter, indicator->map, true);
+ if ((ret != 0) && (ret != -ENOSYS)) {
+ goto out_err;
+ }
+ return 0;
+
+out_err:
+ indicator->map = 0;
+ return ret;
+}
int css_create_css_image(uint8_t cssid, bool default_image)
{
@@ -68,12 +142,12 @@ int css_create_css_image(uint8_t cssid, bool default_image)
if (cssid > MAX_CSSID) {
return -EINVAL;
}
- if (channel_subsys->css[cssid]) {
+ if (channel_subsys.css[cssid]) {
return -EBUSY;
}
- channel_subsys->css[cssid] = g_malloc0(sizeof(CssImage));
+ channel_subsys.css[cssid] = g_malloc0(sizeof(CssImage));
if (default_image) {
- channel_subsys->default_cssid = cssid;
+ channel_subsys.default_cssid = cssid;
}
return 0;
}
@@ -88,7 +162,7 @@ int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap,
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
*id = 0;
- QTAILQ_FOREACH(adapter, &channel_subsys->io_adapters, sibling) {
+ QTAILQ_FOREACH(adapter, &channel_subsys.io_adapters, sibling) {
if ((adapter->type == type) && (adapter->isc == isc)) {
*id = adapter->id;
found = true;
@@ -108,7 +182,7 @@ int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap,
adapter->id = *id;
adapter->isc = isc;
adapter->type = type;
- QTAILQ_INSERT_TAIL(&channel_subsys->io_adapters, adapter, sibling);
+ QTAILQ_INSERT_TAIL(&channel_subsys.io_adapters, adapter, sibling);
} else {
g_free(adapter);
fprintf(stderr, "Unexpected error %d when registering adapter %d\n",
@@ -120,7 +194,7 @@ out:
uint16_t css_build_subchannel_id(SubchDev *sch)
{
- if (channel_subsys->max_cssid > 0) {
+ if (channel_subsys.max_cssid > 0) {
return (sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1;
}
return (sch->ssid << 1) | 1;
@@ -261,11 +335,15 @@ static CCW1 copy_ccw_from_guest(hwaddr addr, bool fmt1)
ret.flags = tmp0.flags;
ret.count = be16_to_cpu(tmp0.count);
ret.cda = be16_to_cpu(tmp0.cda1) | (tmp0.cda0 << 16);
+ if ((ret.cmd_code & 0x0f) == CCW_CMD_TIC) {
+ ret.cmd_code &= 0x0f;
+ }
}
return ret;
}
-static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr)
+static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr,
+ bool suspend_allowed)
{
int ret;
bool check_len;
@@ -287,9 +365,13 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr)
((ccw.cmd_code & 0xf0) != 0)) {
return -EINVAL;
}
+ if (!sch->ccw_fmt_1 && (ccw.count == 0) &&
+ (ccw.cmd_code != CCW_CMD_TIC)) {
+ return -EINVAL;
+ }
if (ccw.flags & CCW_FLAG_SUSPEND) {
- return -EINPROGRESS;
+ return suspend_allowed ? -EINPROGRESS : -EINVAL;
}
check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC));
@@ -387,11 +469,14 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb)
SCSW *s = &sch->curr_status.scsw;
int path;
int ret;
+ bool suspend_allowed;
/* Path management: In our simple css, we always choose the only path. */
path = 0x80;
if (!(s->ctrl & SCSW_ACTL_SUSP)) {
+ s->cstat = 0;
+ s->dstat = 0;
/* Look at the orb and try to execute the channel program. */
assert(orb != NULL); /* resume does not pass an orb */
p->intparm = orb->intparm;
@@ -404,12 +489,15 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb)
}
sch->ccw_fmt_1 = !!(orb->ctrl0 & ORB_CTRL0_MASK_FMT);
sch->ccw_no_data_cnt = 0;
+ suspend_allowed = !!(orb->ctrl0 & ORB_CTRL0_MASK_SPND);
} else {
s->ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND);
+ /* The channel program had been suspended before. */
+ suspend_allowed = true;
}
sch->last_cmd_valid = false;
do {
- ret = css_interpret_ccw(sch, sch->channel_prog);
+ ret = css_interpret_ccw(sch, sch->channel_prog, suspend_allowed);
switch (ret) {
case -EAGAIN:
/* ccw chain, continue processing */
@@ -692,7 +780,7 @@ int css_do_csch(SubchDev *sch)
/* Trigger the clear function. */
s->ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL);
- s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_CLEAR_FUNC;
+ s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_ACTL_CLEAR_PEND;
do_subchannel_work(sch, NULL);
ret = 0;
@@ -767,12 +855,12 @@ static void css_update_chnmon(SubchDev *sch)
offset = sch->curr_status.pmcw.mbi << 5;
count = address_space_lduw(&address_space_memory,
- channel_subsys->chnmon_area + offset,
+ channel_subsys.chnmon_area + offset,
MEMTXATTRS_UNSPECIFIED,
NULL);
count++;
address_space_stw(&address_space_memory,
- channel_subsys->chnmon_area + offset, count,
+ channel_subsys.chnmon_area + offset, count,
MEMTXATTRS_UNSPECIFIED, NULL);
}
}
@@ -801,7 +889,7 @@ int css_do_ssch(SubchDev *sch, ORB *orb)
}
/* If monitoring is active, update counter. */
- if (channel_subsys->chnmon_active) {
+ if (channel_subsys.chnmon_active) {
css_update_chnmon(sch);
}
sch->channel_prog = orb->cpa;
@@ -883,8 +971,14 @@ int css_do_tsch_get_irb(SubchDev *sch, IRB *target_irb, int *irb_len)
/* If a unit check is pending, copy sense data. */
if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) &&
(p->chars & PMCW_CHARS_MASK_CSENSE)) {
+ int i;
+
irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF | SCSW_FLAGS_MASK_ECTL;
+ /* Attention: sense_data is already BE! */
memcpy(irb.ecw, sch->sense_data, sizeof(sch->sense_data));
+ for (i = 0; i < ARRAY_SIZE(irb.ecw); i++) {
+ irb.ecw[i] = be32_to_cpu(irb.ecw[i]);
+ }
irb.esw[1] = 0x01000000 | (sizeof(sch->sense_data) << 8);
}
}
@@ -954,16 +1048,16 @@ int css_do_stcrw(CRW *crw)
CrwContainer *crw_cont;
int ret;
- crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws);
+ crw_cont = QTAILQ_FIRST(&channel_subsys.pending_crws);
if (crw_cont) {
- QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling);
+ QTAILQ_REMOVE(&channel_subsys.pending_crws, crw_cont, sibling);
copy_crw_to_guest(crw, &crw_cont->crw);
g_free(crw_cont);
ret = 0;
} else {
/* List was empty, turn crw machine checks on again. */
memset(crw, 0, sizeof(*crw));
- channel_subsys->do_crw_mchk = true;
+ channel_subsys.do_crw_mchk = true;
ret = 1;
}
@@ -982,12 +1076,12 @@ void css_undo_stcrw(CRW *crw)
crw_cont = g_try_malloc0(sizeof(CrwContainer));
if (!crw_cont) {
- channel_subsys->crws_lost = true;
+ channel_subsys.crws_lost = true;
return;
}
copy_crw_from_guest(&crw_cont->crw, crw);
- QTAILQ_INSERT_HEAD(&channel_subsys->pending_crws, crw_cont, sibling);
+ QTAILQ_INSERT_HEAD(&channel_subsys.pending_crws, crw_cont, sibling);
}
int css_do_tpi(IOIntCode *int_code, int lowcore)
@@ -1005,9 +1099,9 @@ int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid,
CssImage *css;
if (!m && !cssid) {
- css = channel_subsys->css[channel_subsys->default_cssid];
+ css = channel_subsys.css[channel_subsys.default_cssid];
} else {
- css = channel_subsys->css[cssid];
+ css = channel_subsys.css[cssid];
}
if (!css) {
return 0;
@@ -1042,15 +1136,15 @@ void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
{
/* dct is currently ignored (not really meaningful for our devices) */
/* TODO: Don't ignore mbk. */
- if (update && !channel_subsys->chnmon_active) {
+ if (update && !channel_subsys.chnmon_active) {
/* Enable measuring. */
- channel_subsys->chnmon_area = mbo;
- channel_subsys->chnmon_active = true;
+ channel_subsys.chnmon_area = mbo;
+ channel_subsys.chnmon_active = true;
}
- if (!update && channel_subsys->chnmon_active) {
+ if (!update && channel_subsys.chnmon_active) {
/* Disable measuring. */
- channel_subsys->chnmon_area = 0;
- channel_subsys->chnmon_active = false;
+ channel_subsys.chnmon_area = 0;
+ channel_subsys.chnmon_active = false;
}
}
@@ -1078,7 +1172,7 @@ int css_do_rsch(SubchDev *sch)
}
/* If monitoring is active, update counter. */
- if (channel_subsys->chnmon_active) {
+ if (channel_subsys.chnmon_active) {
css_update_chnmon(sch);
}
@@ -1094,23 +1188,23 @@ int css_do_rchp(uint8_t cssid, uint8_t chpid)
{
uint8_t real_cssid;
- if (cssid > channel_subsys->max_cssid) {
+ if (cssid > channel_subsys.max_cssid) {
return -EINVAL;
}
- if (channel_subsys->max_cssid == 0) {
- real_cssid = channel_subsys->default_cssid;
+ if (channel_subsys.max_cssid == 0) {
+ real_cssid = channel_subsys.default_cssid;
} else {
real_cssid = cssid;
}
- if (!channel_subsys->css[real_cssid]) {
+ if (!channel_subsys.css[real_cssid]) {
return -EINVAL;
}
- if (!channel_subsys->css[real_cssid]->chpids[chpid].in_use) {
+ if (!channel_subsys.css[real_cssid]->chpids[chpid].in_use) {
return -ENODEV;
}
- if (!channel_subsys->css[real_cssid]->chpids[chpid].is_virtual) {
+ if (!channel_subsys.css[real_cssid]->chpids[chpid].is_virtual) {
fprintf(stderr,
"rchp unsupported for non-virtual chpid %x.%02x!\n",
real_cssid, chpid);
@@ -1119,8 +1213,8 @@ int css_do_rchp(uint8_t cssid, uint8_t chpid)
/* We don't really use a channel path, so we're done here. */
css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT,
- channel_subsys->max_cssid > 0 ? 1 : 0, chpid);
- if (channel_subsys->max_cssid > 0) {
+ channel_subsys.max_cssid > 0 ? 1 : 0, chpid);
+ if (channel_subsys.max_cssid > 0) {
css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, 0, real_cssid << 8);
}
return 0;
@@ -1131,13 +1225,13 @@ bool css_schid_final(int m, uint8_t cssid, uint8_t ssid, uint16_t schid)
SubchSet *set;
uint8_t real_cssid;
- real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid;
+ real_cssid = (!m && (cssid == 0)) ? channel_subsys.default_cssid : cssid;
if (real_cssid > MAX_CSSID || ssid > MAX_SSID ||
- !channel_subsys->css[real_cssid] ||
- !channel_subsys->css[real_cssid]->sch_set[ssid]) {
+ !channel_subsys.css[real_cssid] ||
+ !channel_subsys.css[real_cssid]->sch_set[ssid]) {
return true;
}
- set = channel_subsys->css[real_cssid]->sch_set[ssid];
+ set = channel_subsys.css[real_cssid]->sch_set[ssid];
return schid > find_last_bit(set->schids_used,
(MAX_SCHID + 1) / sizeof(unsigned long));
}
@@ -1150,7 +1244,7 @@ static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type)
if (cssid > MAX_CSSID) {
return -EINVAL;
}
- css = channel_subsys->css[cssid];
+ css = channel_subsys.css[cssid];
if (!css) {
return -EINVAL;
}
@@ -1171,7 +1265,7 @@ void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type)
PMCW *p = &sch->curr_status.pmcw;
SCSW *s = &sch->curr_status.scsw;
int i;
- CssImage *css = channel_subsys->css[sch->cssid];
+ CssImage *css = channel_subsys.css[sch->cssid];
assert(css != NULL);
memset(p, 0, sizeof(PMCW));
@@ -1197,27 +1291,27 @@ SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid)
{
uint8_t real_cssid;
- real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid;
+ real_cssid = (!m && (cssid == 0)) ? channel_subsys.default_cssid : cssid;
- if (!channel_subsys->css[real_cssid]) {
+ if (!channel_subsys.css[real_cssid]) {
return NULL;
}
- if (!channel_subsys->css[real_cssid]->sch_set[ssid]) {
+ if (!channel_subsys.css[real_cssid]->sch_set[ssid]) {
return NULL;
}
- return channel_subsys->css[real_cssid]->sch_set[ssid]->sch[schid];
+ return channel_subsys.css[real_cssid]->sch_set[ssid]->sch[schid];
}
bool css_subch_visible(SubchDev *sch)
{
- if (sch->ssid > channel_subsys->max_ssid) {
+ if (sch->ssid > channel_subsys.max_ssid) {
return false;
}
- if (sch->cssid != channel_subsys->default_cssid) {
- return (channel_subsys->max_cssid > 0);
+ if (sch->cssid != channel_subsys.default_cssid) {
+ return (channel_subsys.max_cssid > 0);
}
return true;
@@ -1225,20 +1319,20 @@ bool css_subch_visible(SubchDev *sch)
bool css_present(uint8_t cssid)
{
- return (channel_subsys->css[cssid] != NULL);
+ return (channel_subsys.css[cssid] != NULL);
}
bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno)
{
- if (!channel_subsys->css[cssid]) {
+ if (!channel_subsys.css[cssid]) {
return false;
}
- if (!channel_subsys->css[cssid]->sch_set[ssid]) {
+ if (!channel_subsys.css[cssid]->sch_set[ssid]) {
return false;
}
return !!test_bit(devno,
- channel_subsys->css[cssid]->sch_set[ssid]->devnos_used);
+ channel_subsys.css[cssid]->sch_set[ssid]->devnos_used);
}
void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid,
@@ -1249,13 +1343,13 @@ void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid,
trace_css_assign_subch(sch ? "assign" : "deassign", cssid, ssid, schid,
devno);
- if (!channel_subsys->css[cssid]) {
+ if (!channel_subsys.css[cssid]) {
fprintf(stderr,
"Suspicious call to %s (%x.%x.%04x) for non-existing css!\n",
__func__, cssid, ssid, schid);
return;
}
- css = channel_subsys->css[cssid];
+ css = channel_subsys.css[cssid];
if (!css->sch_set[ssid]) {
css->sch_set[ssid] = g_malloc0(sizeof(SubchSet));
@@ -1280,7 +1374,7 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid)
/* TODO: Maybe use a static crw pool? */
crw_cont = g_try_malloc0(sizeof(CrwContainer));
if (!crw_cont) {
- channel_subsys->crws_lost = true;
+ channel_subsys.crws_lost = true;
return;
}
crw_cont->crw.flags = (rsc << 8) | erc;
@@ -1288,15 +1382,15 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid)
crw_cont->crw.flags |= CRW_FLAGS_MASK_C;
}
crw_cont->crw.rsid = rsid;
- if (channel_subsys->crws_lost) {
+ if (channel_subsys.crws_lost) {
crw_cont->crw.flags |= CRW_FLAGS_MASK_R;
- channel_subsys->crws_lost = false;
+ channel_subsys.crws_lost = false;
}
- QTAILQ_INSERT_TAIL(&channel_subsys->pending_crws, crw_cont, sibling);
+ QTAILQ_INSERT_TAIL(&channel_subsys.pending_crws, crw_cont, sibling);
- if (channel_subsys->do_crw_mchk) {
- channel_subsys->do_crw_mchk = false;
+ if (channel_subsys.do_crw_mchk) {
+ channel_subsys.do_crw_mchk = false;
/* Inject crw pending machine check. */
s390_crw_mchk();
}
@@ -1311,9 +1405,9 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
if (add && !hotplugged) {
return;
}
- if (channel_subsys->max_cssid == 0) {
+ if (channel_subsys.max_cssid == 0) {
/* Default cssid shows up as 0. */
- guest_cssid = (cssid == channel_subsys->default_cssid) ? 0 : cssid;
+ guest_cssid = (cssid == channel_subsys.default_cssid) ? 0 : cssid;
} else {
/* Show real cssid to the guest. */
guest_cssid = cssid;
@@ -1322,14 +1416,14 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
* Only notify for higher subchannel sets/channel subsystems if the
* guest has enabled it.
*/
- if ((ssid > channel_subsys->max_ssid) ||
- (guest_cssid > channel_subsys->max_cssid) ||
- ((channel_subsys->max_cssid == 0) &&
- (cssid != channel_subsys->default_cssid))) {
+ if ((ssid > channel_subsys.max_ssid) ||
+ (guest_cssid > channel_subsys.max_cssid) ||
+ ((channel_subsys.max_cssid == 0) &&
+ (cssid != channel_subsys.default_cssid))) {
return;
}
- chain_crw = (channel_subsys->max_ssid > 0) ||
- (channel_subsys->max_cssid > 0);
+ chain_crw = (channel_subsys.max_ssid > 0) ||
+ (channel_subsys.max_cssid > 0);
css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, chain_crw ? 1 : 0, schid);
if (chain_crw) {
css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, 0,
@@ -1344,20 +1438,28 @@ void css_generate_chp_crws(uint8_t cssid, uint8_t chpid)
void css_generate_css_crws(uint8_t cssid)
{
- css_queue_crw(CRW_RSC_CSS, 0, 0, cssid);
+ if (!channel_subsys.sei_pending) {
+ css_queue_crw(CRW_RSC_CSS, 0, 0, cssid);
+ }
+ channel_subsys.sei_pending = true;
+}
+
+void css_clear_sei_pending(void)
+{
+ channel_subsys.sei_pending = false;
}
int css_enable_mcsse(void)
{
trace_css_enable_facility("mcsse");
- channel_subsys->max_cssid = MAX_CSSID;
+ channel_subsys.max_cssid = MAX_CSSID;
return 0;
}
int css_enable_mss(void)
{
trace_css_enable_facility("mss");
- channel_subsys->max_ssid = MAX_SSID;
+ channel_subsys.max_ssid = MAX_SSID;
return 0;
}
@@ -1415,7 +1517,6 @@ void subch_device_save(SubchDev *s, QEMUFile *f)
}
qemu_put_byte(f, s->ccw_fmt_1);
qemu_put_byte(f, s->ccw_no_data_cnt);
- return;
}
int subch_device_load(SubchDev *s, QEMUFile *f)
@@ -1481,27 +1582,15 @@ int subch_device_load(SubchDev *s, QEMUFile *f)
*/
if (s->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA) {
if (s->ssid) {
- channel_subsys->max_ssid = MAX_SSID;
+ channel_subsys.max_ssid = MAX_SSID;
}
- if (s->cssid != channel_subsys->default_cssid) {
- channel_subsys->max_cssid = MAX_CSSID;
+ if (s->cssid != channel_subsys.default_cssid) {
+ channel_subsys.max_cssid = MAX_CSSID;
}
}
return 0;
}
-
-static void css_init(void)
-{
- channel_subsys = g_malloc0(sizeof(*channel_subsys));
- QTAILQ_INIT(&channel_subsys->pending_crws);
- channel_subsys->do_crw_mchk = true;
- channel_subsys->crws_lost = false;
- channel_subsys->chnmon_active = false;
- QTAILQ_INIT(&channel_subsys->io_adapters);
-}
-machine_init(css_init);
-
void css_reset_sch(SubchDev *sch)
{
PMCW *p = &sch->curr_status.pmcw;
@@ -1539,18 +1628,19 @@ void css_reset(void)
CrwContainer *crw_cont;
/* Clean up monitoring. */
- channel_subsys->chnmon_active = false;
- channel_subsys->chnmon_area = 0;
+ channel_subsys.chnmon_active = false;
+ channel_subsys.chnmon_area = 0;
/* Clear pending CRWs. */
- while ((crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws))) {
- QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling);
+ while ((crw_cont = QTAILQ_FIRST(&channel_subsys.pending_crws))) {
+ QTAILQ_REMOVE(&channel_subsys.pending_crws, crw_cont, sibling);
g_free(crw_cont);
}
- channel_subsys->do_crw_mchk = true;
- channel_subsys->crws_lost = false;
+ channel_subsys.sei_pending = false;
+ channel_subsys.do_crw_mchk = true;
+ channel_subsys.crws_lost = false;
/* Reset maximum ids. */
- channel_subsys->max_cssid = 0;
- channel_subsys->max_ssid = 0;
+ channel_subsys.max_cssid = 0;
+ channel_subsys.max_ssid = 0;
}
diff --git a/qemu/hw/s390x/css.h b/qemu/hw/s390x/css.h
index a09bb1f87..a320eea59 100644
--- a/qemu/hw/s390x/css.h
+++ b/qemu/hw/s390x/css.h
@@ -12,6 +12,8 @@
#ifndef CSS_H
#define CSS_H
+#include "hw/s390x/adapter.h"
+#include "hw/s390x/s390_flic.h"
#include "ioinst.h"
/* Channel subsystem constants. */
@@ -86,6 +88,18 @@ struct SubchDev {
void *driver_data;
};
+typedef struct IndAddr {
+ hwaddr addr;
+ uint64_t map;
+ unsigned long refcnt;
+ int len;
+ QTAILQ_ENTRY(IndAddr) sibling;
+} IndAddr;
+
+IndAddr *get_indicator(hwaddr ind_addr, int len);
+void release_indicator(AdapterInfo *adapter, IndAddr *indicator);
+int map_indicator(AdapterInfo *adapter, IndAddr *indicator);
+
typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid,
uint16_t schid);
void subch_device_save(SubchDev *s, QEMUFile *f);
@@ -103,6 +117,7 @@ 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_generate_css_crws(uint8_t cssid);
+void css_clear_sei_pending(void);
void css_adapter_interrupt(uint8_t isc);
#define CSS_IO_ADAPTER_VIRTIO 1
diff --git a/qemu/hw/s390x/event-facility.c b/qemu/hw/s390x/event-facility.c
index 0c700effb..34b2faf01 100644
--- a/qemu/hw/s390x/event-facility.c
+++ b/qemu/hw/s390x/event-facility.c
@@ -15,6 +15,8 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "hw/s390x/sclp.h"
@@ -31,8 +33,6 @@ struct SCLPEventFacility {
unsigned int receive_mask;
};
-static SCLPEvent cpu_hotplug;
-
/* return true if any child has event pending set */
static bool event_pending(SCLPEventFacility *ef)
{
@@ -240,12 +240,13 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb)
sclp_active_selection_mask = sclp_cp_receive_mask;
break;
case SCLP_SELECTIVE_READ:
- if (!(sclp_cp_receive_mask & be32_to_cpu(red->mask))) {
+ sclp_active_selection_mask = be32_to_cpu(red->mask);
+ if (!sclp_cp_receive_mask ||
+ (sclp_active_selection_mask & ~sclp_cp_receive_mask)) {
sccb->h.response_code =
cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK);
goto out;
}
- sclp_active_selection_mask = be32_to_cpu(red->mask);
break;
default:
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION);
@@ -286,8 +287,26 @@ out:
#define TYPE_SCLP_EVENTS_BUS "s390-sclp-events-bus"
+static void sclp_events_bus_realize(BusState *bus, Error **errp)
+{
+ BusChild *kid;
+
+ /* TODO: recursive realization has to be done in common code */
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+
+ object_property_set_bool(OBJECT(dev), true, "realized", errp);
+ if (*errp) {
+ return;
+ }
+ }
+}
+
static void sclp_events_bus_class_init(ObjectClass *klass, void *data)
{
+ BusClass *bc = BUS_CLASS(klass);
+
+ bc->realize = sclp_events_bus_realize;
}
static const TypeInfo sclp_events_bus_info = {
@@ -324,26 +343,26 @@ static const VMStateDescription vmstate_event_facility = {
}
};
-static int init_event_facility(SCLPEventFacility *event_facility)
+static void init_event_facility(Object *obj)
{
- DeviceState *sdev = DEVICE(event_facility);
- DeviceState *quiesce;
+ SCLPEventFacility *event_facility = EVENT_FACILITY(obj);
+ DeviceState *sdev = DEVICE(obj);
+ Object *new;
/* Spawn a new bus for SCLP events */
qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus),
TYPE_SCLP_EVENTS_BUS, sdev, NULL);
- quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce");
- if (!quiesce) {
- return -1;
- }
- qdev_init_nofail(quiesce);
-
- object_initialize(&cpu_hotplug, sizeof(cpu_hotplug), TYPE_SCLP_CPU_HOTPLUG);
- qdev_set_parent_bus(DEVICE(&cpu_hotplug), BUS(&event_facility->sbus));
- object_property_set_bool(OBJECT(&cpu_hotplug), true, "realized", NULL);
+ new = object_new(TYPE_SCLP_QUIESCE);
+ object_property_add_child(obj, TYPE_SCLP_QUIESCE, new, NULL);
+ object_unref(new);
+ qdev_set_parent_bus(DEVICE(new), &event_facility->sbus.qbus);
- return 0;
+ new = object_new(TYPE_SCLP_CPU_HOTPLUG);
+ object_property_add_child(obj, TYPE_SCLP_CPU_HOTPLUG, new, NULL);
+ object_unref(new);
+ qdev_set_parent_bus(DEVICE(new), &event_facility->sbus.qbus);
+ /* the facility will automatically realize the devices via the bus */
}
static void reset_event_facility(DeviceState *dev)
@@ -362,7 +381,6 @@ static void init_event_facility_class(ObjectClass *klass, void *data)
dc->reset = reset_event_facility;
dc->vmsd = &vmstate_event_facility;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- k->init = init_event_facility;
k->command_handler = command_handler;
k->event_pending = event_pending;
}
@@ -370,6 +388,7 @@ static void init_event_facility_class(ObjectClass *klass, void *data)
static const TypeInfo sclp_event_facility_info = {
.name = TYPE_SCLP_EVENT_FACILITY,
.parent = TYPE_SYS_BUS_DEVICE,
+ .instance_init = init_event_facility,
.instance_size = sizeof(SCLPEventFacility),
.class_init = init_event_facility_class,
.class_size = sizeof(SCLPEventFacilityClass),
diff --git a/qemu/hw/s390x/ipl.c b/qemu/hw/s390x/ipl.c
index 2e0a8b6e0..f10420027 100644
--- a/qemu/hw/s390x/ipl.c
+++ b/qemu/hw/s390x/ipl.c
@@ -11,11 +11,12 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "cpu.h"
#include "elf.h"
#include "hw/loader.h"
-#include "hw/sysbus.h"
#include "hw/s390x/virtio-ccw.h"
#include "hw/s390x/css.h"
#include "ipl.h"
@@ -29,44 +30,6 @@
#define ZIPL_IMAGE_START 0x009000UL
#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64)
-#define TYPE_S390_IPL "s390-ipl"
-#define S390_IPL(obj) \
- OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL)
-#if 0
-#define S390_IPL_CLASS(klass) \
- OBJECT_CLASS_CHECK(S390IPLState, (klass), TYPE_S390_IPL)
-#define S390_IPL_GET_CLASS(obj) \
- OBJECT_GET_CLASS(S390IPLState, (obj), TYPE_S390_IPL)
-#endif
-
-typedef struct S390IPLClass {
- /*< private >*/
- SysBusDeviceClass parent_class;
- /*< public >*/
-
- void (*parent_reset) (SysBusDevice *dev);
-} S390IPLClass;
-
-typedef struct S390IPLState {
- /*< private >*/
- SysBusDevice parent_obj;
- uint64_t start_addr;
- uint64_t bios_start_addr;
- bool enforce_bios;
- IplParameterBlock iplb;
- bool iplb_valid;
- bool reipl_requested;
-
- /*< public >*/
- char *kernel;
- char *initrd;
- char *cmdline;
- char *firmware;
- uint8_t cssid;
- uint8_t ssid;
- uint16_t devno;
-} S390IPLState;
-
static const VMStateDescription vmstate_iplb = {
.name = "ipl/iplb",
.version_id = 0,
@@ -95,6 +58,11 @@ static const VMStateDescription vmstate_ipl = {
}
};
+static S390IPLState *get_ipl_device(void)
+{
+ return S390_IPL(object_resolve_path_type("", TYPE_S390_IPL, NULL));
+}
+
static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr)
{
uint64_t dstaddr = *(uint64_t *) opaque;
@@ -105,11 +73,12 @@ static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr)
return srcaddr + dstaddr;
}
-static int s390_ipl_init(SysBusDevice *dev)
+static void s390_ipl_realize(DeviceState *dev, Error **errp)
{
S390IPLState *ipl = S390_IPL(dev);
uint64_t pentry = KERN_IMAGE_START;
int kernel_size;
+ Error *err = NULL;
int bios_size;
char *bios_filename;
@@ -127,17 +96,18 @@ static int s390_ipl_init(SysBusDevice *dev)
bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (bios_filename == NULL) {
- hw_error("could not find stage1 bootloader\n");
+ error_setg(&err, "could not find stage1 bootloader");
+ goto error;
}
bios_size = load_elf(bios_filename, bios_translate_addr, &fwbase,
&ipl->bios_start_addr, NULL, NULL, 1,
- ELF_MACHINE, 0);
+ EM_S390, 0, 0);
if (bios_size > 0) {
/* Adjust ELF start address to final location */
ipl->bios_start_addr += fwbase;
} else {
- /* Try to load non-ELF file (e.g. s390-zipl.rom) */
+ /* Try to load non-ELF file (e.g. s390-ccw.img) */
bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START,
4096);
ipl->bios_start_addr = ZIPL_IMAGE_START;
@@ -145,7 +115,8 @@ static int s390_ipl_init(SysBusDevice *dev)
g_free(bios_filename);
if (bios_size == -1) {
- hw_error("could not load bootloader '%s'\n", bios_name);
+ error_setg(&err, "could not load bootloader '%s'", bios_name);
+ goto error;
}
/* default boot target is the bios */
@@ -154,13 +125,13 @@ static int s390_ipl_init(SysBusDevice *dev)
if (ipl->kernel) {
kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL,
- NULL, 1, ELF_MACHINE, 0);
+ NULL, 1, EM_S390, 0, 0);
if (kernel_size < 0) {
kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
}
if (kernel_size < 0) {
- fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel);
- return -1;
+ error_setg(&err, "could not load kernel '%s'", ipl->kernel);
+ goto error;
}
/*
* Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the
@@ -187,9 +158,8 @@ static int s390_ipl_init(SysBusDevice *dev)
initrd_size = load_image_targphys(ipl->initrd, initrd_offset,
ram_size - initrd_offset);
if (initrd_size == -1) {
- fprintf(stderr, "qemu: could not load initrd '%s'\n",
- ipl->initrd);
- exit(1);
+ error_setg(&err, "could not load initrd '%s'", ipl->initrd);
+ goto error;
}
/*
@@ -200,7 +170,9 @@ static int s390_ipl_init(SysBusDevice *dev)
stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size);
}
}
- return 0;
+ qemu_register_reset(qdev_reset_all_fn, dev);
+error:
+ error_propagate(errp, err);
}
static Property s390_ipl_properties[] = {
@@ -218,7 +190,7 @@ static Property s390_ipl_properties[] = {
* - -1 if no valid boot device was found
* - ccw id of the boot device otherwise
*/
-static uint64_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl)
+static uint64_t s390_update_iplstate(S390IPLState *ipl)
{
DeviceState *dev_st;
@@ -251,25 +223,19 @@ out:
return (uint32_t) (ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno);
}
-int s390_ipl_update_diag308(IplParameterBlock *iplb)
+void s390_ipl_update_diag308(IplParameterBlock *iplb)
{
- S390IPLState *ipl;
+ S390IPLState *ipl = get_ipl_device();
- ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
- if (ipl) {
- ipl->iplb = *iplb;
- ipl->iplb_valid = true;
- return 0;
- }
- return -1;
+ ipl->iplb = *iplb;
+ ipl->iplb_valid = true;
}
IplParameterBlock *s390_ipl_get_iplb(void)
{
- S390IPLState *ipl;
+ S390IPLState *ipl = get_ipl_device();
- ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
- if (!ipl || !ipl->iplb_valid) {
+ if (!ipl->iplb_valid) {
return NULL;
}
return &ipl->iplb;
@@ -277,41 +243,40 @@ IplParameterBlock *s390_ipl_get_iplb(void)
void s390_reipl_request(void)
{
- S390IPLState *ipl;
+ S390IPLState *ipl = get_ipl_device();
- ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
ipl->reipl_requested = true;
qemu_system_reset_request();
}
+void s390_ipl_prepare_cpu(S390CPU *cpu)
+{
+ S390IPLState *ipl = get_ipl_device();
+
+ cpu->env.psw.addr = ipl->start_addr;
+ cpu->env.psw.mask = IPL_PSW_MASK;
+
+ if (!ipl->kernel || ipl->iplb_valid) {
+ cpu->env.psw.addr = ipl->bios_start_addr;
+ cpu->env.regs[7] = s390_update_iplstate(ipl);
+ }
+}
+
static void s390_ipl_reset(DeviceState *dev)
{
S390IPLState *ipl = S390_IPL(dev);
- S390CPU *cpu = S390_CPU(qemu_get_cpu(0));
- CPUS390XState *env = &cpu->env;
-
- env->psw.addr = ipl->start_addr;
- env->psw.mask = IPL_PSW_MASK;
if (!ipl->reipl_requested) {
ipl->iplb_valid = false;
}
ipl->reipl_requested = false;
-
- if (!ipl->kernel || ipl->iplb_valid) {
- env->psw.addr = ipl->bios_start_addr;
- env->regs[7] = s390_update_iplstate(env, ipl);
- }
-
- s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
}
static void s390_ipl_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = s390_ipl_init;
+ dc->realize = s390_ipl_realize;
dc->props = s390_ipl_properties;
dc->reset = s390_ipl_reset;
dc->vmsd = &vmstate_ipl;
@@ -320,8 +285,8 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data)
static const TypeInfo s390_ipl_info = {
.class_init = s390_ipl_class_init,
- .parent = TYPE_SYS_BUS_DEVICE,
- .name = "s390-ipl",
+ .parent = TYPE_DEVICE,
+ .name = TYPE_S390_IPL,
.instance_size = sizeof(S390IPLState),
};
diff --git a/qemu/hw/s390x/ipl.h b/qemu/hw/s390x/ipl.h
index 70497bc65..6b48ed7b9 100644
--- a/qemu/hw/s390x/ipl.h
+++ b/qemu/hw/s390x/ipl.h
@@ -12,14 +12,42 @@
#ifndef HW_S390_IPL_H
#define HW_S390_IPL_H
+#include "hw/qdev.h"
+#include "cpu.h"
+
typedef struct IplParameterBlock {
uint8_t reserved1[110];
uint16_t devno;
uint8_t reserved2[88];
} IplParameterBlock;
-int s390_ipl_update_diag308(IplParameterBlock *iplb);
+void s390_ipl_update_diag308(IplParameterBlock *iplb);
+void s390_ipl_prepare_cpu(S390CPU *cpu);
IplParameterBlock *s390_ipl_get_iplb(void);
void s390_reipl_request(void);
+#define TYPE_S390_IPL "s390-ipl"
+#define S390_IPL(obj) OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL)
+
+struct S390IPLState {
+ /*< private >*/
+ DeviceState parent_obj;
+ uint64_t start_addr;
+ uint64_t bios_start_addr;
+ bool enforce_bios;
+ IplParameterBlock iplb;
+ bool iplb_valid;
+ bool reipl_requested;
+
+ /*< public >*/
+ char *kernel;
+ char *initrd;
+ char *cmdline;
+ char *firmware;
+ uint8_t cssid;
+ uint8_t ssid;
+ uint16_t devno;
+};
+typedef struct S390IPLState S390IPLState;
+
#endif
diff --git a/qemu/hw/s390x/s390-pci-bus.c b/qemu/hw/s390x/s390-pci-bus.c
index 560b66a50..918b58543 100644
--- a/qemu/hw/s390x/s390-pci-bus.c
+++ b/qemu/hw/s390x/s390-pci-bus.c
@@ -11,6 +11,9 @@
* directory.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "s390-pci-bus.h"
#include <hw/pci/pci_bus.h>
#include <hw/pci/msi.h>
@@ -123,7 +126,6 @@ void s390_pci_sclp_configure(int configure, SCCB *sccb)
}
psccb->header.response_code = cpu_to_be16(rc);
- return;
}
static uint32_t s390_pci_get_pfid(PCIDevice *pdev)
@@ -308,9 +310,8 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
{
uint64_t pte;
uint32_t flags;
- S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, mr);
- S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pbdev->pdev)
- ->qbus.parent);
+ S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, iommu_mr);
+ S390pciState *s;
IOMMUTLBEntry ret = {
.target_as = &address_space_memory,
.iova = 0,
@@ -319,8 +320,13 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
.perm = IOMMU_NONE,
};
+ if (!pbdev->configured || !pbdev->pdev || !(pbdev->fh & FH_ENABLED)) {
+ return ret;
+ }
+
DPRINTF("iommu trans addr 0x%" PRIx64 "\n", addr);
+ s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pbdev->pdev)->qbus.parent);
/* s390 does not have an APIC mapped to main storage so we use
* a separate AddressSpace only for msix notifications
*/
@@ -425,6 +431,10 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data,
return;
}
+ if (!(pbdev->fh & FH_ENABLED)) {
+ return;
+ }
+
ind_bit = pbdev->routes.adapter.ind_offset;
sum_bit = pbdev->routes.adapter.summary_offset;
@@ -435,8 +445,6 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data,
io_int_word = (pbdev->isc << 27) | IO_INT_WORD_AI;
s390_io_interrupt(0, 0, 0, io_int_word);
}
-
- return;
}
static uint64_t s390_msi_ctrl_read(void *opaque, hwaddr addr, unsigned size)
@@ -450,14 +458,32 @@ static const MemoryRegionOps s390_msi_ctrl_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
+void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable)
+{
+ pbdev->configured = false;
+
+ if (enable) {
+ uint64_t size = pbdev->pal - pbdev->pba + 1;
+ memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->mr),
+ &s390_iommu_ops, "iommu-s390", size);
+ memory_region_add_subregion(&pbdev->mr, pbdev->pba, &pbdev->iommu_mr);
+ } else {
+ memory_region_del_subregion(&pbdev->mr, &pbdev->iommu_mr);
+ }
+
+ pbdev->configured = true;
+}
+
static void s390_pcihost_init_as(S390pciState *s)
{
int i;
+ S390PCIBusDevice *pbdev;
for (i = 0; i < PCI_SLOT_MAX; i++) {
- memory_region_init_iommu(&s->pbdev[i].mr, OBJECT(s),
- &s390_iommu_ops, "iommu-s390", UINT64_MAX);
- address_space_init(&s->pbdev[i].as, &s->pbdev[i].mr, "iommu-pci");
+ pbdev = &s->pbdev[i];
+ memory_region_init(&pbdev->mr, OBJECT(s),
+ "iommu-root-s390", UINT64_MAX);
+ address_space_init(&pbdev->as, &pbdev->mr, "iommu-pci");
}
memory_region_init_io(&s->msix_notify_mr, OBJECT(s),
@@ -500,7 +526,7 @@ static int s390_pcihost_setup_msix(S390PCIBusDevice *pbdev)
return 0;
}
- ctrl = pci_host_config_read_common(pbdev->pdev, pos + PCI_CAP_FLAGS,
+ ctrl = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_FLAGS,
pci_config_size(pbdev->pdev), sizeof(ctrl));
table = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_TABLE,
pci_config_size(pbdev->pdev), sizeof(table));
@@ -539,7 +565,6 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev,
s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED,
pbdev->fh, pbdev->fid);
}
- return;
}
static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev,
@@ -574,7 +599,7 @@ static void s390_pcihost_class_init(ObjectClass *klass, void *data)
k->init = s390_pcihost_init;
hc->plug = s390_pcihost_hot_plug;
hc->unplug = s390_pcihost_hot_unplug;
- msi_supported = true;
+ msi_nonbroken = true;
}
static const TypeInfo s390_pcihost_info = {
diff --git a/qemu/hw/s390x/s390-pci-bus.h b/qemu/hw/s390x/s390-pci-bus.h
index 464a92eed..59fd5c958 100644
--- a/qemu/hw/s390x/s390-pci-bus.h
+++ b/qemu/hw/s390x/s390-pci-bus.h
@@ -23,6 +23,7 @@
#define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost"
#define FH_VIRT 0x00ff0000
#define ENABLE_BIT_OFFSET 31
+#define FH_ENABLED (1 << ENABLE_BIT_OFFSET)
#define S390_PCIPT_ADAPTER 2
#define S390_PCI_HOST_BRIDGE(obj) \
@@ -231,6 +232,9 @@ typedef struct S390PCIBusDevice {
AdapterRoutes routes;
AddressSpace as;
MemoryRegion mr;
+ MemoryRegion iommu_mr;
+ IndAddr *summary_ind;
+ IndAddr *indicator;
} S390PCIBusDevice;
typedef struct S390pciState {
@@ -244,6 +248,7 @@ typedef struct S390pciState {
int chsc_sei_nt2_get_event(void *res);
int chsc_sei_nt2_have_event(void);
void s390_pci_sclp_configure(int configure, SCCB *sccb);
+void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable);
S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx);
S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh);
S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid);
diff --git a/qemu/hw/s390x/s390-pci-inst.c b/qemu/hw/s390x/s390-pci-inst.c
index f9151a9af..b28e7d14f 100644
--- a/qemu/hw/s390x/s390-pci-inst.c
+++ b/qemu/hw/s390x/s390-pci-inst.c
@@ -11,6 +11,9 @@
* directory.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "s390-pci-inst.h"
#include "s390-pci-bus.h"
#include <exec/memory-internal.h>
@@ -105,7 +108,8 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc)
pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID));
stw_p(&rrb->response.fh_list[idx - resume_token].vendor_id,
pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID));
- stl_p(&rrb->response.fh_list[idx - resume_token].config, 0x80000000);
+ stl_p(&rrb->response.fh_list[idx - resume_token].config,
+ pbdev->configured << 31);
stl_p(&rrb->response.fh_list[idx - resume_token].fid, pbdev->fid);
stl_p(&rrb->response.fh_list[idx - resume_token].fh, pbdev->fh);
@@ -208,12 +212,12 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
switch (reqsetpci->oc) {
case CLP_SET_ENABLE_PCI_FN:
- pbdev->fh = pbdev->fh | 1 << ENABLE_BIT_OFFSET;
+ pbdev->fh = pbdev->fh | FH_ENABLED;
stl_p(&ressetpci->fh, pbdev->fh);
stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
break;
case CLP_SET_DISABLE_PCI_FN:
- pbdev->fh = pbdev->fh & ~(1 << ENABLE_BIT_OFFSET);
+ pbdev->fh = pbdev->fh & ~FH_ENABLED;
pbdev->error_state = false;
pbdev->lgstg_blocked = false;
stl_p(&ressetpci->fh, pbdev->fh);
@@ -313,7 +317,7 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
offset = env->regs[r2 + 1];
pbdev = s390_pci_find_dev_by_fh(fh);
- if (!pbdev) {
+ if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
DPRINTF("pcilg no pci dev\n");
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
return 0;
@@ -430,7 +434,7 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
offset = env->regs[r2 + 1];
pbdev = s390_pci_find_dev_by_fh(fh);
- if (!pbdev) {
+ if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
DPRINTF("pcistg no pci dev\n");
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
return 0;
@@ -521,14 +525,13 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
end = start + env->regs[r2 + 1];
pbdev = s390_pci_find_dev_by_fh(fh);
-
- if (!pbdev) {
+ if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
DPRINTF("rpcit no pci dev\n");
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
goto out;
}
- mr = pci_device_iommu_address_space(pbdev->pdev)->root;
+ mr = &pbdev->iommu_mr;
while (start < end) {
entry = mr->iommu_ops->translate(mr, start, 0);
@@ -586,7 +589,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
}
pbdev = s390_pci_find_dev_by_fh(fh);
- if (!pbdev) {
+ if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
DPRINTF("pcistb no pci dev fh 0x%x\n", fh);
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
return 0;
@@ -620,19 +623,19 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
{
- int ret;
- S390FLICState *fs = s390_get_flic();
- S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
+ int ret, len;
ret = css_register_io_adapter(S390_PCIPT_ADAPTER,
FIB_DATA_ISC(ldl_p(&fib.data)), true, false,
&pbdev->routes.adapter.adapter_id);
assert(ret == 0);
- fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id,
- ldq_p(&fib.aisb), true);
- fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id,
- ldq_p(&fib.aibv), true);
+ pbdev->summary_ind = get_indicator(ldq_p(&fib.aisb), sizeof(uint64_t));
+ len = BITS_TO_LONGS(FIB_DATA_NOI(ldl_p(&fib.data))) * sizeof(unsigned long);
+ pbdev->indicator = get_indicator(ldq_p(&fib.aibv), len);
+
+ map_indicator(&pbdev->routes.adapter, pbdev->summary_ind);
+ map_indicator(&pbdev->routes.adapter, pbdev->indicator);
pbdev->routes.adapter.summary_addr = ldq_p(&fib.aisb);
pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data));
@@ -648,12 +651,11 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
static int dereg_irqs(S390PCIBusDevice *pbdev)
{
- S390FLICState *fs = s390_get_flic();
- S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
-
- fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id,
- pbdev->routes.adapter.ind_addr, false);
+ release_indicator(&pbdev->routes.adapter, pbdev->summary_ind);
+ release_indicator(&pbdev->routes.adapter, pbdev->indicator);
+ pbdev->summary_ind = NULL;
+ pbdev->indicator = NULL;
pbdev->routes.adapter.summary_addr = 0;
pbdev->routes.adapter.summary_offset = 0;
pbdev->routes.adapter.ind_addr = 0;
@@ -689,6 +691,9 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
pbdev->pba = pba;
pbdev->pal = pal;
pbdev->g_iota = g_iota;
+
+ s390_pcihost_iommu_configure(pbdev, true);
+
return 0;
}
@@ -697,6 +702,8 @@ static void dereg_ioat(S390PCIBusDevice *pbdev)
pbdev->pba = 0;
pbdev->pal = 0;
pbdev->g_iota = 0;
+
+ s390_pcihost_iommu_configure(pbdev, false);
}
int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
@@ -722,7 +729,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
}
pbdev = s390_pci_find_dev_by_fh(fh);
- if (!pbdev) {
+ if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
DPRINTF("mpcifc no pci dev fh 0x%x\n", fh);
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
return 0;
@@ -814,7 +821,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset;
stl_p(&fib.data, data);
- if (pbdev->fh >> ENABLE_BIT_OFFSET) {
+ if (pbdev->fh & FH_ENABLED) {
fib.fc |= 0x80;
}
diff --git a/qemu/hw/s390x/s390-skeys-kvm.c b/qemu/hw/s390x/s390-skeys-kvm.c
new file mode 100644
index 000000000..131da56bb
--- /dev/null
+++ b/qemu/hw/s390x/s390-skeys-kvm.c
@@ -0,0 +1,76 @@
+/*
+ * s390 storage key device
+ *
+ * Copyright 2015 IBM Corp.
+ * Author(s): Jason J. Herne <jjherne@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/s390x/storage-keys.h"
+#include "sysemu/kvm.h"
+#include "qemu/error-report.h"
+
+static int kvm_s390_skeys_enabled(S390SKeysState *ss)
+{
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ uint8_t single_key;
+ int r;
+
+ r = skeyclass->get_skeys(ss, 0, 1, &single_key);
+ if (r != 0 && r != KVM_S390_GET_SKEYS_NONE) {
+ error_report("S390_GET_KEYS error %d", r);
+ }
+ return (r == 0);
+}
+
+static int kvm_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ struct kvm_s390_skeys args = {
+ .start_gfn = start_gfn,
+ .count = count,
+ .skeydata_addr = (__u64)keys
+ };
+
+ return kvm_vm_ioctl(kvm_state, KVM_S390_GET_SKEYS, &args);
+}
+
+static int kvm_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ struct kvm_s390_skeys args = {
+ .start_gfn = start_gfn,
+ .count = count,
+ .skeydata_addr = (__u64)keys
+ };
+
+ return kvm_vm_ioctl(kvm_state, KVM_S390_SET_SKEYS, &args);
+}
+
+static void kvm_s390_skeys_class_init(ObjectClass *oc, void *data)
+{
+ S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
+
+ skeyclass->skeys_enabled = kvm_s390_skeys_enabled;
+ skeyclass->get_skeys = kvm_s390_skeys_get;
+ skeyclass->set_skeys = kvm_s390_skeys_set;
+}
+
+static const TypeInfo kvm_s390_skeys_info = {
+ .name = TYPE_KVM_S390_SKEYS,
+ .parent = TYPE_S390_SKEYS,
+ .instance_size = sizeof(S390SKeysState),
+ .class_init = kvm_s390_skeys_class_init,
+ .class_size = sizeof(S390SKeysClass),
+};
+
+static void kvm_s390_skeys_register_types(void)
+{
+ type_register_static(&kvm_s390_skeys_info);
+}
+
+type_init(kvm_s390_skeys_register_types)
diff --git a/qemu/hw/s390x/s390-skeys.c b/qemu/hw/s390x/s390-skeys.c
new file mode 100644
index 000000000..6528ffed1
--- /dev/null
+++ b/qemu/hw/s390x/s390-skeys.c
@@ -0,0 +1,415 @@
+/*
+ * s390 storage key device
+ *
+ * Copyright 2015 IBM Corp.
+ * Author(s): Jason J. Herne <jjherne@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/boards.h"
+#include "qmp-commands.h"
+#include "migration/qemu-file.h"
+#include "hw/s390x/storage-keys.h"
+#include "qemu/error-report.h"
+
+#define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */
+#define S390_SKEYS_SAVE_FLAG_EOS 0x01
+#define S390_SKEYS_SAVE_FLAG_SKEYS 0x02
+#define S390_SKEYS_SAVE_FLAG_ERROR 0x04
+
+S390SKeysState *s390_get_skeys_device(void)
+{
+ S390SKeysState *ss;
+
+ ss = S390_SKEYS(object_resolve_path_type("", TYPE_S390_SKEYS, NULL));
+ assert(ss);
+ return ss;
+}
+
+void s390_skeys_init(void)
+{
+ Object *obj;
+
+ if (kvm_enabled()) {
+ obj = object_new(TYPE_KVM_S390_SKEYS);
+ } else {
+ obj = object_new(TYPE_QEMU_S390_SKEYS);
+ }
+ object_property_add_child(qdev_get_machine(), TYPE_S390_SKEYS,
+ obj, NULL);
+ object_unref(obj);
+
+ qdev_init_nofail(DEVICE(obj));
+}
+
+static void write_keys(QEMUFile *f, uint8_t *keys, uint64_t startgfn,
+ uint64_t count, Error **errp)
+{
+ uint64_t curpage = startgfn;
+ uint64_t maxpage = curpage + count - 1;
+ const char *fmt = "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
+ " ch=%d, reserved=%d\n";
+ char buf[128];
+ int len;
+
+ for (; curpage <= maxpage; curpage++) {
+ uint8_t acc = (*keys & 0xF0) >> 4;
+ int fp = (*keys & 0x08);
+ int ref = (*keys & 0x04);
+ int ch = (*keys & 0x02);
+ int res = (*keys & 0x01);
+
+ len = snprintf(buf, sizeof(buf), fmt, curpage,
+ *keys, acc, fp, ref, ch, res);
+ assert(len < sizeof(buf));
+ qemu_put_buffer(f, (uint8_t *)buf, len);
+ keys++;
+ }
+}
+
+void hmp_info_skeys(Monitor *mon, const QDict *qdict)
+{
+ S390SKeysState *ss = s390_get_skeys_device();
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ uint64_t addr = qdict_get_int(qdict, "addr");
+ uint8_t key;
+ int r;
+
+ /* Quick check to see if guest is using storage keys*/
+ if (!skeyclass->skeys_enabled(ss)) {
+ monitor_printf(mon, "Error: This guest is not using storage keys\n");
+ return;
+ }
+
+ r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
+ if (r < 0) {
+ monitor_printf(mon, "Error: %s\n", strerror(-r));
+ return;
+ }
+
+ monitor_printf(mon, " key: 0x%X\n", key);
+}
+
+void hmp_dump_skeys(Monitor *mon, const QDict *qdict)
+{
+ const char *filename = qdict_get_str(qdict, "filename");
+ Error *err = NULL;
+
+ qmp_dump_skeys(filename, &err);
+ if (err) {
+ error_report_err(err);
+ }
+}
+
+void qmp_dump_skeys(const char *filename, Error **errp)
+{
+ S390SKeysState *ss = s390_get_skeys_device();
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ const uint64_t total_count = ram_size / TARGET_PAGE_SIZE;
+ uint64_t handled_count = 0, cur_count;
+ Error *lerr = NULL;
+ vaddr cur_gfn = 0;
+ uint8_t *buf;
+ int ret;
+ QEMUFile *f;
+
+ /* Quick check to see if guest is using storage keys*/
+ if (!skeyclass->skeys_enabled(ss)) {
+ error_setg(errp, "This guest is not using storage keys - "
+ "nothing to dump");
+ return;
+ }
+
+ f = qemu_fopen(filename, "wb");
+ if (!f) {
+ error_setg_file_open(errp, errno, filename);
+ return;
+ }
+
+ buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
+ if (!buf) {
+ error_setg(errp, "Could not allocate memory");
+ goto out;
+ }
+
+ /* we'll only dump initial memory for now */
+ while (handled_count < total_count) {
+ /* Calculate how many keys to ask for & handle overflow case */
+ cur_count = MIN(total_count - handled_count, S390_SKEYS_BUFFER_SIZE);
+
+ ret = skeyclass->get_skeys(ss, cur_gfn, cur_count, buf);
+ if (ret < 0) {
+ error_setg(errp, "get_keys error %d", ret);
+ goto out_free;
+ }
+
+ /* write keys to stream */
+ write_keys(f, buf, cur_gfn, cur_count, &lerr);
+ if (lerr) {
+ goto out_free;
+ }
+
+ cur_gfn += cur_count;
+ handled_count += cur_count;
+ }
+
+out_free:
+ error_propagate(errp, lerr);
+ g_free(buf);
+out:
+ qemu_fclose(f);
+}
+
+static void qemu_s390_skeys_init(Object *obj)
+{
+ QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj);
+ MachineState *machine = MACHINE(qdev_get_machine());
+
+ skeys->key_count = machine->maxram_size / TARGET_PAGE_SIZE;
+ skeys->keydata = g_malloc0(skeys->key_count);
+}
+
+static int qemu_s390_skeys_enabled(S390SKeysState *ss)
+{
+ return 1;
+}
+
+/*
+ * TODO: for memory hotplug support qemu_s390_skeys_set and qemu_s390_skeys_get
+ * will have to make sure that the given gfn belongs to a memory region and not
+ * a memory hole.
+ */
+static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
+ int i;
+
+ /* Check for uint64 overflow and access beyond end of key data */
+ if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
+ error_report("Error: Setting storage keys for page beyond the end "
+ "of memory: gfn=%" PRIx64 " count=%" PRId64,
+ start_gfn, count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ skeydev->keydata[start_gfn + i] = keys[i];
+ }
+ return 0;
+}
+
+static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
+ int i;
+
+ /* Check for uint64 overflow and access beyond end of key data */
+ if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
+ error_report("Error: Getting storage keys for page beyond the end "
+ "of memory: gfn=%" PRIx64 " count=%" PRId64,
+ start_gfn, count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ keys[i] = skeydev->keydata[start_gfn + i];
+ }
+ return 0;
+}
+
+static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data)
+{
+ S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
+
+ skeyclass->skeys_enabled = qemu_s390_skeys_enabled;
+ skeyclass->get_skeys = qemu_s390_skeys_get;
+ skeyclass->set_skeys = qemu_s390_skeys_set;
+}
+
+static const TypeInfo qemu_s390_skeys_info = {
+ .name = TYPE_QEMU_S390_SKEYS,
+ .parent = TYPE_S390_SKEYS,
+ .instance_init = qemu_s390_skeys_init,
+ .instance_size = sizeof(QEMUS390SKeysState),
+ .class_init = qemu_s390_skeys_class_init,
+ .class_size = sizeof(S390SKeysClass),
+};
+
+static void s390_storage_keys_save(QEMUFile *f, void *opaque)
+{
+ S390SKeysState *ss = S390_SKEYS(opaque);
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ uint64_t pages_left = ram_size / TARGET_PAGE_SIZE;
+ uint64_t read_count, eos = S390_SKEYS_SAVE_FLAG_EOS;
+ vaddr cur_gfn = 0;
+ int error = 0;
+ uint8_t *buf;
+
+ if (!skeyclass->skeys_enabled(ss)) {
+ goto end_stream;
+ }
+
+ buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
+ if (!buf) {
+ error_report("storage key save could not allocate memory");
+ goto end_stream;
+ }
+
+ /* We only support initial memory. Standby memory is not handled yet. */
+ qemu_put_be64(f, (cur_gfn * TARGET_PAGE_SIZE) | S390_SKEYS_SAVE_FLAG_SKEYS);
+ qemu_put_be64(f, pages_left);
+
+ while (pages_left) {
+ read_count = MIN(pages_left, S390_SKEYS_BUFFER_SIZE);
+
+ if (!error) {
+ error = skeyclass->get_skeys(ss, cur_gfn, read_count, buf);
+ if (error) {
+ /*
+ * If error: we want to fill the stream with valid data instead
+ * of stopping early so we pad the stream with 0x00 values and
+ * use S390_SKEYS_SAVE_FLAG_ERROR to indicate failure to the
+ * reading side.
+ */
+ error_report("S390_GET_KEYS error %d", error);
+ memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
+ eos = S390_SKEYS_SAVE_FLAG_ERROR;
+ }
+ }
+
+ qemu_put_buffer(f, buf, read_count);
+ cur_gfn += read_count;
+ pages_left -= read_count;
+ }
+
+ g_free(buf);
+end_stream:
+ qemu_put_be64(f, eos);
+}
+
+static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S390SKeysState *ss = S390_SKEYS(opaque);
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ int ret = 0;
+
+ while (!ret) {
+ ram_addr_t addr;
+ int flags;
+
+ addr = qemu_get_be64(f);
+ flags = addr & ~TARGET_PAGE_MASK;
+ addr &= TARGET_PAGE_MASK;
+
+ switch (flags) {
+ case S390_SKEYS_SAVE_FLAG_SKEYS: {
+ const uint64_t total_count = qemu_get_be64(f);
+ uint64_t handled_count = 0, cur_count;
+ uint64_t cur_gfn = addr / TARGET_PAGE_SIZE;
+ uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
+
+ if (!buf) {
+ error_report("storage key load could not allocate memory");
+ ret = -ENOMEM;
+ break;
+ }
+
+ while (handled_count < total_count) {
+ cur_count = MIN(total_count - handled_count,
+ S390_SKEYS_BUFFER_SIZE);
+ qemu_get_buffer(f, buf, cur_count);
+
+ ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf);
+ if (ret < 0) {
+ error_report("S390_SET_KEYS error %d", ret);
+ break;
+ }
+ handled_count += cur_count;
+ cur_gfn += cur_count;
+ }
+ g_free(buf);
+ break;
+ }
+ case S390_SKEYS_SAVE_FLAG_ERROR: {
+ error_report("Storage key data is incomplete");
+ ret = -EINVAL;
+ break;
+ }
+ case S390_SKEYS_SAVE_FLAG_EOS:
+ /* normal exit */
+ return 0;
+ default:
+ error_report("Unexpected storage key flag data: %#x", flags);
+ ret = -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp)
+{
+ S390SKeysState *ss = S390_SKEYS(obj);
+
+ return ss->migration_enabled;
+}
+
+static inline void s390_skeys_set_migration_enabled(Object *obj, bool value,
+ Error **errp)
+{
+ S390SKeysState *ss = S390_SKEYS(obj);
+
+ /* Prevent double registration of savevm handler */
+ if (ss->migration_enabled == value) {
+ return;
+ }
+
+ ss->migration_enabled = value;
+
+ if (ss->migration_enabled) {
+ register_savevm(NULL, TYPE_S390_SKEYS, 0, 1, s390_storage_keys_save,
+ s390_storage_keys_load, ss);
+ } else {
+ unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss);
+ }
+}
+
+static void s390_skeys_instance_init(Object *obj)
+{
+ object_property_add_bool(obj, "migration-enabled",
+ s390_skeys_get_migration_enabled,
+ s390_skeys_set_migration_enabled, NULL);
+ object_property_set_bool(obj, true, "migration-enabled", NULL);
+}
+
+static void s390_skeys_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->hotpluggable = false;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo s390_skeys_info = {
+ .name = TYPE_S390_SKEYS,
+ .parent = TYPE_DEVICE,
+ .instance_init = s390_skeys_instance_init,
+ .instance_size = sizeof(S390SKeysState),
+ .class_init = s390_skeys_class_init,
+ .class_size = sizeof(S390SKeysClass),
+ .abstract = true,
+};
+
+static void qemu_s390_skeys_register_types(void)
+{
+ type_register_static(&s390_skeys_info);
+ type_register_static(&qemu_s390_skeys_info);
+}
+
+type_init(qemu_s390_skeys_register_types)
diff --git a/qemu/hw/s390x/s390-virtio-bus.c b/qemu/hw/s390x/s390-virtio-bus.c
deleted file mode 100644
index 77aec8a5b..000000000
--- a/qemu/hw/s390x/s390-virtio-bus.c
+++ /dev/null
@@ -1,763 +0,0 @@
-/*
- * QEMU S390 virtio target
- *
- * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
- *
- * 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 "hw/hw.h"
-#include "sysemu/block-backend.h"
-#include "sysemu/sysemu.h"
-#include "hw/boards.h"
-#include "hw/loader.h"
-#include "elf.h"
-#include "hw/virtio/virtio.h"
-#include "hw/virtio/virtio-rng.h"
-#include "hw/virtio/virtio-serial.h"
-#include "hw/virtio/virtio-net.h"
-#include "hw/virtio/vhost-scsi.h"
-#include "hw/sysbus.h"
-#include "sysemu/kvm.h"
-
-#include "hw/s390x/s390-virtio-bus.h"
-#include "hw/virtio/virtio-bus.h"
-
-/* #define DEBUG_S390 */
-
-#ifdef DEBUG_S390
-#define DPRINTF(fmt, ...) \
- do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) \
- do { } while (0)
-#endif
-
-#define VIRTIO_S390_QUEUE_MAX 64
-
-static void virtio_s390_bus_new(VirtioBusState *bus, size_t bus_size,
- VirtIOS390Device *dev);
-
-static const TypeInfo s390_virtio_bus_info = {
- .name = TYPE_S390_VIRTIO_BUS,
- .parent = TYPE_BUS,
- .instance_size = sizeof(VirtIOS390Bus),
-};
-
-static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev);
-
-/* length of VirtIO device pages */
-const hwaddr virtio_size = S390_DEVICE_PAGES * TARGET_PAGE_SIZE;
-
-static void s390_virtio_bus_reset(void *opaque)
-{
- VirtIOS390Bus *bus = opaque;
- bus->next_ring = bus->dev_page + TARGET_PAGE_SIZE;
-}
-
-void s390_virtio_reset_idx(VirtIOS390Device *dev)
-{
- int i;
- hwaddr idx_addr;
- uint8_t num_vq;
-
- num_vq = s390_virtio_device_num_vq(dev);
- for (i = 0; i < num_vq; i++) {
- idx_addr = virtio_queue_get_avail_addr(dev->vdev, i) +
- VIRTIO_VRING_AVAIL_IDX_OFFS;
- address_space_stw(&address_space_memory, idx_addr, 0,
- MEMTXATTRS_UNSPECIFIED, NULL);
- idx_addr = virtio_queue_get_avail_addr(dev->vdev, i) +
- virtio_queue_get_avail_size(dev->vdev, i);
- address_space_stw(&address_space_memory, idx_addr, 0,
- MEMTXATTRS_UNSPECIFIED, NULL);
- idx_addr = virtio_queue_get_used_addr(dev->vdev, i) +
- VIRTIO_VRING_USED_IDX_OFFS;
- address_space_stw(&address_space_memory, idx_addr, 0,
- MEMTXATTRS_UNSPECIFIED, NULL);
- idx_addr = virtio_queue_get_used_addr(dev->vdev, i) +
- virtio_queue_get_used_size(dev->vdev, i);
- address_space_stw(&address_space_memory, idx_addr, 0,
- MEMTXATTRS_UNSPECIFIED, NULL);
- }
-}
-
-VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size)
-{
- VirtIOS390Bus *bus;
- BusState *_bus;
- DeviceState *dev;
-
- /* Create bridge device */
- dev = qdev_create(NULL, "s390-virtio-bridge");
- qdev_init_nofail(dev);
-
- /* Create bus on bridge device */
-
- _bus = qbus_create(TYPE_S390_VIRTIO_BUS, dev, "s390-virtio");
- bus = DO_UPCAST(VirtIOS390Bus, bus, _bus);
-
- bus->dev_page = *ram_size;
- bus->dev_offs = bus->dev_page;
- bus->next_ring = bus->dev_page + TARGET_PAGE_SIZE;
-
- /* Enable hotplugging */
- qbus_set_hotplug_handler(_bus, dev, &error_abort);
-
- /* Allocate RAM for VirtIO device pages (descriptors, queues, rings) */
- *ram_size += S390_DEVICE_PAGES * TARGET_PAGE_SIZE;
-
- qemu_register_reset(s390_virtio_bus_reset, bus);
- return bus;
-}
-
-static void s390_virtio_device_init(VirtIOS390Device *dev,
- VirtIODevice *vdev)
-{
- VirtIOS390Bus *bus;
- int dev_len;
-
- bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus);
- dev->vdev = vdev;
- dev->dev_offs = bus->dev_offs;
- dev->feat_len = sizeof(uint32_t); /* always keep 32 bits features */
-
- dev_len = VIRTIO_DEV_OFFS_CONFIG;
- dev_len += s390_virtio_device_num_vq(dev) * VIRTIO_VQCONFIG_LEN;
- dev_len += dev->feat_len * 2;
- dev_len += virtio_bus_get_vdev_config_len(&dev->bus);
-
- bus->dev_offs += dev_len;
-
- s390_virtio_device_sync(dev);
- s390_virtio_reset_idx(dev);
- if (dev->qdev.hotplugged) {
- s390_virtio_irq(VIRTIO_PARAM_DEV_ADD, dev->dev_offs);
- }
-}
-
-static void s390_virtio_net_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- DeviceState *qdev = DEVICE(s390_dev);
- VirtIONetS390 *dev = VIRTIO_NET_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
-
- virtio_net_set_netclient_name(&dev->vdev, qdev->id,
- object_get_typename(OBJECT(qdev)));
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
-}
-
-static void s390_virtio_net_instance_init(Object *obj)
-{
- VirtIONetS390 *dev = VIRTIO_NET_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_NET);
- object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
- "bootindex", &error_abort);
-}
-
-static void s390_virtio_blk_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- VirtIOBlkS390 *dev = VIRTIO_BLK_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
-
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
-}
-
-static void s390_virtio_blk_instance_init(Object *obj)
-{
- VirtIOBlkS390 *dev = VIRTIO_BLK_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_BLK);
- object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread",
- &error_abort);
- object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
- "bootindex", &error_abort);
-}
-
-static void s390_virtio_serial_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- VirtIOSerialS390 *dev = VIRTIO_SERIAL_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- DeviceState *qdev = DEVICE(s390_dev);
- Error *err = NULL;
- VirtIOS390Bus *bus;
- char *bus_name;
-
- bus = DO_UPCAST(VirtIOS390Bus, bus, qdev->parent_bus);
-
- /*
- * For command line compatibility, this sets the virtio-serial-device bus
- * name as before.
- */
- if (qdev->id) {
- bus_name = g_strdup_printf("%s.0", qdev->id);
- virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name);
- g_free(bus_name);
- }
-
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
- bus->console = s390_dev;
-}
-
-static void s390_virtio_serial_instance_init(Object *obj)
-{
- VirtIOSerialS390 *dev = VIRTIO_SERIAL_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_SERIAL);
-}
-
-static void s390_virtio_scsi_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- DeviceState *qdev = DEVICE(s390_dev);
- Error *err = NULL;
- char *bus_name;
-
- /*
- * For command line compatibility, this sets the virtio-scsi-device bus
- * name as before.
- */
- if (qdev->id) {
- bus_name = g_strdup_printf("%s.0", qdev->id);
- virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name);
- g_free(bus_name);
- }
-
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
-}
-
-static void s390_virtio_scsi_instance_init(Object *obj)
-{
- VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_SCSI);
-}
-
-#ifdef CONFIG_VHOST_SCSI
-static void s390_vhost_scsi_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- VHostSCSIS390 *dev = VHOST_SCSI_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
-
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
-}
-
-static void s390_vhost_scsi_instance_init(Object *obj)
-{
- VHostSCSIS390 *dev = VHOST_SCSI_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VHOST_SCSI);
-}
-#endif
-
-
-static void s390_virtio_rng_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- VirtIORNGS390 *dev = VIRTIO_RNG_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
-
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
- object_property_set_link(OBJECT(dev),
- OBJECT(dev->vdev.conf.rng), "rng",
- NULL);
-
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
-}
-
-static void s390_virtio_rng_instance_init(Object *obj)
-{
- VirtIORNGS390 *dev = VIRTIO_RNG_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_RNG);
- object_property_add_alias(obj, "rng", OBJECT(&dev->vdev),
- "rng", &error_abort);
-}
-
-static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq)
-{
- ram_addr_t token_off;
-
- token_off = (dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG) +
- (vq * VIRTIO_VQCONFIG_LEN) +
- VIRTIO_VQCONFIG_OFFS_TOKEN;
-
- return address_space_ldq_be(&address_space_memory, token_off,
- MEMTXATTRS_UNSPECIFIED, NULL);
-}
-
-static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev)
-{
- VirtIODevice *vdev = dev->vdev;
- int num_vq;
-
- for (num_vq = 0; num_vq < VIRTIO_S390_QUEUE_MAX; num_vq++) {
- if (!virtio_queue_get_num(vdev, num_vq)) {
- break;
- }
- }
-
- return num_vq;
-}
-
-static ram_addr_t s390_virtio_next_ring(VirtIOS390Bus *bus)
-{
- ram_addr_t r = bus->next_ring;
-
- bus->next_ring += VIRTIO_RING_LEN;
- return r;
-}
-
-void s390_virtio_device_sync(VirtIOS390Device *dev)
-{
- VirtIOS390Bus *bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus);
- ram_addr_t cur_offs;
- uint8_t num_vq;
- int i;
-
- virtio_reset(dev->vdev);
-
- /* Sync dev space */
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_TYPE,
- dev->vdev->device_id,
- MEMTXATTRS_UNSPECIFIED,
- NULL);
-
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ,
- s390_virtio_device_num_vq(dev),
- MEMTXATTRS_UNSPECIFIED,
- NULL);
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_FEATURE_LEN,
- dev->feat_len,
- MEMTXATTRS_UNSPECIFIED,
- NULL);
-
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG_LEN,
- dev->vdev->config_len,
- MEMTXATTRS_UNSPECIFIED,
- NULL);
-
- num_vq = s390_virtio_device_num_vq(dev);
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ, num_vq,
- MEMTXATTRS_UNSPECIFIED, NULL);
-
- /* Sync virtqueues */
- for (i = 0; i < num_vq; i++) {
- ram_addr_t vq = (dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG) +
- (i * VIRTIO_VQCONFIG_LEN);
- ram_addr_t vring;
-
- vring = s390_virtio_next_ring(bus);
- virtio_queue_set_addr(dev->vdev, i, vring);
- virtio_queue_set_vector(dev->vdev, i, i);
- address_space_stq_be(&address_space_memory,
- vq + VIRTIO_VQCONFIG_OFFS_ADDRESS, vring,
- MEMTXATTRS_UNSPECIFIED, NULL);
- address_space_stw_be(&address_space_memory,
- vq + VIRTIO_VQCONFIG_OFFS_NUM,
- virtio_queue_get_num(dev->vdev, i),
- MEMTXATTRS_UNSPECIFIED,
- NULL);
- }
-
- cur_offs = dev->dev_offs;
- cur_offs += VIRTIO_DEV_OFFS_CONFIG;
- cur_offs += num_vq * VIRTIO_VQCONFIG_LEN;
-
- /* Sync feature bitmap */
- address_space_stl_le(&address_space_memory, cur_offs,
- dev->vdev->host_features,
- MEMTXATTRS_UNSPECIFIED, NULL);
-
- dev->feat_offs = cur_offs + dev->feat_len;
- cur_offs += dev->feat_len * 2;
-
- /* Sync config space */
- virtio_bus_get_vdev_config(&dev->bus, dev->vdev->config);
-
- cpu_physical_memory_write(cur_offs,
- dev->vdev->config, dev->vdev->config_len);
- cur_offs += dev->vdev->config_len;
-}
-
-void s390_virtio_device_update_status(VirtIOS390Device *dev)
-{
- VirtIODevice *vdev = dev->vdev;
- uint32_t features;
-
- virtio_set_status(vdev,
- address_space_ldub(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_STATUS,
- MEMTXATTRS_UNSPECIFIED, NULL));
-
- /* Update guest supported feature bitmap */
-
- features = bswap32(address_space_ldl_be(&address_space_memory,
- dev->feat_offs,
- MEMTXATTRS_UNSPECIFIED, NULL));
- virtio_set_features(vdev, features);
-}
-
-/* Find a device by vring address */
-VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus,
- ram_addr_t mem,
- int *vq_num)
-{
- BusChild *kid;
- int i;
-
- QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
- VirtIOS390Device *dev = (VirtIOS390Device *)kid->child;
-
- for (i = 0; i < VIRTIO_S390_QUEUE_MAX; i++) {
- if (!virtio_queue_get_addr(dev->vdev, i))
- break;
- if (virtio_queue_get_addr(dev->vdev, i) == mem) {
- if (vq_num) {
- *vq_num = i;
- }
- return dev;
- }
- }
- }
-
- return NULL;
-}
-
-/* Find a device by device descriptor location */
-VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem)
-{
- BusChild *kid;
-
- QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
- VirtIOS390Device *dev = (VirtIOS390Device *)kid->child;
- if (dev->dev_offs == mem) {
- return dev;
- }
- }
-
- return NULL;
-}
-
-/* DeviceState to VirtIOS390Device. Note: used on datapath,
- * be careful and test performance if you change this.
- */
-static inline VirtIOS390Device *to_virtio_s390_device_fast(DeviceState *d)
-{
- return container_of(d, VirtIOS390Device, qdev);
-}
-
-/* DeviceState to VirtIOS390Device. TODO: use QOM. */
-static inline VirtIOS390Device *to_virtio_s390_device(DeviceState *d)
-{
- return container_of(d, VirtIOS390Device, qdev);
-}
-
-static void virtio_s390_notify(DeviceState *d, uint16_t vector)
-{
- VirtIOS390Device *dev = to_virtio_s390_device_fast(d);
- uint64_t token = s390_virtio_device_vq_token(dev, vector);
-
- s390_virtio_irq(0, token);
-}
-
-static void virtio_s390_device_plugged(DeviceState *d, Error **errp)
-{
- VirtIOS390Device *dev = to_virtio_s390_device(d);
- VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
- int n = virtio_get_num_queues(vdev);
-
- if (n > VIRTIO_S390_QUEUE_MAX) {
- error_setg(errp, "The nubmer of virtqueues %d "
- "exceeds s390 limit %d", n,
- VIRTIO_S390_QUEUE_MAX);
- }
-}
-
-/**************** S390 Virtio Bus Device Descriptions *******************/
-
-static void s390_virtio_net_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
-
- k->realize = s390_virtio_net_realize;
- set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
-}
-
-static const TypeInfo s390_virtio_net = {
- .name = TYPE_VIRTIO_NET_S390,
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VirtIONetS390),
- .instance_init = s390_virtio_net_instance_init,
- .class_init = s390_virtio_net_class_init,
-};
-
-static void s390_virtio_blk_class_init(ObjectClass *klass, void *data)
-{
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- k->realize = s390_virtio_blk_realize;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
-}
-
-static const TypeInfo s390_virtio_blk = {
- .name = "virtio-blk-s390",
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VirtIOBlkS390),
- .instance_init = s390_virtio_blk_instance_init,
- .class_init = s390_virtio_blk_class_init,
-};
-
-static Property s390_virtio_serial_properties[] = {
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void s390_virtio_serial_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
-
- k->realize = s390_virtio_serial_realize;
- dc->props = s390_virtio_serial_properties;
- set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
-}
-
-static const TypeInfo s390_virtio_serial = {
- .name = TYPE_VIRTIO_SERIAL_S390,
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VirtIOSerialS390),
- .instance_init = s390_virtio_serial_instance_init,
- .class_init = s390_virtio_serial_class_init,
-};
-
-static void s390_virtio_rng_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
-
- k->realize = s390_virtio_rng_realize;
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
-}
-
-static const TypeInfo s390_virtio_rng = {
- .name = TYPE_VIRTIO_RNG_S390,
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VirtIORNGS390),
- .instance_init = s390_virtio_rng_instance_init,
- .class_init = s390_virtio_rng_class_init,
-};
-
-static void s390_virtio_busdev_realize(DeviceState *dev, Error **errp)
-{
- VirtIOS390Device *_dev = (VirtIOS390Device *)dev;
- VirtIOS390DeviceClass *_info = VIRTIO_S390_DEVICE_GET_CLASS(dev);
-
- virtio_s390_bus_new(&_dev->bus, sizeof(_dev->bus), _dev);
-
- _info->realize(_dev, errp);
-}
-
-static void s390_virtio_busdev_reset(DeviceState *dev)
-{
- VirtIOS390Device *_dev = (VirtIOS390Device *)dev;
-
- virtio_reset(_dev->vdev);
-}
-
-static void virtio_s390_device_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- dc->realize = s390_virtio_busdev_realize;
- dc->bus_type = TYPE_S390_VIRTIO_BUS;
- dc->reset = s390_virtio_busdev_reset;
-}
-
-static const TypeInfo virtio_s390_device_info = {
- .name = TYPE_VIRTIO_S390_DEVICE,
- .parent = TYPE_DEVICE,
- .instance_size = sizeof(VirtIOS390Device),
- .class_init = virtio_s390_device_class_init,
- .class_size = sizeof(VirtIOS390DeviceClass),
- .abstract = true,
-};
-
-static void s390_virtio_scsi_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
-
- k->realize = s390_virtio_scsi_realize;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
-}
-
-static const TypeInfo s390_virtio_scsi = {
- .name = TYPE_VIRTIO_SCSI_S390,
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VirtIOSCSIS390),
- .instance_init = s390_virtio_scsi_instance_init,
- .class_init = s390_virtio_scsi_class_init,
-};
-
-#ifdef CONFIG_VHOST_SCSI
-static void s390_vhost_scsi_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
-
- k->realize = s390_vhost_scsi_realize;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
-}
-
-static const TypeInfo s390_vhost_scsi = {
- .name = TYPE_VHOST_SCSI_S390,
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VHostSCSIS390),
- .instance_init = s390_vhost_scsi_instance_init,
- .class_init = s390_vhost_scsi_class_init,
-};
-#endif
-
-/***************** S390 Virtio Bus Bridge Device *******************/
-/* Only required to have the virtio bus as child in the system bus */
-
-static int s390_virtio_bridge_init(SysBusDevice *dev)
-{
- /* nothing */
- return 0;
-}
-
-static void s390_virtio_bridge_class_init(ObjectClass *klass, void *data)
-{
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- k->init = s390_virtio_bridge_init;
- set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
-}
-
-static const TypeInfo s390_virtio_bridge_info = {
- .name = "s390-virtio-bridge",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(SysBusDevice),
- .class_init = s390_virtio_bridge_class_init,
- .interfaces = (InterfaceInfo[]) {
- { TYPE_HOTPLUG_HANDLER },
- { }
- }
-};
-
-/* virtio-s390-bus */
-
-static void virtio_s390_bus_new(VirtioBusState *bus, size_t bus_size,
- VirtIOS390Device *dev)
-{
- DeviceState *qdev = DEVICE(dev);
- char virtio_bus_name[] = "virtio-bus";
-
- qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_S390_BUS,
- qdev, virtio_bus_name);
-}
-
-static void virtio_s390_bus_class_init(ObjectClass *klass, void *data)
-{
- VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
- BusClass *bus_class = BUS_CLASS(klass);
- bus_class->max_dev = 1;
- k->notify = virtio_s390_notify;
- k->device_plugged = virtio_s390_device_plugged;
-}
-
-static const TypeInfo virtio_s390_bus_info = {
- .name = TYPE_VIRTIO_S390_BUS,
- .parent = TYPE_VIRTIO_BUS,
- .instance_size = sizeof(VirtioS390BusState),
- .class_init = virtio_s390_bus_class_init,
-};
-
-static void s390_virtio_register_types(void)
-{
- type_register_static(&virtio_s390_bus_info);
- type_register_static(&s390_virtio_bus_info);
- type_register_static(&virtio_s390_device_info);
- type_register_static(&s390_virtio_serial);
- type_register_static(&s390_virtio_blk);
- type_register_static(&s390_virtio_net);
- type_register_static(&s390_virtio_scsi);
-#ifdef CONFIG_VHOST_SCSI
- type_register_static(&s390_vhost_scsi);
-#endif
- type_register_static(&s390_virtio_rng);
- type_register_static(&s390_virtio_bridge_info);
-}
-
-type_init(s390_virtio_register_types)
diff --git a/qemu/hw/s390x/s390-virtio-bus.h b/qemu/hw/s390x/s390-virtio-bus.h
deleted file mode 100644
index 7ad295e68..000000000
--- a/qemu/hw/s390x/s390-virtio-bus.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * QEMU S390x VirtIO BUS definitions
- *
- * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
- *
- * 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/>.
- */
-#ifndef HW_S390_VIRTIO_BUS_H
-#define HW_S390_VIRTIO_BUS_H 1
-
-#include <stddef.h>
-
-#include "standard-headers/asm-s390/kvm_virtio.h"
-#include "standard-headers/linux/virtio_ring.h"
-#include "hw/virtio/virtio-blk.h"
-#include "hw/virtio/virtio-net.h"
-#include "hw/virtio/virtio-rng.h"
-#include "hw/virtio/virtio-serial.h"
-#include "hw/virtio/virtio-scsi.h"
-#include "hw/virtio/virtio-bus.h"
-#ifdef CONFIG_VHOST_SCSI
-#include "hw/virtio/vhost-scsi.h"
-#endif
-
-typedef struct kvm_device_desc KvmDeviceDesc;
-
-#define VIRTIO_DEV_OFFS_TYPE offsetof(KvmDeviceDesc, type)
-#define VIRTIO_DEV_OFFS_NUM_VQ offsetof(KvmDeviceDesc, num_vq)
-#define VIRTIO_DEV_OFFS_FEATURE_LEN offsetof(KvmDeviceDesc, feature_len)
-#define VIRTIO_DEV_OFFS_CONFIG_LEN offsetof(KvmDeviceDesc, config_len)
-#define VIRTIO_DEV_OFFS_STATUS offsetof(KvmDeviceDesc, status)
-#define VIRTIO_DEV_OFFS_CONFIG offsetof(KvmDeviceDesc, config)
-
-typedef struct kvm_vqconfig KvmVqConfig;
-#define VIRTIO_VQCONFIG_OFFS_TOKEN offsetof(KvmVqConfig,token) /* 64 bit */
-#define VIRTIO_VQCONFIG_OFFS_ADDRESS offsetof(KvmVqConfig, address) /* 64 bit */
-#define VIRTIO_VQCONFIG_OFFS_NUM offsetof(KvmVqConfig, num) /* 16 bit */
-#define VIRTIO_VQCONFIG_LEN sizeof(KvmVqConfig)
-
-#define VIRTIO_RING_LEN (TARGET_PAGE_SIZE * 3)
-#define VIRTIO_VRING_AVAIL_IDX_OFFS offsetof(struct vring_avail, idx)
-#define VIRTIO_VRING_USED_IDX_OFFS offsetof(struct vring_used, idx)
-#define S390_DEVICE_PAGES 512
-
-#define TYPE_VIRTIO_S390_DEVICE "virtio-s390-device"
-#define VIRTIO_S390_DEVICE(obj) \
- OBJECT_CHECK(VirtIOS390Device, (obj), TYPE_VIRTIO_S390_DEVICE)
-#define VIRTIO_S390_DEVICE_CLASS(klass) \
- OBJECT_CLASS_CHECK(VirtIOS390DeviceClass, (klass), TYPE_VIRTIO_S390_DEVICE)
-#define VIRTIO_S390_DEVICE_GET_CLASS(obj) \
- OBJECT_GET_CLASS(VirtIOS390DeviceClass, (obj), TYPE_VIRTIO_S390_DEVICE)
-
-#define TYPE_S390_VIRTIO_BUS "s390-virtio-bus"
-#define S390_VIRTIO_BUS(obj) \
- OBJECT_CHECK(VirtIOS390Bus, (obj), TYPE_S390_VIRTIO_BUS)
-
-/* virtio-s390-bus */
-
-typedef struct VirtioBusState VirtioS390BusState;
-typedef struct VirtioBusClass VirtioS390BusClass;
-
-#define TYPE_VIRTIO_S390_BUS "virtio-s390-bus"
-#define VIRTIO_S390_BUS(obj) \
- OBJECT_CHECK(VirtioS390BusState, (obj), TYPE_VIRTIO_S390_BUS)
-#define VIRTIO_S390_BUS_GET_CLASS(obj) \
- OBJECT_GET_CLASS(VirtioS390BusClass, obj, TYPE_VIRTIO_S390_BUS)
-#define VIRTIO_S390_BUS_CLASS(klass) \
- OBJECT_CLASS_CHECK(VirtioS390BusClass, klass, TYPE_VIRTIO_S390_BUS)
-
-
-typedef struct VirtIOS390Device VirtIOS390Device;
-
-typedef struct VirtIOS390DeviceClass {
- DeviceClass qdev;
- void (*realize)(VirtIOS390Device *dev, Error **errp);
-} VirtIOS390DeviceClass;
-
-struct VirtIOS390Device {
- DeviceState qdev;
- ram_addr_t dev_offs;
- ram_addr_t feat_offs;
- uint8_t feat_len;
- VirtIODevice *vdev;
- VirtioBusState bus;
-};
-
-typedef struct VirtIOS390Bus {
- BusState bus;
-
- VirtIOS390Device *console;
- ram_addr_t dev_page;
- ram_addr_t dev_offs;
- ram_addr_t next_ring;
-} VirtIOS390Bus;
-
-
-void s390_virtio_device_update_status(VirtIOS390Device *dev);
-
-VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size);
-
-VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus,
- ram_addr_t mem, int *vq_num);
-VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem);
-void s390_virtio_device_sync(VirtIOS390Device *dev);
-void s390_virtio_reset_idx(VirtIOS390Device *dev);
-
-/* virtio-blk-s390 */
-
-#define TYPE_VIRTIO_BLK_S390 "virtio-blk-s390"
-#define VIRTIO_BLK_S390(obj) \
- OBJECT_CHECK(VirtIOBlkS390, (obj), TYPE_VIRTIO_BLK_S390)
-
-typedef struct VirtIOBlkS390 {
- VirtIOS390Device parent_obj;
- VirtIOBlock vdev;
-} VirtIOBlkS390;
-
-/* virtio-scsi-s390 */
-
-#define TYPE_VIRTIO_SCSI_S390 "virtio-scsi-s390"
-#define VIRTIO_SCSI_S390(obj) \
- OBJECT_CHECK(VirtIOSCSIS390, (obj), TYPE_VIRTIO_SCSI_S390)
-
-typedef struct VirtIOSCSIS390 {
- VirtIOS390Device parent_obj;
- VirtIOSCSI vdev;
-} VirtIOSCSIS390;
-
-/* virtio-serial-s390 */
-
-#define TYPE_VIRTIO_SERIAL_S390 "virtio-serial-s390"
-#define VIRTIO_SERIAL_S390(obj) \
- OBJECT_CHECK(VirtIOSerialS390, (obj), TYPE_VIRTIO_SERIAL_S390)
-
-typedef struct VirtIOSerialS390 {
- VirtIOS390Device parent_obj;
- VirtIOSerial vdev;
-} VirtIOSerialS390;
-
-/* virtio-net-s390 */
-
-#define TYPE_VIRTIO_NET_S390 "virtio-net-s390"
-#define VIRTIO_NET_S390(obj) \
- OBJECT_CHECK(VirtIONetS390, (obj), TYPE_VIRTIO_NET_S390)
-
-typedef struct VirtIONetS390 {
- VirtIOS390Device parent_obj;
- VirtIONet vdev;
-} VirtIONetS390;
-
-/* vhost-scsi-s390 */
-
-#ifdef CONFIG_VHOST_SCSI
-#define TYPE_VHOST_SCSI_S390 "vhost-scsi-s390"
-#define VHOST_SCSI_S390(obj) \
- OBJECT_CHECK(VHostSCSIS390, (obj), TYPE_VHOST_SCSI_S390)
-
-typedef struct VHostSCSIS390 {
- VirtIOS390Device parent_obj;
- VHostSCSI vdev;
-} VHostSCSIS390;
-#endif
-
-/* virtio-rng-s390 */
-
-#define TYPE_VIRTIO_RNG_S390 "virtio-rng-s390"
-#define VIRTIO_RNG_S390(obj) \
- OBJECT_CHECK(VirtIORNGS390, (obj), TYPE_VIRTIO_RNG_S390)
-
-typedef struct VirtIORNGS390 {
- VirtIOS390Device parent_obj;
- VirtIORNG vdev;
-} VirtIORNGS390;
-
-#endif
diff --git a/qemu/hw/s390x/s390-virtio-ccw.c b/qemu/hw/s390x/s390-virtio-ccw.c
index 4c51d1a5b..e3df9c78b 100644
--- a/qemu/hw/s390x/s390-virtio-ccw.c
+++ b/qemu/hw/s390x/s390-virtio-ccw.c
@@ -9,6 +9,10 @@
* directory.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/boards.h"
#include "exec/address-spaces.h"
#include "s390-virtio.h"
@@ -19,41 +23,27 @@
#include "virtio-ccw.h"
#include "qemu/config-file.h"
#include "s390-pci-bus.h"
+#include "hw/s390x/storage-keys.h"
+#include "hw/compat.h"
+#include "hw/s390x/s390-virtio-ccw.h"
+
+static const char *const reset_dev_types[] = {
+ "virtual-css-bridge",
+ "s390-sclp-event-facility",
+ "s390-flic",
+ "diag288",
+};
-#define TYPE_S390_CCW_MACHINE "s390-ccw-machine"
-
-#define S390_CCW_MACHINE(obj) \
- OBJECT_CHECK(S390CcwMachineState, (obj), TYPE_S390_CCW_MACHINE)
-
-typedef struct S390CcwMachineState {
- /*< private >*/
- MachineState parent_obj;
-
- /*< public >*/
- bool aes_key_wrap;
- bool dea_key_wrap;
-} S390CcwMachineState;
-
-void io_subsystem_reset(void)
+void subsystem_reset(void)
{
- DeviceState *css, *sclp, *flic, *diag288;
+ DeviceState *dev;
+ int i;
- css = DEVICE(object_resolve_path_type("", "virtual-css-bridge", NULL));
- if (css) {
- qdev_reset_all(css);
- }
- sclp = DEVICE(object_resolve_path_type("",
- "s390-sclp-event-facility", NULL));
- if (sclp) {
- qdev_reset_all(sclp);
- }
- flic = DEVICE(object_resolve_path_type("", "s390-flic", NULL));
- if (flic) {
- qdev_reset_all(flic);
- }
- diag288 = DEVICE(object_resolve_path_type("", "diag288", NULL));
- if (diag288) {
- qdev_reset_all(diag288);
+ for (i = 0; i < ARRAY_SIZE(reset_dev_types); i++) {
+ dev = DEVICE(object_resolve_path_type("", reset_dev_types[i], NULL));
+ if (dev) {
+ qdev_reset_all(dev);
+ }
}
}
@@ -99,58 +89,30 @@ static void virtio_ccw_register_hcalls(void)
virtio_ccw_hcall_early_printk);
}
-static void ccw_init(MachineState *machine)
+void s390_memory_init(ram_addr_t mem_size)
{
- ram_addr_t my_ram_size = machine->ram_size;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
- sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev();
- uint8_t *storage_keys;
+
+ /* allocate RAM for core */
+ memory_region_allocate_system_memory(ram, NULL, "s390.ram", mem_size);
+ memory_region_add_subregion(sysmem, 0, ram);
+
+ /* Initialize storage key device */
+ s390_skeys_init();
+}
+
+static void ccw_init(MachineState *machine)
+{
int ret;
VirtualCssBus *css_bus;
DeviceState *dev;
- QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL);
- ram_addr_t pad_size = 0;
- ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size);
- ram_addr_t standby_mem_size = maxmem - my_ram_size;
- uint64_t kvm_limit;
-
- /* The storage increment size is a multiple of 1M and is a power of 2.
- * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
- * The variable 'mhd->increment_size' is an exponent of 2 that can be
- * used to calculate the size (in bytes) of an increment. */
- mhd->increment_size = 20;
- while ((my_ram_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) {
- mhd->increment_size++;
- }
- while ((standby_mem_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) {
- mhd->increment_size++;
- }
- /* The core and standby memory areas need to be aligned with
- * the increment size. In effect, this can cause the
- * user-specified memory size to be rounded down to align
- * with the nearest increment boundary. */
- standby_mem_size = standby_mem_size >> mhd->increment_size
- << mhd->increment_size;
- my_ram_size = my_ram_size >> mhd->increment_size
- << mhd->increment_size;
-
- /* let's propagate the changed ram size into the global variable. */
- ram_size = my_ram_size;
- machine->maxram_size = my_ram_size + standby_mem_size;
-
- ret = s390_set_memory_limit(machine->maxram_size, &kvm_limit);
- if (ret == -E2BIG) {
- hw_error("qemu: host supports a maximum of %" PRIu64 " GB",
- kvm_limit >> 30);
- } else if (ret) {
- hw_error("qemu: setting the guest size failed");
- }
+ s390_sclp_init();
+ s390_memory_init(machine->ram_size);
/* get a BUS */
css_bus = virtual_css_bus_init();
- s390_sclp_init();
s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
machine->initrd_filename, "s390-ccw.img", true);
s390_flic_init();
@@ -163,27 +125,8 @@ static void ccw_init(MachineState *machine)
/* register hypercalls */
virtio_ccw_register_hcalls();
- /* allocate RAM for core */
- memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort);
- vmstate_register_ram_global(ram);
- memory_region_add_subregion(sysmem, 0, ram);
-
- /* If the size of ram is not on a MEM_SECTION_SIZE boundary,
- calculate the pad size necessary to force this boundary. */
- if (standby_mem_size) {
- if (my_ram_size % MEM_SECTION_SIZE) {
- pad_size = MEM_SECTION_SIZE - my_ram_size % MEM_SECTION_SIZE;
- }
- my_ram_size += standby_mem_size + pad_size;
- mhd->pad_size = pad_size;
- mhd->standby_mem_size = standby_mem_size;
- }
-
- /* allocate storage keys */
- storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
-
/* init CPUs */
- s390_init_cpus(machine->cpu_model, storage_keys);
+ s390_init_cpus(machine);
if (kvm_enabled()) {
kvm_s390_enable_css_support(s390_cpu_addr2state(0));
@@ -203,12 +146,54 @@ static void ccw_init(MachineState *machine)
gtod_save, gtod_load, kvm_state);
}
+static void s390_cpu_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ gchar *name;
+ S390CPU *cpu = S390_CPU(dev);
+ CPUState *cs = CPU(dev);
+
+ name = g_strdup_printf("cpu[%i]", cpu->env.cpu_num);
+ object_property_set_link(OBJECT(hotplug_dev), OBJECT(cs), name,
+ errp);
+ g_free(name);
+}
+
+static void s390_machine_device_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ s390_cpu_plug(hotplug_dev, dev, errp);
+ }
+}
+
+static HotplugHandler *s390_get_hotplug_handler(MachineState *machine,
+ DeviceState *dev)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ return HOTPLUG_HANDLER(machine);
+ }
+ return NULL;
+}
+
+static void s390_hot_add_cpu(const int64_t id, Error **errp)
+{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ Error *err = NULL;
+
+ s390x_new_cpu(machine->cpu_model, id, &err);
+ error_propagate(errp, err);
+}
+
static void ccw_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
mc->init = ccw_init;
+ mc->reset = s390_machine_reset;
+ mc->hot_add_cpu = s390_hot_add_cpu;
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
mc->no_floppy = 1;
@@ -217,6 +202,8 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data)
mc->no_sdcard = 1;
mc->use_sclp = 1;
mc->max_cpus = 255;
+ mc->get_hotplug_handler = s390_get_hotplug_handler;
+ hc->plug = s390_machine_device_plug;
nc->nmi_monitor_handler = s390_nmi;
}
@@ -278,30 +265,117 @@ static const TypeInfo ccw_machine_info = {
.class_init = ccw_machine_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_NMI },
+ { TYPE_HOTPLUG_HANDLER},
{ }
},
};
-static void ccw_machine_2_4_class_init(ObjectClass *oc, void *data)
+#define DEFINE_CCW_MACHINE(suffix, verstr, latest) \
+ static void ccw_machine_##suffix##_class_init(ObjectClass *oc, \
+ void *data) \
+ { \
+ MachineClass *mc = MACHINE_CLASS(oc); \
+ ccw_machine_##suffix##_class_options(mc); \
+ mc->desc = "VirtIO-ccw based S390 machine v" verstr; \
+ if (latest) { \
+ mc->alias = "s390-ccw-virtio"; \
+ mc->is_default = 1; \
+ } \
+ } \
+ static void ccw_machine_##suffix##_instance_init(Object *obj) \
+ { \
+ MachineState *machine = MACHINE(obj); \
+ ccw_machine_##suffix##_instance_options(machine); \
+ } \
+ static const TypeInfo ccw_machine_##suffix##_info = { \
+ .name = MACHINE_TYPE_NAME("s390-ccw-virtio-" verstr), \
+ .parent = TYPE_S390_CCW_MACHINE, \
+ .class_init = ccw_machine_##suffix##_class_init, \
+ .instance_init = ccw_machine_##suffix##_instance_init, \
+ }; \
+ static void ccw_machine_register_##suffix(void) \
+ { \
+ type_register_static(&ccw_machine_##suffix##_info); \
+ } \
+ type_init(ccw_machine_register_##suffix)
+
+#define CCW_COMPAT_2_5 \
+ HW_COMPAT_2_5
+
+#define CCW_COMPAT_2_4 \
+ CCW_COMPAT_2_5 \
+ HW_COMPAT_2_4 \
+ {\
+ .driver = TYPE_S390_SKEYS,\
+ .property = "migration-enabled",\
+ .value = "off",\
+ },{\
+ .driver = "virtio-blk-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-balloon-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-serial-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-9p-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-rng-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-net-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-scsi-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "vhost-scsi-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },
+
+static void ccw_machine_2_6_instance_options(MachineState *machine)
{
- MachineClass *mc = MACHINE_CLASS(oc);
+}
- mc->name = "s390-ccw-virtio-2.4";
- mc->alias = "s390-ccw-virtio";
- mc->desc = "VirtIO-ccw based S390 machine v2.4";
- mc->is_default = 1;
+static void ccw_machine_2_6_class_options(MachineClass *mc)
+{
}
+DEFINE_CCW_MACHINE(2_6, "2.6", true);
-static const TypeInfo ccw_machine_2_4_info = {
- .name = TYPE_S390_CCW_MACHINE "2.4",
- .parent = TYPE_S390_CCW_MACHINE,
- .class_init = ccw_machine_2_4_class_init,
-};
+static void ccw_machine_2_5_instance_options(MachineState *machine)
+{
+}
+
+static void ccw_machine_2_5_class_options(MachineClass *mc)
+{
+ SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_5);
+}
+DEFINE_CCW_MACHINE(2_5, "2.5", false);
+
+static void ccw_machine_2_4_instance_options(MachineState *machine)
+{
+ ccw_machine_2_5_instance_options(machine);
+}
+
+static void ccw_machine_2_4_class_options(MachineClass *mc)
+{
+ SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_4);
+}
+DEFINE_CCW_MACHINE(2_4, "2.4", false);
static void ccw_machine_register_types(void)
{
type_register_static(&ccw_machine_info);
- type_register_static(&ccw_machine_2_4_info);
}
type_init(ccw_machine_register_types)
diff --git a/qemu/hw/s390x/s390-virtio-hcall.c b/qemu/hw/s390x/s390-virtio-hcall.c
index c7bdc2005..23d67d617 100644
--- a/qemu/hw/s390x/s390-virtio-hcall.c
+++ b/qemu/hw/s390x/s390-virtio-hcall.c
@@ -9,6 +9,7 @@
* directory.
*/
+#include "qemu/osdep.h"
#include "cpu.h"
#include "hw/s390x/s390-virtio.h"
diff --git a/qemu/hw/s390x/s390-virtio.c b/qemu/hw/s390x/s390-virtio.c
index 1284e77b2..544c61643 100644
--- a/qemu/hw/s390x/s390-virtio.c
+++ b/qemu/hw/s390x/s390-virtio.c
@@ -21,8 +21,11 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "qapi/qmp/qerror.h"
+#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/sysemu.h"
@@ -30,14 +33,15 @@
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/virtio/virtio.h"
-#include "hw/sysbus.h"
#include "sysemu/kvm.h"
#include "exec/address-spaces.h"
+#include "sysemu/qtest.h"
-#include "hw/s390x/s390-virtio-bus.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/s390_flic.h"
#include "hw/s390x/s390-virtio.h"
+#include "hw/s390x/storage-keys.h"
+#include "hw/s390x/ipl.h"
#include "cpu.h"
//#define DEBUG_S390
@@ -51,94 +55,20 @@
#endif
#define MAX_BLK_DEVS 10
-#define ZIPL_FILENAME "s390-zipl.rom"
-#define TYPE_S390_MACHINE "s390-machine"
#define S390_TOD_CLOCK_VALUE_MISSING 0x00
#define S390_TOD_CLOCK_VALUE_PRESENT 0x01
-static VirtIOS390Bus *s390_bus;
-static S390CPU **ipi_states;
+static S390CPU **cpu_states;
S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
{
- if (cpu_addr >= smp_cpus) {
+ if (cpu_addr >= max_cpus) {
return NULL;
}
- return ipi_states[cpu_addr];
-}
-
-static int s390_virtio_hcall_notify(const uint64_t *args)
-{
- uint64_t mem = args[0];
- int r = 0, i;
-
- if (mem > ram_size) {
- VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus, mem, &i);
- if (dev) {
- /*
- * Older kernels will use the virtqueue before setting DRIVER_OK.
- * In this case the feature bits are not yet up to date, meaning
- * that several funny things can happen, e.g. the guest thinks
- * EVENT_IDX is on and QEMU thinks it is off. Let's force a feature
- * and status sync.
- */
- if (!(dev->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
- s390_virtio_device_update_status(dev);
- }
- virtio_queue_notify(dev->vdev, i);
- } else {
- r = -EINVAL;
- }
- } else {
- /* Early printk */
- }
- return r;
-}
-
-static int s390_virtio_hcall_reset(const uint64_t *args)
-{
- uint64_t mem = args[0];
- VirtIOS390Device *dev;
-
- dev = s390_virtio_bus_find_mem(s390_bus, mem);
- if (dev == NULL) {
- return -EINVAL;
- }
- virtio_reset(dev->vdev);
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0,
- MEMTXATTRS_UNSPECIFIED, NULL);
- s390_virtio_device_sync(dev);
- s390_virtio_reset_idx(dev);
-
- return 0;
-}
-
-static int s390_virtio_hcall_set_status(const uint64_t *args)
-{
- uint64_t mem = args[0];
- int r = 0;
- VirtIOS390Device *dev;
-
- dev = s390_virtio_bus_find_mem(s390_bus, mem);
- if (dev) {
- s390_virtio_device_update_status(dev);
- } else {
- r = -EINVAL;
- }
- return r;
-}
-
-static void s390_virtio_register_hcalls(void)
-{
- s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY,
- s390_virtio_hcall_notify);
- s390_register_virtio_hypercall(KVM_S390_VIRTIO_RESET,
- s390_virtio_hcall_reset);
- s390_register_virtio_hypercall(KVM_S390_VIRTIO_SET_STATUS,
- s390_virtio_hcall_set_status);
+ /* Fast lookup via CPU ID */
+ return cpu_states[cpu_addr];
}
void s390_init_ipl_dev(const char *kernel_filename,
@@ -147,9 +77,9 @@ void s390_init_ipl_dev(const char *kernel_filename,
const char *firmware,
bool enforce_bios)
{
- DeviceState *dev;
+ Object *new = object_new(TYPE_S390_IPL);
+ DeviceState *dev = DEVICE(new);
- dev = qdev_create(NULL, "s390-ipl");
if (kernel_filename) {
qdev_prop_set_string(dev, "kernel", kernel_filename);
}
@@ -159,32 +89,35 @@ void s390_init_ipl_dev(const char *kernel_filename,
qdev_prop_set_string(dev, "cmdline", kernel_cmdline);
qdev_prop_set_string(dev, "firmware", firmware);
qdev_prop_set_bit(dev, "enforce_bios", enforce_bios);
- object_property_add_child(qdev_get_machine(), "s390-ipl",
- OBJECT(dev), NULL);
+ object_property_add_child(qdev_get_machine(), TYPE_S390_IPL,
+ new, NULL);
+ object_unref(new);
qdev_init_nofail(dev);
}
-void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys)
+void s390_init_cpus(MachineState *machine)
{
int i;
+ gchar *name;
- if (cpu_model == NULL) {
- cpu_model = "host";
+ if (machine->cpu_model == NULL) {
+ machine->cpu_model = "host";
}
- ipi_states = g_malloc(sizeof(S390CPU *) * smp_cpus);
-
- for (i = 0; i < smp_cpus; i++) {
- S390CPU *cpu;
- CPUState *cs;
+ cpu_states = g_new0(S390CPU *, max_cpus);
- cpu = cpu_s390x_init(cpu_model);
- cs = CPU(cpu);
+ for (i = 0; i < max_cpus; i++) {
+ name = g_strdup_printf("cpu[%i]", i);
+ object_property_add_link(OBJECT(machine), name, TYPE_S390_CPU,
+ (Object **) &cpu_states[i],
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
+ g_free(name);
+ }
- ipi_states[i] = cpu;
- cs->halted = 1;
- cs->exception_index = EXCP_HLT;
- cpu->env.storage_keys = storage_keys;
+ for (i = 0; i < smp_cpus; i++) {
+ s390x_new_cpu(machine->cpu_model, i, &error_fatal);
}
}
@@ -201,10 +134,7 @@ void s390_create_virtio_net(BusState *bus, const char *name)
nd->model = g_strdup("virtio");
}
- if (strcmp(nd->model, "virtio")) {
- fprintf(stderr, "S390 only supports VirtIO nics\n");
- exit(1);
- }
+ qemu_check_nic_model(nd, "virtio");
dev = qdev_create(bus, name);
qdev_set_nic_properties(dev, nd);
@@ -257,68 +187,6 @@ int gtod_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
-/* PC hardware initialisation */
-static void s390_init(MachineState *machine)
-{
- ram_addr_t my_ram_size = machine->ram_size;
- MemoryRegion *sysmem = get_system_memory();
- MemoryRegion *ram = g_new(MemoryRegion, 1);
- int increment_size = 20;
- uint8_t *storage_keys;
- void *virtio_region;
- hwaddr virtio_region_len;
- hwaddr virtio_region_start;
-
- /*
- * The storage increment size is a multiple of 1M and is a power of 2.
- * The number of storage increments must be MAX_STORAGE_INCREMENTS or
- * fewer.
- */
- while ((my_ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) {
- increment_size++;
- }
- my_ram_size = my_ram_size >> increment_size << increment_size;
-
- /* let's propagate the changed ram size into the global variable. */
- ram_size = my_ram_size;
-
- /* get a BUS */
- s390_bus = s390_virtio_bus_init(&my_ram_size);
- s390_sclp_init();
- s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
- machine->initrd_filename, ZIPL_FILENAME, false);
- s390_flic_init();
-
- /* register hypercalls */
- s390_virtio_register_hcalls();
-
- /* allocate RAM */
- memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort);
- vmstate_register_ram_global(ram);
- memory_region_add_subregion(sysmem, 0, ram);
-
- /* clear virtio region */
- virtio_region_len = my_ram_size - ram_size;
- virtio_region_start = ram_size;
- virtio_region = cpu_physical_memory_map(virtio_region_start,
- &virtio_region_len, true);
- memset(virtio_region, 0, virtio_region_len);
- cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
- virtio_region_len);
-
- /* allocate storage keys */
- storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
-
- /* init CPUs */
- s390_init_cpus(machine->cpu_model, storage_keys);
-
- /* Create VirtIO network adapters */
- s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390");
-
- /* Register savevm handler for guest TOD clock */
- register_savevm(NULL, "todclock", 0, 1, gtod_save, gtod_load, NULL);
-}
-
void s390_nmi(NMIState *n, int cpu_index, Error **errp)
{
CPUState *cs = qemu_get_cpu(cpu_index);
@@ -328,39 +196,15 @@ void s390_nmi(NMIState *n, int cpu_index, Error **errp)
}
}
-static void s390_machine_class_init(ObjectClass *oc, void *data)
+void s390_machine_reset(void)
{
- MachineClass *mc = MACHINE_CLASS(oc);
- NMIClass *nc = NMI_CLASS(oc);
-
- mc->name = "s390-virtio";
- mc->alias = "s390";
- mc->desc = "VirtIO based S390 machine";
- mc->init = s390_init;
- mc->block_default_type = IF_VIRTIO;
- mc->max_cpus = 255;
- mc->no_serial = 1;
- mc->no_parallel = 1;
- mc->use_virtcon = 1;
- mc->no_floppy = 1;
- mc->no_cdrom = 1;
- mc->no_sdcard = 1;
- nc->nmi_monitor_handler = s390_nmi;
-}
+ S390CPU *ipl_cpu = S390_CPU(qemu_get_cpu(0));
-static const TypeInfo s390_machine_info = {
- .name = TYPE_S390_MACHINE,
- .parent = TYPE_MACHINE,
- .class_init = s390_machine_class_init,
- .interfaces = (InterfaceInfo[]) {
- { TYPE_NMI },
- { }
- },
-};
-
-static void s390_machine_register_types(void)
-{
- type_register_static(&s390_machine_info);
-}
+ qemu_devices_reset();
+ s390_cmma_reset();
+ s390_crypto_reset();
-type_init(s390_machine_register_types)
+ /* all cpus are stopped - configure and start the ipl cpu only */
+ s390_ipl_prepare_cpu(ipl_cpu);
+ s390_cpu_set_state(CPU_STATE_OPERATING, ipl_cpu);
+}
diff --git a/qemu/hw/s390x/s390-virtio.h b/qemu/hw/s390x/s390-virtio.h
index c84785395..ffd014cb5 100644
--- a/qemu/hw/s390x/s390-virtio.h
+++ b/qemu/hw/s390x/s390-virtio.h
@@ -19,7 +19,7 @@
typedef int (*s390_virtio_fn)(const uint64_t *args);
void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn);
-void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys);
+void s390_init_cpus(MachineState *machine);
void s390_init_ipl_dev(const char *kernel_filename,
const char *kernel_cmdline,
const char *initrd_filename,
@@ -27,4 +27,6 @@ void s390_init_ipl_dev(const char *kernel_filename,
bool enforce_bios);
void s390_create_virtio_net(BusState *bus, const char *name);
void s390_nmi(NMIState *n, int cpu_index, Error **errp);
+void s390_machine_reset(void);
+void s390_memory_init(ram_addr_t mem_size);
#endif
diff --git a/qemu/hw/s390x/sclp.c b/qemu/hw/s390x/sclp.c
index b3a6c5e5a..85dbe1b60 100644
--- a/qemu/hw/s390x/sclp.c
+++ b/qemu/hw/s390x/sclp.c
@@ -12,42 +12,34 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "cpu.h"
#include "sysemu/kvm.h"
#include "exec/memory.h"
#include "sysemu/sysemu.h"
#include "exec/address-spaces.h"
-#include "qemu/config-file.h"
+#include "hw/boards.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/event-facility.h"
#include "hw/s390x/s390-pci-bus.h"
-static inline SCLPEventFacility *get_event_facility(void)
+static inline SCLPDevice *get_sclp_device(void)
{
- ObjectProperty *op = object_property_find(qdev_get_machine(),
- TYPE_SCLP_EVENT_FACILITY,
- NULL);
- assert(op);
- return op->opaque;
+ return SCLP(object_resolve_path_type("", TYPE_SCLP, NULL));
}
/* Provide information about the configuration, CPUs and storage */
-static void read_SCP_info(SCCB *sccb)
+static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
{
ReadInfo *read_info = (ReadInfo *) sccb;
+ MachineState *machine = MACHINE(qdev_get_machine());
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
CPUState *cpu;
int cpu_count = 0;
int i = 0;
- int increment_size = 20;
int rnsize, rnmax;
- QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL);
- int slots = qemu_opt_get_number(opts, "slots", 0);
- int max_avail_slots = s390_get_memslot_count(kvm_state);
-
- if (slots > max_avail_slots) {
- slots = max_avail_slots;
- }
+ int slots = MIN(machine->ram_slots, s390_get_memslot_count(kvm_state));
CPU_FOREACH(cpu) {
cpu_count++;
@@ -66,23 +58,8 @@ static void read_SCP_info(SCCB *sccb)
read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO |
SCLP_HAS_PCI_RECONFIG);
- /*
- * The storage increment size is a multiple of 1M and is a power of 2.
- * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
- */
- while ((ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) {
- increment_size++;
- }
- rnmax = ram_size >> increment_size;
-
/* Memory Hotplug is only supported for the ccw machine type */
if (mhd) {
- while ((mhd->standby_mem_size >> increment_size) >
- MAX_STORAGE_INCREMENTS) {
- increment_size++;
- }
- assert(increment_size == mhd->increment_size);
-
mhd->standby_subregion_size = MEM_SECTION_SIZE;
/* Deduct the memory slot already used for core */
if (slots > 0) {
@@ -108,13 +85,11 @@ static void read_SCP_info(SCCB *sccb)
}
mhd->padded_ram_size = ram_size + mhd->pad_size;
mhd->rzm = 1 << mhd->increment_size;
- rnmax = ((ram_size + mhd->standby_mem_size + mhd->pad_size)
- >> mhd->increment_size);
read_info->facilities |= cpu_to_be64(SCLP_FC_ASSIGN_ATTACH_READ_STOR);
}
- rnsize = 1 << (increment_size - 20);
+ rnsize = 1 << (sclp->increment_size - 20);
if (rnsize <= 128) {
read_info->rnsize = rnsize;
} else {
@@ -122,6 +97,7 @@ static void read_SCP_info(SCCB *sccb)
read_info->rnsize2 = cpu_to_be32(rnsize);
}
+ rnmax = machine->maxram_size >> sclp->increment_size;
if (rnmax < 0x10000) {
read_info->rnmax = cpu_to_be16(rnmax);
} else {
@@ -132,14 +108,17 @@ static void read_SCP_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
}
-static void read_storage_element0_info(SCCB *sccb)
+static void read_storage_element0_info(SCLPDevice *sclp, SCCB *sccb)
{
int i, assigned;
int subincrement_id = SCLP_STARTING_SUBINCREMENT_ID;
ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
if ((ram_size >> mhd->increment_size) >= 0x10000) {
sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
@@ -158,12 +137,15 @@ static void read_storage_element0_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
}
-static void read_storage_element1_info(SCCB *sccb)
+static void read_storage_element1_info(SCLPDevice *sclp, SCCB *sccb)
{
ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
if ((mhd->standby_mem_size >> mhd->increment_size) >= 0x10000) {
sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
@@ -179,13 +161,17 @@ static void read_storage_element1_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_STANDBY_READ_COMPLETION);
}
-static void attach_storage_element(SCCB *sccb, uint16_t element)
+static void attach_storage_element(SCLPDevice *sclp, SCCB *sccb,
+ uint16_t element)
{
int i, assigned, subincrement_id;
AttachStorageElement *attach_info = (AttachStorageElement *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
if (element != 1) {
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
@@ -203,20 +189,26 @@ static void attach_storage_element(SCCB *sccb, uint16_t element)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
}
-static void assign_storage(SCCB *sccb)
+static void assign_storage(SCLPDevice *sclp, SCCB *sccb)
{
MemoryRegion *mr = NULL;
uint64_t this_subregion_size;
AssignStorage *assign_info = (AssignStorage *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
- ram_addr_t assign_addr = (assign_info->rn - 1) * mhd->rzm;
+ ram_addr_t assign_addr;
MemoryRegion *sysmem = get_system_memory();
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
+ assign_addr = (assign_info->rn - 1) * mhd->rzm;
+
if ((assign_addr % MEM_SECTION_SIZE == 0) &&
(assign_addr >= mhd->padded_ram_size)) {
/* Re-use existing memory region if found */
mr = memory_region_find(sysmem, assign_addr, 1).mr;
+ memory_region_unref(mr);
if (!mr) {
MemoryRegion *standby_ram = g_new(MemoryRegion, 1);
@@ -241,7 +233,13 @@ static void assign_storage(SCCB *sccb)
this_subregion_size = mhd->standby_subregion_size;
}
- memory_region_init_ram(standby_ram, NULL, id, this_subregion_size, &error_abort);
+ memory_region_init_ram(standby_ram, NULL, id, this_subregion_size,
+ &error_fatal);
+ /* This is a hack to make memory hotunplug work again. Once we have
+ * subdevices, we have to unparent them when unassigning memory,
+ * instead of doing it via the ref count of the MemoryRegion. */
+ object_ref(OBJECT(standby_ram));
+ object_unparent(OBJECT(standby_ram));
vmstate_register_ram_global(standby_ram);
memory_region_add_subregion(sysmem, offset, standby_ram);
}
@@ -252,15 +250,20 @@ static void assign_storage(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
}
-static void unassign_storage(SCCB *sccb)
+static void unassign_storage(SCLPDevice *sclp, SCCB *sccb)
{
MemoryRegion *mr = NULL;
AssignStorage *assign_info = (AssignStorage *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
- ram_addr_t unassign_addr = (assign_info->rn - 1) * mhd->rzm;
+ ram_addr_t unassign_addr;
MemoryRegion *sysmem = get_system_memory();
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
+ unassign_addr = (assign_info->rn - 1) * mhd->rzm;
+
/* if the addr is a multiple of 256 MB */
if ((unassign_addr % MEM_SECTION_SIZE == 0) &&
(unassign_addr >= mhd->padded_ram_size)) {
@@ -269,6 +272,7 @@ static void unassign_storage(SCCB *sccb)
/* find the specified memory region and destroy it */
mr = memory_region_find(sysmem, unassign_addr, 1).mr;
+ memory_region_unref(mr);
if (mr) {
int i;
int is_removable = 1;
@@ -287,8 +291,7 @@ static void unassign_storage(SCCB *sccb)
}
if (is_removable) {
memory_region_del_subregion(sysmem, mr);
- object_unparent(OBJECT(mr));
- g_free(mr);
+ object_unref(OBJECT(mr));
}
}
}
@@ -296,7 +299,7 @@ static void unassign_storage(SCCB *sccb)
}
/* Provide information about the CPU */
-static void sclp_read_cpu_info(SCCB *sccb)
+static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb)
{
ReadCpuInfo *cpu_info = (ReadCpuInfo *) sccb;
CPUState *cpu;
@@ -323,34 +326,35 @@ 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, uint32_t code)
+static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code)
{
- SCLPEventFacility *ef = get_event_facility();
+ SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
+ SCLPEventFacility *ef = sclp->event_facility;
SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
switch (code & SCLP_CMD_CODE_MASK) {
case SCLP_CMDW_READ_SCP_INFO:
case SCLP_CMDW_READ_SCP_INFO_FORCED:
- read_SCP_info(sccb);
+ sclp_c->read_SCP_info(sclp, sccb);
break;
case SCLP_CMDW_READ_CPU_INFO:
- sclp_read_cpu_info(sccb);
+ sclp_c->read_cpu_info(sclp, sccb);
break;
case SCLP_READ_STORAGE_ELEMENT_INFO:
if (code & 0xff00) {
- read_storage_element1_info(sccb);
+ sclp_c->read_storage_element1_info(sclp, sccb);
} else {
- read_storage_element0_info(sccb);
+ sclp_c->read_storage_element0_info(sclp, sccb);
}
break;
case SCLP_ATTACH_STORAGE_ELEMENT:
- attach_storage_element(sccb, (code & 0xff00) >> 8);
+ sclp_c->attach_storage_element(sclp, sccb, (code & 0xff00) >> 8);
break;
case SCLP_ASSIGN_STORAGE:
- assign_storage(sccb);
+ sclp_c->assign_storage(sclp, sccb);
break;
case SCLP_UNASSIGN_STORAGE:
- unassign_storage(sccb);
+ sclp_c->unassign_storage(sclp, sccb);
break;
case SCLP_CMDW_CONFIGURE_PCI:
s390_pci_sclp_configure(1, sccb);
@@ -366,6 +370,8 @@ static void sclp_execute(SCCB *sccb, uint32_t code)
int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
{
+ SCLPDevice *sclp = get_sclp_device();
+ SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
int r = 0;
SCCB work_sccb;
@@ -400,20 +406,20 @@ int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
goto out;
}
- sclp_execute((SCCB *)&work_sccb, code);
+ sclp_c->execute(sclp, (SCCB *)&work_sccb, code);
cpu_physical_memory_write(sccb, &work_sccb,
be16_to_cpu(work_sccb.h.length));
- sclp_service_interrupt(sccb);
+ sclp_c->service_interrupt(sclp, sccb);
out:
return r;
}
-void sclp_service_interrupt(uint32_t sccb)
+static void service_interrupt(SCLPDevice *sclp, uint32_t sccb)
{
- SCLPEventFacility *ef = get_event_facility();
+ SCLPEventFacility *ef = sclp->event_facility;
SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
uint32_t param = sccb & ~3;
@@ -428,17 +434,149 @@ void sclp_service_interrupt(uint32_t sccb)
s390_sclp_extint(param);
}
+void sclp_service_interrupt(uint32_t sccb)
+{
+ SCLPDevice *sclp = get_sclp_device();
+ SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
+
+ sclp_c->service_interrupt(sclp, sccb);
+}
+
/* qemu object creation and initialization functions */
void s390_sclp_init(void)
{
- DeviceState *dev = qdev_create(NULL, TYPE_SCLP_EVENT_FACILITY);
+ Object *new = object_new(TYPE_SCLP);
- object_property_add_child(qdev_get_machine(), TYPE_SCLP_EVENT_FACILITY,
- OBJECT(dev), NULL);
- qdev_init_nofail(dev);
+ object_property_add_child(qdev_get_machine(), TYPE_SCLP, new,
+ NULL);
+ object_unref(OBJECT(new));
+ qdev_init_nofail(DEVICE(new));
}
+static void sclp_realize(DeviceState *dev, Error **errp)
+{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ SCLPDevice *sclp = SCLP(dev);
+ Error *err = NULL;
+ uint64_t hw_limit;
+ int ret;
+
+ object_property_set_bool(OBJECT(sclp->event_facility), true, "realized",
+ &err);
+ if (err) {
+ goto out;
+ }
+ /*
+ * qdev_device_add searches the sysbus for TYPE_SCLP_EVENTS_BUS. As long
+ * as we can't find a fitting bus via the qom tree, we have to add the
+ * event facility to the sysbus, so e.g. a sclp console can be created.
+ */
+ qdev_set_parent_bus(DEVICE(sclp->event_facility), sysbus_get_default());
+
+ ret = s390_set_memory_limit(machine->maxram_size, &hw_limit);
+ if (ret == -E2BIG) {
+ error_setg(&err, "qemu: host supports a maximum of %" PRIu64 " GB",
+ hw_limit >> 30);
+ } else if (ret) {
+ error_setg(&err, "qemu: setting the guest size failed");
+ }
+
+out:
+ error_propagate(errp, err);
+}
+
+static void sclp_memory_init(SCLPDevice *sclp)
+{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ ram_addr_t initial_mem = machine->ram_size;
+ ram_addr_t max_mem = machine->maxram_size;
+ ram_addr_t standby_mem = max_mem - initial_mem;
+ ram_addr_t pad_mem = 0;
+ int increment_size = 20;
+
+ /* The storage increment size is a multiple of 1M and is a power of 2.
+ * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
+ * The variable 'increment_size' is an exponent of 2 that can be
+ * used to calculate the size (in bytes) of an increment. */
+ while ((initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) {
+ increment_size++;
+ }
+ if (machine->ram_slots) {
+ while ((standby_mem >> increment_size) > MAX_STORAGE_INCREMENTS) {
+ increment_size++;
+ }
+ }
+ sclp->increment_size = increment_size;
+
+ /* The core and standby memory areas need to be aligned with
+ * the increment size. In effect, this can cause the
+ * user-specified memory size to be rounded down to align
+ * with the nearest increment boundary. */
+ initial_mem = initial_mem >> increment_size << increment_size;
+ standby_mem = standby_mem >> increment_size << increment_size;
+
+ /* If the size of ram is not on a MEM_SECTION_SIZE boundary,
+ calculate the pad size necessary to force this boundary. */
+ if (machine->ram_slots && standby_mem) {
+ sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev();
+
+ if (initial_mem % MEM_SECTION_SIZE) {
+ pad_mem = MEM_SECTION_SIZE - initial_mem % MEM_SECTION_SIZE;
+ }
+ mhd->increment_size = increment_size;
+ mhd->pad_size = pad_mem;
+ mhd->standby_mem_size = standby_mem;
+ }
+ machine->ram_size = initial_mem;
+ machine->maxram_size = initial_mem + pad_mem + standby_mem;
+ /* let's propagate the changed ram size into the global variable. */
+ ram_size = initial_mem;
+}
+
+static void sclp_init(Object *obj)
+{
+ SCLPDevice *sclp = SCLP(obj);
+ Object *new;
+
+ new = object_new(TYPE_SCLP_EVENT_FACILITY);
+ object_property_add_child(obj, TYPE_SCLP_EVENT_FACILITY, new, NULL);
+ object_unref(new);
+ sclp->event_facility = EVENT_FACILITY(new);
+
+ sclp_memory_init(sclp);
+}
+
+static void sclp_class_init(ObjectClass *oc, void *data)
+{
+ SCLPDeviceClass *sc = SCLP_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->desc = "SCLP (Service-Call Logical Processor)";
+ dc->realize = sclp_realize;
+ dc->hotpluggable = false;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+
+ sc->read_SCP_info = read_SCP_info;
+ sc->read_storage_element0_info = read_storage_element0_info;
+ sc->read_storage_element1_info = read_storage_element1_info;
+ sc->attach_storage_element = attach_storage_element;
+ sc->assign_storage = assign_storage;
+ sc->unassign_storage = unassign_storage;
+ sc->read_cpu_info = sclp_read_cpu_info;
+ sc->execute = sclp_execute;
+ sc->service_interrupt = service_interrupt;
+}
+
+static TypeInfo sclp_info = {
+ .name = TYPE_SCLP,
+ .parent = TYPE_DEVICE,
+ .instance_init = sclp_init,
+ .instance_size = sizeof(SCLPDevice),
+ .class_init = sclp_class_init,
+ .class_size = sizeof(SCLPDeviceClass),
+};
+
sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void)
{
DeviceState *dev;
@@ -475,5 +613,6 @@ static TypeInfo sclp_memory_hotplug_dev_info = {
static void register_types(void)
{
type_register_static(&sclp_memory_hotplug_dev_info);
+ type_register_static(&sclp_info);
}
type_init(register_types);
diff --git a/qemu/hw/s390x/sclpcpu.c b/qemu/hw/s390x/sclpcpu.c
index 2fe8b5aa4..b1f3ef8c7 100644
--- a/qemu/hw/s390x/sclpcpu.c
+++ b/qemu/hw/s390x/sclpcpu.c
@@ -12,6 +12,7 @@
* option) any later version. See the COPYING file in the top-level directory.
*
*/
+#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/event-facility.h"
@@ -25,13 +26,16 @@ typedef struct ConfigMgtData {
uint8_t event_qualifier;
} QEMU_PACKED ConfigMgtData;
-static qemu_irq *irq_cpu_hotplug; /* Only used in this file */
-
#define EVENT_QUAL_CPU_CHANGE 1
void raise_irq_cpu_hotplug(void)
{
- qemu_irq_raise(*irq_cpu_hotplug);
+ Object *obj = object_resolve_path_type("", TYPE_SCLP_CPU_HOTPLUG, NULL);
+
+ SCLP_EVENT(obj)->event_pending = true;
+
+ /* Trigger SCLP read operation */
+ sclp_service_interrupt(0);
}
static unsigned int send_mask(void)
@@ -70,36 +74,19 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
return 1;
}
-static void trigger_signal(void *opaque, int n, int level)
-{
- SCLPEvent *event = opaque;
- event->event_pending = true;
-
- /* Trigger SCLP read operation */
- sclp_service_interrupt(0);
-}
-
-static int irq_cpu_hotplug_init(SCLPEvent *event)
-{
- irq_cpu_hotplug = qemu_allocate_irqs(trigger_signal, event, 1);
- return 0;
-}
-
static void cpu_class_init(ObjectClass *oc, void *data)
{
SCLPEventClass *k = SCLP_EVENT_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
- k->init = irq_cpu_hotplug_init;
k->get_send_mask = send_mask;
k->get_receive_mask = receive_mask;
k->read_event_data = read_event_data;
- k->write_event_data = NULL;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo sclp_cpu_info = {
- .name = "sclp-cpu-hotplug",
+ .name = TYPE_SCLP_CPU_HOTPLUG,
.parent = TYPE_SCLP_EVENT,
.instance_size = sizeof(SCLPEvent),
.class_init = cpu_class_init,
diff --git a/qemu/hw/s390x/sclpquiesce.c b/qemu/hw/s390x/sclpquiesce.c
index ffa555313..c0ecab9c3 100644
--- a/qemu/hw/s390x/sclpquiesce.c
+++ b/qemu/hw/s390x/sclpquiesce.c
@@ -11,6 +11,7 @@
* option) any later version. See the COPYING file in the top-level directory.
*
*/
+#include "qemu/osdep.h"
#include <hw/qdev.h>
#include "sysemu/sysemu.h"
#include "hw/s390x/sclp.h"
@@ -66,7 +67,7 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
}
static const VMStateDescription vmstate_sclpquiesce = {
- .name = "sclpquiesce",
+ .name = TYPE_SCLP_QUIESCE,
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
@@ -127,7 +128,7 @@ static void quiesce_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo sclp_quiesce_info = {
- .name = "sclpquiesce",
+ .name = TYPE_SCLP_QUIESCE,
.parent = TYPE_SCLP_EVENT,
.instance_size = sizeof(SCLPEvent),
.class_init = quiesce_class_init,
diff --git a/qemu/hw/s390x/virtio-ccw.c b/qemu/hw/s390x/virtio-ccw.c
index d36373e88..d51642db0 100644
--- a/qemu/hw/s390x/virtio-ccw.c
+++ b/qemu/hw/s390x/virtio-ccw.c
@@ -10,6 +10,8 @@
* directory.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
@@ -31,69 +33,6 @@
#include "virtio-ccw.h"
#include "trace.h"
-static QTAILQ_HEAD(, IndAddr) indicator_addresses =
- QTAILQ_HEAD_INITIALIZER(indicator_addresses);
-
-static IndAddr *get_indicator(hwaddr ind_addr, int len)
-{
- IndAddr *indicator;
-
- QTAILQ_FOREACH(indicator, &indicator_addresses, sibling) {
- if (indicator->addr == ind_addr) {
- indicator->refcnt++;
- return indicator;
- }
- }
- indicator = g_new0(IndAddr, 1);
- indicator->addr = ind_addr;
- indicator->len = len;
- indicator->refcnt = 1;
- QTAILQ_INSERT_TAIL(&indicator_addresses, indicator, sibling);
- return indicator;
-}
-
-static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr,
- bool do_map)
-{
- S390FLICState *fs = s390_get_flic();
- S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
-
- return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map);
-}
-
-static void release_indicator(AdapterInfo *adapter, IndAddr *indicator)
-{
- assert(indicator->refcnt > 0);
- indicator->refcnt--;
- if (indicator->refcnt > 0) {
- return;
- }
- QTAILQ_REMOVE(&indicator_addresses, indicator, sibling);
- if (indicator->map) {
- s390_io_adapter_map(adapter, indicator->map, false);
- }
- g_free(indicator);
-}
-
-static int map_indicator(AdapterInfo *adapter, IndAddr *indicator)
-{
- int ret;
-
- if (indicator->map) {
- return 0; /* already mapped is not an error */
- }
- indicator->map = indicator->addr;
- ret = s390_io_adapter_map(adapter, indicator->map, true);
- if ((ret != 0) && (ret != -ENOSYS)) {
- goto out_err;
- }
- return 0;
-
-out_err:
- indicator->map = 0;
- return ret;
-}
-
static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
VirtioCcwDevice *dev);
@@ -307,11 +246,18 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info,
if (!desc) {
virtio_queue_set_vector(vdev, index, VIRTIO_NO_VECTOR);
} else {
- /* Fail if we don't have a big enough queue. */
- /* TODO: Add interface to handle vring.num changing */
- if (virtio_queue_get_num(vdev, index) > num) {
+ if (info) {
+ /* virtio-1 allows changing the ring size. */
+ if (virtio_queue_get_num(vdev, index) < num) {
+ /* Fail if we exceed the maximum number. */
+ return -EINVAL;
+ }
+ virtio_queue_set_num(vdev, index, num);
+ } else if (virtio_queue_get_num(vdev, index) > num) {
+ /* Fail if we don't have a big enough queue. */
return -EINVAL;
}
+ /* We ignore possible increased num for legacy for compatibility. */
virtio_queue_set_vector(vdev, index, index);
}
/* tell notify handler in case of config change */
@@ -460,16 +406,19 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
MEMTXATTRS_UNSPECIFIED,
NULL);
if (features.index == 0) {
- features.features = (uint32_t)vdev->host_features;
- } else if (features.index == 1) {
- features.features = (uint32_t)(vdev->host_features >> 32);
+ if (dev->revision >= 1) {
+ /* Don't offer legacy features for modern devices. */
+ features.features = (uint32_t)
+ (vdev->host_features & ~VIRTIO_LEGACY_FEATURES);
+ } else {
+ features.features = (uint32_t)vdev->host_features;
+ }
+ } else if ((features.index == 1) && (dev->revision >= 1)) {
/*
- * Don't offer version 1 to the guest if it did not
- * negotiate at least revision 1.
+ * Only offer feature bits beyond 31 if the guest has
+ * negotiated at least revision 1.
*/
- if (dev->revision <= 0) {
- features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32));
- }
+ features.features = (uint32_t)(vdev->host_features >> 32);
} else {
/* Return zeroes if the guest supports more feature bits. */
features.features = 0;
@@ -508,14 +457,12 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
virtio_set_features(vdev,
(vdev->guest_features & 0xffffffff00000000ULL) |
features.features);
- } else if (features.index == 1) {
+ } else if ((features.index == 1) && (dev->revision >= 1)) {
/*
- * The guest should not set version 1 if it didn't
- * negotiate a revision >= 1.
+ * If the guest did not negotiate at least revision 1,
+ * we did not offer it any feature bits beyond 31. Such a
+ * guest passing us any bit here is therefore buggy.
*/
- if (dev->revision <= 0) {
- features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32));
- }
virtio_set_features(vdev,
(vdev->guest_features & 0x00000000ffffffffULL) |
((uint64_t)features.features << 32));
@@ -766,7 +713,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
* need to fetch it here. Nothing to do for now, though.
*/
if (dev->revision >= 0 ||
- revinfo.revision > virtio_ccw_rev_max(vdev)) {
+ revinfo.revision > virtio_ccw_rev_max(dev)) {
ret = -ENOSYS;
break;
}
@@ -1169,7 +1116,8 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
SubchDev *sch = dev->sch;
uint64_t indicators;
- if (vector >= 128) {
+ /* queue indicators + secondary indicators */
+ if (vector >= VIRTIO_CCW_QUEUE_MAX + 64) {
return;
}
@@ -1539,10 +1487,25 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp)
sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus);
+ if (dev->max_rev >= 1) {
+ virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1);
+ }
+
css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid,
d->hotplugged, 1);
}
+static void virtio_ccw_post_plugged(DeviceState *d, Error **errp)
+{
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+
+ if (!virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ /* A backend didn't support modern virtio. */
+ dev->max_rev = 0;
+ }
+}
+
static void virtio_ccw_device_unplugged(DeviceState *d)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
@@ -1555,6 +1518,8 @@ static Property virtio_ccw_net_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1582,6 +1547,8 @@ static Property virtio_ccw_blk_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1609,6 +1576,8 @@ static Property virtio_ccw_serial_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1636,6 +1605,8 @@ static Property virtio_ccw_balloon_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1663,6 +1634,8 @@ static Property virtio_ccw_scsi_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1689,6 +1662,8 @@ static const TypeInfo virtio_ccw_scsi = {
#ifdef CONFIG_VHOST_SCSI
static Property vhost_ccw_scsi_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1727,6 +1702,8 @@ static Property virtio_ccw_rng_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1865,6 +1842,7 @@ static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
k->save_config = virtio_ccw_save_config;
k->load_config = virtio_ccw_load_config;
k->device_plugged = virtio_ccw_device_plugged;
+ k->post_plugged = virtio_ccw_post_plugged;
k->device_unplugged = virtio_ccw_device_unplugged;
}
@@ -1880,6 +1858,8 @@ static Property virtio_ccw_9p_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/qemu/hw/s390x/virtio-ccw.h b/qemu/hw/s390x/virtio-ccw.h
index 692ddd731..66c831ba8 100644
--- a/qemu/hw/s390x/virtio-ccw.h
+++ b/qemu/hw/s390x/virtio-ccw.h
@@ -23,7 +23,8 @@
#include <hw/virtio/virtio-balloon.h>
#include <hw/virtio/virtio-rng.h>
#include <hw/virtio/virtio-bus.h>
-#include <hw/s390x/s390_flic.h>
+
+#include "css.h"
#define VIRTUAL_CSSID 0xfe
@@ -75,19 +76,12 @@ typedef struct VirtIOCCWDeviceClass {
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT)
-typedef struct IndAddr {
- hwaddr addr;
- uint64_t map;
- unsigned long refcnt;
- int len;
- QTAILQ_ENTRY(IndAddr) sibling;
-} IndAddr;
-
struct VirtioCcwDevice {
DeviceState parent_obj;
SubchDev *sch;
char *bus_id;
int revision;
+ uint32_t max_rev;
VirtioBusState bus;
bool ioeventfd_started;
bool ioeventfd_disabled;
@@ -102,9 +96,10 @@ struct VirtioCcwDevice {
};
/* The maximum virtio revision we support. */
-static inline int virtio_ccw_rev_max(VirtIODevice *vdev)
+#define VIRTIO_CCW_MAX_REV 1
+static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev)
{
- return 0;
+ return dev->max_rev;
}
/* virtual css bus type */
@@ -208,7 +203,7 @@ VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch);
typedef struct V9fsCCWState {
VirtioCcwDevice parent_obj;
- V9fsState vdev;
+ V9fsVirtioState vdev;
} V9fsCCWState;
#endif /* CONFIG_VIRTFS */