| 1 | /* |
| 2 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | * |
| 4 | * SCLP event type 11 - Control-Program Identification (CPI): |
| 5 | * CPI is used to send program identifiers from the guest to the |
| 6 | * Service-Call Logical Processor (SCLP). It is not sent by the SCLP. |
| 7 | * |
| 8 | * Control-program identifiers provide data about the guest operating |
| 9 | * system. The control-program identifiers are: system type, system name, |
| 10 | * system level and sysplex name. |
| 11 | * |
| 12 | * In Linux, all the control-program identifiers are user configurable. The |
| 13 | * system type, system name, and sysplex name use EBCDIC characters from |
| 14 | * this set: capital A-Z, 0-9, $, @, #, and blank. In Linux, the system |
| 15 | * type, system name and sysplex name are arbitrary free-form texts. |
| 16 | * |
| 17 | * In Linux, the 8-byte hexadecimal system-level has the format |
| 18 | * 0x<a><b><cc><dd><eeee><ff><gg><hh>, where: |
| 19 | * <a>: is a 4-bit digit, its most significant bit indicates hypervisor use |
| 20 | * <b>: is one digit that represents Linux distributions as follows |
| 21 | * 0: generic Linux |
| 22 | * 1: Red Hat Enterprise Linux |
| 23 | * 2: SUSE Linux Enterprise Server |
| 24 | * 3: Canonical Ubuntu |
| 25 | * 4: Fedora |
| 26 | * 5: openSUSE Leap |
| 27 | * 6: Debian GNU/Linux |
| 28 | * 7: Red Hat Enterprise Linux CoreOS |
| 29 | * <cc>: are two digits for a distribution-specific encoding of the major |
| 30 | * version of the distribution |
| 31 | * <dd>: are two digits for a distribution-specific encoding of the minor |
| 32 | * version of the distribution |
| 33 | * <eeee>: are four digits for the patch level of the distribution |
| 34 | * <ff>: are two digits for the major version of the kernel |
| 35 | * <gg>: are two digits for the minor version of the kernel |
| 36 | * <hh>: are two digits for the stable version of the kernel |
| 37 | * (e.g. 74872343805430528, when converted to hex is 0x010a000000060b00). On |
| 38 | * machines prior to z16, some of the values are not available to display. |
| 39 | * |
| 40 | * Sysplex refers to a cluster of logical partitions that communicates and |
| 41 | * co-operates with each other. |
| 42 | * |
| 43 | * The CPI feature is supported since 10.1. |
| 44 | * |
| 45 | * Copyright IBM, Corp. 2024 |
| 46 | * |
| 47 | * Authors: |
| 48 | * Shalini Chellathurai Saroja <shalini@linux.ibm.com> |
| 49 | * |
| 50 | */ |
| 51 | |
| 52 | #include "qemu/osdep.h" |
| 53 | #include "qemu/timer.h" |
| 54 | #include "hw/s390x/event-facility.h" |
| 55 | #include "hw/s390x/ebcdic.h" |
| 56 | #include "qapi/qapi-visit-machine.h" |
| 57 | #include "qapi/qapi-events-machine-s390x.h" |
| 58 | #include "migration/vmstate.h" |
| 59 | |
| 60 | typedef struct Data { |
| 61 | uint8_t id_format; |
| 62 | uint8_t reserved0; |
| 63 | uint8_t system_type[8]; |
| 64 | uint64_t reserved1; |
| 65 | uint8_t system_name[8]; |
| 66 | uint64_t reserved2; |
| 67 | uint64_t system_level; |
| 68 | uint64_t reserved3; |
| 69 | uint8_t sysplex_name[8]; |
| 70 | uint8_t reserved4[16]; |
| 71 | } QEMU_PACKED Data; |
| 72 | |
| 73 | typedef struct ControlProgramIdMsg { |
| 74 | EventBufferHeader ebh; |
| 75 | Data data; |
| 76 | } QEMU_PACKED ControlProgramIdMsg; |
| 77 | |
| 78 | static bool can_handle_event(uint8_t type) |
| 79 | { |
| 80 | return type == SCLP_EVENT_CTRL_PGM_ID; |
| 81 | } |
| 82 | |
| 83 | static sccb_mask_t send_mask(void) |
| 84 | { |
| 85 | return 0; |
| 86 | } |
| 87 | |
| 88 | /* Enable SCLP to accept buffers of event type CPI from the control-program. */ |
| 89 | static sccb_mask_t receive_mask(void) |
| 90 | { |
| 91 | return SCLP_EVENT_MASK_CTRL_PGM_ID; |
| 92 | } |
| 93 | |
| 94 | static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr) |
| 95 | { |
| 96 | ControlProgramIdMsg *cpim = container_of(evt_buf_hdr, ControlProgramIdMsg, |
| 97 | ebh); |
| 98 | SCLPEventCPI *e = SCLP_EVENT_CPI(event); |
| 99 | |
| 100 | /* Caller checks sccb length, buffer header checking is our duty */ |
| 101 | if (be16_to_cpu(evt_buf_hdr->length) != sizeof(ControlProgramIdMsg)) { |
| 102 | return SCLP_RC_INCONSISTENT_LENGTHS; |
| 103 | } |
| 104 | |
| 105 | ascii_put(e->system_type, (char *)cpim->data.system_type, |
| 106 | sizeof(cpim->data.system_type)); |
| 107 | ascii_put(e->system_name, (char *)cpim->data.system_name, |
| 108 | sizeof(cpim->data.system_name)); |
| 109 | ascii_put(e->sysplex_name, (char *)cpim->data.sysplex_name, |
| 110 | sizeof(cpim->data.sysplex_name)); |
| 111 | e->system_level = ldq_be_p(&cpim->data.system_level); |
| 112 | e->timestamp = qemu_clock_get_ns(QEMU_CLOCK_HOST); |
| 113 | |
| 114 | cpim->ebh.flags = SCLP_EVENT_BUFFER_ACCEPTED; |
| 115 | |
| 116 | qapi_event_send_sclp_cpi_info_available(); |
| 117 | |
| 118 | return SCLP_RC_NORMAL_COMPLETION; |
| 119 | } |
| 120 | |
| 121 | static char *get_system_type(Object *obj, Error **errp) |
| 122 | { |
| 123 | SCLPEventCPI *e = SCLP_EVENT_CPI(obj); |
| 124 | |
| 125 | return g_strndup((char *) e->system_type, sizeof(e->system_type)); |
| 126 | } |
| 127 | |
| 128 | static char *get_system_name(Object *obj, Error **errp) |
| 129 | { |
| 130 | SCLPEventCPI *e = SCLP_EVENT_CPI(obj); |
| 131 | |
| 132 | return g_strndup((char *) e->system_name, sizeof(e->system_name)); |
| 133 | } |
| 134 | |
| 135 | static char *get_sysplex_name(Object *obj, Error **errp) |
| 136 | { |
| 137 | SCLPEventCPI *e = SCLP_EVENT_CPI(obj); |
| 138 | |
| 139 | return g_strndup((char *) e->sysplex_name, sizeof(e->sysplex_name)); |
| 140 | } |
| 141 | |
| 142 | static void get_system_level(Object *obj, Visitor *v, const char *name, |
| 143 | void *opaque, Error **errp) |
| 144 | { |
| 145 | SCLPEventCPI *e = SCLP_EVENT_CPI(obj); |
| 146 | |
| 147 | visit_type_uint64(v, name, &e->system_level, errp); |
| 148 | } |
| 149 | |
| 150 | static void get_timestamp(Object *obj, Visitor *v, const char *name, |
| 151 | void *opaque, Error **errp) |
| 152 | { |
| 153 | SCLPEventCPI *e = SCLP_EVENT_CPI(obj); |
| 154 | |
| 155 | visit_type_uint64(v, name, &e->timestamp, errp); |
| 156 | } |
| 157 | |
| 158 | static const VMStateDescription vmstate_sclpcpi = { |
| 159 | .name = "s390_control_program_id", |
| 160 | .version_id = 0, |
| 161 | .fields = (const VMStateField[]) { |
| 162 | VMSTATE_UINT8_ARRAY(system_type, SCLPEventCPI, 8), |
| 163 | VMSTATE_UINT8_ARRAY(system_name, SCLPEventCPI, 8), |
| 164 | VMSTATE_UINT64(system_level, SCLPEventCPI), |
| 165 | VMSTATE_UINT8_ARRAY(sysplex_name, SCLPEventCPI, 8), |
| 166 | VMSTATE_UINT64(timestamp, SCLPEventCPI), |
| 167 | VMSTATE_END_OF_LIST() |
| 168 | } |
| 169 | }; |
| 170 | |
| 171 | static void cpi_class_init(ObjectClass *klass, const void *data) |
| 172 | { |
| 173 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 174 | SCLPEventClass *k = SCLP_EVENT_CLASS(klass); |
| 175 | |
| 176 | dc->user_creatable = false; |
| 177 | dc->vmsd = &vmstate_sclpcpi; |
| 178 | |
| 179 | k->can_handle_event = can_handle_event; |
| 180 | k->get_send_mask = send_mask; |
| 181 | k->get_receive_mask = receive_mask; |
| 182 | k->write_event_data = write_event_data; |
| 183 | |
| 184 | object_class_property_add_str(klass, "system_type", get_system_type, NULL); |
| 185 | object_class_property_set_description(klass, "system_type", |
| 186 | "operating system e.g. \"LINUX \""); |
| 187 | |
| 188 | object_class_property_add_str(klass, "system_name", get_system_name, NULL); |
| 189 | object_class_property_set_description(klass, "system_name", |
| 190 | "user configurable name of the VM e.g. \"TESTVM \""); |
| 191 | |
| 192 | object_class_property_add_str(klass, "sysplex_name", get_sysplex_name, |
| 193 | NULL); |
| 194 | object_class_property_set_description(klass, "sysplex_name", |
| 195 | "name of the cluster which the VM belongs to, if any" |
| 196 | " e.g. \"PLEX \""); |
| 197 | |
| 198 | object_class_property_add(klass, "system_level", "uint64", get_system_level, |
| 199 | NULL, NULL, NULL); |
| 200 | object_class_property_set_description(klass, "system_level", |
| 201 | "distribution and kernel version in Linux e.g. 74872343805430528"); |
| 202 | |
| 203 | object_class_property_add(klass, "timestamp", "uint64", get_timestamp, |
| 204 | NULL, NULL, NULL); |
| 205 | object_class_property_set_description(klass, "timestamp", |
| 206 | "latest update of CPI data in nanoseconds since the UNIX EPOCH"); |
| 207 | } |
| 208 | |
| 209 | static const TypeInfo sclp_cpi_info = { |
| 210 | .name = TYPE_SCLP_EVENT_CPI, |
| 211 | .parent = TYPE_SCLP_EVENT, |
| 212 | .instance_size = sizeof(SCLPEventCPI), |
| 213 | .class_init = cpi_class_init, |
| 214 | }; |
| 215 | |
| 216 | static void sclp_cpi_register_types(void) |
| 217 | { |
| 218 | type_register_static(&sclp_cpi_info); |
| 219 | } |
| 220 | |
| 221 | type_init(sclp_cpi_register_types) |