aboutsummaryrefslogtreecommitdiff
path: root/hw/s390x
diff options
context:
space:
mode:
Diffstat (limited to 'hw/s390x')
-rw-r--r--hw/s390x/css.c1
-rw-r--r--hw/s390x/event-facility.c2
-rw-r--r--hw/s390x/sclp.c142
3 files changed, 104 insertions, 41 deletions
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index 519dc91316..9961cfe7bf 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -353,7 +353,6 @@ 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,
.indicator_addresses =
diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c
index 645b4080c5..ed92ce510d 100644
--- a/hw/s390x/event-facility.c
+++ b/hw/s390x/event-facility.c
@@ -213,7 +213,7 @@ static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb,
event_buf = &red->ebh;
event_buf->length = 0;
- slen = sizeof(sccb->data);
+ slen = sccb_data_len(sccb);
rc = SCLP_RC_NO_EVENT_BUFFERS_STORED;
diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c
index a0ce444b4b..00f1e4648d 100644
--- a/hw/s390x/sclp.c
+++ b/hw/s390x/sclp.c
@@ -49,9 +49,37 @@ static inline bool sclp_command_code_valid(uint32_t code)
return false;
}
-static void prepare_cpu_entries(SCLPDevice *sclp, CPUEntry *entry, int *count)
+static bool sccb_verify_boundary(uint64_t sccb_addr, uint16_t sccb_len,
+ uint32_t code)
+{
+ uint64_t sccb_max_addr = sccb_addr + sccb_len - 1;
+ uint64_t sccb_boundary = (sccb_addr & PAGE_MASK) + PAGE_SIZE;
+
+ switch (code & SCLP_CMD_CODE_MASK) {
+ case SCLP_CMDW_READ_SCP_INFO:
+ case SCLP_CMDW_READ_SCP_INFO_FORCED:
+ case SCLP_CMDW_READ_CPU_INFO:
+ /*
+ * An extended-length SCCB is only allowed for Read SCP/CPU Info and
+ * is allowed to exceed the 4k boundary. The respective commands will
+ * set the length field to the required length if an insufficient
+ * SCCB length is provided.
+ */
+ if (s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB)) {
+ return true;
+ }
+ /* fallthrough */
+ default:
+ if (sccb_max_addr < sccb_boundary) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void prepare_cpu_entries(MachineState *ms, CPUEntry *entry, int *count)
{
- MachineState *ms = MACHINE(qdev_get_machine());
uint8_t features[SCCB_CPU_FEATURE_LEN] = { 0 };
int i;
@@ -67,6 +95,14 @@ static void prepare_cpu_entries(SCLPDevice *sclp, CPUEntry *entry, int *count)
}
}
+#define SCCB_REQ_LEN(s, max_cpus) (sizeof(s) + max_cpus * sizeof(CPUEntry))
+
+static inline bool ext_len_sccb_supported(SCCBHeader header)
+{
+ return s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) &&
+ header.control_mask[2] & SCLP_VARIABLE_LENGTH_RESPONSE;
+}
+
/* Provide information about the configuration, CPUs and storage */
static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
{
@@ -75,27 +111,39 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
int cpu_count;
int rnsize, rnmax;
IplParameterBlock *ipib = s390_ipl_get_iplb();
+ int required_len = SCCB_REQ_LEN(ReadInfo, machine->possible_cpus->len);
+ int offset_cpu = s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) ?
+ offsetof(ReadInfo, entries) :
+ SCLP_READ_SCP_INFO_FIXED_CPU_OFFSET;
+ CPUEntry *entries_start = (void *)sccb + offset_cpu;
+
+ if (be16_to_cpu(sccb->h.length) < required_len) {
+ if (ext_len_sccb_supported(sccb->h)) {
+ sccb->h.length = cpu_to_be16(required_len);
+ }
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH);
+ return;
+ }
/* CPU information */
- prepare_cpu_entries(sclp, read_info->entries, &cpu_count);
+ prepare_cpu_entries(machine, entries_start, &cpu_count);
read_info->entries_cpu = cpu_to_be16(cpu_count);
- read_info->offset_cpu = cpu_to_be16(offsetof(ReadInfo, entries));
+ read_info->offset_cpu = cpu_to_be16(offset_cpu);
read_info->highest_cpu = cpu_to_be16(machine->smp.max_cpus - 1);
read_info->ibc_val = cpu_to_be32(s390_get_ibc_val());
- if (be16_to_cpu(sccb->h.length) <
- (sizeof(ReadInfo) + cpu_count * sizeof(CPUEntry))) {
- sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH);
- return;
- }
-
/* Configuration Characteristic (Extension) */
s390_get_feat_block(S390_FEAT_TYPE_SCLP_CONF_CHAR,
read_info->conf_char);
s390_get_feat_block(S390_FEAT_TYPE_SCLP_CONF_CHAR_EXT,
read_info->conf_char_ext);
+ if (s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB)) {
+ s390_get_feat_block(S390_FEAT_TYPE_SCLP_FAC134,
+ &read_info->fac134);
+ }
+
read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO |
SCLP_HAS_IOA_RECONFIG);
@@ -132,20 +180,24 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
/* Provide information about the CPU */
static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb)
{
+ MachineState *machine = MACHINE(qdev_get_machine());
ReadCpuInfo *cpu_info = (ReadCpuInfo *) sccb;
int cpu_count;
+ int required_len = SCCB_REQ_LEN(ReadCpuInfo, machine->possible_cpus->len);
- prepare_cpu_entries(sclp, cpu_info->entries, &cpu_count);
- cpu_info->nr_configured = cpu_to_be16(cpu_count);
- cpu_info->offset_configured = cpu_to_be16(offsetof(ReadCpuInfo, entries));
- cpu_info->nr_standby = cpu_to_be16(0);
-
- if (be16_to_cpu(sccb->h.length) <
- (sizeof(ReadCpuInfo) + cpu_count * sizeof(CPUEntry))) {
+ if (be16_to_cpu(sccb->h.length) < required_len) {
+ if (ext_len_sccb_supported(sccb->h)) {
+ sccb->h.length = cpu_to_be16(required_len);
+ }
sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH);
return;
}
+ prepare_cpu_entries(machine, cpu_info->entries, &cpu_count);
+ cpu_info->nr_configured = cpu_to_be16(cpu_count);
+ cpu_info->offset_configured = cpu_to_be16(offsetof(ReadCpuInfo, entries));
+ cpu_info->nr_standby = cpu_to_be16(0);
+
/* The standby offset is 16-byte for each CPU */
cpu_info->offset_standby = cpu_to_be16(cpu_info->offset_configured
+ cpu_info->nr_configured*sizeof(CPUEntry));
@@ -219,20 +271,29 @@ int sclp_service_call_protected(CPUS390XState *env, uint64_t sccb,
{
SCLPDevice *sclp = get_sclp_device();
SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
- SCCB work_sccb;
- hwaddr sccb_len = sizeof(SCCB);
+ SCCBHeader header;
+ g_autofree SCCB *work_sccb = NULL;
+
+ s390_cpu_pv_mem_read(env_archcpu(env), 0, &header, sizeof(SCCBHeader));
- s390_cpu_pv_mem_read(env_archcpu(env), 0, &work_sccb, sccb_len);
+ work_sccb = g_malloc0(be16_to_cpu(header.length));
+ s390_cpu_pv_mem_read(env_archcpu(env), 0, work_sccb,
+ be16_to_cpu(header.length));
if (!sclp_command_code_valid(code)) {
- work_sccb.h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ work_sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
goto out_write;
}
- sclp_c->execute(sclp, &work_sccb, code);
+ if (!sccb_verify_boundary(sccb, be16_to_cpu(work_sccb->h.length), code)) {
+ work_sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
+ goto out_write;
+ }
+
+ sclp_c->execute(sclp, work_sccb, code);
out_write:
- s390_cpu_pv_mem_write(env_archcpu(env), 0, &work_sccb,
- be16_to_cpu(work_sccb.h.length));
+ s390_cpu_pv_mem_write(env_archcpu(env), 0, work_sccb,
+ be16_to_cpu(work_sccb->h.length));
sclp_c->service_interrupt(sclp, SCLP_PV_DUMMY_ADDR);
return 0;
}
@@ -241,9 +302,8 @@ int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
{
SCLPDevice *sclp = get_sclp_device();
SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
- SCCB work_sccb;
-
- hwaddr sccb_len = sizeof(SCCB);
+ SCCBHeader header;
+ g_autofree SCCB *work_sccb = NULL;
/* first some basic checks on program checks */
if (env->psw.mask & PSW_MASK_PSTATE) {
@@ -257,32 +317,36 @@ int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
return -PGM_SPECIFICATION;
}
+ /* the header contains the actual length of the sccb */
+ cpu_physical_memory_read(sccb, &header, sizeof(SCCBHeader));
+
+ /* Valid sccb sizes */
+ if (be16_to_cpu(header.length) < sizeof(SCCBHeader)) {
+ return -PGM_SPECIFICATION;
+ }
+
/*
* we want to work on a private copy of the sccb, to prevent guests
* from playing dirty tricks by modifying the memory content after
* the host has checked the values
*/
- cpu_physical_memory_read(sccb, &work_sccb, sccb_len);
-
- /* Valid sccb sizes */
- if (be16_to_cpu(work_sccb.h.length) < sizeof(SCCBHeader)) {
- return -PGM_SPECIFICATION;
- }
+ work_sccb = g_malloc0(be16_to_cpu(header.length));
+ cpu_physical_memory_read(sccb, work_sccb, be16_to_cpu(header.length));
if (!sclp_command_code_valid(code)) {
- work_sccb.h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ work_sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
goto out_write;
}
- if ((sccb + be16_to_cpu(work_sccb.h.length)) > ((sccb & PAGE_MASK) + PAGE_SIZE)) {
- work_sccb.h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
+ if (!sccb_verify_boundary(sccb, be16_to_cpu(work_sccb->h.length), code)) {
+ work_sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
goto out_write;
}
- sclp_c->execute(sclp, &work_sccb, code);
+ sclp_c->execute(sclp, work_sccb, code);
out_write:
- cpu_physical_memory_write(sccb, &work_sccb,
- be16_to_cpu(work_sccb.h.length));
+ cpu_physical_memory_write(sccb, work_sccb,
+ be16_to_cpu(work_sccb->h.length));
sclp_c->service_interrupt(sclp, sccb);