blob: 25399443b14d1a167be0246675bdbf959e71155c [file] [log] [blame] [edit]
/* 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");
}
/* Returns true if we are running under z/VM 6.x */
static bool check_for_zvm6(void)
{
int dcbt; /* Descriptor block count */
int nr;
static const unsigned char zvm6[] = {
/* This is "z/VM 6" in EBCDIC */
0xa9, 0x61, 0xe5, 0xd4, 0x40, 0x40, 0x40, 0x40, 0xf6
};
if (stsi(pagebuf, 3, 2, 2))
return false;
dcbt = pagebuf[31] & 0xf;
for (nr = 0; nr < dcbt; nr++) {
if (!memcmp(&pagebuf[32 + nr * 64 + 24], zvm6, sizeof(zvm6)))
return true;
}
return false;
}
static void test_priv(void)
{
union skey skey;
bool is_zvm6 = check_for_zvm6();
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");
if (is_zvm6) {
/* There is a known bug with z/VM 6, so skip the test there */
report_skip("not working on z/VM 6");
} else {
expect_pgm_int();
enter_pstate();
get_storage_key(pagebuf);
check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
}
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_set();
test_set_mb();
test_chg();
done:
report_prefix_pop();
return report_summary();
}