| // 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; | |
| } |