blob: 6955823662f1c0ad7a1e6180a89b238c669778eb [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* CPU Topology
*
* Copyright IBM Corp. 2022
*
* Authors:
* Pierre Morel <pmorel@linux.ibm.com>
*/
#include <libcflat.h>
#include <asm/page.h>
#include <asm/asm-offsets.h>
#include <asm/interrupt.h>
#include <asm/facility.h>
#include <asm/barrier.h>
#include <smp.h>
#include <sclp.h>
#include <s390x/hardware.h>
#include <s390x/stsi.h>
static uint8_t pagebuf[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
static int max_nested_lvl;
static int number_of_cpus;
static int cpus_in_masks;
static int max_cpus;
/*
* Topology level as defined by architecture, all levels exists with
* a single container unless overwritten by the QEMU -smp parameter.
*/
static int expected_topo_lvl[CPU_TOPOLOGY_MAX_LEVEL] = { 1, 1, 1, 1, 1, 1 };
#define PTF_REQ_HORIZONTAL 0
#define PTF_REQ_VERTICAL 1
#define PTF_CHECK 2
#define PTF_ERR_NO_REASON 0
#define PTF_ERR_ALRDY_POLARIZED 1
#define PTF_ERR_IN_PROGRESS 2
extern int diag308_load_reset(u64);
static int ptf(unsigned long fc, unsigned long *rc)
{
int cc;
asm volatile(
" ptf %1 \n"
" ipm %0 \n"
" srl %0,28 \n"
: "=d" (cc), "+d" (fc)
:
: "cc");
*rc = fc >> 8;
return cc;
}
static void check_privilege(int fc)
{
unsigned long rc;
report_prefix_pushf("Privileged fc %d", fc);
enter_pstate();
expect_pgm_int();
ptf(fc, &rc);
check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
report_prefix_pop();
}
static void check_specifications(void)
{
unsigned long error = 0;
unsigned long ptf_bits;
unsigned long rc;
int i;
report_prefix_push("Specifications");
/* Function codes above 3 are undefined */
for (i = 4; i < 255; i++) {
expect_pgm_int();
ptf(i, &rc);
if (clear_pgm_int() != PGM_INT_CODE_SPECIFICATION) {
report_fail("FC %d did not yield specification exception", i);
error = 1;
}
}
report(!error, "Undefined function codes");
/* Reserved bits must be 0 */
for (i = 8, error = 0; i < 64; i++) {
ptf_bits = 0x01UL << i;
expect_pgm_int();
ptf(ptf_bits, &rc);
if (clear_pgm_int() != PGM_INT_CODE_SPECIFICATION) {
report_fail("Reserved bit %d did not yield specification exception", i);
error = 1;
}
}
report(!error, "Reserved bits");
report_prefix_pop();
}
static void check_polarization_change(void)
{
unsigned long rc;
int cc;
report_prefix_push("Polarization change");
/* We expect a clean state through reset */
report(diag308_load_reset(1), "load normal reset done");
/*
* Set vertical polarization to verify that RESET sets
* horizontal polarization back.
*/
cc = ptf(PTF_REQ_VERTICAL, &rc);
report(cc == 0, "Set vertical polarization.");
report(diag308_load_reset(1), "load normal reset done");
cc = ptf(PTF_CHECK, &rc);
report(cc == 0, "Reset should clear topology report");
cc = ptf(PTF_REQ_HORIZONTAL, &rc);
report(cc == 2 && rc == PTF_ERR_ALRDY_POLARIZED,
"After RESET polarization is horizontal");
/* Flip between vertical and horizontal polarization */
cc = ptf(PTF_REQ_VERTICAL, &rc);
report(cc == 0, "Change to vertical");
cc = ptf(PTF_CHECK, &rc);
report(cc == 1, "Should report");
cc = ptf(PTF_REQ_VERTICAL, &rc);
report(cc == 2 && rc == PTF_ERR_ALRDY_POLARIZED, "Double change to vertical");
cc = ptf(PTF_CHECK, &rc);
report(cc == 0, "Should not report");
cc = ptf(PTF_REQ_HORIZONTAL, &rc);
report(cc == 0, "Change to horizontal");
cc = ptf(PTF_CHECK, &rc);
report(cc == 1, "Should Report");
cc = ptf(PTF_REQ_HORIZONTAL, &rc);
report(cc == 2 && rc == PTF_ERR_ALRDY_POLARIZED, "Double change to horizontal");
cc = ptf(PTF_CHECK, &rc);
report(cc == 0, "Should not report");
report_prefix_pop();
}
static void test_ptf(void)
{
check_privilege(PTF_REQ_HORIZONTAL);
check_privilege(PTF_REQ_VERTICAL);
check_privilege(PTF_CHECK);
check_specifications();
check_polarization_change();
}
/*
* stsi_check_maxcpus
* @info: Pointer to the stsi information
*
* The product of the numbers of containers per level
* is the maximum number of CPU allowed by the machine.
*/
static void stsi_check_maxcpus(struct sysinfo_15_1_x *info)
{
int n, i;
for (i = 0, n = 1; i < CPU_TOPOLOGY_MAX_LEVEL; i++)
n *= info->mag[i] ?: 1;
report(n == max_cpus, "Calculated max CPUs: %d", n);
}
/*
* stsi_check_mag
* @info: Pointer to the stsi information
*
* MAG field should match the architecture defined containers
* when MNEST as returned by SCLP matches MNEST of the SYSIB.
*/
static void stsi_check_mag(struct sysinfo_15_1_x *info)
{
int i;
report_prefix_push("MAG");
stsi_check_maxcpus(info);
/*
* It is not clear how the MAG fields are calculated when mnest
* in the SYSIB 15.x is different from the maximum nested level
* in the SCLP info, so we skip here for now.
*/
if (max_nested_lvl != info->mnest) {
report_skip("No specification on layer aggregation");
goto done;
}
/*
* MAG up to max_nested_lvl must match the architecture
* defined containers.
*/
for (i = 0; i < max_nested_lvl; i++)
report(info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1] == expected_topo_lvl[i],
"MAG %d field match %d == %d",
i + 1,
info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1],
expected_topo_lvl[i]);
/* Above max_nested_lvl the MAG field must be null */
for (; i < CPU_TOPOLOGY_MAX_LEVEL; i++)
report(info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1] == 0,
"MAG %d field match %d == %d", i + 1,
info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1], 0);
done:
report_prefix_pop();
}
/**
* check_tle:
* @tc: pointer to first TLE
*
* Recursively check the containers TLEs until we
* find a CPU TLE.
*/
static uint8_t *check_tle(void *tc)
{
struct topology_container *container = tc;
struct topology_core *cpus;
int n;
if (container->nl) {
report_info("NL: %d id: %d", container->nl, container->id);
report(!(*(uint64_t *)tc & CONTAINER_TLE_RES_BITS),
"reserved bits %016lx",
*(uint64_t *)tc & CONTAINER_TLE_RES_BITS);
return check_tle(tc + sizeof(*container));
}
report_info("NL: %d", container->nl);
cpus = tc;
report(!(*(uint64_t *)tc & CPUS_TLE_RES_BITS), "reserved bits %016lx",
*(uint64_t *)tc & CPUS_TLE_RES_BITS);
report(cpus->type == 0x03, "type IFL");
report_info("origin: %d", cpus->origin);
report_info("mask: %016lx", cpus->mask);
report_info("dedicated: %d entitlement: %d", cpus->d, cpus->pp);
n = __builtin_popcountl(cpus->mask);
report(n <= expected_topo_lvl[0], "CPUs per mask: %d out of max %d",
n, expected_topo_lvl[0]);
cpus_in_masks += n;
if (!cpus->d)
report_skip("Not dedicated");
else
report(cpus->pp == 3 || cpus->pp == 0, "Dedicated CPUs are either vertically polarized or have high entitlement");
return tc + sizeof(*cpus);
}
/**
* stsi_check_tle_coherency:
* @info: Pointer to the stsi information
*
* We verify that we get the expected number of Topology List Entry
* containers for a specific level.
*/
static void stsi_check_tle_coherency(struct sysinfo_15_1_x *info)
{
void *tc, *end;
report_prefix_push("TLE");
cpus_in_masks = 0;
tc = info->tle;
end = (void *)info + info->length;
while (tc < end)
tc = check_tle(tc);
report(cpus_in_masks == number_of_cpus, "CPUs in mask %d",
cpus_in_masks);
report_prefix_pop();
}
/**
* stsi_get_sysib:
* @info: pointer to the STSI info structure
* @sel2: the selector giving the topology level to check
*
* Fill the sysinfo_15_1_x info structure and check the
* SYSIB header.
*
* Returns instruction validity.
*/
static int stsi_get_sysib(struct sysinfo_15_1_x *info, int sel2)
{
int ret;
report_prefix_pushf("SYSIB");
ret = stsi(pagebuf, 15, 1, sel2);
if (max_nested_lvl >= sel2) {
report(!ret, "Valid instruction");
report(sel2 == info->mnest, "Valid mnest");
} else {
report(ret, "Invalid instruction");
}
report_prefix_pop();
return ret;
}
/**
* check_sysinfo_15_1_x:
* @info: pointer to the STSI info structure
* @sel2: the selector giving the topology level to check
*
* Check if the validity of the STSI instruction and then
* calls specific checks on the information buffer.
*/
static void check_sysinfo_15_1_x(struct sysinfo_15_1_x *info, int sel2)
{
int ret;
int cc;
unsigned long rc;
report_prefix_pushf("15_1_%d", sel2);
ret = stsi_get_sysib(info, sel2);
if (ret) {
report_skip("Selector 2 not supported by architecture");
goto end;
}
report_prefix_pushf("H");
cc = ptf(PTF_REQ_HORIZONTAL, &rc);
if (cc != 0 && rc != PTF_ERR_ALRDY_POLARIZED) {
report_fail("Unable to set horizontal polarization");
goto vertical;
}
stsi_check_mag(info);
stsi_check_tle_coherency(info);
vertical:
report_prefix_pop();
report_prefix_pushf("V");
cc = ptf(PTF_REQ_VERTICAL, &rc);
if (cc != 0 && rc != PTF_ERR_ALRDY_POLARIZED) {
report_fail("Unable to set vertical polarization");
goto end;
}
stsi_check_mag(info);
stsi_check_tle_coherency(info);
report_prefix_pop();
end:
report_prefix_pop();
}
/*
* The Maximum Nested level is given by SCLP READ_SCP_INFO if the MNEST facility
* is available.
* If the MNEST facility is not available, sclp_get_stsi_mnest returns 0 and the
* Maximum Nested level is 2
*/
#define S390_DEFAULT_MNEST 2
static int sclp_get_mnest(void)
{
return sclp_get_stsi_mnest() ?: S390_DEFAULT_MNEST;
}
static int expected_num_cpus(void)
{
int i;
int ncpus = 1;
for (i = 0; i < CPU_TOPOLOGY_MAX_LEVEL; i++)
ncpus *= expected_topo_lvl[i] ?: 1;
return ncpus;
}
/**
* test_stsi:
*
* Retrieves the maximum nested topology level supported by the architecture
* and the number of CPUs.
* Calls the checking for the STSI instruction in sel2 reverse level order
* from 6 (CPU_TOPOLOGY_MAX_LEVEL) to 2 to have the most interesting level,
* the one triggering a topology-change-report-pending condition, level 2,
* at the end of the report.
*
*/
static void test_stsi(void)
{
int sel2;
max_cpus = expected_num_cpus();
report_info("Architecture max CPUs: %d", max_cpus);
max_nested_lvl = sclp_get_mnest();
report_info("SCLP maximum nested level : %d", max_nested_lvl);
number_of_cpus = sclp_get_cpu_num();
report_info("SCLP number of CPU: %d", number_of_cpus);
/* STSI selector 2 can takes values between 2 and 6 */
for (sel2 = 6; sel2 >= 2; sel2--)
check_sysinfo_15_1_x((struct sysinfo_15_1_x *)pagebuf, sel2);
}
/**
* parse_topology_args:
* @argc: number of arguments
* @argv: argument array
*
* This function initialize the architecture topology levels
* which should be the same as the one provided by the hypervisor.
*
* We use the current names found in IBM/Z literature, Linux and QEMU:
* cores, sockets/packages, books, drawers and nodes to facilitate the
* human machine interface but store the result in a machine abstract
* array of architecture topology levels.
* Note that when QEMU uses socket as a name for the topology level 1
* Linux uses package or physical_package.
*/
static void parse_topology_args(int argc, char **argv)
{
int i;
static const char * const levels[] = { "cores", "sockets",
"books", "drawers" };
for (i = 1; i < argc; i++) {
char *flag = argv[i];
int level;
if (flag[0] != '-')
report_abort("Argument is expected to begin with '-'");
flag++;
for (level = 0; ARRAY_SIZE(levels); level++) {
if (!strcmp(levels[level], flag))
break;
}
if (level == ARRAY_SIZE(levels))
report_abort("Unknown parameter %s", flag);
expected_topo_lvl[level] = atol(argv[++i]);
report_info("%s: %d", levels[level], expected_topo_lvl[level]);
}
}
static struct {
const char *name;
void (*func)(void);
} tests[] = {
{ "PTF", test_ptf },
{ "STSI", test_stsi },
{ NULL, NULL }
};
int main(int argc, char *argv[])
{
int i;
report_prefix_push("CPU Topology");
parse_topology_args(argc, argv);
if (!test_facility(11)) {
report_skip("Topology facility not present");
goto end;
}
report_info("Virtual machine level %ld", stsi_get_fc());
for (i = 0; tests[i].name; i++) {
report_prefix_push(tests[i].name);
tests[i].func();
report_prefix_pop();
}
end:
report_prefix_pop();
return report_summary();
}