mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-02 23:33:54 -06:00
target/s390x/cpu topology: handle STSI(15) and build the SYSIB
On interception of STSI(15.1.x) the System Information Block (SYSIB) is built from the list of pre-ordered topology entries. Signed-off-by: Pierre Morel <pmorel@linux.ibm.com> Reviewed-by: Nina Schoetterl-Glausch <nsg@linux.ibm.com> Co-developed-by: Nina Schoetterl-Glausch <nsg@linux.ibm.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Nina Schoetterl-Glausch <nsg@linux.ibm.com> Message-ID: <20231016183925.2384704-5-nsg@linux.ibm.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
This commit is contained in:
parent
c809bbc8e9
commit
f4f54b582f
9 changed files with 456 additions and 2 deletions
334
target/s390x/kvm/stsi-topology.c
Normal file
334
target/s390x/kvm/stsi-topology.c
Normal file
|
@ -0,0 +1,334 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU S390x CPU Topology
|
||||
*
|
||||
* Copyright IBM Corp. 2022, 2023
|
||||
* Author(s): Pierre Morel <pmorel@linux.ibm.com>
|
||||
*
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/s390x/sclp.h"
|
||||
#include "hw/s390x/cpu-topology.h"
|
||||
|
||||
QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_LOW != 1);
|
||||
QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_MEDIUM != 2);
|
||||
QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_HIGH != 3);
|
||||
|
||||
/**
|
||||
* fill_container:
|
||||
* @p: The address of the container TLE to fill
|
||||
* @level: The level of nesting for this container
|
||||
* @id: The container receives a unique ID inside its own container
|
||||
*
|
||||
* Returns the next free TLE entry.
|
||||
*/
|
||||
static char *fill_container(char *p, int level, int id)
|
||||
{
|
||||
SYSIBContainerListEntry *tle = (SYSIBContainerListEntry *)p;
|
||||
|
||||
tle->nl = level;
|
||||
tle->id = id;
|
||||
return p + sizeof(*tle);
|
||||
}
|
||||
|
||||
/**
|
||||
* fill_tle_cpu:
|
||||
* @p: The address of the CPU TLE to fill
|
||||
* @entry: a pointer to the S390TopologyEntry defining this
|
||||
* CPU container.
|
||||
*
|
||||
* Returns the next free TLE entry.
|
||||
*/
|
||||
static char *fill_tle_cpu(char *p, S390TopologyEntry *entry)
|
||||
{
|
||||
SysIBCPUListEntry *tle = (SysIBCPUListEntry *)p;
|
||||
S390TopologyId topology_id = entry->id;
|
||||
|
||||
tle->nl = 0;
|
||||
tle->flags = 0;
|
||||
if (topology_id.vertical) {
|
||||
tle->flags |= topology_id.entitlement;
|
||||
}
|
||||
if (topology_id.dedicated) {
|
||||
tle->flags |= SYSIB_TLE_DEDICATED;
|
||||
}
|
||||
tle->type = topology_id.type;
|
||||
tle->origin = cpu_to_be16(topology_id.origin * 64);
|
||||
tle->mask = cpu_to_be64(entry->mask);
|
||||
return p + sizeof(*tle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Macro to check that the size of data after increment
|
||||
* will not get bigger than the size of the SysIB.
|
||||
*/
|
||||
#define SYSIB_GUARD(data, x) do { \
|
||||
data += x; \
|
||||
if (data > sizeof(SysIB)) { \
|
||||
return 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* stsi_topology_fill_sysib:
|
||||
* @p: A pointer to the position of the first TLE
|
||||
* @level: The nested level wanted by the guest
|
||||
*
|
||||
* Fill the SYSIB with the topology information as described in
|
||||
* the PoP, nesting containers as appropriate, with the maximum
|
||||
* nesting limited by @level.
|
||||
*
|
||||
* Return value:
|
||||
* On success: the size of the SysIB_15x after being filled with TLE.
|
||||
* On error: 0 in the case we would overrun the end of the SysIB.
|
||||
*/
|
||||
static int stsi_topology_fill_sysib(S390TopologyList *topology_list,
|
||||
char *p, int level)
|
||||
{
|
||||
S390TopologyEntry *entry;
|
||||
int last_drawer = -1;
|
||||
int last_book = -1;
|
||||
int last_socket = -1;
|
||||
int drawer_id = 0;
|
||||
int book_id = 0;
|
||||
int socket_id = 0;
|
||||
int n = sizeof(SysIB_151x);
|
||||
|
||||
QTAILQ_FOREACH(entry, topology_list, next) {
|
||||
bool drawer_change = last_drawer != entry->id.drawer;
|
||||
bool book_change = drawer_change || last_book != entry->id.book;
|
||||
bool socket_change = book_change || last_socket != entry->id.socket;
|
||||
|
||||
if (level > 3 && drawer_change) {
|
||||
SYSIB_GUARD(n, sizeof(SYSIBContainerListEntry));
|
||||
p = fill_container(p, 3, drawer_id++);
|
||||
book_id = 0;
|
||||
}
|
||||
if (level > 2 && book_change) {
|
||||
SYSIB_GUARD(n, sizeof(SYSIBContainerListEntry));
|
||||
p = fill_container(p, 2, book_id++);
|
||||
socket_id = 0;
|
||||
}
|
||||
if (socket_change) {
|
||||
SYSIB_GUARD(n, sizeof(SYSIBContainerListEntry));
|
||||
p = fill_container(p, 1, socket_id++);
|
||||
}
|
||||
|
||||
SYSIB_GUARD(n, sizeof(SysIBCPUListEntry));
|
||||
p = fill_tle_cpu(p, entry);
|
||||
last_drawer = entry->id.drawer;
|
||||
last_book = entry->id.book;
|
||||
last_socket = entry->id.socket;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* setup_stsi:
|
||||
* @topology_list: ordered list of groups of CPUs with same properties
|
||||
* @sysib: pointer to a SysIB to be filled with SysIB_151x data
|
||||
* @level: Nested level specified by the guest
|
||||
*
|
||||
* Setup the SYSIB for STSI 15.1, the header as well as the description
|
||||
* of the topology.
|
||||
*/
|
||||
static int setup_stsi(S390TopologyList *topology_list, SysIB_151x *sysib,
|
||||
int level)
|
||||
{
|
||||
sysib->mnest = level;
|
||||
switch (level) {
|
||||
case 4:
|
||||
sysib->mag[S390_TOPOLOGY_MAG4] = current_machine->smp.drawers;
|
||||
sysib->mag[S390_TOPOLOGY_MAG3] = current_machine->smp.books;
|
||||
sysib->mag[S390_TOPOLOGY_MAG2] = current_machine->smp.sockets;
|
||||
sysib->mag[S390_TOPOLOGY_MAG1] = current_machine->smp.cores;
|
||||
break;
|
||||
case 3:
|
||||
sysib->mag[S390_TOPOLOGY_MAG3] = current_machine->smp.drawers *
|
||||
current_machine->smp.books;
|
||||
sysib->mag[S390_TOPOLOGY_MAG2] = current_machine->smp.sockets;
|
||||
sysib->mag[S390_TOPOLOGY_MAG1] = current_machine->smp.cores;
|
||||
break;
|
||||
case 2:
|
||||
sysib->mag[S390_TOPOLOGY_MAG2] = current_machine->smp.drawers *
|
||||
current_machine->smp.books *
|
||||
current_machine->smp.sockets;
|
||||
sysib->mag[S390_TOPOLOGY_MAG1] = current_machine->smp.cores;
|
||||
break;
|
||||
}
|
||||
|
||||
return stsi_topology_fill_sysib(topology_list, sysib->tle, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* s390_topology_add_cpu_to_entry:
|
||||
* @entry: Topology entry to setup
|
||||
* @cpu: the S390CPU to add
|
||||
*
|
||||
* Set the core bit inside the topology mask.
|
||||
*/
|
||||
static void s390_topology_add_cpu_to_entry(S390TopologyEntry *entry,
|
||||
S390CPU *cpu)
|
||||
{
|
||||
set_bit(63 - (cpu->env.core_id % 64), &entry->mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* s390_topology_from_cpu:
|
||||
* @cpu: S390CPU to calculate the topology id
|
||||
*
|
||||
* Initialize the topology id from the CPU environment.
|
||||
*/
|
||||
static S390TopologyId s390_topology_from_cpu(S390CPU *cpu)
|
||||
{
|
||||
S390TopologyId topology_id = {
|
||||
.drawer = cpu->env.drawer_id,
|
||||
.book = cpu->env.book_id,
|
||||
.socket = cpu->env.socket_id,
|
||||
.type = S390_TOPOLOGY_CPU_IFL,
|
||||
.vertical = s390_topology.polarization == S390_CPU_POLARIZATION_VERTICAL,
|
||||
.entitlement = cpu->env.entitlement,
|
||||
.dedicated = cpu->env.dedicated,
|
||||
.origin = cpu->env.core_id / 64,
|
||||
};
|
||||
|
||||
return topology_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* s390_topology_id_cmp:
|
||||
* @l: first S390TopologyId
|
||||
* @r: second S390TopologyId
|
||||
*
|
||||
* Compare two topology ids according to the sorting order specified by the PoP.
|
||||
*
|
||||
* Returns a negative number if the first id is less than, 0 if it is equal to
|
||||
* and positive if it is larger than the second id.
|
||||
*/
|
||||
static int s390_topology_id_cmp(const S390TopologyId *l,
|
||||
const S390TopologyId *r)
|
||||
{
|
||||
/*
|
||||
* lexical order, compare less significant values only if more significant
|
||||
* ones are equal
|
||||
*/
|
||||
return l->sentinel - r->sentinel ?:
|
||||
l->drawer - r->drawer ?:
|
||||
l->book - r->book ?:
|
||||
l->socket - r->socket ?:
|
||||
l->type - r->type ?:
|
||||
/* logic is inverted for the next three */
|
||||
r->vertical - l->vertical ?:
|
||||
r->entitlement - l->entitlement ?:
|
||||
r->dedicated - l->dedicated ?:
|
||||
l->origin - r->origin;
|
||||
}
|
||||
|
||||
static bool s390_topology_id_eq(const S390TopologyId *l,
|
||||
const S390TopologyId *r)
|
||||
{
|
||||
return !s390_topology_id_cmp(l, r);
|
||||
}
|
||||
|
||||
static bool s390_topology_id_lt(const S390TopologyId *l,
|
||||
const S390TopologyId *r)
|
||||
{
|
||||
return s390_topology_id_cmp(l, r) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* s390_topology_fill_list_sorted:
|
||||
* @topology_list: list to fill
|
||||
*
|
||||
* Create S390TopologyEntrys as appropriate from all CPUs and fill the
|
||||
* topology_list with the entries according to the order specified by the PoP.
|
||||
*/
|
||||
static void s390_topology_fill_list_sorted(S390TopologyList *topology_list)
|
||||
{
|
||||
CPUState *cs;
|
||||
S390TopologyEntry sentinel = { .id.sentinel = 1 };
|
||||
|
||||
QTAILQ_INIT(topology_list);
|
||||
|
||||
QTAILQ_INSERT_HEAD(topology_list, &sentinel, next);
|
||||
|
||||
CPU_FOREACH(cs) {
|
||||
S390TopologyId id = s390_topology_from_cpu(S390_CPU(cs));
|
||||
S390TopologyEntry *entry = NULL, *tmp;
|
||||
|
||||
QTAILQ_FOREACH(tmp, topology_list, next) {
|
||||
if (s390_topology_id_eq(&id, &tmp->id)) {
|
||||
entry = tmp;
|
||||
break;
|
||||
} else if (s390_topology_id_lt(&id, &tmp->id)) {
|
||||
entry = g_malloc0(sizeof(*entry));
|
||||
entry->id = id;
|
||||
QTAILQ_INSERT_BEFORE(tmp, entry, next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(entry);
|
||||
s390_topology_add_cpu_to_entry(entry, S390_CPU(cs));
|
||||
}
|
||||
|
||||
QTAILQ_REMOVE(topology_list, &sentinel, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* s390_topology_empty_list:
|
||||
*
|
||||
* Clear all entries in the S390Topology list.
|
||||
*/
|
||||
static void s390_topology_empty_list(S390TopologyList *topology_list)
|
||||
{
|
||||
S390TopologyEntry *entry = NULL;
|
||||
S390TopologyEntry *tmp = NULL;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(entry, topology_list, next, tmp) {
|
||||
QTAILQ_REMOVE(topology_list, entry, next);
|
||||
g_free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_stsi_15_1_x:
|
||||
* @cpu: the CPU doing the call for which we set CC
|
||||
* @sel2: the selector 2, containing the nested level
|
||||
* @addr: Guest logical address of the guest SysIB
|
||||
* @ar: the access register number
|
||||
* @ra: the return address
|
||||
*
|
||||
* Emulate STSI 15.1.x, that is, perform all necessary checks and
|
||||
* fill the SYSIB.
|
||||
* In case the topology description is too long to fit into the SYSIB,
|
||||
* set CC=3 and abort without writing the SYSIB.
|
||||
*/
|
||||
void insert_stsi_15_1_x(S390CPU *cpu, int sel2, uint64_t addr, uint8_t ar, uintptr_t ra)
|
||||
{
|
||||
S390TopologyList topology_list;
|
||||
SysIB sysib = {0};
|
||||
int length;
|
||||
|
||||
if (!s390_has_topology() || sel2 < 2 || sel2 > SCLP_READ_SCP_INFO_MNEST) {
|
||||
setcc(cpu, 3);
|
||||
return;
|
||||
}
|
||||
|
||||
s390_topology_fill_list_sorted(&topology_list);
|
||||
length = setup_stsi(&topology_list, &sysib.sysib_151x, sel2);
|
||||
s390_topology_empty_list(&topology_list);
|
||||
|
||||
if (!length) {
|
||||
setcc(cpu, 3);
|
||||
return;
|
||||
}
|
||||
|
||||
sysib.sysib_151x.length = cpu_to_be16(length);
|
||||
if (!s390_cpu_virt_mem_write(cpu, addr, ar, &sysib, length)) {
|
||||
setcc(cpu, 0);
|
||||
} else {
|
||||
s390_cpu_virt_mem_handle_exc(cpu, ra);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue