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