| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * Test for s390x KVM_S390_MEM_OP |
| * |
| * Copyright (C) 2019, Red Hat, Inc. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| |
| #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; |
| } |