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