linux/tools/testing/selftests/kvm/lib/s390x/diag318_test_handler.c
Collin Walling efaa83a373 KVM: selftests: sync_regs test for diag318
The DIAGNOSE 0x0318 instruction, unique to s390x, is a privileged call
that must be intercepted via SIE, handled in userspace, and the
information set by the instruction is communicated back to KVM.

To test the instruction interception, an ad-hoc handler is defined which
simply has a VM execute the instruction and then userspace will extract
the necessary info. The handler is defined such that the instruction
invocation occurs only once. It is up to the caller to determine how the
info returned by this handler should be used.

The diag318 info is communicated from userspace to KVM via a sync_regs
call. This is tested during a sync_regs test, where the diag318 info is
requested via the handler, then the info is stored in the appropriate
register in KVM via a sync registers call.

If KVM does not support diag318, then the tests will print a message
stating that diag318 was skipped, and the asserts will simply test
against a value of 0.

Signed-off-by: Collin Walling <walling@linux.ibm.com>
Link: https://lore.kernel.org/r/20201207154125.10322-1-walling@linux.ibm.com
Acked-by: Janosch Frank <frankja@linux.ibm.com>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
2020-12-10 13:36:05 +01:00

83 lines
2.0 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Test handler for the s390x DIAGNOSE 0x0318 instruction.
*
* Copyright (C) 2020, IBM
*/
#include "test_util.h"
#include "kvm_util.h"
#define VCPU_ID 6
#define ICPT_INSTRUCTION 0x04
#define IPA0_DIAG 0x8300
static void guest_code(void)
{
uint64_t diag318_info = 0x12345678;
asm volatile ("diag %0,0,0x318\n" : : "d" (diag318_info));
}
/*
* The DIAGNOSE 0x0318 instruction call must be handled via userspace. As such,
* we create an ad-hoc VM here to handle the instruction then extract the
* necessary data. It is up to the caller to decide what to do with that data.
*/
static uint64_t diag318_handler(void)
{
struct kvm_vm *vm;
struct kvm_run *run;
uint64_t reg;
uint64_t diag318_info;
vm = vm_create_default(VCPU_ID, 0, guest_code);
vcpu_run(vm, VCPU_ID);
run = vcpu_state(vm, VCPU_ID);
TEST_ASSERT(run->exit_reason == KVM_EXIT_S390_SIEIC,
"DIAGNOSE 0x0318 instruction was not intercepted");
TEST_ASSERT(run->s390_sieic.icptcode == ICPT_INSTRUCTION,
"Unexpected intercept code: 0x%x", run->s390_sieic.icptcode);
TEST_ASSERT((run->s390_sieic.ipa & 0xff00) == IPA0_DIAG,
"Unexpected IPA0 code: 0x%x", (run->s390_sieic.ipa & 0xff00));
reg = (run->s390_sieic.ipa & 0x00f0) >> 4;
diag318_info = run->s.regs.gprs[reg];
TEST_ASSERT(diag318_info != 0, "DIAGNOSE 0x0318 info not set");
kvm_vm_free(vm);
return diag318_info;
}
uint64_t get_diag318_info(void)
{
static uint64_t diag318_info;
static bool printed_skip;
/*
* If KVM does not support diag318, then return 0 to
* ensure tests do not break.
*/
if (!kvm_check_cap(KVM_CAP_S390_DIAG318)) {
if (!printed_skip) {
fprintf(stdout, "KVM_CAP_S390_DIAG318 not supported. "
"Skipping diag318 test.\n");
printed_skip = true;
}
return 0;
}
/*
* If a test has previously requested the diag318 info,
* then don't bother spinning up a temporary VM again.
*/
if (!diag318_info)
diag318_info = diag318_handler();
return diag318_info;
}