/* * 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 #include #include #include #include #include #include #include #include #include /* "Indirect" mfspr/mtspr which accept a non-constant spr number */ static uint64_t __mfspr(unsigned spr) { uint64_t tmp; uint64_t ret; asm volatile( " bcl 20, 31, 1f \n" "1: mflr %0 \n" " addi %0, %0, (2f-1b) \n" " add %0, %0, %2 \n" " mtctr %0 \n" " bctr \n" "2: \n" ".LSPR=0 \n" ".rept 1024 \n" " mfspr %1, .LSPR \n" " b 3f \n" " .LSPR=.LSPR+1 \n" ".endr \n" "3: \n" : "=&r"(tmp), "=r"(ret) : "r"(spr*8) /* 8 bytes per 'mfspr ; b' block */ : "lr", "ctr"); return ret; } static void __mtspr(unsigned spr, uint64_t val) { uint64_t tmp; asm volatile( " bcl 20, 31, 1f \n" "1: mflr %0 \n" " addi %0, %0, (2f-1b) \n" " add %0, %0, %2 \n" " mtctr %0 \n" " bctr \n" "2: \n" ".LSPR=0 \n" ".rept 1024 \n" " mtspr .LSPR, %1 \n" " b 3f \n" " .LSPR=.LSPR+1 \n" ".endr \n" "3: \n" : "=&r"(tmp) : "r"(val), "r"(spr*8) /* 8 bytes per 'mfspr ; b' block */ : "lr", "ctr", "xer"); } static uint64_t before[1024], after[1024]; #define SPR_PR_READ 0x0001 #define SPR_PR_WRITE 0x0002 #define SPR_OS_READ 0x0010 #define SPR_OS_WRITE 0x0020 #define SPR_HV_READ 0x0100 #define SPR_HV_WRITE 0x0200 #define RW 0x333 #define RO 0x111 #define WO 0x222 #define OS_RW 0x330 #define OS_RO 0x110 #define OS_WO 0x220 #define HV_RW 0x300 #define HV_RO 0x100 #define HV_WO 0x200 #define SPR_ASYNC 0x1000 /* May be updated asynchronously */ #define SPR_INT 0x2000 /* May be updated by synchronous interrupt */ #define SPR_HARNESS 0x4000 /* Test harness uses the register */ struct spr { const char *name; uint8_t width; uint16_t access; uint16_t type; }; /* SPRs common denominator back to PowerPC Operating Environment Architecture */ static const struct spr sprs_common[1024] = { [1] = { "XER", 64, RW, SPR_HARNESS, }, /* Used by compiler */ [8] = { "LR", 64, RW, SPR_HARNESS, }, /* Compiler, mfspr/mtspr */ [9] = { "CTR", 64, RW, SPR_HARNESS, }, /* Compiler, mfspr/mtspr */ [18] = { "DSISR", 32, OS_RW, SPR_INT, }, [19] = { "DAR", 64, OS_RW, SPR_INT, }, [26] = { "SRR0", 64, OS_RW, SPR_INT, }, [27] = { "SRR1", 64, OS_RW, SPR_INT, }, [268] = { "TB", 64, RO , SPR_ASYNC, }, [269] = { "TBU", 32, RO, SPR_ASYNC, }, [272] = { "SPRG0", 64, OS_RW, SPR_HARNESS, }, /* Interrupt stacr */ [273] = { "SPRG1", 64, OS_RW, SPR_HARNESS, }, /* Interrupt Scratch */ [274] = { "SPRG2", 64, OS_RW, }, [275] = { "SPRG3", 64, OS_RW, }, [287] = { "PVR", 32, OS_RO, }, }; /* SPRs from PowerPC Operating Environment Architecture, Book III, Vers. 2.01 */ static const struct spr sprs_201[1024] = { [22] = { "DEC", 32, OS_RW, SPR_ASYNC, }, [25] = { "SDR1", 64, HV_RW | OS_RO, }, [29] = { "ACCR", 64, OS_RW, }, [136] = { "CTRL", 32, RO, }, [152] = { "CTRL", 32, OS_WO, }, [259] = { "SPRG3", 64, RO, }, /* ASR, EAR omitted */ [284] = { "TBL", 32, HV_WO, }, [285] = { "TBU", 32, HV_WO, }, [310] = { "HDEC", 32, HV_RW, SPR_ASYNC, }, [1013]= { "DABR", 64, HV_RW | OS_RO, }, [1023]= { "PIR", 32, OS_RO, SPR_ASYNC, }, /* Can't be virtualised, appears to be async */ }; static const struct spr sprs_970_pmu[1024] = { /* POWER4+ PMU, should confirm with PPC970 */ [770] = { "MMCRA", 64, RO, }, [771] = { "PMC1", 32, RO, }, [772] = { "PMC2", 32, RO, }, [773] = { "PMC3", 32, RO, }, [774] = { "PMC4", 32, RO, }, [775] = { "PMC5", 32, RO, }, [776] = { "PMC6", 32, RO, }, [777] = { "PMC7", 32, RO, }, [778] = { "PMC8", 32, RO, }, [779] = { "MMCR0", 64, RO, }, [780] = { "SIAR", 64, RO, }, [781] = { "SDAR", 64, RO, }, [782] = { "MMCR1", 64, RO, }, [786] = { "MMCRA", 64, OS_RW, }, [787] = { "PMC1", 32, OS_RW, }, [788] = { "PMC2", 32, OS_RW, }, [789] = { "PMC3", 32, OS_RW, }, [790] = { "PMC4", 32, OS_RW, }, [791] = { "PMC5", 32, OS_RW, }, [792] = { "PMC6", 32, OS_RW, }, [793] = { "PMC7", 32, OS_RW, }, [794] = { "PMC8", 32, OS_RW, }, [795] = { "MMCR0", 64, OS_RW, }, [796] = { "SIAR", 64, OS_RW, }, [797] = { "SDAR", 64, OS_RW, }, [798] = { "MMCR1", 64, OS_RW, }, }; /* These are common SPRs from 2.07S onward (POWER CPUs that support KVM HV) */ static const struct spr sprs_power_common[1024] = { [3] = { "DSCR", 64, RW, }, [13] = { "AMR", 64, RW, }, [17] = { "DSCR", 64, OS_RW, }, [28] = { "CFAR", 64, OS_RW, SPR_ASYNC, }, /* Effectively async */ [29] = { "AMR", 64, OS_RW, }, [61] = { "IAMR", 64, OS_RW, }, [136] = { "CTRL", 32, RO, }, [152] = { "CTRL", 32, OS_WO, }, [153] = { "FSCR", 64, OS_RW, }, [157] = { "UAMOR", 64, OS_RW, }, [159] = { "PSPB", 32, OS_RW, }, [176] = { "DPDES", 64, HV_RW | OS_RO, }, [180] = { "DAWR0", 64, HV_RW, }, [186] = { "RPR", 64, HV_RW, }, [187] = { "CIABR", 64, HV_RW, }, [188] = { "DAWRX0", 32, HV_RW, }, [190] = { "HFSCR", 64, HV_RW, }, [256] = { "VRSAVE", 32, RW, }, [259] = { "SPRG3", 64, RO, }, [284] = { "TBL", 32, HV_WO, }, /* Things can go a bit wonky with */ [285] = { "TBU", 32, HV_WO, }, /* Timebase changing. Should save */ [286] = { "TBU40", 64, HV_WO, }, /* and restore it. */ [304] = { "HSPRG0", 64, HV_RW, }, [305] = { "HSPRG1", 64, HV_RW, }, [306] = { "HDSISR", 32, HV_RW, SPR_INT, }, [307] = { "HDAR", 64, HV_RW, SPR_INT, }, [308] = { "SPURR", 64, HV_RW | OS_RO, SPR_ASYNC, }, [309] = { "PURR", 64, HV_RW | OS_RO, SPR_ASYNC, }, [313] = { "HRMOR", 64, HV_RW, SPR_HARNESS, }, /* Harness can't cope with HRMOR changing */ [314] = { "HSRR0", 64, HV_RW, SPR_INT, }, [315] = { "HSRR1", 64, HV_RW, SPR_INT, }, [318] = { "LPCR", 64, HV_RW, }, [319] = { "LPIDR", 32, HV_RW, }, [336] = { "HMER", 64, HV_RW, }, [337] = { "HMEER", 64, HV_RW, }, [338] = { "PCR", 64, HV_RW, }, [349] = { "AMOR", 64, HV_RW, }, [446] = { "TIR", 64, OS_RO, }, [800] = { "BESCRS", 64, RW, }, [801] = { "BESCRSU", 32, RW, }, [802] = { "BESCRR", 64, RW, }, [803] = { "BESCRRU", 32, RW, }, [804] = { "EBBHR", 64, RW, }, [805] = { "EBBRR", 64, RW, }, [806] = { "BESCR", 64, RW, }, [815] = { "TAR", 64, RW, }, [848] = { "IC", 64, HV_RW | OS_RO, SPR_ASYNC, }, [849] = { "VTB", 64, HV_RW | OS_RO, SPR_ASYNC, }, [896] = { "PPR", 64, RW, SPR_ASYNC, }, /* PPR(32) is changed by cpu_relax(), appears to be async */ [898] = { "PPR32", 32, RW, SPR_ASYNC, }, [1023]= { "PIR", 32, OS_RO, SPR_ASYNC, }, /* Can't be virtualised, appears to be async */ }; static const struct spr sprs_tm[1024] = { #if 0 /* XXX: leave these out until enabling TM facility (and more testing) */ [128] = { "TFHAR", 64, RW, }, [129] = { "TFIAR", 64, RW, }, [130] = { "TEXASR", 64, RW, }, [131] = { "TEXASRU", 32, RW, }, #endif }; /* SPRs from PowerISA 2.07 Book III-S */ static const struct spr sprs_207[1024] = { [22] = { "DEC", 32, OS_RW, SPR_ASYNC, }, [25] = { "SDR1", 64, HV_RW, }, [177] = { "DHDES", 64, HV_RW, }, [283] = { "CIR", 32, OS_RO, }, [310] = { "HDEC", 32, HV_RW, SPR_ASYNC, }, [312] = { "RMOR", 64, HV_RW, }, [339] = { "HEIR", 32, HV_RW, SPR_INT, }, }; /* SPRs from PowerISA 3.00 Book III */ static const struct spr sprs_300[1024] = { [22] = { "DEC", 64, OS_RW, SPR_ASYNC, }, [48] = { "PIDR", 32, OS_RW, }, [144] = { "TIDR", 64, OS_RW, }, [283] = { "CIR", 32, OS_RO, }, [310] = { "HDEC", 64, HV_RW, SPR_ASYNC, }, [339] = { "HEIR", 32, HV_RW, SPR_INT, }, [464] = { "PTCR", 64, HV_RW, }, [816] = { "ASDR", 64, HV_RW, SPR_INT, }, [823] = { "PSSCR", 64, OS_RW, }, [855] = { "PSSCR", 64, HV_RW, }, }; /* SPRs from PowerISA 3.1B Book III */ static const struct spr sprs_31[1024] = { [22] = { "DEC", 64, OS_RW, SPR_ASYNC, }, [48] = { "PIDR", 32, OS_RW, }, [181] = { "DAWR1", 64, HV_RW, }, [189] = { "DAWRX1", 32, HV_RW, }, [310] = { "HDEC", 64, HV_RW, SPR_ASYNC, }, [339] = { "HEIR", 64, HV_RW, SPR_INT, }, [455] = { "HDEXCR", 32, RO, }, [464] = { "PTCR", 64, HV_RW, }, [468] = { "HASHKEYR", 64, OS_RW, }, [469] = { "HASHPKEYR", 64, HV_RW, }, [471] = { "HDEXCR", 64, HV_RW, }, [812] = { "DEXCR", 32, RO, }, [816] = { "ASDR", 64, HV_RW, SPR_INT, }, [823] = { "PSSCR", 64, OS_RW, }, [828] = { "DEXCR", 64, OS_RW, }, [855] = { "PSSCR", 64, HV_RW, }, }; /* SPRs POWER9, POWER10 User Manual */ static const struct spr sprs_power9_10[1024] = { [276] = { "SPRC", 64, HV_RW, }, [277] = { "SPRD", 64, HV_RW, }, [317] = { "TFMR", 64, HV_RW, }, [799] = { "IMC", 64, HV_RW, }, [850] = { "LDBAR", 64, HV_RO, }, [851] = { "MMCRC", 32, HV_RW, }, [853] = { "PMSR", 32, HV_RO, }, [861] = { "L2QOSR", 64, HV_WO, }, [881] = { "TRIG1", 64, OS_WO, }, [882] = { "TRIG2", 64, OS_WO, }, [884] = { "PMCR", 64, HV_RW, }, [885] = { "RWMR", 64, HV_RW, }, [895] = { "WORT", 64, OS_RW, }, /* UM says 18-bits! */ [921] = { "TSCR", 32, HV_RW, }, [922] = { "TTR", 64, HV_RW, }, [1006]= { "TRACE", 64, WO, }, [1008]= { "HID", 64, HV_RW, SPR_HARNESS, }, /* HILE would be unhelpful to change */ }; /* This covers POWER8 and POWER9 PMUs */ static const struct spr sprs_power_common_pmu[1024] = { [768] = { "SIER", 64, RO, }, [769] = { "MMCR2", 64, RW, }, [770] = { "MMCRA", 64, RW, }, [771] = { "PMC1", 32, RW, }, [772] = { "PMC2", 32, RW, }, [773] = { "PMC3", 32, RW, }, [774] = { "PMC4", 32, RW, }, [775] = { "PMC5", 32, RW, }, [776] = { "PMC6", 32, RW, }, [779] = { "MMCR0", 64, RW, }, [780] = { "SIAR", 64, RO, }, [781] = { "SDAR", 64, RO, }, [782] = { "MMCR1", 64, RO, }, [784] = { "SIER", 64, OS_RW, }, [785] = { "MMCR2", 64, OS_RW, }, [786] = { "MMCRA", 64, OS_RW, }, [787] = { "PMC1", 32, OS_RW, }, [788] = { "PMC2", 32, OS_RW, }, [789] = { "PMC3", 32, OS_RW, }, [790] = { "PMC4", 32, OS_RW, }, [791] = { "PMC5", 32, OS_RW, }, [792] = { "PMC6", 32, OS_RW, }, [795] = { "MMCR0", 64, OS_RW, }, [796] = { "SIAR", 64, OS_RW, }, [797] = { "SDAR", 64, OS_RW, }, [798] = { "MMCR1", 64, OS_RW, }, }; static const struct spr sprs_power10_pmu[1024] = { [736] = { "SIER2", 64, RO, }, [737] = { "SIER3", 64, RO, }, [738] = { "MMCR3", 64, RO, }, [752] = { "SIER2", 64, OS_RW, }, [753] = { "SIER3", 64, OS_RW, }, [754] = { "MMCR3", 64, OS_RW, }, }; static struct spr sprs[1024]; static bool spr_read_perms(int spr) { if (cpu_has_hv) return !!(sprs[spr].access & SPR_HV_READ); else return !!(sprs[spr].access & SPR_OS_READ); } static bool spr_write_perms(int spr) { if (cpu_has_hv) return !!(sprs[spr].access & SPR_HV_WRITE); else return !!(sprs[spr].access & SPR_OS_WRITE); } static void setup_sprs(void) { int i; for (i = 0; i < 1024; i++) { if (sprs_common[i].name) { memcpy(&sprs[i], &sprs_common[i], sizeof(struct spr)); } } switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) { case PVR_VER_970: case PVR_VER_970FX: case PVR_VER_970MP: for (i = 0; i < 1024; i++) { if (sprs_201[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_201[i], sizeof(struct spr)); } if (sprs_970_pmu[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_power_common_pmu[i], sizeof(struct spr)); } } break; case PVR_VER_POWER8E: case PVR_VER_POWER8NVL: case PVR_VER_POWER8: for (i = 0; i < 1024; i++) { if (sprs_power_common[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_power_common[i], sizeof(struct spr)); } if (sprs_207[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_207[i], sizeof(struct spr)); } if (sprs_tm[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_tm[i], sizeof(struct spr)); } if (sprs_power_common_pmu[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_power_common_pmu[i], sizeof(struct spr)); } } break; case PVR_VER_POWER9: for (i = 0; i < 1024; i++) { if (sprs_power_common[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_power_common[i], sizeof(struct spr)); } if (sprs_300[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_300[i], sizeof(struct spr)); } if (sprs_tm[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_tm[i], sizeof(struct spr)); } if (sprs_power9_10[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_power9_10[i], sizeof(struct spr)); } if (sprs_power_common_pmu[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_power_common_pmu[i], sizeof(struct spr)); } } break; case PVR_VER_POWER10: for (i = 0; i < 1024; i++) { if (sprs_power_common[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_power_common[i], sizeof(struct spr)); } if (sprs_31[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_31[i], sizeof(struct spr)); } if (sprs_power9_10[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_power9_10[i], sizeof(struct spr)); } if (sprs_power_common_pmu[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_power_common_pmu[i], sizeof(struct spr)); } if (sprs_power10_pmu[i].name) { assert(!sprs[i].name); memcpy(&sprs[i], &sprs_power10_pmu[i], sizeof(struct spr)); } } break; default: memcpy(sprs, sprs_common, sizeof(sprs)); puts("Warning: Unknown processor version, falling back to common SPRs!\n"); break; } } static void get_sprs(uint64_t *v) { int i; for (i = 0; i < 1024; i++) { if (!spr_read_perms(i)) continue; v[i] = __mfspr(i); } } static void set_sprs(uint64_t val) { int i; for (i = 0; i < 1024; i++) { if (!spr_write_perms(i)) continue; if (sprs[i].type & SPR_HARNESS) continue; __mtspr(i, val); } } 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]); } } setup_sprs(); printf("Setting SPRs to 0x%lx...\n", pat); set_sprs(pat); memset(before, 0, sizeof(before)); memset(after, 0, sizeof(after)); get_sprs(before); if (pause) { migrate_once(); /* Reload regs changed by getchar/putchar hcalls */ before[SPR_SRR0] = mfspr(SPR_SRR0); before[SPR_SRR1] = mfspr(SPR_SRR1); /* WORT seems to go to 0 after KVM switch, perhaps CPU idle */ if (sprs[895].name) before[895] = mfspr(895); } else { /* * msleep will enable MSR[EE] and take a decrementer * interrupt. Must account for changed registers and * prevent taking unhandled interrupts. */ /* Prevent PMU interrupt */ mtspr(SPR_MMCR0, (mfspr(SPR_MMCR0) | MMCR0_FC) & ~(MMCR0_PMAO | MMCR0_PMAE)); before[SPR_MMCR0] = mfspr(SPR_MMCR0); before[779] = mfspr(SPR_MMCR0); msleep(2000); /* Reload regs changed by dec interrupt */ before[SPR_SRR0] = mfspr(SPR_SRR0); before[SPR_SRR1] = mfspr(SPR_SRR1); before[SPR_SPRG1] = mfspr(SPR_SPRG1); /* WORT seems to go to 0 after KVM switch, perhaps CPU idle */ if (sprs[895].name) before[895] = mfspr(895); } get_sprs(after); puts("Checking SPRs...\n"); for (i = 0; i < 1024; i++) { bool pass = true; if (!spr_read_perms(i)) continue; if (sprs[i].width == 32) { if (before[i] >> 32) pass = false; } if (!(sprs[i].type & (SPR_HARNESS|SPR_ASYNC)) && (before[i] != after[i])) pass = false; if (sprs[i].width == 32 && !(before[i] >> 32) && !(after[i] >> 32)) { /* known failure KVM migration of CTRL */ report_kfail(host_is_kvm && i == 136, pass, "%-10s(%4d):\t 0x%08lx <==> 0x%08lx", sprs[i].name, i, before[i], after[i]); } else { report(pass, "%-10s(%4d):\t0x%016lx <==> 0x%016lx", sprs[i].name, i, before[i], after[i]); } } return report_summary(); }