blob: 110547ad6ced0d65ad4034f8fb374552dc3c4277 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* PV virtualization interception tests for diagnose instructions.
*
* Copyright (c) 2021 IBM Corp
*
* Authors:
* Janosch Frank <frankja@linux.ibm.com>
*/
#include <libcflat.h>
#include <asm/asm-offsets.h>
#include <asm-generic/barrier.h>
#include <asm/interrupt.h>
#include <asm/pgtable.h>
#include <mmu.h>
#include <asm/page.h>
#include <asm/facility.h>
#include <asm/mem.h>
#include <asm/sigp.h>
#include <smp.h>
#include <alloc_page.h>
#include <vm.h>
#include <vmalloc.h>
#include <sclp.h>
#include <snippet.h>
#include <sie.h>
#include <uv.h>
#include <asm/uv.h>
static struct vm vm;
static void test_diag_500(void)
{
extern const char SNIPPET_NAME_START(asm, snippet_pv_diag_500)[];
extern const char SNIPPET_NAME_END(asm, snippet_pv_diag_500)[];
extern const char SNIPPET_HDR_START(asm, snippet_pv_diag_500)[];
extern const char SNIPPET_HDR_END(asm, snippet_pv_diag_500)[];
int size_hdr = SNIPPET_HDR_LEN(asm, snippet_pv_diag_500);
int size_gbin = SNIPPET_LEN(asm, snippet_pv_diag_500);
report_prefix_push("diag 0x500");
snippet_pv_init(&vm, SNIPPET_NAME_START(asm, snippet_pv_diag_500),
SNIPPET_HDR_START(asm, snippet_pv_diag_500),
size_gbin, size_hdr, SNIPPET_OFF_ASM);
sie(&vm);
report(vm.sblk->icptcode == ICPT_PV_INSTR && vm.sblk->ipa == 0x8302 &&
vm.sblk->ipb == 0x50000000 && vm.save_area.guest.grs[5] == 0x500,
"intercept values");
report(vm.save_area.guest.grs[1] == 1 &&
vm.save_area.guest.grs[2] == 2 &&
vm.save_area.guest.grs[3] == 3 &&
vm.save_area.guest.grs[4] == 4,
"register values");
/*
* Check if we can inject a PGM operand which we are always
* allowed to do after a diag500 exit.
*/
vm.sblk->iictl = IICTL_CODE_OPERAND;
sie(&vm);
report(vm.sblk->icptcode == ICPT_PV_NOTIFY && vm.sblk->ipa == 0x8302 &&
vm.sblk->ipb == 0x50000000 && vm.save_area.guest.grs[5] == 0x9c
&& vm.save_area.guest.grs[0] == PGM_INT_CODE_OPERAND,
"operand exception");
/*
* Check if we can inject a PGM specification which we are always
* allowed to do after a diag500 exit.
*/
sie(&vm);
vm.sblk->iictl = IICTL_CODE_SPECIFICATION;
/* Inject PGM, next exit should be 9c */
sie(&vm);
report(vm.sblk->icptcode == ICPT_PV_NOTIFY && vm.sblk->ipa == 0x8302 &&
vm.sblk->ipb == 0x50000000 && vm.save_area.guest.grs[5] == 0x9c
&& vm.save_area.guest.grs[0] == PGM_INT_CODE_SPECIFICATION,
"specification exception");
/* No need for cleanup, just tear down the VM */
uv_destroy_guest(&vm);
report_prefix_pop();
}
static void test_diag_288(void)
{
extern const char SNIPPET_NAME_START(asm, snippet_pv_diag_288)[];
extern const char SNIPPET_NAME_END(asm, snippet_pv_diag_288)[];
extern const char SNIPPET_HDR_START(asm, snippet_pv_diag_288)[];
extern const char SNIPPET_HDR_END(asm, snippet_pv_diag_288)[];
int size_hdr = SNIPPET_HDR_LEN(asm, snippet_pv_diag_288);
int size_gbin = SNIPPET_LEN(asm, snippet_pv_diag_288);
report_prefix_push("diag 0x288");
snippet_pv_init(&vm, SNIPPET_NAME_START(asm, snippet_pv_diag_288),
SNIPPET_HDR_START(asm, snippet_pv_diag_288),
size_gbin, size_hdr, SNIPPET_OFF_ASM);
sie(&vm);
report(vm.sblk->icptcode == ICPT_PV_INSTR && vm.sblk->ipa == 0x8302 &&
vm.sblk->ipb == 0x50000000 && vm.save_area.guest.grs[5] == 0x288,
"intercept values");
report(vm.save_area.guest.grs[0] == 1 &&
vm.save_area.guest.grs[1] == 2 &&
vm.save_area.guest.grs[2] == 3,
"register values");
/*
* Check if we can inject a PGM spec which we are always
* allowed to do after a diag288 exit.
*/
vm.sblk->iictl = IICTL_CODE_SPECIFICATION;
sie(&vm);
report(vm.sblk->icptcode == ICPT_PV_NOTIFY && vm.sblk->ipa == 0x8302 &&
vm.sblk->ipb == 0x50000000 && vm.save_area.guest.grs[5] == 0x9c
&& vm.save_area.guest.grs[0] == PGM_INT_CODE_SPECIFICATION,
"specification exception");
/* No need for cleanup, just tear down the VM */
uv_destroy_guest(&vm);
report_prefix_pop();
}
static void test_diag_yield(void)
{
extern const char SNIPPET_NAME_START(asm, snippet_pv_diag_yield)[];
extern const char SNIPPET_NAME_END(asm, snippet_pv_diag_yield)[];
extern const char SNIPPET_HDR_START(asm, snippet_pv_diag_yield)[];
extern const char SNIPPET_HDR_END(asm, snippet_pv_diag_yield)[];
int size_hdr = SNIPPET_HDR_LEN(asm, snippet_pv_diag_yield);
int size_gbin = SNIPPET_LEN(asm, snippet_pv_diag_yield);
report_prefix_push("diag yield");
snippet_pv_init(&vm, SNIPPET_NAME_START(asm, snippet_pv_diag_yield),
SNIPPET_HDR_START(asm, snippet_pv_diag_yield),
size_gbin, size_hdr, SNIPPET_OFF_ASM);
/* 0x44 */
report_prefix_push("0x44");
sie(&vm);
report(vm.sblk->icptcode == ICPT_PV_NOTIFY && vm.sblk->ipa == 0x8302 &&
vm.sblk->ipb == 0x50000000 && vm.save_area.guest.grs[5] == 0x44,
"intercept values");
report_prefix_pop();
/* 0x9c */
report_prefix_push("0x9c");
sie(&vm);
report(vm.sblk->icptcode == ICPT_PV_NOTIFY && vm.sblk->ipa == 0x8302 &&
vm.sblk->ipb == 0x50000000 && vm.save_area.guest.grs[5] == 0x9c,
"intercept values");
report(vm.save_area.guest.grs[0] == 42, "r1 correct");
report_prefix_pop();
uv_destroy_guest(&vm);
report_prefix_pop();
}
int main(void)
{
report_prefix_push("pv-diags");
if (!test_facility(158)) {
report_skip("UV Call facility unavailable");
goto done;
}
if (!sclp_facilities.has_sief2) {
report_skip("SIEF2 facility unavailable");
goto done;
}
uv_setup_asces();
snippet_setup_guest(&vm, true);
test_diag_yield();
test_diag_288();
test_diag_500();
sie_guest_destroy(&vm);
done:
report_prefix_pop();
return report_summary();
}