// SPDX-License-Identifier: GPL-2.0-or-later /* * Test for s390x KVM_S390_MEM_OP * * Copyright (C) 2019, Red Hat, Inc. */ #include #include #include #include #include "test_util.h" #include "kvm_util.h" #define VCPU_ID 1 static uint8_t mem1[65536]; static uint8_t mem2[65536]; static void guest_code(void) { int i; for (;;) { for (i = 0; i < sizeof(mem2); i++) mem2[i] = mem1[i]; GUEST_SYNC(0); } } int main(int argc, char *argv[]) { struct kvm_vm *vm; struct kvm_run *run; struct kvm_s390_mem_op ksmo; int rv, i, maxsize; setbuf(stdout, NULL); /* Tell stdout not to buffer its content */ maxsize = kvm_check_cap(KVM_CAP_S390_MEM_OP); if (!maxsize) { print_skip("CAP_S390_MEM_OP not supported"); exit(KSFT_SKIP); } if (maxsize > sizeof(mem1)) maxsize = sizeof(mem1); /* Create VM */ vm = vm_create_default(VCPU_ID, 0, guest_code); run = vcpu_state(vm, VCPU_ID); for (i = 0; i < sizeof(mem1); i++) mem1[i] = i * i + i; /* Set the first array */ ksmo.gaddr = addr_gva2gpa(vm, (uintptr_t)mem1); ksmo.flags = 0; ksmo.size = maxsize; ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE; ksmo.buf = (uintptr_t)mem1; ksmo.ar = 0; vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo); /* Let the guest code copy the first array to the second */ vcpu_run(vm, VCPU_ID); TEST_ASSERT(run->exit_reason == KVM_EXIT_S390_SIEIC, "Unexpected exit reason: %u (%s)\n", run->exit_reason, exit_reason_str(run->exit_reason)); memset(mem2, 0xaa, sizeof(mem2)); /* Get the second array */ ksmo.gaddr = (uintptr_t)mem2; ksmo.flags = 0; ksmo.size = maxsize; ksmo.op = KVM_S390_MEMOP_LOGICAL_READ; ksmo.buf = (uintptr_t)mem2; ksmo.ar = 0; vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo); TEST_ASSERT(!memcmp(mem1, mem2, maxsize), "Memory contents do not match!"); /* Check error conditions - first bad size: */ ksmo.gaddr = (uintptr_t)mem1; ksmo.flags = 0; ksmo.size = -1; ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE; ksmo.buf = (uintptr_t)mem1; ksmo.ar = 0; rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo); TEST_ASSERT(rv == -1 && errno == E2BIG, "ioctl allows insane sizes"); /* Zero size: */ ksmo.gaddr = (uintptr_t)mem1; ksmo.flags = 0; ksmo.size = 0; ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE; ksmo.buf = (uintptr_t)mem1; ksmo.ar = 0; rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo); TEST_ASSERT(rv == -1 && (errno == EINVAL || errno == ENOMEM), "ioctl allows 0 as size"); /* Bad flags: */ ksmo.gaddr = (uintptr_t)mem1; ksmo.flags = -1; ksmo.size = maxsize; ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE; ksmo.buf = (uintptr_t)mem1; ksmo.ar = 0; rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo); TEST_ASSERT(rv == -1 && errno == EINVAL, "ioctl allows all flags"); /* Bad operation: */ ksmo.gaddr = (uintptr_t)mem1; ksmo.flags = 0; ksmo.size = maxsize; ksmo.op = -1; ksmo.buf = (uintptr_t)mem1; ksmo.ar = 0; rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo); TEST_ASSERT(rv == -1 && errno == EINVAL, "ioctl allows bad operations"); /* Bad guest address: */ ksmo.gaddr = ~0xfffUL; ksmo.flags = KVM_S390_MEMOP_F_CHECK_ONLY; ksmo.size = maxsize; ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE; ksmo.buf = (uintptr_t)mem1; ksmo.ar = 0; rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo); TEST_ASSERT(rv > 0, "ioctl does not report bad guest memory access"); /* Bad host address: */ ksmo.gaddr = (uintptr_t)mem1; ksmo.flags = 0; ksmo.size = maxsize; ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE; ksmo.buf = 0; ksmo.ar = 0; rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo); TEST_ASSERT(rv == -1 && errno == EFAULT, "ioctl does not report bad host memory address"); /* Bad access register: */ run->psw_mask &= ~(3UL << (63 - 17)); run->psw_mask |= 1UL << (63 - 17); /* Enable AR mode */ vcpu_run(vm, VCPU_ID); /* To sync new state to SIE block */ ksmo.gaddr = (uintptr_t)mem1; ksmo.flags = 0; ksmo.size = maxsize; ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE; ksmo.buf = (uintptr_t)mem1; ksmo.ar = 17; rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo); TEST_ASSERT(rv == -1 && errno == EINVAL, "ioctl allows ARs > 15"); run->psw_mask &= ~(3UL << (63 - 17)); /* Disable AR mode */ vcpu_run(vm, VCPU_ID); /* Run to sync new state */ kvm_vm_free(vm); return 0; }