Re: [kvm-unit-tests PATCH 2/2] powerpc: Add Special Purpose Register persistency test

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 03/09/2017 06:27 PM, Thomas Huth wrote:
> This test has two purposes: First, check whether the hypervisor can be
> destabilized by writing random values into the SPRs of the PowerPC CPU
> (this indeed revealed a bug last year, see CVE-2016-3044).
> Second, this test can be used to check whether the SPRs are synchronized
> properly between the KVM host CPU and QEMU, e.g. when migrating the VM
> from one QEMU instance to another.
> The test first fills the various SPRs with some non-zero value, then reads
> the values back into a first array. It then either sleeps a short period
> of time (for testing without migration, in the hope that we're rescheduled
> on another host CPU), or it waits for a key or NMI (with the '-w' option)
> so that it is possible to migrate the VM before continuing. The test then
> finally reads the values from the SPRs back into another array and then
> compares them with the initial values.
> Currently the test only supports the SPRs from the PowerISA v2.01
> (PowerPC 970) and PowerISA v2.07 specification (i.e. POWER8 CPUs),
> but other versions should be pretty easy to add later.
> 
> Signed-off-by: Thomas Huth <thuth@xxxxxxxxxx>

It looks good to me. I gave it a try and it worked fine with some
extra tuning in the migration script.  

LDFLAGS needs fix for binutils 2.28 but that is another issue.

Tested-by: Cédric Le Goater <clg@xxxxxxxx>

C.

> ---
>  powerpc/Makefile.common |   3 +-
>  powerpc/cstart64.S      |   2 +
>  powerpc/sprs.c          | 303 ++++++++++++++++++++++++++++++++++++++++++++++++
>  powerpc/unittests.cfg   |   5 +
>  4 files changed, 312 insertions(+), 1 deletion(-)
>  create mode 100644 powerpc/sprs.c
> 
> diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
> index 37f8caa..92809a5 100644
> --- a/powerpc/Makefile.common
> +++ b/powerpc/Makefile.common
> @@ -9,7 +9,8 @@ tests-common = \
>  	$(TEST_DIR)/spapr_hcall.elf \
>  	$(TEST_DIR)/rtas.elf \
>  	$(TEST_DIR)/emulator.elf \
> -	$(TEST_DIR)/tm.elf
> +	$(TEST_DIR)/tm.elf \
> +	$(TEST_DIR)/sprs.elf
>  
>  tests-all = $(tests-common) $(tests)
>  all: $(TEST_DIR)/boot_rom.bin $(tests-all)
> diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S
> index 2204e3b..ec673b3 100644
> --- a/powerpc/cstart64.S
> +++ b/powerpc/cstart64.S
> @@ -247,6 +247,8 @@ call_handler:
>  	.globl __start_interrupts
>  __start_interrupts:
>  
> +VECTOR(0x100)
> +VECTOR(0x200)
>  VECTOR(0x300)
>  VECTOR(0x400)
>  VECTOR(0x500)
> diff --git a/powerpc/sprs.c b/powerpc/sprs.c
> new file mode 100644
> index 0000000..7792db4
> --- /dev/null
> +++ b/powerpc/sprs.c
> @@ -0,0 +1,303 @@
> +/*
> + * 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 a key or NMI (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-
> + * ledges or have bad side effects, so we have to omit those registers.
> + */
> +#include <libcflat.h>
> +#include <util.h>
> +#include <alloc.h>
> +#include <asm/handlers.h>
> +#include <asm/hcall.h>
> +#include <asm/processor.h>
> +
> +#define mfspr(nr) ({ \
> +	uint64_t ret; \
> +	asm volatile("mfspr %0,%1" : "=r"(ret) : "i"(nr)); \
> +	ret; \
> +})
> +
> +#define mtspr(nr, val) \
> +	asm volatile("mtspr %0,%1" : : "i"(nr), "r"(val))
> +
> +uint64_t before[1024], after[1024];
> +
> +volatile int nmi_occurred;
> +
> +static void nmi_handler(struct pt_regs *regs __unused, void *opaque __unused)
> +{
> +	nmi_occurred = 1;
> +}
> +
> +static int h_get_term_char(uint64_t termno)
> +{
> +	register uint64_t r3 asm("r3") = 0x54; /* H_GET_TERM_CHAR */
> +	register uint64_t r4 asm("r4") = termno;
> +	register uint64_t r5 asm("r5");
> +
> +	asm volatile (" sc 1 "	: "+r"(r3), "+r"(r4), "=r"(r5)
> +				: "r"(r3),  "r"(r4));
> +
> +	return r3 == H_SUCCESS && r4 > 0 ? r5 >> 48 : 0;
> +}
> +
> +/* 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 */
> +}
> +
> +static void set_sprs(uint64_t val)
> +{
> +	uint32_t pvr = mfspr(287);	/* Processor Version Register */
> +
> +	set_sprs_common(val);
> +
> +	switch (pvr >> 16) {
> +	case 0x39:			/* PPC970 */
> +	case 0x3C:			/* PPC970FX */
> +	case 0x44:			/* PPC970MP */
> +		set_sprs_book3s_201(val);
> +		break;
> +	case 0x4b:			/* POWER8E */
> +	case 0x4c:			/* POWER8NVL */
> +	case 0x4d:			/* POWER8 */
> +		set_sprs_book3s_207(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(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;
> +	}
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	int i;
> +	bool waitkey = 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")) {
> +			waitkey = 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 0x%lx...\n", pat);
> +	set_sprs(pat);
> +
> +	memset(before, 0, sizeof(before));
> +	memset(after, 0, sizeof(after));
> +
> +	get_sprs(before);
> +
> +	if (waitkey) {
> +		handle_exception(0x100, &nmi_handler, NULL);
> +		puts("Now migrate the VM, then press a key or send NMI...\n");
> +		while (!nmi_occurred && h_get_term_char(0) == 0)
> +			asm volatile(" nop " ::: "memory");
> +	} else {
> +		puts("Sleeping...\n");
> +		handle_exception(0x900, &dec_except_handler, NULL);
> +		asm volatile("mtdec %0" : : "r" (0x3FFFFFFF));
> +		hcall(H_CEDE);
> +	}
> +
> +	get_sprs(after);
> +
> +	puts("Checking SPRs...\n");
> +	for (i = 0; i < 1024; i++) {
> +		if (before[i] != 0 || after[i] != 0)
> +			report("SPR %d:\t0x%016lx <==> 0x%016lx",
> +				before[i] == after[i], i, before[i], after[i]);
> +	}
> +
> +	return report_summary();
> +}
> diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
> index 20dbde6..fb6b70e 100644
> --- a/powerpc/unittests.cfg
> +++ b/powerpc/unittests.cfg
> @@ -59,3 +59,8 @@ file = tm.elf
>  smp = 2,threads=2
>  extra_params = -append "h_cede_tm"
>  groups = nodefault,h_cede_tm
> +
> +[sprs]
> +file = sprs.elf
> +extra_params = -append '-w'
> +groups = migration
> 




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux