|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | +/* |
| 3 | + * Test for s390x CPU resets |
| 4 | + * |
| 5 | + * Copyright (C) 2020, IBM |
| 6 | + */ |
| 7 | + |
| 8 | +#include <stdio.h> |
| 9 | +#include <stdlib.h> |
| 10 | +#include <string.h> |
| 11 | +#include <sys/ioctl.h> |
| 12 | + |
| 13 | +#include "test_util.h" |
| 14 | +#include "kvm_util.h" |
| 15 | + |
| 16 | +#define VCPU_ID 3 |
| 17 | + |
| 18 | +struct kvm_vm *vm; |
| 19 | +struct kvm_run *run; |
| 20 | +struct kvm_sync_regs *regs; |
| 21 | +static uint64_t regs_null[16]; |
| 22 | + |
| 23 | +static uint64_t crs[16] = { 0x40000ULL, |
| 24 | + 0x42000ULL, |
| 25 | + 0, 0, 0, 0, 0, |
| 26 | + 0x43000ULL, |
| 27 | + 0, 0, 0, 0, 0, |
| 28 | + 0x44000ULL, |
| 29 | + 0, 0 |
| 30 | +}; |
| 31 | + |
| 32 | +static void guest_code_initial(void) |
| 33 | +{ |
| 34 | + /* Round toward 0 */ |
| 35 | + uint32_t fpc = 0x11; |
| 36 | + |
| 37 | + /* Dirty registers */ |
| 38 | + asm volatile ( |
| 39 | + " lctlg 0,15,%0\n" |
| 40 | + " sfpc %1\n" |
| 41 | + : : "Q" (crs), "d" (fpc)); |
| 42 | + GUEST_SYNC(0); |
| 43 | +} |
| 44 | + |
| 45 | +static void test_one_reg(uint64_t id, uint64_t value) |
| 46 | +{ |
| 47 | + struct kvm_one_reg reg; |
| 48 | + uint64_t eval_reg; |
| 49 | + |
| 50 | + reg.addr = (uintptr_t)&eval_reg; |
| 51 | + reg.id = id; |
| 52 | + vcpu_get_reg(vm, VCPU_ID, ®); |
| 53 | + TEST_ASSERT(eval_reg == value, "value == %s", value); |
| 54 | +} |
| 55 | + |
| 56 | +static void assert_clear(void) |
| 57 | +{ |
| 58 | + struct kvm_sregs sregs; |
| 59 | + struct kvm_regs regs; |
| 60 | + struct kvm_fpu fpu; |
| 61 | + |
| 62 | + vcpu_regs_get(vm, VCPU_ID, ®s); |
| 63 | + TEST_ASSERT(!memcmp(®s.gprs, regs_null, sizeof(regs.gprs)), "grs == 0"); |
| 64 | + |
| 65 | + vcpu_sregs_get(vm, VCPU_ID, &sregs); |
| 66 | + TEST_ASSERT(!memcmp(&sregs.acrs, regs_null, sizeof(sregs.acrs)), "acrs == 0"); |
| 67 | + |
| 68 | + vcpu_fpu_get(vm, VCPU_ID, &fpu); |
| 69 | + TEST_ASSERT(!memcmp(&fpu.fprs, regs_null, sizeof(fpu.fprs)), "fprs == 0"); |
| 70 | +} |
| 71 | + |
| 72 | +static void assert_initial(void) |
| 73 | +{ |
| 74 | + struct kvm_sregs sregs; |
| 75 | + struct kvm_fpu fpu; |
| 76 | + |
| 77 | + vcpu_sregs_get(vm, VCPU_ID, &sregs); |
| 78 | + TEST_ASSERT(sregs.crs[0] == 0xE0UL, "cr0 == 0xE0"); |
| 79 | + TEST_ASSERT(sregs.crs[14] == 0xC2000000UL, "cr14 == 0xC2000000"); |
| 80 | + TEST_ASSERT(!memcmp(&sregs.crs[1], regs_null, sizeof(sregs.crs[1]) * 12), |
| 81 | + "cr1-13 == 0"); |
| 82 | + TEST_ASSERT(sregs.crs[15] == 0, "cr15 == 0"); |
| 83 | + |
| 84 | + vcpu_fpu_get(vm, VCPU_ID, &fpu); |
| 85 | + TEST_ASSERT(!fpu.fpc, "fpc == 0"); |
| 86 | + |
| 87 | + test_one_reg(KVM_REG_S390_GBEA, 1); |
| 88 | + test_one_reg(KVM_REG_S390_PP, 0); |
| 89 | + test_one_reg(KVM_REG_S390_TODPR, 0); |
| 90 | + test_one_reg(KVM_REG_S390_CPU_TIMER, 0); |
| 91 | + test_one_reg(KVM_REG_S390_CLOCK_COMP, 0); |
| 92 | +} |
| 93 | + |
| 94 | +static void assert_normal(void) |
| 95 | +{ |
| 96 | + test_one_reg(KVM_REG_S390_PFTOKEN, KVM_S390_PFAULT_TOKEN_INVALID); |
| 97 | +} |
| 98 | + |
| 99 | +static void test_normal(void) |
| 100 | +{ |
| 101 | + printf("Testing normal reset\n"); |
| 102 | + /* Create VM */ |
| 103 | + vm = vm_create_default(VCPU_ID, 0, guest_code_initial); |
| 104 | + run = vcpu_state(vm, VCPU_ID); |
| 105 | + regs = &run->s.regs; |
| 106 | + |
| 107 | + vcpu_run(vm, VCPU_ID); |
| 108 | + |
| 109 | + vcpu_ioctl(vm, VCPU_ID, KVM_S390_NORMAL_RESET, 0); |
| 110 | + assert_normal(); |
| 111 | + kvm_vm_free(vm); |
| 112 | +} |
| 113 | + |
| 114 | +static void test_initial(void) |
| 115 | +{ |
| 116 | + printf("Testing initial reset\n"); |
| 117 | + vm = vm_create_default(VCPU_ID, 0, guest_code_initial); |
| 118 | + run = vcpu_state(vm, VCPU_ID); |
| 119 | + regs = &run->s.regs; |
| 120 | + |
| 121 | + vcpu_run(vm, VCPU_ID); |
| 122 | + |
| 123 | + vcpu_ioctl(vm, VCPU_ID, KVM_S390_INITIAL_RESET, 0); |
| 124 | + assert_normal(); |
| 125 | + assert_initial(); |
| 126 | + kvm_vm_free(vm); |
| 127 | +} |
| 128 | + |
| 129 | +static void test_clear(void) |
| 130 | +{ |
| 131 | + printf("Testing clear reset\n"); |
| 132 | + vm = vm_create_default(VCPU_ID, 0, guest_code_initial); |
| 133 | + run = vcpu_state(vm, VCPU_ID); |
| 134 | + regs = &run->s.regs; |
| 135 | + |
| 136 | + vcpu_run(vm, VCPU_ID); |
| 137 | + |
| 138 | + vcpu_ioctl(vm, VCPU_ID, KVM_S390_CLEAR_RESET, 0); |
| 139 | + assert_normal(); |
| 140 | + assert_initial(); |
| 141 | + assert_clear(); |
| 142 | + kvm_vm_free(vm); |
| 143 | +} |
| 144 | + |
| 145 | +int main(int argc, char *argv[]) |
| 146 | +{ |
| 147 | + setbuf(stdout, NULL); /* Tell stdout not to buffer its content */ |
| 148 | + |
| 149 | + test_initial(); |
| 150 | + if (kvm_check_cap(KVM_CAP_S390_VCPU_RESETS)) { |
| 151 | + test_normal(); |
| 152 | + test_clear(); |
| 153 | + } |
| 154 | + return 0; |
| 155 | +} |
0 commit comments