| /* 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(); |
| } |