xref: /kvm-unit-tests/s390x/skey.c (revision fdab948bc134fb9989a8265380a55e809879418e)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Storage key tests
4  *
5  * Copyright (c) 2018 IBM Corp
6  *
7  * Authors:
8  *  Janosch Frank <frankja@linux.vnet.ibm.com>
9  */
10 #include <libcflat.h>
11 #include <asm/asm-offsets.h>
12 #include <asm/interrupt.h>
13 #include <asm/page.h>
14 #include <asm/facility.h>
15 #include <asm/mem.h>
16 
17 
18 static uint8_t pagebuf[PAGE_SIZE * 2] __attribute__((aligned(PAGE_SIZE * 2)));
19 
20 static void test_set_mb(void)
21 {
22 	union skey skey, ret1, ret2;
23 	void *addr = (void *)0x10000 - 2 * PAGE_SIZE;
24 	void *end = (void *)0x10000;
25 
26 	/* Multi block support came with EDAT 1 */
27 	if (!test_facility(8))
28 		return;
29 
30 	skey.val = 0x30;
31 	while (addr < end)
32 		addr = set_storage_key_mb(addr, skey.val);
33 
34 	ret1.val = get_storage_key(end - PAGE_SIZE) & (SKEY_ACC | SKEY_FP);
35 	ret2.val = get_storage_key(end - PAGE_SIZE * 2) & (SKEY_ACC | SKEY_FP);
36 	report(ret1.val == ret2.val && ret1.val == skey.val, "multi block");
37 }
38 
39 static void test_chg(void)
40 {
41 	union skey skey1, skey2;
42 
43 	skey1.val = 0x30;
44 	set_storage_key(pagebuf, skey1.val, 0);
45 	skey1.val = get_storage_key(pagebuf);
46 	pagebuf[0] = 3;
47 	skey2.val = get_storage_key(pagebuf);
48 	report(!skey1.str.ch && skey2.str.ch, "chg bit test");
49 }
50 
51 static void test_set(void)
52 {
53 	union skey skey, ret;
54 
55 	skey.val = 0x30;
56 	ret.val = get_storage_key(pagebuf);
57 	set_storage_key(pagebuf, skey.val, 0);
58 	ret.val = get_storage_key(pagebuf);
59 	/*
60 	 * For all set tests we only test the ACC and FP bits. RF and
61 	 * CH are set by the machine for memory references and changes
62 	 * and hence might change between a set and a get.
63 	 */
64 	report(skey.str.acc == ret.str.acc && skey.str.fp == ret.str.fp,
65 	       "set key test");
66 }
67 
68 /* Returns true if we are running under z/VM 6.x */
69 static bool check_for_zvm6(void)
70 {
71 	int dcbt;	/* Descriptor block count */
72 	int nr;
73 	static const unsigned char zvm6[] = {
74 		/* This is "z/VM    6" in EBCDIC */
75 		0xa9, 0x61, 0xe5, 0xd4, 0x40, 0x40, 0x40, 0x40, 0xf6
76 	};
77 
78 	if (stsi(pagebuf, 3, 2, 2))
79 		return false;
80 
81 	dcbt = pagebuf[31] & 0xf;
82 
83 	for (nr = 0; nr < dcbt; nr++) {
84 		if (!memcmp(&pagebuf[32 + nr * 64 + 24], zvm6, sizeof(zvm6)))
85 			return true;
86 	}
87 
88 	return false;
89 }
90 
91 static void test_priv(void)
92 {
93 	union skey skey;
94 	bool is_zvm6 = check_for_zvm6();
95 
96 	memset(pagebuf, 0, PAGE_SIZE * 2);
97 	report_prefix_push("privileged");
98 	report_prefix_push("sske");
99 	expect_pgm_int();
100 	enter_pstate();
101 	set_storage_key(pagebuf, 0x30, 0);
102 	check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
103 	report_prefix_pop();
104 
105 	skey.val = get_storage_key(pagebuf);
106 	report(skey.str.acc != 3, "skey did not change on exception");
107 
108 	report_prefix_push("iske");
109 	if (is_zvm6) {
110 		/* There is a known bug with z/VM 6, so skip the test there */
111 		report_skip("not working on z/VM 6");
112 	} else {
113 		expect_pgm_int();
114 		enter_pstate();
115 		get_storage_key(pagebuf);
116 		check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
117 	}
118 	report_prefix_pop();
119 
120 	report_prefix_pop();
121 }
122 
123 int main(void)
124 {
125 	report_prefix_push("skey");
126 	if (test_facility(169)) {
127 		report_skip("storage key removal facility is active");
128 		goto done;
129 	}
130 	test_priv();
131 	test_set();
132 	test_set_mb();
133 	test_chg();
134 done:
135 	report_prefix_pop();
136 	return report_summary();
137 }
138