blob: ea41b455c1cfc7513af353b21795422193c9eb19 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Diagnose 0x308 hypercall tests
*
* Copyright (c) 2019 Thomas Huth, Red Hat Inc.
*/
#include <libcflat.h>
#include <asm/asm-offsets.h>
#include <asm/interrupt.h>
#include <hardware.h>
/* The diagnose calls should be blocked in problem state */
static void test_priv(void)
{
expect_pgm_int();
enter_pstate();
asm volatile ("diag %0,%1,0x308" :: "d"(0), "d"(3));
check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
}
/*
* Check that diag308 with subcode 0 and 1 loads the PSW at address 0, i.e.
* that we can put a pointer into address 4 which then gets executed.
*/
extern int diag308_load_reset(u64);
static void test_subcode0(void)
{
report(diag308_load_reset(0), "load modified clear done");
}
static void test_subcode1(void)
{
report(diag308_load_reset(1), "load normal reset done");
}
/* Expect a specification exception when using an uneven register */
static void test_uneven_reg(unsigned int subcode)
{
register unsigned long sc asm("6") = subcode;
register unsigned long r3 asm("9") = 0x2000;
report_prefix_push("uneven register");
expect_pgm_int();
asm volatile ("diag %0,%1,0x308" :: "d"(r3), "d"(sc));
check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
report_prefix_pop();
}
/* Expect a specification exception when using an unaligned address */
static void test_unaligned_address(unsigned int subcode)
{
register unsigned long sc asm("6") = subcode;
register unsigned long addr asm("8") = 54321;
report_prefix_push("unaligned address");
expect_pgm_int();
asm volatile ("diag %0,%1,0x308" :: "d"(addr), "d"(sc));
check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
report_prefix_pop();
}
static void test_subcode5(void)
{
test_uneven_reg(5);
test_unaligned_address(5);
}
static void test_subcode6(void)
{
test_uneven_reg(6);
test_unaligned_address(6);
}
/* Unsupported subcodes should generate a specification exception */
static void test_unsupported_subcode(void)
{
int subcodes[] = { 0x101, 0xffff, 0x10001, -1 };
int idx;
for (idx = 0; idx < ARRAY_SIZE(subcodes); idx++) {
report_prefix_pushf("0x%04x", subcodes[idx]);
expect_pgm_int();
asm volatile ("diag %0,%1,0x308" :: "d"(0), "d"(subcodes[idx]));
check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
report_prefix_pop();
}
/*
* Subcode 2 is not available under QEMU but might be on other
* hypervisors so we only check for the specification
* exception on QEMU.
*/
report_prefix_pushf("0x%04x", 2);
if (host_is_qemu()) {
expect_pgm_int();
asm volatile ("diag %0,%1,0x308" :: "d"(0), "d"(2));
check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
} else {
report_skip("subcode is supported");
}
report_prefix_pop();
}
static struct {
const char *name;
void (*func)(void);
} tests[] = {
{ "privileged", test_priv },
{ "subcode 0", test_subcode0 },
{ "subcode 1", test_subcode1 },
{ "subcode 5", test_subcode5 },
{ "subcode 6", test_subcode6 },
{ "unsupported", test_unsupported_subcode },
{ NULL, NULL }
};
int main(int argc, char**argv)
{
int i;
report_prefix_push("diag308");
for (i = 0; tests[i].name; i++) {
report_prefix_push(tests[i].name);
tests[i].func();
report_prefix_pop();
}
report_prefix_pop();
return report_summary();
}