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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 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 */ 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 */ 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