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