xref: /qemu/target/s390x/kvm/stsi-topology.c (revision f4f54b582f4b78f4cfd5a6912e88aef4f11e3e3c)
1*f4f54b58SPierre Morel /* SPDX-License-Identifier: GPL-2.0-or-later */
2*f4f54b58SPierre Morel /*
3*f4f54b58SPierre Morel  * QEMU S390x CPU Topology
4*f4f54b58SPierre Morel  *
5*f4f54b58SPierre Morel  * Copyright IBM Corp. 2022, 2023
6*f4f54b58SPierre Morel  * Author(s): Pierre Morel <pmorel@linux.ibm.com>
7*f4f54b58SPierre Morel  *
8*f4f54b58SPierre Morel  */
9*f4f54b58SPierre Morel #include "qemu/osdep.h"
10*f4f54b58SPierre Morel #include "cpu.h"
11*f4f54b58SPierre Morel #include "hw/s390x/sclp.h"
12*f4f54b58SPierre Morel #include "hw/s390x/cpu-topology.h"
13*f4f54b58SPierre Morel 
14*f4f54b58SPierre Morel QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_LOW != 1);
15*f4f54b58SPierre Morel QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_MEDIUM != 2);
16*f4f54b58SPierre Morel QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_HIGH != 3);
17*f4f54b58SPierre Morel 
18*f4f54b58SPierre Morel /**
19*f4f54b58SPierre Morel  * fill_container:
20*f4f54b58SPierre Morel  * @p: The address of the container TLE to fill
21*f4f54b58SPierre Morel  * @level: The level of nesting for this container
22*f4f54b58SPierre Morel  * @id: The container receives a unique ID inside its own container
23*f4f54b58SPierre Morel  *
24*f4f54b58SPierre Morel  * Returns the next free TLE entry.
25*f4f54b58SPierre Morel  */
26*f4f54b58SPierre Morel static char *fill_container(char *p, int level, int id)
27*f4f54b58SPierre Morel {
28*f4f54b58SPierre Morel     SYSIBContainerListEntry *tle = (SYSIBContainerListEntry *)p;
29*f4f54b58SPierre Morel 
30*f4f54b58SPierre Morel     tle->nl = level;
31*f4f54b58SPierre Morel     tle->id = id;
32*f4f54b58SPierre Morel     return p + sizeof(*tle);
33*f4f54b58SPierre Morel }
34*f4f54b58SPierre Morel 
35*f4f54b58SPierre Morel /**
36*f4f54b58SPierre Morel  * fill_tle_cpu:
37*f4f54b58SPierre Morel  * @p: The address of the CPU TLE to fill
38*f4f54b58SPierre Morel  * @entry: a pointer to the S390TopologyEntry defining this
39*f4f54b58SPierre Morel  *         CPU container.
40*f4f54b58SPierre Morel  *
41*f4f54b58SPierre Morel  * Returns the next free TLE entry.
42*f4f54b58SPierre Morel  */
43*f4f54b58SPierre Morel static char *fill_tle_cpu(char *p, S390TopologyEntry *entry)
44*f4f54b58SPierre Morel {
45*f4f54b58SPierre Morel     SysIBCPUListEntry *tle = (SysIBCPUListEntry *)p;
46*f4f54b58SPierre Morel     S390TopologyId topology_id = entry->id;
47*f4f54b58SPierre Morel 
48*f4f54b58SPierre Morel     tle->nl = 0;
49*f4f54b58SPierre Morel     tle->flags = 0;
50*f4f54b58SPierre Morel     if (topology_id.vertical) {
51*f4f54b58SPierre Morel         tle->flags |= topology_id.entitlement;
52*f4f54b58SPierre Morel     }
53*f4f54b58SPierre Morel     if (topology_id.dedicated) {
54*f4f54b58SPierre Morel         tle->flags |= SYSIB_TLE_DEDICATED;
55*f4f54b58SPierre Morel     }
56*f4f54b58SPierre Morel     tle->type = topology_id.type;
57*f4f54b58SPierre Morel     tle->origin = cpu_to_be16(topology_id.origin * 64);
58*f4f54b58SPierre Morel     tle->mask = cpu_to_be64(entry->mask);
59*f4f54b58SPierre Morel     return p + sizeof(*tle);
60*f4f54b58SPierre Morel }
61*f4f54b58SPierre Morel 
62*f4f54b58SPierre Morel /*
63*f4f54b58SPierre Morel  * Macro to check that the size of data after increment
64*f4f54b58SPierre Morel  * will not get bigger than the size of the SysIB.
65*f4f54b58SPierre Morel  */
66*f4f54b58SPierre Morel #define SYSIB_GUARD(data, x) do {       \
67*f4f54b58SPierre Morel         data += x;                      \
68*f4f54b58SPierre Morel         if (data > sizeof(SysIB)) {     \
69*f4f54b58SPierre Morel             return 0;                   \
70*f4f54b58SPierre Morel         }                               \
71*f4f54b58SPierre Morel     } while (0)
72*f4f54b58SPierre Morel 
73*f4f54b58SPierre Morel /**
74*f4f54b58SPierre Morel  * stsi_topology_fill_sysib:
75*f4f54b58SPierre Morel  * @p: A pointer to the position of the first TLE
76*f4f54b58SPierre Morel  * @level: The nested level wanted by the guest
77*f4f54b58SPierre Morel  *
78*f4f54b58SPierre Morel  * Fill the SYSIB with the topology information as described in
79*f4f54b58SPierre Morel  * the PoP, nesting containers as appropriate, with the maximum
80*f4f54b58SPierre Morel  * nesting limited by @level.
81*f4f54b58SPierre Morel  *
82*f4f54b58SPierre Morel  * Return value:
83*f4f54b58SPierre Morel  * On success: the size of the SysIB_15x after being filled with TLE.
84*f4f54b58SPierre Morel  * On error: 0 in the case we would overrun the end of the SysIB.
85*f4f54b58SPierre Morel  */
86*f4f54b58SPierre Morel static int stsi_topology_fill_sysib(S390TopologyList *topology_list,
87*f4f54b58SPierre Morel                                     char *p, int level)
88*f4f54b58SPierre Morel {
89*f4f54b58SPierre Morel     S390TopologyEntry *entry;
90*f4f54b58SPierre Morel     int last_drawer = -1;
91*f4f54b58SPierre Morel     int last_book = -1;
92*f4f54b58SPierre Morel     int last_socket = -1;
93*f4f54b58SPierre Morel     int drawer_id = 0;
94*f4f54b58SPierre Morel     int book_id = 0;
95*f4f54b58SPierre Morel     int socket_id = 0;
96*f4f54b58SPierre Morel     int n = sizeof(SysIB_151x);
97*f4f54b58SPierre Morel 
98*f4f54b58SPierre Morel     QTAILQ_FOREACH(entry, topology_list, next) {
99*f4f54b58SPierre Morel         bool drawer_change = last_drawer != entry->id.drawer;
100*f4f54b58SPierre Morel         bool book_change = drawer_change || last_book != entry->id.book;
101*f4f54b58SPierre Morel         bool socket_change = book_change || last_socket != entry->id.socket;
102*f4f54b58SPierre Morel 
103*f4f54b58SPierre Morel         if (level > 3 && drawer_change) {
104*f4f54b58SPierre Morel             SYSIB_GUARD(n, sizeof(SYSIBContainerListEntry));
105*f4f54b58SPierre Morel             p = fill_container(p, 3, drawer_id++);
106*f4f54b58SPierre Morel             book_id = 0;
107*f4f54b58SPierre Morel         }
108*f4f54b58SPierre Morel         if (level > 2 && book_change) {
109*f4f54b58SPierre Morel             SYSIB_GUARD(n, sizeof(SYSIBContainerListEntry));
110*f4f54b58SPierre Morel             p = fill_container(p, 2, book_id++);
111*f4f54b58SPierre Morel             socket_id = 0;
112*f4f54b58SPierre Morel         }
113*f4f54b58SPierre Morel         if (socket_change) {
114*f4f54b58SPierre Morel             SYSIB_GUARD(n, sizeof(SYSIBContainerListEntry));
115*f4f54b58SPierre Morel             p = fill_container(p, 1, socket_id++);
116*f4f54b58SPierre Morel         }
117*f4f54b58SPierre Morel 
118*f4f54b58SPierre Morel         SYSIB_GUARD(n, sizeof(SysIBCPUListEntry));
119*f4f54b58SPierre Morel         p = fill_tle_cpu(p, entry);
120*f4f54b58SPierre Morel         last_drawer = entry->id.drawer;
121*f4f54b58SPierre Morel         last_book = entry->id.book;
122*f4f54b58SPierre Morel         last_socket = entry->id.socket;
123*f4f54b58SPierre Morel     }
124*f4f54b58SPierre Morel 
125*f4f54b58SPierre Morel     return n;
126*f4f54b58SPierre Morel }
127*f4f54b58SPierre Morel 
128*f4f54b58SPierre Morel /**
129*f4f54b58SPierre Morel  * setup_stsi:
130*f4f54b58SPierre Morel  * @topology_list: ordered list of groups of CPUs with same properties
131*f4f54b58SPierre Morel  * @sysib: pointer to a SysIB to be filled with SysIB_151x data
132*f4f54b58SPierre Morel  * @level: Nested level specified by the guest
133*f4f54b58SPierre Morel  *
134*f4f54b58SPierre Morel  * Setup the SYSIB for STSI 15.1, the header as well as the description
135*f4f54b58SPierre Morel  * of the topology.
136*f4f54b58SPierre Morel  */
137*f4f54b58SPierre Morel static int setup_stsi(S390TopologyList *topology_list, SysIB_151x *sysib,
138*f4f54b58SPierre Morel                       int level)
139*f4f54b58SPierre Morel {
140*f4f54b58SPierre Morel     sysib->mnest = level;
141*f4f54b58SPierre Morel     switch (level) {
142*f4f54b58SPierre Morel     case 4:
143*f4f54b58SPierre Morel         sysib->mag[S390_TOPOLOGY_MAG4] = current_machine->smp.drawers;
144*f4f54b58SPierre Morel         sysib->mag[S390_TOPOLOGY_MAG3] = current_machine->smp.books;
145*f4f54b58SPierre Morel         sysib->mag[S390_TOPOLOGY_MAG2] = current_machine->smp.sockets;
146*f4f54b58SPierre Morel         sysib->mag[S390_TOPOLOGY_MAG1] = current_machine->smp.cores;
147*f4f54b58SPierre Morel         break;
148*f4f54b58SPierre Morel     case 3:
149*f4f54b58SPierre Morel         sysib->mag[S390_TOPOLOGY_MAG3] = current_machine->smp.drawers *
150*f4f54b58SPierre Morel                                          current_machine->smp.books;
151*f4f54b58SPierre Morel         sysib->mag[S390_TOPOLOGY_MAG2] = current_machine->smp.sockets;
152*f4f54b58SPierre Morel         sysib->mag[S390_TOPOLOGY_MAG1] = current_machine->smp.cores;
153*f4f54b58SPierre Morel         break;
154*f4f54b58SPierre Morel     case 2:
155*f4f54b58SPierre Morel         sysib->mag[S390_TOPOLOGY_MAG2] = current_machine->smp.drawers *
156*f4f54b58SPierre Morel                                          current_machine->smp.books *
157*f4f54b58SPierre Morel                                          current_machine->smp.sockets;
158*f4f54b58SPierre Morel         sysib->mag[S390_TOPOLOGY_MAG1] = current_machine->smp.cores;
159*f4f54b58SPierre Morel         break;
160*f4f54b58SPierre Morel     }
161*f4f54b58SPierre Morel 
162*f4f54b58SPierre Morel     return stsi_topology_fill_sysib(topology_list, sysib->tle, level);
163*f4f54b58SPierre Morel }
164*f4f54b58SPierre Morel 
165*f4f54b58SPierre Morel /**
166*f4f54b58SPierre Morel  * s390_topology_add_cpu_to_entry:
167*f4f54b58SPierre Morel  * @entry: Topology entry to setup
168*f4f54b58SPierre Morel  * @cpu: the S390CPU to add
169*f4f54b58SPierre Morel  *
170*f4f54b58SPierre Morel  * Set the core bit inside the topology mask.
171*f4f54b58SPierre Morel  */
172*f4f54b58SPierre Morel static void s390_topology_add_cpu_to_entry(S390TopologyEntry *entry,
173*f4f54b58SPierre Morel                                            S390CPU *cpu)
174*f4f54b58SPierre Morel {
175*f4f54b58SPierre Morel     set_bit(63 - (cpu->env.core_id % 64), &entry->mask);
176*f4f54b58SPierre Morel }
177*f4f54b58SPierre Morel 
178*f4f54b58SPierre Morel /**
179*f4f54b58SPierre Morel  * s390_topology_from_cpu:
180*f4f54b58SPierre Morel  * @cpu: S390CPU to calculate the topology id
181*f4f54b58SPierre Morel  *
182*f4f54b58SPierre Morel  * Initialize the topology id from the CPU environment.
183*f4f54b58SPierre Morel  */
184*f4f54b58SPierre Morel static S390TopologyId s390_topology_from_cpu(S390CPU *cpu)
185*f4f54b58SPierre Morel {
186*f4f54b58SPierre Morel     S390TopologyId topology_id = {
187*f4f54b58SPierre Morel         .drawer = cpu->env.drawer_id,
188*f4f54b58SPierre Morel         .book = cpu->env.book_id,
189*f4f54b58SPierre Morel         .socket = cpu->env.socket_id,
190*f4f54b58SPierre Morel         .type = S390_TOPOLOGY_CPU_IFL,
191*f4f54b58SPierre Morel         .vertical = s390_topology.polarization == S390_CPU_POLARIZATION_VERTICAL,
192*f4f54b58SPierre Morel         .entitlement = cpu->env.entitlement,
193*f4f54b58SPierre Morel         .dedicated = cpu->env.dedicated,
194*f4f54b58SPierre Morel         .origin = cpu->env.core_id / 64,
195*f4f54b58SPierre Morel     };
196*f4f54b58SPierre Morel 
197*f4f54b58SPierre Morel     return topology_id;
198*f4f54b58SPierre Morel }
199*f4f54b58SPierre Morel 
200*f4f54b58SPierre Morel /**
201*f4f54b58SPierre Morel  * s390_topology_id_cmp:
202*f4f54b58SPierre Morel  * @l: first S390TopologyId
203*f4f54b58SPierre Morel  * @r: second S390TopologyId
204*f4f54b58SPierre Morel  *
205*f4f54b58SPierre Morel  * Compare two topology ids according to the sorting order specified by the PoP.
206*f4f54b58SPierre Morel  *
207*f4f54b58SPierre Morel  * Returns a negative number if the first id is less than, 0 if it is equal to
208*f4f54b58SPierre Morel  *     and positive if it is larger than the second id.
209*f4f54b58SPierre Morel  */
210*f4f54b58SPierre Morel static int s390_topology_id_cmp(const S390TopologyId *l,
211*f4f54b58SPierre Morel                                 const S390TopologyId *r)
212*f4f54b58SPierre Morel {
213*f4f54b58SPierre Morel     /*
214*f4f54b58SPierre Morel      * lexical order, compare less significant values only if more significant
215*f4f54b58SPierre Morel      * ones are equal
216*f4f54b58SPierre Morel      */
217*f4f54b58SPierre Morel     return l->sentinel - r->sentinel ?:
218*f4f54b58SPierre Morel            l->drawer - r->drawer ?:
219*f4f54b58SPierre Morel            l->book - r->book ?:
220*f4f54b58SPierre Morel            l->socket - r->socket ?:
221*f4f54b58SPierre Morel            l->type - r->type ?:
222*f4f54b58SPierre Morel            /* logic is inverted for the next three */
223*f4f54b58SPierre Morel            r->vertical - l->vertical ?:
224*f4f54b58SPierre Morel            r->entitlement - l->entitlement ?:
225*f4f54b58SPierre Morel            r->dedicated - l->dedicated ?:
226*f4f54b58SPierre Morel            l->origin - r->origin;
227*f4f54b58SPierre Morel }
228*f4f54b58SPierre Morel 
229*f4f54b58SPierre Morel static bool s390_topology_id_eq(const S390TopologyId *l,
230*f4f54b58SPierre Morel                                 const S390TopologyId *r)
231*f4f54b58SPierre Morel {
232*f4f54b58SPierre Morel     return !s390_topology_id_cmp(l, r);
233*f4f54b58SPierre Morel }
234*f4f54b58SPierre Morel 
235*f4f54b58SPierre Morel static bool s390_topology_id_lt(const S390TopologyId *l,
236*f4f54b58SPierre Morel                                 const S390TopologyId *r)
237*f4f54b58SPierre Morel {
238*f4f54b58SPierre Morel     return s390_topology_id_cmp(l, r) < 0;
239*f4f54b58SPierre Morel }
240*f4f54b58SPierre Morel 
241*f4f54b58SPierre Morel /**
242*f4f54b58SPierre Morel  * s390_topology_fill_list_sorted:
243*f4f54b58SPierre Morel  * @topology_list: list to fill
244*f4f54b58SPierre Morel  *
245*f4f54b58SPierre Morel  * Create S390TopologyEntrys as appropriate from all CPUs and fill the
246*f4f54b58SPierre Morel  * topology_list with the entries according to the order specified by the PoP.
247*f4f54b58SPierre Morel  */
248*f4f54b58SPierre Morel static void s390_topology_fill_list_sorted(S390TopologyList *topology_list)
249*f4f54b58SPierre Morel {
250*f4f54b58SPierre Morel     CPUState *cs;
251*f4f54b58SPierre Morel     S390TopologyEntry sentinel = { .id.sentinel = 1 };
252*f4f54b58SPierre Morel 
253*f4f54b58SPierre Morel     QTAILQ_INIT(topology_list);
254*f4f54b58SPierre Morel 
255*f4f54b58SPierre Morel     QTAILQ_INSERT_HEAD(topology_list, &sentinel, next);
256*f4f54b58SPierre Morel 
257*f4f54b58SPierre Morel     CPU_FOREACH(cs) {
258*f4f54b58SPierre Morel         S390TopologyId id = s390_topology_from_cpu(S390_CPU(cs));
259*f4f54b58SPierre Morel         S390TopologyEntry *entry = NULL, *tmp;
260*f4f54b58SPierre Morel 
261*f4f54b58SPierre Morel         QTAILQ_FOREACH(tmp, topology_list, next) {
262*f4f54b58SPierre Morel             if (s390_topology_id_eq(&id, &tmp->id)) {
263*f4f54b58SPierre Morel                 entry = tmp;
264*f4f54b58SPierre Morel                 break;
265*f4f54b58SPierre Morel             } else if (s390_topology_id_lt(&id, &tmp->id)) {
266*f4f54b58SPierre Morel                 entry = g_malloc0(sizeof(*entry));
267*f4f54b58SPierre Morel                 entry->id = id;
268*f4f54b58SPierre Morel                 QTAILQ_INSERT_BEFORE(tmp, entry, next);
269*f4f54b58SPierre Morel                 break;
270*f4f54b58SPierre Morel             }
271*f4f54b58SPierre Morel         }
272*f4f54b58SPierre Morel         assert(entry);
273*f4f54b58SPierre Morel         s390_topology_add_cpu_to_entry(entry, S390_CPU(cs));
274*f4f54b58SPierre Morel     }
275*f4f54b58SPierre Morel 
276*f4f54b58SPierre Morel     QTAILQ_REMOVE(topology_list, &sentinel, next);
277*f4f54b58SPierre Morel }
278*f4f54b58SPierre Morel 
279*f4f54b58SPierre Morel /**
280*f4f54b58SPierre Morel  * s390_topology_empty_list:
281*f4f54b58SPierre Morel  *
282*f4f54b58SPierre Morel  * Clear all entries in the S390Topology list.
283*f4f54b58SPierre Morel  */
284*f4f54b58SPierre Morel static void s390_topology_empty_list(S390TopologyList *topology_list)
285*f4f54b58SPierre Morel {
286*f4f54b58SPierre Morel     S390TopologyEntry *entry = NULL;
287*f4f54b58SPierre Morel     S390TopologyEntry *tmp = NULL;
288*f4f54b58SPierre Morel 
289*f4f54b58SPierre Morel     QTAILQ_FOREACH_SAFE(entry, topology_list, next, tmp) {
290*f4f54b58SPierre Morel         QTAILQ_REMOVE(topology_list, entry, next);
291*f4f54b58SPierre Morel         g_free(entry);
292*f4f54b58SPierre Morel     }
293*f4f54b58SPierre Morel }
294*f4f54b58SPierre Morel 
295*f4f54b58SPierre Morel /**
296*f4f54b58SPierre Morel  * insert_stsi_15_1_x:
297*f4f54b58SPierre Morel  * @cpu: the CPU doing the call for which we set CC
298*f4f54b58SPierre Morel  * @sel2: the selector 2, containing the nested level
299*f4f54b58SPierre Morel  * @addr: Guest logical address of the guest SysIB
300*f4f54b58SPierre Morel  * @ar: the access register number
301*f4f54b58SPierre Morel  * @ra: the return address
302*f4f54b58SPierre Morel  *
303*f4f54b58SPierre Morel  * Emulate STSI 15.1.x, that is, perform all necessary checks and
304*f4f54b58SPierre Morel  * fill the SYSIB.
305*f4f54b58SPierre Morel  * In case the topology description is too long to fit into the SYSIB,
306*f4f54b58SPierre Morel  * set CC=3 and abort without writing the SYSIB.
307*f4f54b58SPierre Morel  */
308*f4f54b58SPierre Morel void insert_stsi_15_1_x(S390CPU *cpu, int sel2, uint64_t addr, uint8_t ar, uintptr_t ra)
309*f4f54b58SPierre Morel {
310*f4f54b58SPierre Morel     S390TopologyList topology_list;
311*f4f54b58SPierre Morel     SysIB sysib = {0};
312*f4f54b58SPierre Morel     int length;
313*f4f54b58SPierre Morel 
314*f4f54b58SPierre Morel     if (!s390_has_topology() || sel2 < 2 || sel2 > SCLP_READ_SCP_INFO_MNEST) {
315*f4f54b58SPierre Morel         setcc(cpu, 3);
316*f4f54b58SPierre Morel         return;
317*f4f54b58SPierre Morel     }
318*f4f54b58SPierre Morel 
319*f4f54b58SPierre Morel     s390_topology_fill_list_sorted(&topology_list);
320*f4f54b58SPierre Morel     length = setup_stsi(&topology_list, &sysib.sysib_151x, sel2);
321*f4f54b58SPierre Morel     s390_topology_empty_list(&topology_list);
322*f4f54b58SPierre Morel 
323*f4f54b58SPierre Morel     if (!length) {
324*f4f54b58SPierre Morel         setcc(cpu, 3);
325*f4f54b58SPierre Morel         return;
326*f4f54b58SPierre Morel     }
327*f4f54b58SPierre Morel 
328*f4f54b58SPierre Morel     sysib.sysib_151x.length = cpu_to_be16(length);
329*f4f54b58SPierre Morel     if (!s390_cpu_virt_mem_write(cpu, addr, ar, &sysib, length)) {
330*f4f54b58SPierre Morel         setcc(cpu, 0);
331*f4f54b58SPierre Morel     } else {
332*f4f54b58SPierre Morel         s390_cpu_virt_mem_handle_exc(cpu, ra);
333*f4f54b58SPierre Morel     }
334*f4f54b58SPierre Morel }
335