blob: a19d80a1a90560cf1f01eb1e000032f7139820b9 [file] [log] [blame]
/*
* Test Special Purpose Registers
*
* Copyright 2017 Thomas Huth, Red Hat Inc.
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*
* The basic idea of this test is to check whether the contents of the Special
* Purpose Registers (SPRs) are preserved correctly during migration. So we
* fill in the SPRs with a well-known value, read the values back (since not
* all bits might be retained in the SPRs), then wait for migration to complete
* (if the '-w' option has been specified) so that the user has a chance to
* migrate the VM. Alternatively, the test can also simply sleep a little bit
* with the H_CEDE hypercall, in the hope that we'll get scheduled to another
* host CPU and thus register contents might have changed, too (in case of
* bugs). Finally, we read back the values from the SPRs and compare them with
* the values before the migration. Mismatches are reported as test failures.
* Note that we do not test all SPRs since some of the registers change their
* content automatically, and some are only accessible with hypervisor privi-
* leges or have bad side effects, so we have to omit those registers.
*/
#include <libcflat.h>
#include <util.h>
#include <migrate.h>
#include <alloc.h>
#include <asm/ppc_asm.h>
#include <asm/handlers.h>
#include <asm/hcall.h>
#include <asm/processor.h>
#include <asm/time.h>
#include <asm/barrier.h>
uint64_t before[1024], after[1024];
/* Common SPRs for all PowerPC CPUs */
static void set_sprs_common(uint64_t val)
{
mtspr(9, val); /* CTR */
// mtspr(273, val); /* SPRG1 */ /* Used by our exception handler */
mtspr(274, val); /* SPRG2 */
mtspr(275, val); /* SPRG3 */
}
/* SPRs from PowerPC Operating Environment Architecture, Book III, Vers. 2.01 */
static void set_sprs_book3s_201(uint64_t val)
{
mtspr(18, val); /* DSISR */
mtspr(19, val); /* DAR */
mtspr(152, val); /* CTRL */
mtspr(256, val); /* VRSAVE */
mtspr(786, val); /* MMCRA */
mtspr(795, val); /* MMCR0 */
mtspr(798, val); /* MMCR1 */
}
/* SPRs from PowerISA 2.07 Book III-S */
static void set_sprs_book3s_207(uint64_t val)
{
mtspr(3, val); /* DSCR */
mtspr(13, val); /* AMR */
mtspr(17, val); /* DSCR */
mtspr(18, val); /* DSISR */
mtspr(19, val); /* DAR */
mtspr(29, val); /* AMR */
mtspr(61, val); /* IAMR */
// mtspr(152, val); /* CTRL */ /* TODO: Needs a fix in KVM */
mtspr(153, val); /* FSCR */
mtspr(157, val); /* UAMOR */
mtspr(159, val); /* PSPB */
mtspr(256, val); /* VRSAVE */
// mtspr(272, val); /* SPRG0 */ /* Used by our exception handler */
mtspr(769, val); /* MMCR2 */
mtspr(770, val); /* MMCRA */
mtspr(771, val); /* PMC1 */
mtspr(772, val); /* PMC2 */
mtspr(773, val); /* PMC3 */
mtspr(774, val); /* PMC4 */
mtspr(775, val); /* PMC5 */
mtspr(776, val); /* PMC6 */
mtspr(779, (val & 0xfffffffffbab3fffULL) | 0xfa0b2070); /* MMCR0 */
mtspr(784, val); /* SIER */
mtspr(785, val); /* MMCR2 */
mtspr(786, val); /* MMCRA */
mtspr(787, val); /* PMC1 */
mtspr(788, val); /* PMC2 */
mtspr(789, val); /* PMC3 */
mtspr(790, val); /* PMC4 */
mtspr(791, val); /* PMC5 */
mtspr(792, val); /* PMC6 */
mtspr(795, (val & 0xfffffffffbab3fffULL) | 0xfa0b2070); /* MMCR0 */
mtspr(796, val); /* SIAR */
mtspr(797, val); /* SDAR */
mtspr(798, val); /* MMCR1 */
mtspr(800, val); /* BESCRS */
mtspr(801, val); /* BESCCRSU */
mtspr(802, val); /* BESCRR */
mtspr(803, val); /* BESCRRU */
mtspr(804, val); /* EBBHR */
mtspr(805, val); /* EBBRR */
mtspr(806, val); /* BESCR */
mtspr(815, val); /* TAR */
}
/* SPRs from PowerISA 3.00 Book III */
static void set_sprs_book3s_300(uint64_t val)
{
set_sprs_book3s_207(val);
mtspr(48, val); /* PIDR */
mtspr(144, val); /* TIDR */
mtspr(823, val); /* PSSCR */
}
/* SPRs from Power ISA Version 3.1B */
static void set_sprs_book3s_31(uint64_t val)
{
set_sprs_book3s_207(val);
mtspr(48, val); /* PIDR */
/* 3.1 removes TIDR */
mtspr(823, val); /* PSSCR */
}
static void set_sprs(uint64_t val)
{
set_sprs_common(val);
switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
case PVR_VER_970:
case PVR_VER_970FX:
case PVR_VER_970MP:
set_sprs_book3s_201(val);
break;
case PVR_VER_POWER8E:
case PVR_VER_POWER8NVL:
case PVR_VER_POWER8:
set_sprs_book3s_207(val);
break;
case PVR_VER_POWER9:
set_sprs_book3s_300(val);
break;
case PVR_VER_POWER10:
set_sprs_book3s_31(val);
break;
default:
puts("Warning: Unknown processor version!\n");
}
}
static void get_sprs_common(uint64_t *v)
{
v[9] = mfspr(9); /* CTR */
// v[273] = mfspr(273); /* SPRG1 */ /* Used by our exception handler */
v[274] = mfspr(274); /* SPRG2 */
v[275] = mfspr(275); /* SPRG3 */
}
static void get_sprs_book3s_201(uint64_t *v)
{
v[18] = mfspr(18); /* DSISR */
v[19] = mfspr(19); /* DAR */
v[136] = mfspr(136); /* CTRL */
v[256] = mfspr(256); /* VRSAVE */
v[786] = mfspr(786); /* MMCRA */
v[795] = mfspr(795); /* MMCR0 */
v[798] = mfspr(798); /* MMCR1 */
}
static void get_sprs_book3s_207(uint64_t *v)
{
v[3] = mfspr(3); /* DSCR */
v[13] = mfspr(13); /* AMR */
v[17] = mfspr(17); /* DSCR */
v[18] = mfspr(18); /* DSISR */
v[19] = mfspr(19); /* DAR */
v[29] = mfspr(29); /* AMR */
v[61] = mfspr(61); /* IAMR */
// v[136] = mfspr(136); /* CTRL */ /* TODO: Needs a fix in KVM */
v[153] = mfspr(153); /* FSCR */
v[157] = mfspr(157); /* UAMOR */
v[159] = mfspr(159); /* PSPB */
v[256] = mfspr(256); /* VRSAVE */
v[259] = mfspr(259); /* SPRG3 (read only) */
// v[272] = mfspr(272); /* SPRG0 */ /* Used by our exception handler */
v[769] = mfspr(769); /* MMCR2 */
v[770] = mfspr(770); /* MMCRA */
v[771] = mfspr(771); /* PMC1 */
v[772] = mfspr(772); /* PMC2 */
v[773] = mfspr(773); /* PMC3 */
v[774] = mfspr(774); /* PMC4 */
v[775] = mfspr(775); /* PMC5 */
v[776] = mfspr(776); /* PMC6 */
v[779] = mfspr(779); /* MMCR0 */
v[780] = mfspr(780); /* SIAR (read only) */
v[781] = mfspr(781); /* SDAR (read only) */
v[782] = mfspr(782); /* MMCR1 (read only) */
v[784] = mfspr(784); /* SIER */
v[785] = mfspr(785); /* MMCR2 */
v[786] = mfspr(786); /* MMCRA */
v[787] = mfspr(787); /* PMC1 */
v[788] = mfspr(788); /* PMC2 */
v[789] = mfspr(789); /* PMC3 */
v[790] = mfspr(790); /* PMC4 */
v[791] = mfspr(791); /* PMC5 */
v[792] = mfspr(792); /* PMC6 */
v[795] = mfspr(795); /* MMCR0 */
v[796] = mfspr(796); /* SIAR */
v[797] = mfspr(797); /* SDAR */
v[798] = mfspr(798); /* MMCR1 */
v[800] = mfspr(800); /* BESCRS */
v[801] = mfspr(801); /* BESCCRSU */
v[802] = mfspr(802); /* BESCRR */
v[803] = mfspr(803); /* BESCRRU */
v[804] = mfspr(804); /* EBBHR */
v[805] = mfspr(805); /* EBBRR */
v[806] = mfspr(806); /* BESCR */
v[815] = mfspr(815); /* TAR */
}
static void get_sprs_book3s_300(uint64_t *v)
{
get_sprs_book3s_207(v);
v[48] = mfspr(48); /* PIDR */
v[144] = mfspr(144); /* TIDR */
v[823] = mfspr(823); /* PSSCR */
}
static void get_sprs_book3s_31(uint64_t *v)
{
get_sprs_book3s_207(v);
v[48] = mfspr(48); /* PIDR */
v[823] = mfspr(823); /* PSSCR */
}
static void get_sprs(uint64_t *v)
{
uint32_t pvr = mfspr(287); /* Processor Version Register */
get_sprs_common(v);
switch (pvr >> 16) {
case 0x39: /* PPC970 */
case 0x3C: /* PPC970FX */
case 0x44: /* PPC970MP */
get_sprs_book3s_201(v);
break;
case 0x4b: /* POWER8E */
case 0x4c: /* POWER8NVL */
case 0x4d: /* POWER8 */
get_sprs_book3s_207(v);
break;
case 0x4e: /* POWER9 */
get_sprs_book3s_300(v);
break;
case 0x80: /* POWER10 */
get_sprs_book3s_31(v);
break;
}
}
int main(int argc, char **argv)
{
int i;
bool pause = false;
uint64_t pat = 0xcafefacec0debabeULL;
const uint64_t patterns[] = {
0xcafefacec0debabeULL, ~0xcafefacec0debabeULL,
0xAAAA5555AAAA5555ULL, 0x5555AAAA5555AAAAULL,
0x1234567890ABCDEFULL, 0xFEDCBA0987654321ULL,
-1ULL,
};
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-w")) {
pause = true;
} else if (!strcmp(argv[i], "-p")) {
i += 1;
if (i >= argc || *argv[i] < '0'
|| *argv[i] >= '0' + ARRAY_SIZE(patterns))
report_abort("Error: bad value for -p");
pat ^= patterns[*argv[i] - '0'];
} else if (!strcmp(argv[i], "-t")) {
/* Randomize with timebase register */
asm volatile("mftb %0" : "=r"(i));
pat ^= i;
asm volatile("mftb %0" : "=r"(i));
pat ^= ~(uint64_t)i << 32;
} else {
report_abort("Warning: Unsupported argument: %s",
argv[i]);
}
}
printf("Settings SPRs to %#lx...\n", pat);
set_sprs(pat);
memset(before, 0, sizeof(before));
memset(after, 0, sizeof(after));
get_sprs(before);
if (pause) {
migrate_once();
} else {
msleep(2000);
}
get_sprs(after);
puts("Checking SPRs...\n");
for (i = 0; i < 1024; i++) {
if (before[i] != 0 || after[i] != 0)
report(before[i] == after[i],
"SPR %d:\t%#018lx <==> %#018lx", i, before[i],
after[i]);
}
return report_summary();
}