| /* SPDX-License-Identifier: GPL-2.0-only */ |
| /* |
| * Storage key tests |
| * |
| * Copyright (c) 2018 IBM Corp |
| * |
| * Authors: |
| * Janosch Frank <frankja@linux.vnet.ibm.com> |
| */ |
| #include <libcflat.h> |
| #include <asm/asm-offsets.h> |
| #include <asm/interrupt.h> |
| #include <asm/page.h> |
| #include <asm/facility.h> |
| #include <asm/mem.h> |
| |
| |
| static uint8_t pagebuf[PAGE_SIZE * 2] __attribute__((aligned(PAGE_SIZE * 2))); |
| |
| static void test_set_mb(void) |
| { |
| union skey skey, ret1, ret2; |
| void *addr = (void *)0x10000 - 2 * PAGE_SIZE; |
| void *end = (void *)0x10000; |
| |
| /* Multi block support came with EDAT 1 */ |
| if (!test_facility(8)) |
| return; |
| |
| skey.val = 0x30; |
| while (addr < end) |
| addr = set_storage_key_mb(addr, skey.val); |
| |
| ret1.val = get_storage_key(end - PAGE_SIZE) & (SKEY_ACC | SKEY_FP); |
| ret2.val = get_storage_key(end - PAGE_SIZE * 2) & (SKEY_ACC | SKEY_FP); |
| report(ret1.val == ret2.val && ret1.val == skey.val, "multi block"); |
| } |
| |
| static void test_chg(void) |
| { |
| union skey skey1, skey2; |
| |
| skey1.val = 0x30; |
| set_storage_key(pagebuf, skey1.val, 0); |
| skey1.val = get_storage_key(pagebuf); |
| pagebuf[0] = 3; |
| skey2.val = get_storage_key(pagebuf); |
| report(!skey1.str.ch && skey2.str.ch, "chg bit test"); |
| } |
| |
| static void test_set(void) |
| { |
| union skey skey, ret; |
| |
| skey.val = 0x30; |
| ret.val = get_storage_key(pagebuf); |
| set_storage_key(pagebuf, skey.val, 0); |
| ret.val = get_storage_key(pagebuf); |
| /* |
| * For all set tests we only test the ACC and FP bits. RF and |
| * CH are set by the machine for memory references and changes |
| * and hence might change between a set and a get. |
| */ |
| report(skey.str.acc == ret.str.acc && skey.str.fp == ret.str.fp, |
| "set key test"); |
| } |
| |
| static void test_priv(void) |
| { |
| union skey skey; |
| |
| memset(pagebuf, 0, PAGE_SIZE * 2); |
| report_prefix_push("privileged"); |
| report_prefix_push("sske"); |
| expect_pgm_int(); |
| enter_pstate(); |
| set_storage_key(pagebuf, 0x30, 0); |
| check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); |
| report_prefix_pop(); |
| |
| skey.val = get_storage_key(pagebuf); |
| report(skey.str.acc != 3, "skey did not change on exception"); |
| |
| report_prefix_push("iske"); |
| expect_pgm_int(); |
| enter_pstate(); |
| get_storage_key(pagebuf); |
| check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); |
| report_prefix_pop(); |
| |
| report_prefix_pop(); |
| } |
| |
| static void test_invalid_address(void) |
| { |
| void *inv_addr = (void *)-1ull; |
| |
| report_prefix_push("invalid address"); |
| |
| report_prefix_push("sske"); |
| expect_pgm_int(); |
| set_storage_key(inv_addr, 0, 0); |
| check_pgm_int_code(PGM_INT_CODE_ADDRESSING); |
| report_prefix_pop(); |
| |
| report_prefix_push("iske"); |
| expect_pgm_int(); |
| get_storage_key(inv_addr); |
| check_pgm_int_code(PGM_INT_CODE_ADDRESSING); |
| report_prefix_pop(); |
| |
| report_prefix_push("rrbe"); |
| expect_pgm_int(); |
| reset_reference_bit(inv_addr); |
| check_pgm_int_code(PGM_INT_CODE_ADDRESSING); |
| report_prefix_pop(); |
| |
| report_prefix_pop(); |
| } |
| |
| int main(void) |
| { |
| report_prefix_push("skey"); |
| if (test_facility(169)) { |
| report_skip("storage key removal facility is active"); |
| goto done; |
| } |
| test_priv(); |
| test_invalid_address(); |
| test_set(); |
| test_set_mb(); |
| test_chg(); |
| done: |
| report_prefix_pop(); |
| return report_summary(); |
| } |