xref: /kvm-unit-tests/s390x/skey.c (revision dbd3800490429358367c717669aab76678429ef1)
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 static void test_invalid_address(void)
124 {
125 	void *inv_addr = (void *)-1ull;
126 
127 	report_prefix_push("invalid address");
128 
129 	report_prefix_push("sske");
130 	expect_pgm_int();
131 	set_storage_key(inv_addr, 0, 0);
132 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
133 	report_prefix_pop();
134 
135 	report_prefix_push("iske");
136 	expect_pgm_int();
137 	get_storage_key(inv_addr);
138 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
139 	report_prefix_pop();
140 
141 	report_prefix_push("rrbe");
142 	expect_pgm_int();
143 	reset_reference_bit(inv_addr);
144 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
145 	report_prefix_pop();
146 
147 	report_prefix_pop();
148 }
149 
150 int main(void)
151 {
152 	report_prefix_push("skey");
153 	if (test_facility(169)) {
154 		report_skip("storage key removal facility is active");
155 		goto done;
156 	}
157 	test_priv();
158 	test_invalid_address();
159 	test_set();
160 	test_set_mb();
161 	test_chg();
162 done:
163 	report_prefix_pop();
164 	return report_summary();
165 }
166