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