xref: /openbmc/qemu/hw/s390x/sclp.c (revision e17e57e862faf6e1f372385c18dcf6d3fd31158e)
1f6c98f92SHeinz Graalfs /*
2f6c98f92SHeinz Graalfs  * SCLP Support
3f6c98f92SHeinz Graalfs  *
4f6c98f92SHeinz Graalfs  * Copyright IBM, Corp. 2012
5f6c98f92SHeinz Graalfs  *
6f6c98f92SHeinz Graalfs  * Authors:
7f6c98f92SHeinz Graalfs  *  Christian Borntraeger <borntraeger@de.ibm.com>
8f6c98f92SHeinz Graalfs  *  Heinz Graalfs <graalfs@linux.vnet.ibm.com>
9f6c98f92SHeinz Graalfs  *
10f6c98f92SHeinz Graalfs  * This work is licensed under the terms of the GNU GPL, version 2 or (at your
11f6c98f92SHeinz Graalfs  * option) any later version.  See the COPYING file in the top-level directory.
12f6c98f92SHeinz Graalfs  *
13f6c98f92SHeinz Graalfs  */
14f6c98f92SHeinz Graalfs 
159615495aSPeter Maydell #include "qemu/osdep.h"
16393fc4c7SPhilippe Mathieu-Daudé #include "qemu/units.h"
17da34e65cSMarkus Armbruster #include "qapi/error.h"
18311467f7SDavid Hildenbrand #include "hw/boards.h"
1983c9f4caSPaolo Bonzini #include "hw/s390x/sclp.h"
20477a72a1SHeinz Graalfs #include "hw/s390x/event-facility.h"
218cba80c3SFrank Blaschka #include "hw/s390x/s390-pci-bus.h"
22b038411dSFarhan Ali #include "hw/s390x/ipl.h"
23a67f05b3SPierre Morel #include "hw/s390x/cpu-topology.h"
243d9836e4SCédric Le Goater #include "hw/s390x/s390-virtio-ccw.h"
25f6c98f92SHeinz Graalfs 
get_sclp_device(void)263d9836e4SCédric Le Goater static SCLPDevice *get_sclp_device(void)
2725a3c5afSDavid Hildenbrand {
28989fd865SChristian Borntraeger     static SCLPDevice *sclp;
29989fd865SChristian Borntraeger 
30989fd865SChristian Borntraeger     if (!sclp) {
313d9836e4SCédric Le Goater         sclp = S390_CCW_MACHINE(qdev_get_machine())->sclp;
32989fd865SChristian Borntraeger     }
33989fd865SChristian Borntraeger     return sclp;
3425a3c5afSDavid Hildenbrand }
3525a3c5afSDavid Hildenbrand 
sclp_command_code_valid(uint32_t code)360f73c5b3SJanosch Frank static inline bool sclp_command_code_valid(uint32_t code)
370f73c5b3SJanosch Frank {
380f73c5b3SJanosch Frank     switch (code & SCLP_CMD_CODE_MASK) {
390f73c5b3SJanosch Frank     case SCLP_CMDW_READ_SCP_INFO:
400f73c5b3SJanosch Frank     case SCLP_CMDW_READ_SCP_INFO_FORCED:
410f73c5b3SJanosch Frank     case SCLP_CMDW_READ_CPU_INFO:
420f73c5b3SJanosch Frank     case SCLP_CMDW_CONFIGURE_IOA:
430f73c5b3SJanosch Frank     case SCLP_CMDW_DECONFIGURE_IOA:
440f73c5b3SJanosch Frank     case SCLP_CMD_READ_EVENT_DATA:
450f73c5b3SJanosch Frank     case SCLP_CMD_WRITE_EVENT_DATA:
460f73c5b3SJanosch Frank     case SCLP_CMD_WRITE_EVENT_MASK:
470f73c5b3SJanosch Frank         return true;
480f73c5b3SJanosch Frank     }
490f73c5b3SJanosch Frank     return false;
500f73c5b3SJanosch Frank }
510f73c5b3SJanosch Frank 
sccb_verify_boundary(uint64_t sccb_addr,uint16_t sccb_len,uint32_t code)521ecd6078SCollin Walling static bool sccb_verify_boundary(uint64_t sccb_addr, uint16_t sccb_len,
531ecd6078SCollin Walling                                  uint32_t code)
54db13387cSCollin Walling {
55db13387cSCollin Walling     uint64_t sccb_max_addr = sccb_addr + sccb_len - 1;
56ed3288ffSThomas Huth     uint64_t sccb_boundary = (sccb_addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
57db13387cSCollin Walling 
581ecd6078SCollin Walling     switch (code & SCLP_CMD_CODE_MASK) {
591ecd6078SCollin Walling     case SCLP_CMDW_READ_SCP_INFO:
601ecd6078SCollin Walling     case SCLP_CMDW_READ_SCP_INFO_FORCED:
611ecd6078SCollin Walling     case SCLP_CMDW_READ_CPU_INFO:
621ecd6078SCollin Walling         /*
631ecd6078SCollin Walling          * An extended-length SCCB is only allowed for Read SCP/CPU Info and
641ecd6078SCollin Walling          * is allowed to exceed the 4k boundary. The respective commands will
651ecd6078SCollin Walling          * set the length field to the required length if an insufficient
661ecd6078SCollin Walling          * SCCB length is provided.
671ecd6078SCollin Walling          */
681ecd6078SCollin Walling         if (s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB)) {
691ecd6078SCollin Walling             return true;
701ecd6078SCollin Walling         }
711ecd6078SCollin Walling         /* fallthrough */
721ecd6078SCollin Walling     default:
73db13387cSCollin Walling         if (sccb_max_addr < sccb_boundary) {
74db13387cSCollin Walling             return true;
75db13387cSCollin Walling         }
761ecd6078SCollin Walling     }
77db13387cSCollin Walling 
78db13387cSCollin Walling     return false;
79db13387cSCollin Walling }
80db13387cSCollin Walling 
prepare_cpu_entries(MachineState * ms,CPUEntry * entry,int * count)81912d70d2SCollin Walling static void prepare_cpu_entries(MachineState *ms, CPUEntry *entry, int *count)
82026546e6SDavid Hildenbrand {
834dd4200eSDavid Hildenbrand     uint8_t features[SCCB_CPU_FEATURE_LEN] = { 0 };
84026546e6SDavid Hildenbrand     int i;
85026546e6SDavid Hildenbrand 
864dd4200eSDavid Hildenbrand     s390_get_feat_block(S390_FEAT_TYPE_SCLP_CPU, features);
87bb535bb6SDavid Hildenbrand     for (i = 0, *count = 0; i < ms->possible_cpus->len; i++) {
88bb535bb6SDavid Hildenbrand         if (!ms->possible_cpus->cpus[i].cpu) {
89bb535bb6SDavid Hildenbrand             continue;
90bb535bb6SDavid Hildenbrand         }
91bb535bb6SDavid Hildenbrand         entry[*count].address = ms->possible_cpus->cpus[i].arch_id;
92bb535bb6SDavid Hildenbrand         entry[*count].type = 0;
93bb535bb6SDavid Hildenbrand         memcpy(entry[*count].features, features, sizeof(features));
94bb535bb6SDavid Hildenbrand         (*count)++;
95026546e6SDavid Hildenbrand     }
96026546e6SDavid Hildenbrand }
97026546e6SDavid Hildenbrand 
980260b978SCollin Walling #define SCCB_REQ_LEN(s, max_cpus) (sizeof(s) + max_cpus * sizeof(CPUEntry))
990260b978SCollin Walling 
ext_len_sccb_supported(SCCBHeader header)1001ecd6078SCollin Walling static inline bool ext_len_sccb_supported(SCCBHeader header)
1011ecd6078SCollin Walling {
1021ecd6078SCollin Walling     return s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) &&
1031ecd6078SCollin Walling            header.control_mask[2] & SCLP_VARIABLE_LENGTH_RESPONSE;
1041ecd6078SCollin Walling }
1051ecd6078SCollin Walling 
106f6c98f92SHeinz Graalfs /* Provide information about the configuration, CPUs and storage */
read_SCP_info(SCLPDevice * sclp,SCCB * sccb)10725a3c5afSDavid Hildenbrand static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
108f6c98f92SHeinz Graalfs {
109f6c98f92SHeinz Graalfs     ReadInfo *read_info = (ReadInfo *) sccb;
110311467f7SDavid Hildenbrand     MachineState *machine = MACHINE(qdev_get_machine());
111bb535bb6SDavid Hildenbrand     int cpu_count;
1121def6656SMatthew Rosato     int rnsize, rnmax;
1130260b978SCollin Walling     int required_len = SCCB_REQ_LEN(ReadInfo, machine->possible_cpus->len);
1141ecd6078SCollin Walling     int offset_cpu = s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) ?
1151ecd6078SCollin Walling                      offsetof(ReadInfo, entries) :
1161ecd6078SCollin Walling                      SCLP_READ_SCP_INFO_FIXED_CPU_OFFSET;
1171a7a5688SCollin Walling     CPUEntry *entries_start = (void *)sccb + offset_cpu;
1180260b978SCollin Walling 
1190260b978SCollin Walling     if (be16_to_cpu(sccb->h.length) < required_len) {
1201ecd6078SCollin Walling         if (ext_len_sccb_supported(sccb->h)) {
1211ecd6078SCollin Walling             sccb->h.length = cpu_to_be16(required_len);
1221ecd6078SCollin Walling         }
1230260b978SCollin Walling         sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH);
1240260b978SCollin Walling         return;
1250260b978SCollin Walling     }
1268cc3aecfSJason J. Herne 
127a67f05b3SPierre Morel     if (s390_has_topology()) {
128a67f05b3SPierre Morel         read_info->stsi_parm = SCLP_READ_SCP_INFO_MNEST;
129a67f05b3SPierre Morel     }
130a67f05b3SPierre Morel 
1318cc3aecfSJason J. Herne     /* CPU information */
1321a7a5688SCollin Walling     prepare_cpu_entries(machine, entries_start, &cpu_count);
1338cc3aecfSJason J. Herne     read_info->entries_cpu = cpu_to_be16(cpu_count);
1341a7a5688SCollin Walling     read_info->offset_cpu = cpu_to_be16(offset_cpu);
135ae71ed86SLike Xu     read_info->highest_cpu = cpu_to_be16(machine->smp.max_cpus - 1);
1368cc3aecfSJason J. Herne 
137059be520SDavid Hildenbrand     read_info->ibc_val = cpu_to_be32(s390_get_ibc_val());
138059be520SDavid Hildenbrand 
1394dd4200eSDavid Hildenbrand     /* Configuration Characteristic (Extension) */
1404dd4200eSDavid Hildenbrand     s390_get_feat_block(S390_FEAT_TYPE_SCLP_CONF_CHAR,
1414dd4200eSDavid Hildenbrand                          read_info->conf_char);
1424dd4200eSDavid Hildenbrand     s390_get_feat_block(S390_FEAT_TYPE_SCLP_CONF_CHAR_EXT,
1434dd4200eSDavid Hildenbrand                          read_info->conf_char_ext);
1444dd4200eSDavid Hildenbrand 
145fabdada9SCollin Walling     if (s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB)) {
146fabdada9SCollin Walling         s390_get_feat_block(S390_FEAT_TYPE_SCLP_FAC134,
147fabdada9SCollin Walling                             &read_info->fac134);
148fabdada9SCollin Walling     }
149fabdada9SCollin Walling 
1508cba80c3SFrank Blaschka     read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO |
15180b7a265SCornelia Huck                                         SCLP_HAS_IOA_RECONFIG);
152f6c98f92SHeinz Graalfs 
1533fad3252SDavid Hildenbrand     read_info->mha_pow = s390_get_mha_pow();
154a3669307SDavid Hildenbrand     read_info->hmfai = cpu_to_be32(s390_get_hmfai());
1551def6656SMatthew Rosato 
15671a2fd35SDavid Hildenbrand     rnsize = 1 << (sclp->increment_size - 20);
1571def6656SMatthew Rosato     if (rnsize <= 128) {
1581def6656SMatthew Rosato         read_info->rnsize = rnsize;
1591def6656SMatthew Rosato     } else {
1601def6656SMatthew Rosato         read_info->rnsize = 0;
1611def6656SMatthew Rosato         read_info->rnsize2 = cpu_to_be32(rnsize);
1621def6656SMatthew Rosato     }
1631def6656SMatthew Rosato 
16482fab5c5SDavid Hildenbrand     /* we don't support standby memory, maxram_size is never exposed */
16582fab5c5SDavid Hildenbrand     rnmax = machine->ram_size >> sclp->increment_size;
1661def6656SMatthew Rosato     if (rnmax < 0x10000) {
1671def6656SMatthew Rosato         read_info->rnmax = cpu_to_be16(rnmax);
1681def6656SMatthew Rosato     } else {
1691def6656SMatthew Rosato         read_info->rnmax = cpu_to_be16(0);
1701def6656SMatthew Rosato         read_info->rnmax2 = cpu_to_be64(rnmax);
1711def6656SMatthew Rosato     }
1721def6656SMatthew Rosato 
173*bb185de4SJared Rossi     s390_ipl_convert_loadparm((char *)S390_CCW_MACHINE(machine)->loadparm,
174*bb185de4SJared Rossi                                 read_info->loadparm);
175b038411dSFarhan Ali 
176f6c98f92SHeinz Graalfs     sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
177f6c98f92SHeinz Graalfs }
178f6c98f92SHeinz Graalfs 
1798cc3aecfSJason J. Herne /* Provide information about the CPU */
sclp_read_cpu_info(SCLPDevice * sclp,SCCB * sccb)18025a3c5afSDavid Hildenbrand static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb)
1818cc3aecfSJason J. Herne {
182912d70d2SCollin Walling     MachineState *machine = MACHINE(qdev_get_machine());
1838cc3aecfSJason J. Herne     ReadCpuInfo *cpu_info = (ReadCpuInfo *) sccb;
184bb535bb6SDavid Hildenbrand     int cpu_count;
1850260b978SCollin Walling     int required_len = SCCB_REQ_LEN(ReadCpuInfo, machine->possible_cpus->len);
1860260b978SCollin Walling 
1870260b978SCollin Walling     if (be16_to_cpu(sccb->h.length) < required_len) {
1881ecd6078SCollin Walling         if (ext_len_sccb_supported(sccb->h)) {
1891ecd6078SCollin Walling             sccb->h.length = cpu_to_be16(required_len);
1901ecd6078SCollin Walling         }
1910260b978SCollin Walling         sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH);
1920260b978SCollin Walling         return;
1930260b978SCollin Walling     }
1948cc3aecfSJason J. Herne 
195912d70d2SCollin Walling     prepare_cpu_entries(machine, cpu_info->entries, &cpu_count);
1968cc3aecfSJason J. Herne     cpu_info->nr_configured = cpu_to_be16(cpu_count);
1978cc3aecfSJason J. Herne     cpu_info->offset_configured = cpu_to_be16(offsetof(ReadCpuInfo, entries));
1988cc3aecfSJason J. Herne     cpu_info->nr_standby = cpu_to_be16(0);
1998cc3aecfSJason J. Herne 
2008cc3aecfSJason J. Herne     /* The standby offset is 16-byte for each CPU */
2018cc3aecfSJason J. Herne     cpu_info->offset_standby = cpu_to_be16(cpu_info->offset_configured
2028cc3aecfSJason J. Herne         + cpu_info->nr_configured*sizeof(CPUEntry));
2038cc3aecfSJason J. Herne 
2048cc3aecfSJason J. Herne 
2058cc3aecfSJason J. Herne     sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
2068cc3aecfSJason J. Herne }
2078cc3aecfSJason J. Herne 
sclp_configure_io_adapter(SCLPDevice * sclp,SCCB * sccb,bool configure)20880b7a265SCornelia Huck static void sclp_configure_io_adapter(SCLPDevice *sclp, SCCB *sccb,
20980b7a265SCornelia Huck                                       bool configure)
21080b7a265SCornelia Huck {
21180b7a265SCornelia Huck     int rc;
21280b7a265SCornelia Huck 
21380b7a265SCornelia Huck     if (be16_to_cpu(sccb->h.length) < 16) {
21480b7a265SCornelia Huck         rc = SCLP_RC_INSUFFICIENT_SCCB_LENGTH;
21580b7a265SCornelia Huck         goto out_err;
21680b7a265SCornelia Huck     }
21780b7a265SCornelia Huck 
21880b7a265SCornelia Huck     switch (((IoaCfgSccb *)sccb)->atype) {
21980b7a265SCornelia Huck     case SCLP_RECONFIG_PCI_ATYPE:
22080b7a265SCornelia Huck         if (s390_has_feat(S390_FEAT_ZPCI)) {
22180b7a265SCornelia Huck             if (configure) {
22280b7a265SCornelia Huck                 s390_pci_sclp_configure(sccb);
22380b7a265SCornelia Huck             } else {
22480b7a265SCornelia Huck                 s390_pci_sclp_deconfigure(sccb);
22580b7a265SCornelia Huck             }
22680b7a265SCornelia Huck             return;
22780b7a265SCornelia Huck         }
22880b7a265SCornelia Huck         /* fallthrough */
22980b7a265SCornelia Huck     default:
23080b7a265SCornelia Huck         rc = SCLP_RC_ADAPTER_TYPE_NOT_RECOGNIZED;
23180b7a265SCornelia Huck     }
23280b7a265SCornelia Huck 
23380b7a265SCornelia Huck  out_err:
23480b7a265SCornelia Huck     sccb->h.response_code = cpu_to_be16(rc);
23580b7a265SCornelia Huck }
23680b7a265SCornelia Huck 
sclp_execute(SCLPDevice * sclp,SCCB * sccb,uint32_t code)23725a3c5afSDavid Hildenbrand static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code)
238f6c98f92SHeinz Graalfs {
23925a3c5afSDavid Hildenbrand     SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
24025a3c5afSDavid Hildenbrand     SCLPEventFacility *ef = sclp->event_facility;
241477a72a1SHeinz Graalfs     SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
242559a17a1SHeinz Graalfs 
2435f04c14aSJason J. Herne     switch (code & SCLP_CMD_CODE_MASK) {
244f6c98f92SHeinz Graalfs     case SCLP_CMDW_READ_SCP_INFO:
245f6c98f92SHeinz Graalfs     case SCLP_CMDW_READ_SCP_INFO_FORCED:
24625a3c5afSDavid Hildenbrand         sclp_c->read_SCP_info(sclp, sccb);
247f6c98f92SHeinz Graalfs         break;
2488cc3aecfSJason J. Herne     case SCLP_CMDW_READ_CPU_INFO:
24925a3c5afSDavid Hildenbrand         sclp_c->read_cpu_info(sclp, sccb);
2508cc3aecfSJason J. Herne         break;
25180b7a265SCornelia Huck     case SCLP_CMDW_CONFIGURE_IOA:
25280b7a265SCornelia Huck         sclp_configure_io_adapter(sclp, sccb, true);
2538cba80c3SFrank Blaschka         break;
25480b7a265SCornelia Huck     case SCLP_CMDW_DECONFIGURE_IOA:
25580b7a265SCornelia Huck         sclp_configure_io_adapter(sclp, sccb, false);
2568cba80c3SFrank Blaschka         break;
257f6c98f92SHeinz Graalfs     default:
258477a72a1SHeinz Graalfs         efc->command_handler(ef, sccb, code);
259f6c98f92SHeinz Graalfs         break;
260f6c98f92SHeinz Graalfs     }
261f6c98f92SHeinz Graalfs }
262f6c98f92SHeinz Graalfs 
2630f73c5b3SJanosch Frank /*
2640f73c5b3SJanosch Frank  * We only need the address to have something valid for the
2650f73c5b3SJanosch Frank  * service_interrupt call.
2660f73c5b3SJanosch Frank  */
2670f73c5b3SJanosch Frank #define SCLP_PV_DUMMY_ADDR 0x4000
sclp_service_call_protected(S390CPU * cpu,uint64_t sccb,uint32_t code)2686d3910c9SPhilippe Mathieu-Daudé int sclp_service_call_protected(S390CPU *cpu, uint64_t sccb, uint32_t code)
2690f73c5b3SJanosch Frank {
2706d3910c9SPhilippe Mathieu-Daudé     CPUS390XState *env = &cpu->env;
2710f73c5b3SJanosch Frank     SCLPDevice *sclp = get_sclp_device();
2720f73c5b3SJanosch Frank     SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
273c1db53a5SCollin Walling     SCCBHeader header;
274c1db53a5SCollin Walling     g_autofree SCCB *work_sccb = NULL;
2750f73c5b3SJanosch Frank 
276c1db53a5SCollin Walling     s390_cpu_pv_mem_read(env_archcpu(env), 0, &header, sizeof(SCCBHeader));
277c1db53a5SCollin Walling 
278c1db53a5SCollin Walling     work_sccb = g_malloc0(be16_to_cpu(header.length));
279c1db53a5SCollin Walling     s390_cpu_pv_mem_read(env_archcpu(env), 0, work_sccb,
280c1db53a5SCollin Walling                          be16_to_cpu(header.length));
2810f73c5b3SJanosch Frank 
2820f73c5b3SJanosch Frank     if (!sclp_command_code_valid(code)) {
283c1db53a5SCollin Walling         work_sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
2840f73c5b3SJanosch Frank         goto out_write;
2850f73c5b3SJanosch Frank     }
2860f73c5b3SJanosch Frank 
287c1db53a5SCollin Walling     sclp_c->execute(sclp, work_sccb, code);
2880f73c5b3SJanosch Frank out_write:
289c1db53a5SCollin Walling     s390_cpu_pv_mem_write(env_archcpu(env), 0, work_sccb,
290c1db53a5SCollin Walling                           be16_to_cpu(work_sccb->h.length));
2910f73c5b3SJanosch Frank     sclp_c->service_interrupt(sclp, SCLP_PV_DUMMY_ADDR);
2920f73c5b3SJanosch Frank     return 0;
2930f73c5b3SJanosch Frank }
2940f73c5b3SJanosch Frank 
sclp_service_call(S390CPU * cpu,uint64_t sccb,uint32_t code)2956d3910c9SPhilippe Mathieu-Daudé int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code)
296f6c98f92SHeinz Graalfs {
2976d3910c9SPhilippe Mathieu-Daudé     CPUS390XState *env = &cpu->env;
29825a3c5afSDavid Hildenbrand     SCLPDevice *sclp = get_sclp_device();
29925a3c5afSDavid Hildenbrand     SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
300c1db53a5SCollin Walling     SCCBHeader header;
301c1db53a5SCollin Walling     g_autofree SCCB *work_sccb = NULL;
302f6c98f92SHeinz Graalfs 
303f6c98f92SHeinz Graalfs     /* first some basic checks on program checks */
3046e252802SThomas Huth     if (env->psw.mask & PSW_MASK_PSTATE) {
305e6de76fcSDaniel Henrique Barboza         return -PGM_PRIVILEGED;
3066e252802SThomas Huth     }
307f6c98f92SHeinz Graalfs     if (cpu_physical_memory_is_io(sccb)) {
308e6de76fcSDaniel Henrique Barboza         return -PGM_ADDRESSING;
309f6c98f92SHeinz Graalfs     }
3106e252802SThomas Huth     if ((sccb & ~0x1fffUL) == 0 || (sccb & ~0x1fffUL) == env->psa
3116e252802SThomas Huth         || (sccb & ~0x7ffffff8UL) != 0) {
312e6de76fcSDaniel Henrique Barboza         return -PGM_SPECIFICATION;
313f6c98f92SHeinz Graalfs     }
314f6c98f92SHeinz Graalfs 
315c1db53a5SCollin Walling     /* the header contains the actual length of the sccb */
316c1db53a5SCollin Walling     cpu_physical_memory_read(sccb, &header, sizeof(SCCBHeader));
317c1db53a5SCollin Walling 
318c1db53a5SCollin Walling     /* Valid sccb sizes */
319c1db53a5SCollin Walling     if (be16_to_cpu(header.length) < sizeof(SCCBHeader)) {
320c1db53a5SCollin Walling         return -PGM_SPECIFICATION;
321c1db53a5SCollin Walling     }
322c1db53a5SCollin Walling 
323f6c98f92SHeinz Graalfs     /*
324f6c98f92SHeinz Graalfs      * we want to work on a private copy of the sccb, to prevent guests
325f6c98f92SHeinz Graalfs      * from playing dirty tricks by modifying the memory content after
326f6c98f92SHeinz Graalfs      * the host has checked the values
327f6c98f92SHeinz Graalfs      */
328c1db53a5SCollin Walling     work_sccb = g_malloc0(be16_to_cpu(header.length));
329c1db53a5SCollin Walling     cpu_physical_memory_read(sccb, work_sccb, be16_to_cpu(header.length));
330f6c98f92SHeinz Graalfs 
3310f73c5b3SJanosch Frank     if (!sclp_command_code_valid(code)) {
332c1db53a5SCollin Walling         work_sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
333679b8447SJanosch Frank         goto out_write;
334679b8447SJanosch Frank     }
335f6c98f92SHeinz Graalfs 
3361ecd6078SCollin Walling     if (!sccb_verify_boundary(sccb, be16_to_cpu(work_sccb->h.length), code)) {
337c1db53a5SCollin Walling         work_sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
3386f6c9333SJanosch Frank         goto out_write;
3396f6c9333SJanosch Frank     }
3406f6c9333SJanosch Frank 
341c1db53a5SCollin Walling     sclp_c->execute(sclp, work_sccb, code);
342679b8447SJanosch Frank out_write:
343c1db53a5SCollin Walling     cpu_physical_memory_write(sccb, work_sccb,
344c1db53a5SCollin Walling                               be16_to_cpu(work_sccb->h.length));
345f6c98f92SHeinz Graalfs 
3461723a1b6SDavid Hildenbrand     sclp_c->service_interrupt(sclp, sccb);
347f6c98f92SHeinz Graalfs 
348e6de76fcSDaniel Henrique Barboza     return 0;
349f6c98f92SHeinz Graalfs }
350f6c98f92SHeinz Graalfs 
service_interrupt(SCLPDevice * sclp,uint32_t sccb)3511723a1b6SDavid Hildenbrand static void service_interrupt(SCLPDevice *sclp, uint32_t sccb)
352f6c98f92SHeinz Graalfs {
3531723a1b6SDavid Hildenbrand     SCLPEventFacility *ef = sclp->event_facility;
354477a72a1SHeinz Graalfs     SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
355477a72a1SHeinz Graalfs 
356559a17a1SHeinz Graalfs     uint32_t param = sccb & ~3;
357559a17a1SHeinz Graalfs 
358559a17a1SHeinz Graalfs     /* Indicate whether an event is still pending */
359477a72a1SHeinz Graalfs     param |= efc->event_pending(ef) ? 1 : 0;
360559a17a1SHeinz Graalfs 
361559a17a1SHeinz Graalfs     if (!param) {
362559a17a1SHeinz Graalfs         /* No need to send an interrupt, there's nothing to be notified about */
363559a17a1SHeinz Graalfs         return;
364559a17a1SHeinz Graalfs     }
365559a17a1SHeinz Graalfs     s390_sclp_extint(param);
366f6c98f92SHeinz Graalfs }
367f6c98f92SHeinz Graalfs 
sclp_service_interrupt(uint32_t sccb)3681723a1b6SDavid Hildenbrand void sclp_service_interrupt(uint32_t sccb)
3691723a1b6SDavid Hildenbrand {
3701723a1b6SDavid Hildenbrand     SCLPDevice *sclp = get_sclp_device();
3711723a1b6SDavid Hildenbrand     SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
3721723a1b6SDavid Hildenbrand 
3731723a1b6SDavid Hildenbrand     sclp_c->service_interrupt(sclp, sccb);
3741723a1b6SDavid Hildenbrand }
3751723a1b6SDavid Hildenbrand 
376f6c98f92SHeinz Graalfs /* qemu object creation and initialization functions */
sclp_realize(DeviceState * dev,Error ** errp)377515190d9SDavid Hildenbrand static void sclp_realize(DeviceState *dev, Error **errp)
378515190d9SDavid Hildenbrand {
3791cf065fbSDavid Hildenbrand     MachineState *machine = MACHINE(qdev_get_machine());
380515190d9SDavid Hildenbrand     SCLPDevice *sclp = SCLP(dev);
3811cf065fbSDavid Hildenbrand     uint64_t hw_limit;
3821cf065fbSDavid Hildenbrand     int ret;
383515190d9SDavid Hildenbrand 
3848b638c43SDavid Hildenbrand     /*
3858b638c43SDavid Hildenbrand      * qdev_device_add searches the sysbus for TYPE_SCLP_EVENTS_BUS. As long
3868b638c43SDavid Hildenbrand      * as we can't find a fitting bus via the qom tree, we have to add the
3878b638c43SDavid Hildenbrand      * event facility to the sysbus, so e.g. a sclp console can be created.
3888b638c43SDavid Hildenbrand      */
389992861fbSMarkus Armbruster     if (!sysbus_realize(SYS_BUS_DEVICE(sclp->event_facility), errp)) {
390992861fbSMarkus Armbruster         return;
39168424112SMarkus Armbruster     }
3921cf065fbSDavid Hildenbrand 
3931cf065fbSDavid Hildenbrand     ret = s390_set_memory_limit(machine->maxram_size, &hw_limit);
3941cf065fbSDavid Hildenbrand     if (ret == -E2BIG) {
395992861fbSMarkus Armbruster         error_setg(errp, "host supports a maximum of %" PRIu64 " GB",
396393fc4c7SPhilippe Mathieu-Daudé                    hw_limit / GiB);
3971cf065fbSDavid Hildenbrand     } else if (ret) {
398992861fbSMarkus Armbruster         error_setg(errp, "setting the guest size failed");
3991cf065fbSDavid Hildenbrand     }
400515190d9SDavid Hildenbrand }
401515190d9SDavid Hildenbrand 
sclp_memory_init(SCLPDevice * sclp)4021cf065fbSDavid Hildenbrand static void sclp_memory_init(SCLPDevice *sclp)
4031cf065fbSDavid Hildenbrand {
4041cf065fbSDavid Hildenbrand     MachineState *machine = MACHINE(qdev_get_machine());
4055c30ef93SChristian Borntraeger     MachineClass *machine_class = MACHINE_GET_CLASS(qdev_get_machine());
4061cf065fbSDavid Hildenbrand     ram_addr_t initial_mem = machine->ram_size;
4071cf065fbSDavid Hildenbrand     int increment_size = 20;
4081cf065fbSDavid Hildenbrand 
4091cf065fbSDavid Hildenbrand     /* The storage increment size is a multiple of 1M and is a power of 2.
4105c30ef93SChristian Borntraeger      * For some machine types, the number of storage increments must be
4115c30ef93SChristian Borntraeger      * MAX_STORAGE_INCREMENTS or fewer.
4121cf065fbSDavid Hildenbrand      * The variable 'increment_size' is an exponent of 2 that can be
4131cf065fbSDavid Hildenbrand      * used to calculate the size (in bytes) of an increment. */
4145c30ef93SChristian Borntraeger     while (machine_class->fixup_ram_size != NULL &&
4155c30ef93SChristian Borntraeger            (initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) {
4161cf065fbSDavid Hildenbrand         increment_size++;
4171cf065fbSDavid Hildenbrand     }
41871a2fd35SDavid Hildenbrand     sclp->increment_size = increment_size;
4191cf065fbSDavid Hildenbrand }
4201cf065fbSDavid Hildenbrand 
sclp_init(Object * obj)421515190d9SDavid Hildenbrand static void sclp_init(Object *obj)
422515190d9SDavid Hildenbrand {
423515190d9SDavid Hildenbrand     SCLPDevice *sclp = SCLP(obj);
424515190d9SDavid Hildenbrand     Object *new;
425515190d9SDavid Hildenbrand 
426515190d9SDavid Hildenbrand     new = object_new(TYPE_SCLP_EVENT_FACILITY);
427d2623129SMarkus Armbruster     object_property_add_child(obj, TYPE_SCLP_EVENT_FACILITY, new);
428515190d9SDavid Hildenbrand     object_unref(new);
429515190d9SDavid Hildenbrand     sclp->event_facility = EVENT_FACILITY(new);
4301cf065fbSDavid Hildenbrand 
4311cf065fbSDavid Hildenbrand     sclp_memory_init(sclp);
432515190d9SDavid Hildenbrand }
433515190d9SDavid Hildenbrand 
sclp_class_init(ObjectClass * oc,void * data)434515190d9SDavid Hildenbrand static void sclp_class_init(ObjectClass *oc, void *data)
435515190d9SDavid Hildenbrand {
43625a3c5afSDavid Hildenbrand     SCLPDeviceClass *sc = SCLP_CLASS(oc);
437515190d9SDavid Hildenbrand     DeviceClass *dc = DEVICE_CLASS(oc);
438515190d9SDavid Hildenbrand 
439515190d9SDavid Hildenbrand     dc->desc = "SCLP (Service-Call Logical Processor)";
440515190d9SDavid Hildenbrand     dc->realize = sclp_realize;
441515190d9SDavid Hildenbrand     dc->hotpluggable = false;
442515190d9SDavid Hildenbrand     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
443e6cb60bfSThomas Huth     /*
444e6cb60bfSThomas Huth      * Reason: Creates TYPE_SCLP_EVENT_FACILITY in sclp_init
445e6cb60bfSThomas Huth      * which is a non-pluggable sysbus device
446e6cb60bfSThomas Huth      */
447e6cb60bfSThomas Huth     dc->user_creatable = false;
44825a3c5afSDavid Hildenbrand 
44925a3c5afSDavid Hildenbrand     sc->read_SCP_info = read_SCP_info;
45025a3c5afSDavid Hildenbrand     sc->read_cpu_info = sclp_read_cpu_info;
45125a3c5afSDavid Hildenbrand     sc->execute = sclp_execute;
4521723a1b6SDavid Hildenbrand     sc->service_interrupt = service_interrupt;
453515190d9SDavid Hildenbrand }
454515190d9SDavid Hildenbrand 
4555e78c98bSBernhard Beschow static const TypeInfo sclp_info = {
456515190d9SDavid Hildenbrand     .name = TYPE_SCLP,
457515190d9SDavid Hildenbrand     .parent = TYPE_DEVICE,
458515190d9SDavid Hildenbrand     .instance_init = sclp_init,
459515190d9SDavid Hildenbrand     .instance_size = sizeof(SCLPDevice),
460515190d9SDavid Hildenbrand     .class_init = sclp_class_init,
461515190d9SDavid Hildenbrand     .class_size = sizeof(SCLPDeviceClass),
462515190d9SDavid Hildenbrand };
463515190d9SDavid Hildenbrand 
register_types(void)4640844df77SMatthew Rosato static void register_types(void)
4650844df77SMatthew Rosato {
466515190d9SDavid Hildenbrand     type_register_static(&sclp_info);
4670844df77SMatthew Rosato }
4680844df77SMatthew Rosato type_init(register_types);
469