18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Test for s390x CPU resets
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2020, IBM
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <stdio.h>
98c2ecf20Sopenharmony_ci#include <stdlib.h>
108c2ecf20Sopenharmony_ci#include <string.h>
118c2ecf20Sopenharmony_ci#include <sys/ioctl.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "test_util.h"
148c2ecf20Sopenharmony_ci#include "kvm_util.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define VCPU_ID 3
178c2ecf20Sopenharmony_ci#define LOCAL_IRQS 32
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct kvm_s390_irq buf[VCPU_ID + LOCAL_IRQS];
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct kvm_vm *vm;
228c2ecf20Sopenharmony_cistruct kvm_run *run;
238c2ecf20Sopenharmony_cistruct kvm_sync_regs *sync_regs;
248c2ecf20Sopenharmony_cistatic uint8_t regs_null[512];
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic void guest_code_initial(void)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	/* set several CRs to "safe" value */
298c2ecf20Sopenharmony_ci	unsigned long cr2_59 = 0x10;	/* enable guarded storage */
308c2ecf20Sopenharmony_ci	unsigned long cr8_63 = 0x1;	/* monitor mask = 1 */
318c2ecf20Sopenharmony_ci	unsigned long cr10 = 1;		/* PER START */
328c2ecf20Sopenharmony_ci	unsigned long cr11 = -1;	/* PER END */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	/* Dirty registers */
368c2ecf20Sopenharmony_ci	asm volatile (
378c2ecf20Sopenharmony_ci		"	lghi	2,0x11\n"	/* Round toward 0 */
388c2ecf20Sopenharmony_ci		"	sfpc	2\n"		/* set fpc to !=0 */
398c2ecf20Sopenharmony_ci		"	lctlg	2,2,%0\n"
408c2ecf20Sopenharmony_ci		"	lctlg	8,8,%1\n"
418c2ecf20Sopenharmony_ci		"	lctlg	10,10,%2\n"
428c2ecf20Sopenharmony_ci		"	lctlg	11,11,%3\n"
438c2ecf20Sopenharmony_ci		/* now clobber some general purpose regs */
448c2ecf20Sopenharmony_ci		"	llihh	0,0xffff\n"
458c2ecf20Sopenharmony_ci		"	llihl	1,0x5555\n"
468c2ecf20Sopenharmony_ci		"	llilh	2,0xaaaa\n"
478c2ecf20Sopenharmony_ci		"	llill	3,0x0000\n"
488c2ecf20Sopenharmony_ci		/* now clobber a floating point reg */
498c2ecf20Sopenharmony_ci		"	lghi	4,0x1\n"
508c2ecf20Sopenharmony_ci		"	cdgbr	0,4\n"
518c2ecf20Sopenharmony_ci		/* now clobber an access reg */
528c2ecf20Sopenharmony_ci		"	sar	9,4\n"
538c2ecf20Sopenharmony_ci		/* We embed diag 501 here to control register content */
548c2ecf20Sopenharmony_ci		"	diag 0,0,0x501\n"
558c2ecf20Sopenharmony_ci		:
568c2ecf20Sopenharmony_ci		: "m" (cr2_59), "m" (cr8_63), "m" (cr10), "m" (cr11)
578c2ecf20Sopenharmony_ci		/* no clobber list as this should not return */
588c2ecf20Sopenharmony_ci		);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic void test_one_reg(uint64_t id, uint64_t value)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct kvm_one_reg reg;
648c2ecf20Sopenharmony_ci	uint64_t eval_reg;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	reg.addr = (uintptr_t)&eval_reg;
678c2ecf20Sopenharmony_ci	reg.id = id;
688c2ecf20Sopenharmony_ci	vcpu_get_reg(vm, VCPU_ID, &reg);
698c2ecf20Sopenharmony_ci	TEST_ASSERT(eval_reg == value, "value == 0x%lx", value);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic void assert_noirq(void)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct kvm_s390_irq_state irq_state;
758c2ecf20Sopenharmony_ci	int irqs;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	irq_state.len = sizeof(buf);
788c2ecf20Sopenharmony_ci	irq_state.buf = (unsigned long)buf;
798c2ecf20Sopenharmony_ci	irqs = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_GET_IRQ_STATE, &irq_state);
808c2ecf20Sopenharmony_ci	/*
818c2ecf20Sopenharmony_ci	 * irqs contains the number of retrieved interrupts. Any interrupt
828c2ecf20Sopenharmony_ci	 * (notably, the emergency call interrupt we have injected) should
838c2ecf20Sopenharmony_ci	 * be cleared by the resets, so this should be 0.
848c2ecf20Sopenharmony_ci	 */
858c2ecf20Sopenharmony_ci	TEST_ASSERT(irqs >= 0, "Could not fetch IRQs: errno %d\n", errno);
868c2ecf20Sopenharmony_ci	TEST_ASSERT(!irqs, "IRQ pending");
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void assert_clear(void)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct kvm_sregs sregs;
928c2ecf20Sopenharmony_ci	struct kvm_regs regs;
938c2ecf20Sopenharmony_ci	struct kvm_fpu fpu;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	vcpu_regs_get(vm, VCPU_ID, &regs);
968c2ecf20Sopenharmony_ci	TEST_ASSERT(!memcmp(&regs.gprs, regs_null, sizeof(regs.gprs)), "grs == 0");
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	vcpu_sregs_get(vm, VCPU_ID, &sregs);
998c2ecf20Sopenharmony_ci	TEST_ASSERT(!memcmp(&sregs.acrs, regs_null, sizeof(sregs.acrs)), "acrs == 0");
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	vcpu_fpu_get(vm, VCPU_ID, &fpu);
1028c2ecf20Sopenharmony_ci	TEST_ASSERT(!memcmp(&fpu.fprs, regs_null, sizeof(fpu.fprs)), "fprs == 0");
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	/* sync regs */
1058c2ecf20Sopenharmony_ci	TEST_ASSERT(!memcmp(sync_regs->gprs, regs_null, sizeof(sync_regs->gprs)),
1068c2ecf20Sopenharmony_ci		    "gprs0-15 == 0 (sync_regs)");
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	TEST_ASSERT(!memcmp(sync_regs->acrs, regs_null, sizeof(sync_regs->acrs)),
1098c2ecf20Sopenharmony_ci		    "acrs0-15 == 0 (sync_regs)");
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	TEST_ASSERT(!memcmp(sync_regs->vrs, regs_null, sizeof(sync_regs->vrs)),
1128c2ecf20Sopenharmony_ci		    "vrs0-15 == 0 (sync_regs)");
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic void assert_initial_noclear(void)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->gprs[0] == 0xffff000000000000UL,
1188c2ecf20Sopenharmony_ci		    "gpr0 == 0xffff000000000000 (sync_regs)");
1198c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->gprs[1] == 0x0000555500000000UL,
1208c2ecf20Sopenharmony_ci		    "gpr1 == 0x0000555500000000 (sync_regs)");
1218c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->gprs[2] == 0x00000000aaaa0000UL,
1228c2ecf20Sopenharmony_ci		    "gpr2 == 0x00000000aaaa0000 (sync_regs)");
1238c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->gprs[3] == 0x0000000000000000UL,
1248c2ecf20Sopenharmony_ci		    "gpr3 == 0x0000000000000000 (sync_regs)");
1258c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->fprs[0] == 0x3ff0000000000000UL,
1268c2ecf20Sopenharmony_ci		    "fpr0 == 0f1 (sync_regs)");
1278c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->acrs[9] == 1, "ar9 == 1 (sync_regs)");
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void assert_initial(void)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct kvm_sregs sregs;
1338c2ecf20Sopenharmony_ci	struct kvm_fpu fpu;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* KVM_GET_SREGS */
1368c2ecf20Sopenharmony_ci	vcpu_sregs_get(vm, VCPU_ID, &sregs);
1378c2ecf20Sopenharmony_ci	TEST_ASSERT(sregs.crs[0] == 0xE0UL, "cr0 == 0xE0 (KVM_GET_SREGS)");
1388c2ecf20Sopenharmony_ci	TEST_ASSERT(sregs.crs[14] == 0xC2000000UL,
1398c2ecf20Sopenharmony_ci		    "cr14 == 0xC2000000 (KVM_GET_SREGS)");
1408c2ecf20Sopenharmony_ci	TEST_ASSERT(!memcmp(&sregs.crs[1], regs_null, sizeof(sregs.crs[1]) * 12),
1418c2ecf20Sopenharmony_ci		    "cr1-13 == 0 (KVM_GET_SREGS)");
1428c2ecf20Sopenharmony_ci	TEST_ASSERT(sregs.crs[15] == 0, "cr15 == 0 (KVM_GET_SREGS)");
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* sync regs */
1458c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->crs[0] == 0xE0UL, "cr0 == 0xE0 (sync_regs)");
1468c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->crs[14] == 0xC2000000UL,
1478c2ecf20Sopenharmony_ci		    "cr14 == 0xC2000000 (sync_regs)");
1488c2ecf20Sopenharmony_ci	TEST_ASSERT(!memcmp(&sync_regs->crs[1], regs_null, 8 * 12),
1498c2ecf20Sopenharmony_ci		    "cr1-13 == 0 (sync_regs)");
1508c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->crs[15] == 0, "cr15 == 0 (sync_regs)");
1518c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->fpc == 0, "fpc == 0 (sync_regs)");
1528c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->todpr == 0, "todpr == 0 (sync_regs)");
1538c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->cputm == 0, "cputm == 0 (sync_regs)");
1548c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->ckc == 0, "ckc == 0 (sync_regs)");
1558c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->pp == 0, "pp == 0 (sync_regs)");
1568c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->gbea == 1, "gbea == 1 (sync_regs)");
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* kvm_run */
1598c2ecf20Sopenharmony_ci	TEST_ASSERT(run->psw_addr == 0, "psw_addr == 0 (kvm_run)");
1608c2ecf20Sopenharmony_ci	TEST_ASSERT(run->psw_mask == 0, "psw_mask == 0 (kvm_run)");
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	vcpu_fpu_get(vm, VCPU_ID, &fpu);
1638c2ecf20Sopenharmony_ci	TEST_ASSERT(!fpu.fpc, "fpc == 0");
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	test_one_reg(KVM_REG_S390_GBEA, 1);
1668c2ecf20Sopenharmony_ci	test_one_reg(KVM_REG_S390_PP, 0);
1678c2ecf20Sopenharmony_ci	test_one_reg(KVM_REG_S390_TODPR, 0);
1688c2ecf20Sopenharmony_ci	test_one_reg(KVM_REG_S390_CPU_TIMER, 0);
1698c2ecf20Sopenharmony_ci	test_one_reg(KVM_REG_S390_CLOCK_COMP, 0);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic void assert_normal_noclear(void)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->crs[2] == 0x10, "cr2 == 10 (sync_regs)");
1758c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->crs[8] == 1, "cr10 == 1 (sync_regs)");
1768c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->crs[10] == 1, "cr10 == 1 (sync_regs)");
1778c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->crs[11] == -1, "cr11 == -1 (sync_regs)");
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic void assert_normal(void)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	test_one_reg(KVM_REG_S390_PFTOKEN, KVM_S390_PFAULT_TOKEN_INVALID);
1838c2ecf20Sopenharmony_ci	TEST_ASSERT(sync_regs->pft == KVM_S390_PFAULT_TOKEN_INVALID,
1848c2ecf20Sopenharmony_ci			"pft == 0xff.....  (sync_regs)");
1858c2ecf20Sopenharmony_ci	assert_noirq();
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void inject_irq(int cpu_id)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct kvm_s390_irq_state irq_state;
1918c2ecf20Sopenharmony_ci	struct kvm_s390_irq *irq = &buf[0];
1928c2ecf20Sopenharmony_ci	int irqs;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* Inject IRQ */
1958c2ecf20Sopenharmony_ci	irq_state.len = sizeof(struct kvm_s390_irq);
1968c2ecf20Sopenharmony_ci	irq_state.buf = (unsigned long)buf;
1978c2ecf20Sopenharmony_ci	irq->type = KVM_S390_INT_EMERGENCY;
1988c2ecf20Sopenharmony_ci	irq->u.emerg.code = cpu_id;
1998c2ecf20Sopenharmony_ci	irqs = _vcpu_ioctl(vm, cpu_id, KVM_S390_SET_IRQ_STATE, &irq_state);
2008c2ecf20Sopenharmony_ci	TEST_ASSERT(irqs >= 0, "Error injecting EMERGENCY IRQ errno %d\n", errno);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic void test_normal(void)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	pr_info("Testing normal reset\n");
2068c2ecf20Sopenharmony_ci	/* Create VM */
2078c2ecf20Sopenharmony_ci	vm = vm_create_default(VCPU_ID, 0, guest_code_initial);
2088c2ecf20Sopenharmony_ci	run = vcpu_state(vm, VCPU_ID);
2098c2ecf20Sopenharmony_ci	sync_regs = &run->s.regs;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	vcpu_run(vm, VCPU_ID);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	inject_irq(VCPU_ID);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	vcpu_ioctl(vm, VCPU_ID, KVM_S390_NORMAL_RESET, 0);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* must clears */
2188c2ecf20Sopenharmony_ci	assert_normal();
2198c2ecf20Sopenharmony_ci	/* must not clears */
2208c2ecf20Sopenharmony_ci	assert_normal_noclear();
2218c2ecf20Sopenharmony_ci	assert_initial_noclear();
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	kvm_vm_free(vm);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic void test_initial(void)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	pr_info("Testing initial reset\n");
2298c2ecf20Sopenharmony_ci	vm = vm_create_default(VCPU_ID, 0, guest_code_initial);
2308c2ecf20Sopenharmony_ci	run = vcpu_state(vm, VCPU_ID);
2318c2ecf20Sopenharmony_ci	sync_regs = &run->s.regs;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	vcpu_run(vm, VCPU_ID);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	inject_irq(VCPU_ID);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	vcpu_ioctl(vm, VCPU_ID, KVM_S390_INITIAL_RESET, 0);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* must clears */
2408c2ecf20Sopenharmony_ci	assert_normal();
2418c2ecf20Sopenharmony_ci	assert_initial();
2428c2ecf20Sopenharmony_ci	/* must not clears */
2438c2ecf20Sopenharmony_ci	assert_initial_noclear();
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	kvm_vm_free(vm);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void test_clear(void)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	pr_info("Testing clear reset\n");
2518c2ecf20Sopenharmony_ci	vm = vm_create_default(VCPU_ID, 0, guest_code_initial);
2528c2ecf20Sopenharmony_ci	run = vcpu_state(vm, VCPU_ID);
2538c2ecf20Sopenharmony_ci	sync_regs = &run->s.regs;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	vcpu_run(vm, VCPU_ID);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	inject_irq(VCPU_ID);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	vcpu_ioctl(vm, VCPU_ID, KVM_S390_CLEAR_RESET, 0);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/* must clears */
2628c2ecf20Sopenharmony_ci	assert_normal();
2638c2ecf20Sopenharmony_ci	assert_initial();
2648c2ecf20Sopenharmony_ci	assert_clear();
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	kvm_vm_free(vm);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ciint main(int argc, char *argv[])
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	setbuf(stdout, NULL);	/* Tell stdout not to buffer its content */
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	test_initial();
2748c2ecf20Sopenharmony_ci	if (kvm_check_cap(KVM_CAP_S390_VCPU_RESETS)) {
2758c2ecf20Sopenharmony_ci		test_normal();
2768c2ecf20Sopenharmony_ci		test_clear();
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci	return 0;
2798c2ecf20Sopenharmony_ci}
280