1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * QEMU S390x CPU Topology
5 * Copyright IBM Corp. 2022, 2023
6 * Author(s): Pierre Morel <pmorel@linux.ibm.com>
9 #include "qemu/osdep.h"
11 #include "hw/s390x/sclp.h"
12 #include "hw/s390x/cpu-topology.h"
14 QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_LOW
!= 1);
15 QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_MEDIUM
!= 2);
16 QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_HIGH
!= 3);
20 * @p: The address of the container TLE to fill
21 * @level: The level of nesting for this container
22 * @id: The container receives a unique ID inside its own container
24 * Returns the next free TLE entry.
26 static char *fill_container(char *p
, int level
, int id
)
28 SYSIBContainerListEntry
*tle
= (SYSIBContainerListEntry
*)p
;
32 return p
+ sizeof(*tle
);
37 * @p: The address of the CPU TLE to fill
38 * @entry: a pointer to the S390TopologyEntry defining this
41 * Returns the next free TLE entry.
43 static char *fill_tle_cpu(char *p
, S390TopologyEntry
*entry
)
45 SysIBCPUListEntry
*tle
= (SysIBCPUListEntry
*)p
;
46 S390TopologyId topology_id
= entry
->id
;
50 if (topology_id
.vertical
) {
51 tle
->flags
|= topology_id
.entitlement
;
53 if (topology_id
.dedicated
) {
54 tle
->flags
|= SYSIB_TLE_DEDICATED
;
56 tle
->type
= topology_id
.type
;
57 tle
->origin
= cpu_to_be16(topology_id
.origin
* 64);
58 tle
->mask
= cpu_to_be64(entry
->mask
);
59 return p
+ sizeof(*tle
);
63 * Macro to check that the size of data after increment
64 * will not get bigger than the size of the SysIB.
66 #define SYSIB_GUARD(data, x) do { \
68 if (data > sizeof(SysIB)) { \
74 * stsi_topology_fill_sysib:
75 * @p: A pointer to the position of the first TLE
76 * @level: The nested level wanted by the guest
78 * Fill the SYSIB with the topology information as described in
79 * the PoP, nesting containers as appropriate, with the maximum
80 * nesting limited by @level.
83 * On success: the size of the SysIB_15x after being filled with TLE.
84 * On error: 0 in the case we would overrun the end of the SysIB.
86 static int stsi_topology_fill_sysib(S390TopologyList
*topology_list
,
89 S390TopologyEntry
*entry
;
96 int n
= sizeof(SysIB_151x
);
98 QTAILQ_FOREACH(entry
, topology_list
, next
) {
99 bool drawer_change
= last_drawer
!= entry
->id
.drawer
;
100 bool book_change
= drawer_change
|| last_book
!= entry
->id
.book
;
101 bool socket_change
= book_change
|| last_socket
!= entry
->id
.socket
;
103 if (level
> 3 && drawer_change
) {
104 SYSIB_GUARD(n
, sizeof(SYSIBContainerListEntry
));
105 p
= fill_container(p
, 3, drawer_id
++);
108 if (level
> 2 && book_change
) {
109 SYSIB_GUARD(n
, sizeof(SYSIBContainerListEntry
));
110 p
= fill_container(p
, 2, book_id
++);
114 SYSIB_GUARD(n
, sizeof(SYSIBContainerListEntry
));
115 p
= fill_container(p
, 1, socket_id
++);
118 SYSIB_GUARD(n
, sizeof(SysIBCPUListEntry
));
119 p
= fill_tle_cpu(p
, entry
);
120 last_drawer
= entry
->id
.drawer
;
121 last_book
= entry
->id
.book
;
122 last_socket
= entry
->id
.socket
;
130 * @topology_list: ordered list of groups of CPUs with same properties
131 * @sysib: pointer to a SysIB to be filled with SysIB_151x data
132 * @level: Nested level specified by the guest
134 * Setup the SYSIB for STSI 15.1, the header as well as the description
137 static int setup_stsi(S390TopologyList
*topology_list
, SysIB_151x
*sysib
,
140 sysib
->mnest
= level
;
143 sysib
->mag
[S390_TOPOLOGY_MAG4
] = current_machine
->smp
.drawers
;
144 sysib
->mag
[S390_TOPOLOGY_MAG3
] = current_machine
->smp
.books
;
145 sysib
->mag
[S390_TOPOLOGY_MAG2
] = current_machine
->smp
.sockets
;
146 sysib
->mag
[S390_TOPOLOGY_MAG1
] = current_machine
->smp
.cores
;
149 sysib
->mag
[S390_TOPOLOGY_MAG3
] = current_machine
->smp
.drawers
*
150 current_machine
->smp
.books
;
151 sysib
->mag
[S390_TOPOLOGY_MAG2
] = current_machine
->smp
.sockets
;
152 sysib
->mag
[S390_TOPOLOGY_MAG1
] = current_machine
->smp
.cores
;
155 sysib
->mag
[S390_TOPOLOGY_MAG2
] = current_machine
->smp
.drawers
*
156 current_machine
->smp
.books
*
157 current_machine
->smp
.sockets
;
158 sysib
->mag
[S390_TOPOLOGY_MAG1
] = current_machine
->smp
.cores
;
162 return stsi_topology_fill_sysib(topology_list
, sysib
->tle
, level
);
166 * s390_topology_add_cpu_to_entry:
167 * @entry: Topology entry to setup
168 * @cpu: the S390CPU to add
170 * Set the core bit inside the topology mask.
172 static void s390_topology_add_cpu_to_entry(S390TopologyEntry
*entry
,
175 set_bit(63 - (cpu
->env
.core_id
% 64), &entry
->mask
);
179 * s390_topology_from_cpu:
180 * @cpu: S390CPU to calculate the topology id
182 * Initialize the topology id from the CPU environment.
184 static S390TopologyId
s390_topology_from_cpu(S390CPU
*cpu
)
186 S390TopologyId topology_id
= {
187 .drawer
= cpu
->env
.drawer_id
,
188 .book
= cpu
->env
.book_id
,
189 .socket
= cpu
->env
.socket_id
,
190 .type
= S390_TOPOLOGY_CPU_IFL
,
191 .vertical
= s390_topology
.polarization
== S390_CPU_POLARIZATION_VERTICAL
,
192 .entitlement
= cpu
->env
.entitlement
,
193 .dedicated
= cpu
->env
.dedicated
,
194 .origin
= cpu
->env
.core_id
/ 64,
201 * s390_topology_id_cmp:
202 * @l: first S390TopologyId
203 * @r: second S390TopologyId
205 * Compare two topology ids according to the sorting order specified by the PoP.
207 * Returns a negative number if the first id is less than, 0 if it is equal to
208 * and positive if it is larger than the second id.
210 static int s390_topology_id_cmp(const S390TopologyId
*l
,
211 const S390TopologyId
*r
)
214 * lexical order, compare less significant values only if more significant
217 return l
->sentinel
- r
->sentinel
?:
218 l
->drawer
- r
->drawer
?:
220 l
->socket
- r
->socket
?:
222 /* logic is inverted for the next three */
223 r
->vertical
- l
->vertical
?:
224 r
->entitlement
- l
->entitlement
?:
225 r
->dedicated
- l
->dedicated
?:
226 l
->origin
- r
->origin
;
229 static bool s390_topology_id_eq(const S390TopologyId
*l
,
230 const S390TopologyId
*r
)
232 return !s390_topology_id_cmp(l
, r
);
235 static bool s390_topology_id_lt(const S390TopologyId
*l
,
236 const S390TopologyId
*r
)
238 return s390_topology_id_cmp(l
, r
) < 0;
242 * s390_topology_fill_list_sorted:
243 * @topology_list: list to fill
245 * Create S390TopologyEntrys as appropriate from all CPUs and fill the
246 * topology_list with the entries according to the order specified by the PoP.
248 static void s390_topology_fill_list_sorted(S390TopologyList
*topology_list
)
251 S390TopologyEntry sentinel
= { .id
.sentinel
= 1 };
253 QTAILQ_INIT(topology_list
);
255 QTAILQ_INSERT_HEAD(topology_list
, &sentinel
, next
);
258 S390TopologyId id
= s390_topology_from_cpu(S390_CPU(cs
));
259 S390TopologyEntry
*entry
= NULL
, *tmp
;
261 QTAILQ_FOREACH(tmp
, topology_list
, next
) {
262 if (s390_topology_id_eq(&id
, &tmp
->id
)) {
265 } else if (s390_topology_id_lt(&id
, &tmp
->id
)) {
266 entry
= g_malloc0(sizeof(*entry
));
268 QTAILQ_INSERT_BEFORE(tmp
, entry
, next
);
273 s390_topology_add_cpu_to_entry(entry
, S390_CPU(cs
));
276 QTAILQ_REMOVE(topology_list
, &sentinel
, next
);
280 * s390_topology_empty_list:
282 * Clear all entries in the S390Topology list.
284 static void s390_topology_empty_list(S390TopologyList
*topology_list
)
286 S390TopologyEntry
*entry
= NULL
;
287 S390TopologyEntry
*tmp
= NULL
;
289 QTAILQ_FOREACH_SAFE(entry
, topology_list
, next
, tmp
) {
290 QTAILQ_REMOVE(topology_list
, entry
, next
);
296 * insert_stsi_15_1_x:
297 * @cpu: the CPU doing the call for which we set CC
298 * @sel2: the selector 2, containing the nested level
299 * @addr: Guest logical address of the guest SysIB
300 * @ar: the access register number
301 * @ra: the return address
303 * Emulate STSI 15.1.x, that is, perform all necessary checks and
305 * In case the topology description is too long to fit into the SYSIB,
306 * set CC=3 and abort without writing the SYSIB.
308 void insert_stsi_15_1_x(S390CPU
*cpu
, int sel2
, uint64_t addr
, uint8_t ar
, uintptr_t ra
)
310 S390TopologyList topology_list
;
314 if (!s390_has_topology() || sel2
< 2 || sel2
> SCLP_READ_SCP_INFO_MNEST
) {
319 s390_topology_fill_list_sorted(&topology_list
);
320 length
= setup_stsi(&topology_list
, &sysib
.sysib_151x
, sel2
);
321 s390_topology_empty_list(&topology_list
);
328 sysib
.sysib_151x
.length
= cpu_to_be16(length
);
329 if (!s390_cpu_virt_mem_write(cpu
, addr
, ar
, &sysib
, length
)) {
332 s390_cpu_virt_mem_handle_exc(cpu
, ra
);