xref: /kvm-unit-tests/s390x/skey.c (revision e11a0e2f881d7bc038f44d8d4f99b2d55a01bc4e)
1fa624cc2SJanosch Frank /* SPDX-License-Identifier: GPL-2.0-only */
2f6b354fbSJanosch Frank /*
3f6b354fbSJanosch Frank  * Storage key tests
4f6b354fbSJanosch Frank  *
5f6b354fbSJanosch Frank  * Copyright (c) 2018 IBM Corp
6f6b354fbSJanosch Frank  *
7f6b354fbSJanosch Frank  * Authors:
8f6b354fbSJanosch Frank  *  Janosch Frank <frankja@linux.vnet.ibm.com>
9f6b354fbSJanosch Frank  */
10f6b354fbSJanosch Frank #include <libcflat.h>
11b02598aeSJanis Schoetterl-Glausch #include <asm/arch_def.h>
12f6b354fbSJanosch Frank #include <asm/asm-offsets.h>
13f6b354fbSJanosch Frank #include <asm/interrupt.h>
1466abce92SJanis Schoetterl-Glausch #include <vmalloc.h>
156c3882f6SJanis Schoetterl-Glausch #include <css.h>
16f6b354fbSJanosch Frank #include <asm/page.h>
17f6b354fbSJanosch Frank #include <asm/facility.h>
18f6b354fbSJanosch Frank #include <asm/mem.h>
19f6b354fbSJanosch Frank 
20f6b354fbSJanosch Frank 
21f6b354fbSJanosch Frank static uint8_t pagebuf[PAGE_SIZE * 2] __attribute__((aligned(PAGE_SIZE * 2)));
22f6b354fbSJanosch Frank 
test_set_mb(void)23f6b354fbSJanosch Frank static void test_set_mb(void)
24f6b354fbSJanosch Frank {
25f6b354fbSJanosch Frank 	union skey skey, ret1, ret2;
267b9ca995SJanosch Frank 	void *addr = (void *)0x10000 - 2 * PAGE_SIZE;
277b9ca995SJanosch Frank 	void *end = (void *)0x10000;
28f6b354fbSJanosch Frank 
29f6b354fbSJanosch Frank 	/* Multi block support came with EDAT 1 */
30f6b354fbSJanosch Frank 	if (!test_facility(8))
31f6b354fbSJanosch Frank 		return;
32f6b354fbSJanosch Frank 
33f6b354fbSJanosch Frank 	skey.val = 0x30;
34f6b354fbSJanosch Frank 	while (addr < end)
35f6b354fbSJanosch Frank 		addr = set_storage_key_mb(addr, skey.val);
36f6b354fbSJanosch Frank 
37d0bfafdfSJanosch Frank 	ret1.val = get_storage_key(end - PAGE_SIZE) & (SKEY_ACC | SKEY_FP);
38d0bfafdfSJanosch Frank 	ret2.val = get_storage_key(end - PAGE_SIZE * 2) & (SKEY_ACC | SKEY_FP);
39a299895bSThomas Huth 	report(ret1.val == ret2.val && ret1.val == skey.val, "multi block");
40f6b354fbSJanosch Frank }
41f6b354fbSJanosch Frank 
test_chg(void)42f6b354fbSJanosch Frank static void test_chg(void)
43f6b354fbSJanosch Frank {
44f6b354fbSJanosch Frank 	union skey skey1, skey2;
45f6b354fbSJanosch Frank 
46f6b354fbSJanosch Frank 	skey1.val = 0x30;
477b9ca995SJanosch Frank 	set_storage_key(pagebuf, skey1.val, 0);
487b9ca995SJanosch Frank 	skey1.val = get_storage_key(pagebuf);
49f6b354fbSJanosch Frank 	pagebuf[0] = 3;
507b9ca995SJanosch Frank 	skey2.val = get_storage_key(pagebuf);
51a299895bSThomas Huth 	report(!skey1.str.ch && skey2.str.ch, "chg bit test");
52f6b354fbSJanosch Frank }
53f6b354fbSJanosch Frank 
test_set(void)54f6b354fbSJanosch Frank static void test_set(void)
55f6b354fbSJanosch Frank {
56f6b354fbSJanosch Frank 	union skey skey, ret;
57f6b354fbSJanosch Frank 
58f6b354fbSJanosch Frank 	skey.val = 0x30;
597b9ca995SJanosch Frank 	ret.val = get_storage_key(pagebuf);
607b9ca995SJanosch Frank 	set_storage_key(pagebuf, skey.val, 0);
617b9ca995SJanosch Frank 	ret.val = get_storage_key(pagebuf);
62d0bfafdfSJanosch Frank 	/*
63d0bfafdfSJanosch Frank 	 * For all set tests we only test the ACC and FP bits. RF and
64d0bfafdfSJanosch Frank 	 * CH are set by the machine for memory references and changes
65d0bfafdfSJanosch Frank 	 * and hence might change between a set and a get.
66d0bfafdfSJanosch Frank 	 */
67a299895bSThomas Huth 	report(skey.str.acc == ret.str.acc && skey.str.fp == ret.str.fp,
68a299895bSThomas Huth 	       "set key test");
69f6b354fbSJanosch Frank }
70f6b354fbSJanosch Frank 
test_priv(void)71f6b354fbSJanosch Frank static void test_priv(void)
72f6b354fbSJanosch Frank {
73f6b354fbSJanosch Frank 	union skey skey;
74f6b354fbSJanosch Frank 
75f6b354fbSJanosch Frank 	memset(pagebuf, 0, PAGE_SIZE * 2);
76e4654a1bSJanosch Frank 	report_prefix_push("privileged");
77e4654a1bSJanosch Frank 	report_prefix_push("sske");
78f6b354fbSJanosch Frank 	expect_pgm_int();
79f6b354fbSJanosch Frank 	enter_pstate();
807b9ca995SJanosch Frank 	set_storage_key(pagebuf, 0x30, 0);
81f6b354fbSJanosch Frank 	check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
82e4654a1bSJanosch Frank 	report_prefix_pop();
83f6b354fbSJanosch Frank 
847b9ca995SJanosch Frank 	skey.val = get_storage_key(pagebuf);
85a299895bSThomas Huth 	report(skey.str.acc != 3, "skey did not change on exception");
86f6b354fbSJanosch Frank 
87e4654a1bSJanosch Frank 	report_prefix_push("iske");
88f6b354fbSJanosch Frank 	expect_pgm_int();
89f6b354fbSJanosch Frank 	enter_pstate();
907b9ca995SJanosch Frank 	get_storage_key(pagebuf);
91f6b354fbSJanosch Frank 	check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
92e4654a1bSJanosch Frank 	report_prefix_pop();
93e4654a1bSJanosch Frank 
94e4654a1bSJanosch Frank 	report_prefix_pop();
95f6b354fbSJanosch Frank }
96f6b354fbSJanosch Frank 
test_invalid_address(void)97429c9cc2SDavid Hildenbrand static void test_invalid_address(void)
98429c9cc2SDavid Hildenbrand {
99429c9cc2SDavid Hildenbrand 	void *inv_addr = (void *)-1ull;
100429c9cc2SDavid Hildenbrand 
101429c9cc2SDavid Hildenbrand 	report_prefix_push("invalid address");
102429c9cc2SDavid Hildenbrand 
103429c9cc2SDavid Hildenbrand 	report_prefix_push("sske");
104429c9cc2SDavid Hildenbrand 	expect_pgm_int();
105429c9cc2SDavid Hildenbrand 	set_storage_key(inv_addr, 0, 0);
106429c9cc2SDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
107429c9cc2SDavid Hildenbrand 	report_prefix_pop();
108429c9cc2SDavid Hildenbrand 
109429c9cc2SDavid Hildenbrand 	report_prefix_push("iske");
110429c9cc2SDavid Hildenbrand 	expect_pgm_int();
111429c9cc2SDavid Hildenbrand 	get_storage_key(inv_addr);
112429c9cc2SDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
113429c9cc2SDavid Hildenbrand 	report_prefix_pop();
114429c9cc2SDavid Hildenbrand 
115429c9cc2SDavid Hildenbrand 	report_prefix_push("rrbe");
116429c9cc2SDavid Hildenbrand 	expect_pgm_int();
117429c9cc2SDavid Hildenbrand 	reset_reference_bit(inv_addr);
118429c9cc2SDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
119429c9cc2SDavid Hildenbrand 	report_prefix_pop();
120429c9cc2SDavid Hildenbrand 
121429c9cc2SDavid Hildenbrand 	report_prefix_pop();
122429c9cc2SDavid Hildenbrand }
123429c9cc2SDavid Hildenbrand 
test_test_protection(void)12466abce92SJanis Schoetterl-Glausch static void test_test_protection(void)
12566abce92SJanis Schoetterl-Glausch {
12666abce92SJanis Schoetterl-Glausch 	unsigned long addr = (unsigned long)pagebuf;
12766abce92SJanis Schoetterl-Glausch 
12866abce92SJanis Schoetterl-Glausch 	report_prefix_push("TPROT");
12966abce92SJanis Schoetterl-Glausch 
13066abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x10, 0);
13166abce92SJanis Schoetterl-Glausch 	report(tprot(addr, 0) == TPROT_READ_WRITE, "zero key: no protection");
13266abce92SJanis Schoetterl-Glausch 	report(tprot(addr, 1) == TPROT_READ_WRITE, "matching key: no protection");
13366abce92SJanis Schoetterl-Glausch 
13466abce92SJanis Schoetterl-Glausch 	report_prefix_push("mismatching key");
13566abce92SJanis Schoetterl-Glausch 
13666abce92SJanis Schoetterl-Glausch 	report(tprot(addr, 2) == TPROT_READ, "no fetch protection: store protection");
13766abce92SJanis Schoetterl-Glausch 
13866abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x18, 0);
13966abce92SJanis Schoetterl-Glausch 	report(tprot(addr, 2) == TPROT_RW_PROTECTED,
14066abce92SJanis Schoetterl-Glausch 	       "fetch protection: fetch & store protection");
14166abce92SJanis Schoetterl-Glausch 
14266abce92SJanis Schoetterl-Glausch 	report_prefix_push("fetch-protection override");
14366abce92SJanis Schoetterl-Glausch 	set_storage_key(0, 0x18, 0);
14466abce92SJanis Schoetterl-Glausch 	report(tprot(0, 2) == TPROT_RW_PROTECTED, "disabled: fetch & store protection");
14566abce92SJanis Schoetterl-Glausch 	ctl_set_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE);
14666abce92SJanis Schoetterl-Glausch 	report(tprot(0, 2) == TPROT_READ, "enabled: store protection");
14766abce92SJanis Schoetterl-Glausch 	report(tprot(2048, 2) == TPROT_RW_PROTECTED, "invalid: fetch & store protection");
14866abce92SJanis Schoetterl-Glausch 	ctl_clear_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE);
14966abce92SJanis Schoetterl-Glausch 	set_storage_key(0, 0x00, 0);
15066abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
15166abce92SJanis Schoetterl-Glausch 
15266abce92SJanis Schoetterl-Glausch 	ctl_set_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE);
15366abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x90, 0);
15466abce92SJanis Schoetterl-Glausch 	report(tprot(addr, 2) == TPROT_READ_WRITE,
15566abce92SJanis Schoetterl-Glausch 	       "storage-protection override: no protection");
15666abce92SJanis Schoetterl-Glausch 	ctl_clear_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE);
15766abce92SJanis Schoetterl-Glausch 
15866abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
15966abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x00, 0);
16066abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
16166abce92SJanis Schoetterl-Glausch }
16266abce92SJanis Schoetterl-Glausch 
163b02598aeSJanis Schoetterl-Glausch enum access {
164b02598aeSJanis Schoetterl-Glausch 	ACC_STORE = 1,
165b02598aeSJanis Schoetterl-Glausch 	ACC_FETCH = 2,
166b02598aeSJanis Schoetterl-Glausch 	ACC_UPDATE = 3,
167b02598aeSJanis Schoetterl-Glausch };
168b02598aeSJanis Schoetterl-Glausch 
169b02598aeSJanis Schoetterl-Glausch enum protection {
170b02598aeSJanis Schoetterl-Glausch 	PROT_STORE = 1,
171b02598aeSJanis Schoetterl-Glausch 	PROT_FETCH_STORE = 3,
172b02598aeSJanis Schoetterl-Glausch };
173b02598aeSJanis Schoetterl-Glausch 
check_key_prot_exc(enum access access,enum protection prot)174b02598aeSJanis Schoetterl-Glausch static void check_key_prot_exc(enum access access, enum protection prot)
175b02598aeSJanis Schoetterl-Glausch {
176b02598aeSJanis Schoetterl-Glausch 	union teid teid;
177b02598aeSJanis Schoetterl-Glausch 	int access_code;
178b02598aeSJanis Schoetterl-Glausch 
179b02598aeSJanis Schoetterl-Glausch 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
180b02598aeSJanis Schoetterl-Glausch 	report_prefix_push("TEID");
181b02598aeSJanis Schoetterl-Glausch 	teid.val = lowcore.trans_exc_id;
182b02598aeSJanis Schoetterl-Glausch 	switch (get_supp_on_prot_facility()) {
183b02598aeSJanis Schoetterl-Glausch 	case SOP_NONE:
184b02598aeSJanis Schoetterl-Glausch 	case SOP_BASIC:
185b02598aeSJanis Schoetterl-Glausch 		/* let's ignore ancient/irrelevant machines */
186b02598aeSJanis Schoetterl-Glausch 		break;
187b02598aeSJanis Schoetterl-Glausch 	case SOP_ENHANCED_1:
188b02598aeSJanis Schoetterl-Glausch 		report(!teid.sop_teid_predictable, "valid protection code");
189b02598aeSJanis Schoetterl-Glausch 		/* no access code in case of key protection */
190b02598aeSJanis Schoetterl-Glausch 		break;
191b02598aeSJanis Schoetterl-Glausch 	case SOP_ENHANCED_2:
192b02598aeSJanis Schoetterl-Glausch 		switch (teid_esop2_prot_code(teid)) {
193b02598aeSJanis Schoetterl-Glausch 		case PROT_KEY:
194b02598aeSJanis Schoetterl-Glausch 			/* ESOP-2: no need to check facility */
195b02598aeSJanis Schoetterl-Glausch 			access_code = teid.acc_exc_fetch_store;
196b02598aeSJanis Schoetterl-Glausch 
197b02598aeSJanis Schoetterl-Glausch 			switch (access_code) {
198b02598aeSJanis Schoetterl-Glausch 			case 0:
199b02598aeSJanis Schoetterl-Glausch 				report_pass("valid access code");
200b02598aeSJanis Schoetterl-Glausch 				break;
201b02598aeSJanis Schoetterl-Glausch 			case 1:
202b02598aeSJanis Schoetterl-Glausch 			case 2:
203b02598aeSJanis Schoetterl-Glausch 				report((access & access_code) && (prot & access_code),
204b02598aeSJanis Schoetterl-Glausch 				       "valid access code");
205b02598aeSJanis Schoetterl-Glausch 				break;
206b02598aeSJanis Schoetterl-Glausch 			case 3:
207b02598aeSJanis Schoetterl-Glausch 				/*
208b02598aeSJanis Schoetterl-Glausch 				 * This is incorrect in that reserved values
209b02598aeSJanis Schoetterl-Glausch 				 * should be ignored, but kvm should not return
210b02598aeSJanis Schoetterl-Glausch 				 * a reserved value and having a test for that
211b02598aeSJanis Schoetterl-Glausch 				 * is more valuable.
212b02598aeSJanis Schoetterl-Glausch 				 */
213b02598aeSJanis Schoetterl-Glausch 				report_fail("valid access code");
214b02598aeSJanis Schoetterl-Glausch 				break;
215b02598aeSJanis Schoetterl-Glausch 			}
216b02598aeSJanis Schoetterl-Glausch 			/* fallthrough */
217b02598aeSJanis Schoetterl-Glausch 		case PROT_KEY_OR_LAP:
218b02598aeSJanis Schoetterl-Glausch 			report_pass("valid protection code");
219b02598aeSJanis Schoetterl-Glausch 			break;
220b02598aeSJanis Schoetterl-Glausch 		default:
221b02598aeSJanis Schoetterl-Glausch 			report_fail("valid protection code");
222b02598aeSJanis Schoetterl-Glausch 		}
223b02598aeSJanis Schoetterl-Glausch 		break;
224b02598aeSJanis Schoetterl-Glausch 	}
225b02598aeSJanis Schoetterl-Glausch 	report_prefix_pop();
226b02598aeSJanis Schoetterl-Glausch }
227b02598aeSJanis Schoetterl-Glausch 
22866abce92SJanis Schoetterl-Glausch /*
22966abce92SJanis Schoetterl-Glausch  * Perform STORE CPU ADDRESS (STAP) instruction while temporarily executing
23066abce92SJanis Schoetterl-Glausch  * with access key 1.
23166abce92SJanis Schoetterl-Glausch  */
store_cpu_address_key_1(uint16_t * out)23266abce92SJanis Schoetterl-Glausch static void store_cpu_address_key_1(uint16_t *out)
23366abce92SJanis Schoetterl-Glausch {
23466abce92SJanis Schoetterl-Glausch 	asm volatile (
23566abce92SJanis Schoetterl-Glausch 		"spka	0x10\n\t"
23666abce92SJanis Schoetterl-Glausch 		"stap	%0\n\t"
23766abce92SJanis Schoetterl-Glausch 		"spka	0\n"
23866abce92SJanis Schoetterl-Glausch 	     : "+Q" (*out) /* exception: old value remains in out -> + constraint */
23966abce92SJanis Schoetterl-Glausch 	);
24066abce92SJanis Schoetterl-Glausch }
24166abce92SJanis Schoetterl-Glausch 
test_store_cpu_address(void)24266abce92SJanis Schoetterl-Glausch static void test_store_cpu_address(void)
24366abce92SJanis Schoetterl-Glausch {
24466abce92SJanis Schoetterl-Glausch 	uint16_t *out = (uint16_t *)pagebuf;
24566abce92SJanis Schoetterl-Glausch 	uint16_t cpu_addr;
24666abce92SJanis Schoetterl-Glausch 
24766abce92SJanis Schoetterl-Glausch 	report_prefix_push("STORE CPU ADDRESS");
24866abce92SJanis Schoetterl-Glausch 	asm ("stap %0" : "=Q" (cpu_addr));
24966abce92SJanis Schoetterl-Glausch 
25066abce92SJanis Schoetterl-Glausch 	report_prefix_push("zero key");
25166abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x20, 0);
25266abce92SJanis Schoetterl-Glausch 	WRITE_ONCE(*out, 0xbeef);
25366abce92SJanis Schoetterl-Glausch 	asm ("stap %0" : "=Q" (*out));
25466abce92SJanis Schoetterl-Glausch 	report(*out == cpu_addr, "store occurred");
25566abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
25666abce92SJanis Schoetterl-Glausch 
25766abce92SJanis Schoetterl-Glausch 	report_prefix_push("matching key");
25866abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x10, 0);
25966abce92SJanis Schoetterl-Glausch 	*out = 0xbeef;
26066abce92SJanis Schoetterl-Glausch 	store_cpu_address_key_1(out);
26166abce92SJanis Schoetterl-Glausch 	report(*out == cpu_addr, "store occurred");
26266abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
26366abce92SJanis Schoetterl-Glausch 
26466abce92SJanis Schoetterl-Glausch 	report_prefix_push("mismatching key");
26566abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x20, 0);
26666abce92SJanis Schoetterl-Glausch 	expect_pgm_int();
26766abce92SJanis Schoetterl-Glausch 	*out = 0xbeef;
26866abce92SJanis Schoetterl-Glausch 	store_cpu_address_key_1(out);
269b02598aeSJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_STORE, PROT_STORE);
27066abce92SJanis Schoetterl-Glausch 	report(*out == 0xbeef, "no store occurred");
27166abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
27266abce92SJanis Schoetterl-Glausch 
27366abce92SJanis Schoetterl-Glausch 	ctl_set_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE);
27466abce92SJanis Schoetterl-Glausch 
27566abce92SJanis Schoetterl-Glausch 	report_prefix_push("storage-protection override, invalid key");
27666abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x20, 0);
27766abce92SJanis Schoetterl-Glausch 	expect_pgm_int();
27866abce92SJanis Schoetterl-Glausch 	*out = 0xbeef;
27966abce92SJanis Schoetterl-Glausch 	store_cpu_address_key_1(out);
280b02598aeSJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_STORE, PROT_STORE);
28166abce92SJanis Schoetterl-Glausch 	report(*out == 0xbeef, "no store occurred");
28266abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
28366abce92SJanis Schoetterl-Glausch 
28466abce92SJanis Schoetterl-Glausch 	report_prefix_push("storage-protection override, override key");
28566abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x90, 0);
28666abce92SJanis Schoetterl-Glausch 	*out = 0xbeef;
28766abce92SJanis Schoetterl-Glausch 	store_cpu_address_key_1(out);
28866abce92SJanis Schoetterl-Glausch 	report(*out == cpu_addr, "override occurred");
28966abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
29066abce92SJanis Schoetterl-Glausch 
29166abce92SJanis Schoetterl-Glausch 	ctl_clear_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE);
29266abce92SJanis Schoetterl-Glausch 
29366abce92SJanis Schoetterl-Glausch 	report_prefix_push("storage-protection override disabled, override key");
29466abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x90, 0);
29566abce92SJanis Schoetterl-Glausch 	expect_pgm_int();
29666abce92SJanis Schoetterl-Glausch 	*out = 0xbeef;
29766abce92SJanis Schoetterl-Glausch 	store_cpu_address_key_1(out);
298b02598aeSJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_STORE, PROT_STORE);
29966abce92SJanis Schoetterl-Glausch 	report(*out == 0xbeef, "no store occurred");
30066abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
30166abce92SJanis Schoetterl-Glausch 
30266abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x00, 0);
30366abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
30466abce92SJanis Schoetterl-Glausch }
30566abce92SJanis Schoetterl-Glausch 
test_diag_308(void)306965e38a0SJanis Schoetterl-Glausch static void test_diag_308(void)
307965e38a0SJanis Schoetterl-Glausch {
308965e38a0SJanis Schoetterl-Glausch 	uint16_t response;
309965e38a0SJanis Schoetterl-Glausch 	uint32_t *ipib = (uint32_t *)pagebuf;
310965e38a0SJanis Schoetterl-Glausch 
311965e38a0SJanis Schoetterl-Glausch 	report_prefix_push("DIAG 308");
312965e38a0SJanis Schoetterl-Glausch 	WRITE_ONCE(ipib[0], 0); /* Invalid length */
313965e38a0SJanis Schoetterl-Glausch 	set_storage_key(ipib, 0x28, 0);
314965e38a0SJanis Schoetterl-Glausch 	/* key-controlled protection does not apply */
315965e38a0SJanis Schoetterl-Glausch 	asm volatile (
316965e38a0SJanis Schoetterl-Glausch 		"lr	%%r2,%[ipib]\n\t"
317965e38a0SJanis Schoetterl-Glausch 		"spka	0x10\n\t"
318965e38a0SJanis Schoetterl-Glausch 		"diag	%%r2,%[code],0x308\n\t"
319965e38a0SJanis Schoetterl-Glausch 		"spka	0\n\t"
320965e38a0SJanis Schoetterl-Glausch 		"lr	%[response],%%r3\n"
321965e38a0SJanis Schoetterl-Glausch 		: [response] "=d" (response)
322965e38a0SJanis Schoetterl-Glausch 		: [ipib] "d" (ipib),
323*e11a0e2fSNina Schoetterl-Glausch 		  [code] "d" (5L)
324965e38a0SJanis Schoetterl-Glausch 		: "%r2", "%r3"
325965e38a0SJanis Schoetterl-Glausch 	);
326965e38a0SJanis Schoetterl-Glausch 	report(response == 0x402, "no exception on fetch, response: invalid IPIB");
327965e38a0SJanis Schoetterl-Glausch 	set_storage_key(ipib, 0x00, 0);
328965e38a0SJanis Schoetterl-Glausch 	report_prefix_pop();
329965e38a0SJanis Schoetterl-Glausch }
330965e38a0SJanis Schoetterl-Glausch 
33166abce92SJanis Schoetterl-Glausch /*
3326c3882f6SJanis Schoetterl-Glausch  * Perform CHANNEL SUBSYSTEM CALL (CHSC)  instruction while temporarily executing
3336c3882f6SJanis Schoetterl-Glausch  * with access key 1.
3346c3882f6SJanis Schoetterl-Glausch  */
chsc_key_1(void * comm_block)3356c3882f6SJanis Schoetterl-Glausch static unsigned int chsc_key_1(void *comm_block)
3366c3882f6SJanis Schoetterl-Glausch {
3376c3882f6SJanis Schoetterl-Glausch 	uint32_t program_mask;
3386c3882f6SJanis Schoetterl-Glausch 
3396c3882f6SJanis Schoetterl-Glausch 	asm volatile (
3406c3882f6SJanis Schoetterl-Glausch 		"spka	0x10\n\t"
3416c3882f6SJanis Schoetterl-Glausch 		".insn	rre,0xb25f0000,%[comm_block],0\n\t"
3426c3882f6SJanis Schoetterl-Glausch 		"spka	0\n\t"
3436c3882f6SJanis Schoetterl-Glausch 		"ipm	%[program_mask]\n"
3446c3882f6SJanis Schoetterl-Glausch 		: [program_mask] "=d" (program_mask)
3456c3882f6SJanis Schoetterl-Glausch 		: [comm_block] "d" (comm_block)
3466c3882f6SJanis Schoetterl-Glausch 		: "memory"
3476c3882f6SJanis Schoetterl-Glausch 	);
3486c3882f6SJanis Schoetterl-Glausch 	return program_mask >> 28;
3496c3882f6SJanis Schoetterl-Glausch }
3506c3882f6SJanis Schoetterl-Glausch 
3516c3882f6SJanis Schoetterl-Glausch static const char chsc_msg[] = "Performed store-channel-subsystem-characteristics";
init_comm_block(uint16_t * comm_block)3526c3882f6SJanis Schoetterl-Glausch static void init_comm_block(uint16_t *comm_block)
3536c3882f6SJanis Schoetterl-Glausch {
3546c3882f6SJanis Schoetterl-Glausch 	memset(comm_block, 0, PAGE_SIZE);
3556c3882f6SJanis Schoetterl-Glausch 	/* store-channel-subsystem-characteristics command */
3566c3882f6SJanis Schoetterl-Glausch 	comm_block[0] = 0x10;
3576c3882f6SJanis Schoetterl-Glausch 	comm_block[1] = 0x10;
3586c3882f6SJanis Schoetterl-Glausch 	comm_block[9] = 0;
3596c3882f6SJanis Schoetterl-Glausch }
3606c3882f6SJanis Schoetterl-Glausch 
test_channel_subsystem_call(void)3616c3882f6SJanis Schoetterl-Glausch static void test_channel_subsystem_call(void)
3626c3882f6SJanis Schoetterl-Glausch {
3636c3882f6SJanis Schoetterl-Glausch 	uint16_t *comm_block = (uint16_t *)&pagebuf;
3646c3882f6SJanis Schoetterl-Glausch 	unsigned int cc;
3656c3882f6SJanis Schoetterl-Glausch 
3666c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("CHANNEL SUBSYSTEM CALL");
3676c3882f6SJanis Schoetterl-Glausch 
3686c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("zero key");
3696c3882f6SJanis Schoetterl-Glausch 	init_comm_block(comm_block);
3706c3882f6SJanis Schoetterl-Glausch 	set_storage_key(comm_block, 0x10, 0);
3716c3882f6SJanis Schoetterl-Glausch 	asm volatile (
3726c3882f6SJanis Schoetterl-Glausch 		".insn	rre,0xb25f0000,%[comm_block],0\n\t"
3736c3882f6SJanis Schoetterl-Glausch 		"ipm	%[cc]\n"
3746c3882f6SJanis Schoetterl-Glausch 		: [cc] "=d" (cc)
3756c3882f6SJanis Schoetterl-Glausch 		: [comm_block] "d" (comm_block)
3766c3882f6SJanis Schoetterl-Glausch 		: "memory"
3776c3882f6SJanis Schoetterl-Glausch 	);
3786c3882f6SJanis Schoetterl-Glausch 	cc = cc >> 28;
3796c3882f6SJanis Schoetterl-Glausch 	report(cc == 0 && comm_block[9], chsc_msg);
3806c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
3816c3882f6SJanis Schoetterl-Glausch 
3826c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("matching key");
3836c3882f6SJanis Schoetterl-Glausch 	init_comm_block(comm_block);
3846c3882f6SJanis Schoetterl-Glausch 	set_storage_key(comm_block, 0x10, 0);
3856c3882f6SJanis Schoetterl-Glausch 	cc = chsc_key_1(comm_block);
3866c3882f6SJanis Schoetterl-Glausch 	report(cc == 0 && comm_block[9], chsc_msg);
3876c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
3886c3882f6SJanis Schoetterl-Glausch 
3896c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("mismatching key");
3906c3882f6SJanis Schoetterl-Glausch 
3916c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("no fetch protection");
3926c3882f6SJanis Schoetterl-Glausch 	init_comm_block(comm_block);
3936c3882f6SJanis Schoetterl-Glausch 	set_storage_key(comm_block, 0x20, 0);
3946c3882f6SJanis Schoetterl-Glausch 	expect_pgm_int();
3956c3882f6SJanis Schoetterl-Glausch 	chsc_key_1(comm_block);
3966c3882f6SJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_UPDATE, PROT_STORE);
3976c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
3986c3882f6SJanis Schoetterl-Glausch 
3996c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("fetch protection");
4006c3882f6SJanis Schoetterl-Glausch 	init_comm_block(comm_block);
4016c3882f6SJanis Schoetterl-Glausch 	set_storage_key(comm_block, 0x28, 0);
4026c3882f6SJanis Schoetterl-Glausch 	expect_pgm_int();
4036c3882f6SJanis Schoetterl-Glausch 	chsc_key_1(comm_block);
4046c3882f6SJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_UPDATE, PROT_FETCH_STORE);
4056c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
4066c3882f6SJanis Schoetterl-Glausch 
4076c3882f6SJanis Schoetterl-Glausch 	ctl_set_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE);
4086c3882f6SJanis Schoetterl-Glausch 
4096c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("storage-protection override, invalid key");
4106c3882f6SJanis Schoetterl-Glausch 	set_storage_key(comm_block, 0x20, 0);
4116c3882f6SJanis Schoetterl-Glausch 	init_comm_block(comm_block);
4126c3882f6SJanis Schoetterl-Glausch 	expect_pgm_int();
4136c3882f6SJanis Schoetterl-Glausch 	chsc_key_1(comm_block);
4146c3882f6SJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_UPDATE, PROT_STORE);
4156c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
4166c3882f6SJanis Schoetterl-Glausch 
4176c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("storage-protection override, override key");
4186c3882f6SJanis Schoetterl-Glausch 	init_comm_block(comm_block);
4196c3882f6SJanis Schoetterl-Glausch 	set_storage_key(comm_block, 0x90, 0);
4206c3882f6SJanis Schoetterl-Glausch 	cc = chsc_key_1(comm_block);
4216c3882f6SJanis Schoetterl-Glausch 	report(cc == 0 && comm_block[9], chsc_msg);
4226c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
4236c3882f6SJanis Schoetterl-Glausch 
4246c3882f6SJanis Schoetterl-Glausch 	ctl_clear_bit(0, CTL0_STORAGE_PROTECTION_OVERRIDE);
4256c3882f6SJanis Schoetterl-Glausch 
4266c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("storage-protection override disabled, override key");
4276c3882f6SJanis Schoetterl-Glausch 	init_comm_block(comm_block);
4286c3882f6SJanis Schoetterl-Glausch 	set_storage_key(comm_block, 0x90, 0);
4296c3882f6SJanis Schoetterl-Glausch 	expect_pgm_int();
4306c3882f6SJanis Schoetterl-Glausch 	chsc_key_1(comm_block);
4316c3882f6SJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_UPDATE, PROT_STORE);
4326c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
4336c3882f6SJanis Schoetterl-Glausch 
4346c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
4356c3882f6SJanis Schoetterl-Glausch 
4366c3882f6SJanis Schoetterl-Glausch 	set_storage_key(comm_block, 0x00, 0);
4376c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
4386c3882f6SJanis Schoetterl-Glausch }
4396c3882f6SJanis Schoetterl-Glausch 
4406c3882f6SJanis Schoetterl-Glausch /*
44166abce92SJanis Schoetterl-Glausch  * Perform SET PREFIX (SPX) instruction while temporarily executing
44266abce92SJanis Schoetterl-Glausch  * with access key 1.
44366abce92SJanis Schoetterl-Glausch  */
set_prefix_key_1(uint32_t * prefix_ptr)44466abce92SJanis Schoetterl-Glausch static void set_prefix_key_1(uint32_t *prefix_ptr)
44566abce92SJanis Schoetterl-Glausch {
44666abce92SJanis Schoetterl-Glausch 	asm volatile (
44766abce92SJanis Schoetterl-Glausch 		"spka	0x10\n\t"
44866abce92SJanis Schoetterl-Glausch 		"spx	%0\n\t"
44966abce92SJanis Schoetterl-Glausch 		"spka	0\n"
45066abce92SJanis Schoetterl-Glausch 	     :: "Q" (*prefix_ptr)
45166abce92SJanis Schoetterl-Glausch 	);
45266abce92SJanis Schoetterl-Glausch }
45366abce92SJanis Schoetterl-Glausch 
45466abce92SJanis Schoetterl-Glausch #define PREFIX_AREA_SIZE (PAGE_SIZE * 2)
45566abce92SJanis Schoetterl-Glausch static char lowcore_tmp[PREFIX_AREA_SIZE] __attribute__((aligned(PREFIX_AREA_SIZE)));
45666abce92SJanis Schoetterl-Glausch 
45766abce92SJanis Schoetterl-Glausch /*
45866abce92SJanis Schoetterl-Glausch  * Test accessibility of the operand to SET PREFIX given different configurations
45966abce92SJanis Schoetterl-Glausch  * with regards to storage keys. That is, check the accessibility of the location
46066abce92SJanis Schoetterl-Glausch  * holding the new prefix, not that of the new prefix area. The new prefix area
46166abce92SJanis Schoetterl-Glausch  * is a valid lowcore, so that the test does not crash on failure.
46266abce92SJanis Schoetterl-Glausch  */
test_set_prefix(void)46366abce92SJanis Schoetterl-Glausch static void test_set_prefix(void)
46466abce92SJanis Schoetterl-Glausch {
46566abce92SJanis Schoetterl-Glausch 	uint32_t *prefix_ptr = (uint32_t *)pagebuf;
46666abce92SJanis Schoetterl-Glausch 	uint32_t *no_override_prefix_ptr;
46766abce92SJanis Schoetterl-Glausch 	uint32_t old_prefix;
46866abce92SJanis Schoetterl-Glausch 	pgd_t *root;
46966abce92SJanis Schoetterl-Glausch 
47066abce92SJanis Schoetterl-Glausch 	report_prefix_push("SET PREFIX");
47166abce92SJanis Schoetterl-Glausch 	root = (pgd_t *)(stctg(1) & PAGE_MASK);
47266abce92SJanis Schoetterl-Glausch 	old_prefix = get_prefix();
47366abce92SJanis Schoetterl-Glausch 	memcpy(lowcore_tmp, 0, sizeof(lowcore_tmp));
47466abce92SJanis Schoetterl-Glausch 	assert(((uint64_t)&lowcore_tmp >> 31) == 0);
47566abce92SJanis Schoetterl-Glausch 	*prefix_ptr = (uint32_t)(uint64_t)&lowcore_tmp;
47666abce92SJanis Schoetterl-Glausch 
47766abce92SJanis Schoetterl-Glausch 	report_prefix_push("zero key");
47866abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
47966abce92SJanis Schoetterl-Glausch 	set_storage_key(prefix_ptr, 0x20, 0);
48066abce92SJanis Schoetterl-Glausch 	set_prefix(*prefix_ptr);
48166abce92SJanis Schoetterl-Glausch 	report(get_prefix() == *prefix_ptr, "set prefix");
48266abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
48366abce92SJanis Schoetterl-Glausch 
48466abce92SJanis Schoetterl-Glausch 	report_prefix_push("matching key");
48566abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
48666abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x10, 0);
48766abce92SJanis Schoetterl-Glausch 	set_prefix_key_1(prefix_ptr);
48866abce92SJanis Schoetterl-Glausch 	report(get_prefix() == *prefix_ptr, "set prefix");
48966abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
49066abce92SJanis Schoetterl-Glausch 
49166abce92SJanis Schoetterl-Glausch 	report_prefix_push("mismatching key");
49266abce92SJanis Schoetterl-Glausch 
49366abce92SJanis Schoetterl-Glausch 	report_prefix_push("no fetch protection");
49466abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
49566abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x20, 0);
49666abce92SJanis Schoetterl-Glausch 	set_prefix_key_1(prefix_ptr);
49766abce92SJanis Schoetterl-Glausch 	report(get_prefix() == *prefix_ptr, "set prefix");
49866abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
49966abce92SJanis Schoetterl-Glausch 
50066abce92SJanis Schoetterl-Glausch 	report_prefix_push("fetch protection");
50166abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
50266abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x28, 0);
50366abce92SJanis Schoetterl-Glausch 	expect_pgm_int();
50466abce92SJanis Schoetterl-Glausch 	set_prefix_key_1(prefix_ptr);
505b02598aeSJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
50666abce92SJanis Schoetterl-Glausch 	report(get_prefix() == old_prefix, "did not set prefix");
50766abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
50866abce92SJanis Schoetterl-Glausch 
509fc1a9e45SClaudio Imbrenda 	/*
510fc1a9e45SClaudio Imbrenda 	 * Page 0 will be remapped, making the lowcore inaccessible, which
511fc1a9e45SClaudio Imbrenda 	 * breaks the normal handler and breaks skipping the faulting
512fc1a9e45SClaudio Imbrenda 	 * instruction. Disable dynamic address translation for the
513fc1a9e45SClaudio Imbrenda 	 * interrupt handler to make things work.
514fc1a9e45SClaudio Imbrenda 	 */
515fc1a9e45SClaudio Imbrenda 	lowcore.pgm_new_psw.mask &= ~PSW_MASK_DAT;
51666abce92SJanis Schoetterl-Glausch 
51766abce92SJanis Schoetterl-Glausch 	report_prefix_push("remapped page, fetch protection");
51866abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
51966abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x28, 0);
52066abce92SJanis Schoetterl-Glausch 	expect_pgm_int();
52166abce92SJanis Schoetterl-Glausch 	install_page(root, virt_to_pte_phys(root, pagebuf), 0);
52266abce92SJanis Schoetterl-Glausch 	set_prefix_key_1((uint32_t *)0);
52366abce92SJanis Schoetterl-Glausch 	install_page(root, 0, 0);
524b02598aeSJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
52566abce92SJanis Schoetterl-Glausch 	report(get_prefix() == old_prefix, "did not set prefix");
52666abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
52766abce92SJanis Schoetterl-Glausch 
52866abce92SJanis Schoetterl-Glausch 	ctl_set_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE);
52966abce92SJanis Schoetterl-Glausch 
53066abce92SJanis Schoetterl-Glausch 	report_prefix_push("fetch protection override applies");
53166abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
53266abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x28, 0);
53366abce92SJanis Schoetterl-Glausch 	install_page(root, virt_to_pte_phys(root, pagebuf), 0);
53466abce92SJanis Schoetterl-Glausch 	set_prefix_key_1((uint32_t *)0);
53566abce92SJanis Schoetterl-Glausch 	install_page(root, 0, 0);
53666abce92SJanis Schoetterl-Glausch 	report(get_prefix() == *prefix_ptr, "set prefix");
53766abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
53866abce92SJanis Schoetterl-Glausch 
53966abce92SJanis Schoetterl-Glausch 	no_override_prefix_ptr = (uint32_t *)(pagebuf + 2048);
54066abce92SJanis Schoetterl-Glausch 	WRITE_ONCE(*no_override_prefix_ptr, (uint32_t)(uint64_t)&lowcore_tmp);
54166abce92SJanis Schoetterl-Glausch 	report_prefix_push("fetch protection override does not apply");
54266abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
54366abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x28, 0);
54466abce92SJanis Schoetterl-Glausch 	expect_pgm_int();
54566abce92SJanis Schoetterl-Glausch 	install_page(root, virt_to_pte_phys(root, pagebuf), 0);
5465af83aa8SJanis Schoetterl-Glausch 	set_prefix_key_1(OPAQUE_PTR(2048));
54766abce92SJanis Schoetterl-Glausch 	install_page(root, 0, 0);
548b02598aeSJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
54966abce92SJanis Schoetterl-Glausch 	report(get_prefix() == old_prefix, "did not set prefix");
55066abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
55166abce92SJanis Schoetterl-Glausch 
55266abce92SJanis Schoetterl-Glausch 	ctl_clear_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE);
553fc1a9e45SClaudio Imbrenda 	lowcore.pgm_new_psw.mask |= PSW_MASK_DAT;
55466abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
55566abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x00, 0);
55666abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
55766abce92SJanis Schoetterl-Glausch }
55866abce92SJanis Schoetterl-Glausch 
5596c3882f6SJanis Schoetterl-Glausch /*
5606c3882f6SJanis Schoetterl-Glausch  * Perform MODIFY SUBCHANNEL (MSCH) instruction while temporarily executing
5616c3882f6SJanis Schoetterl-Glausch  * with access key 1.
5626c3882f6SJanis Schoetterl-Glausch  */
modify_subchannel_key_1(uint32_t sid,struct schib * schib)5636c3882f6SJanis Schoetterl-Glausch static uint32_t modify_subchannel_key_1(uint32_t sid, struct schib *schib)
5646c3882f6SJanis Schoetterl-Glausch {
5656c3882f6SJanis Schoetterl-Glausch 	uint32_t program_mask;
5666c3882f6SJanis Schoetterl-Glausch 
5676c3882f6SJanis Schoetterl-Glausch 	asm volatile (
5686c3882f6SJanis Schoetterl-Glausch 		"lr %%r1,%[sid]\n\t"
5696c3882f6SJanis Schoetterl-Glausch 		"spka	0x10\n\t"
5706c3882f6SJanis Schoetterl-Glausch 		"msch	%[schib]\n\t"
5716c3882f6SJanis Schoetterl-Glausch 		"spka	0\n\t"
5726c3882f6SJanis Schoetterl-Glausch 		"ipm	%[program_mask]\n"
5736c3882f6SJanis Schoetterl-Glausch 		: [program_mask] "=d" (program_mask)
5746c3882f6SJanis Schoetterl-Glausch 		: [sid] "d" (sid),
5756c3882f6SJanis Schoetterl-Glausch 		  [schib] "Q" (*schib)
5766c3882f6SJanis Schoetterl-Glausch 		: "%r1"
5776c3882f6SJanis Schoetterl-Glausch 	);
5786c3882f6SJanis Schoetterl-Glausch 	return program_mask >> 28;
5796c3882f6SJanis Schoetterl-Glausch }
5806c3882f6SJanis Schoetterl-Glausch 
test_msch(void)5816c3882f6SJanis Schoetterl-Glausch static void test_msch(void)
5826c3882f6SJanis Schoetterl-Glausch {
5836c3882f6SJanis Schoetterl-Glausch 	struct schib *schib = (struct schib *)pagebuf;
5846c3882f6SJanis Schoetterl-Glausch 	struct schib *no_override_schib;
5856c3882f6SJanis Schoetterl-Glausch 	int test_device_sid;
5866c3882f6SJanis Schoetterl-Glausch 	pgd_t *root;
5876c3882f6SJanis Schoetterl-Glausch 	int cc;
5886c3882f6SJanis Schoetterl-Glausch 
5896c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("MSCH");
5906c3882f6SJanis Schoetterl-Glausch 	root = (pgd_t *)(stctg(1) & PAGE_MASK);
5916c3882f6SJanis Schoetterl-Glausch 	test_device_sid = css_enumerate();
5926c3882f6SJanis Schoetterl-Glausch 
5936c3882f6SJanis Schoetterl-Glausch 	if (!(test_device_sid & SCHID_ONE)) {
5946c3882f6SJanis Schoetterl-Glausch 		report_fail("no I/O device found");
5956c3882f6SJanis Schoetterl-Glausch 		return;
5966c3882f6SJanis Schoetterl-Glausch 	}
5976c3882f6SJanis Schoetterl-Glausch 
5986c3882f6SJanis Schoetterl-Glausch 	cc = stsch(test_device_sid, schib);
5996c3882f6SJanis Schoetterl-Glausch 	if (cc) {
6006c3882f6SJanis Schoetterl-Glausch 		report_fail("could not store SCHIB");
6016c3882f6SJanis Schoetterl-Glausch 		return;
6026c3882f6SJanis Schoetterl-Glausch 	}
6036c3882f6SJanis Schoetterl-Glausch 
6046c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("zero key");
6056c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 100;
6066c3882f6SJanis Schoetterl-Glausch 	set_storage_key(schib, 0x28, 0);
6076c3882f6SJanis Schoetterl-Glausch 	cc = msch(test_device_sid, schib);
6086c3882f6SJanis Schoetterl-Glausch 	if (!cc) {
6096c3882f6SJanis Schoetterl-Glausch 		WRITE_ONCE(schib->pmcw.intparm, 0);
6106c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6116c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 100, "fetched from SCHIB");
6126c3882f6SJanis Schoetterl-Glausch 	} else {
6136c3882f6SJanis Schoetterl-Glausch 		report_fail("MSCH cc != 0");
6146c3882f6SJanis Schoetterl-Glausch 	}
6156c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
6166c3882f6SJanis Schoetterl-Glausch 
6176c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("matching key");
6186c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 200;
6196c3882f6SJanis Schoetterl-Glausch 	set_storage_key(schib, 0x18, 0);
6206c3882f6SJanis Schoetterl-Glausch 	cc = modify_subchannel_key_1(test_device_sid, schib);
6216c3882f6SJanis Schoetterl-Glausch 	if (!cc) {
6226c3882f6SJanis Schoetterl-Glausch 		WRITE_ONCE(schib->pmcw.intparm, 0);
6236c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6246c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 200, "fetched from SCHIB");
6256c3882f6SJanis Schoetterl-Glausch 	} else {
6266c3882f6SJanis Schoetterl-Glausch 		report_fail("MSCH cc != 0");
6276c3882f6SJanis Schoetterl-Glausch 	}
6286c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
6296c3882f6SJanis Schoetterl-Glausch 
6306c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("mismatching key");
6316c3882f6SJanis Schoetterl-Glausch 
6326c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("no fetch protection");
6336c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 300;
6346c3882f6SJanis Schoetterl-Glausch 	set_storage_key(schib, 0x20, 0);
6356c3882f6SJanis Schoetterl-Glausch 	cc = modify_subchannel_key_1(test_device_sid, schib);
6366c3882f6SJanis Schoetterl-Glausch 	if (!cc) {
6376c3882f6SJanis Schoetterl-Glausch 		WRITE_ONCE(schib->pmcw.intparm, 0);
6386c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6396c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 300, "fetched from SCHIB");
6406c3882f6SJanis Schoetterl-Glausch 	} else {
6416c3882f6SJanis Schoetterl-Glausch 		report_fail("MSCH cc != 0");
6426c3882f6SJanis Schoetterl-Glausch 	}
6436c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
6446c3882f6SJanis Schoetterl-Glausch 
6456c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 0;
6466c3882f6SJanis Schoetterl-Glausch 	if (!msch(test_device_sid, schib)) {
6476c3882f6SJanis Schoetterl-Glausch 		report_prefix_push("fetch protection");
6486c3882f6SJanis Schoetterl-Glausch 		schib->pmcw.intparm = 400;
6496c3882f6SJanis Schoetterl-Glausch 		set_storage_key(schib, 0x28, 0);
6506c3882f6SJanis Schoetterl-Glausch 		expect_pgm_int();
6516c3882f6SJanis Schoetterl-Glausch 		modify_subchannel_key_1(test_device_sid, schib);
6526c3882f6SJanis Schoetterl-Glausch 		check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
6536c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6546c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 0, "did not modify subchannel");
6556c3882f6SJanis Schoetterl-Glausch 		report_prefix_pop();
6566c3882f6SJanis Schoetterl-Glausch 	} else {
6576c3882f6SJanis Schoetterl-Glausch 		report_fail("could not reset SCHIB");
6586c3882f6SJanis Schoetterl-Glausch 	}
6596c3882f6SJanis Schoetterl-Glausch 
660fc1a9e45SClaudio Imbrenda 	/*
661fc1a9e45SClaudio Imbrenda 	 * Page 0 will be remapped, making the lowcore inaccessible, which
662fc1a9e45SClaudio Imbrenda 	 * breaks the normal handler and breaks skipping the faulting
663fc1a9e45SClaudio Imbrenda 	 * instruction. Disable dynamic address translation for the
664fc1a9e45SClaudio Imbrenda 	 * interrupt handler to make things work.
665fc1a9e45SClaudio Imbrenda 	 */
666fc1a9e45SClaudio Imbrenda 	lowcore.pgm_new_psw.mask &= ~PSW_MASK_DAT;
6676c3882f6SJanis Schoetterl-Glausch 
6686c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 0;
6696c3882f6SJanis Schoetterl-Glausch 	if (!msch(test_device_sid, schib)) {
6706c3882f6SJanis Schoetterl-Glausch 		report_prefix_push("remapped page, fetch protection");
6716c3882f6SJanis Schoetterl-Glausch 		schib->pmcw.intparm = 500;
6726c3882f6SJanis Schoetterl-Glausch 		set_storage_key(pagebuf, 0x28, 0);
6736c3882f6SJanis Schoetterl-Glausch 		expect_pgm_int();
6746c3882f6SJanis Schoetterl-Glausch 		install_page(root, virt_to_pte_phys(root, pagebuf), 0);
6756c3882f6SJanis Schoetterl-Glausch 		modify_subchannel_key_1(test_device_sid, (struct schib *)0);
6766c3882f6SJanis Schoetterl-Glausch 		install_page(root, 0, 0);
6776c3882f6SJanis Schoetterl-Glausch 		check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
6786c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6796c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 0, "did not modify subchannel");
6806c3882f6SJanis Schoetterl-Glausch 		report_prefix_pop();
6816c3882f6SJanis Schoetterl-Glausch 	} else {
6826c3882f6SJanis Schoetterl-Glausch 		report_fail("could not reset SCHIB");
6836c3882f6SJanis Schoetterl-Glausch 	}
6846c3882f6SJanis Schoetterl-Glausch 
6856c3882f6SJanis Schoetterl-Glausch 	ctl_set_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE);
6866c3882f6SJanis Schoetterl-Glausch 
6876c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("fetch-protection override applies");
6886c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 600;
6896c3882f6SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x28, 0);
6906c3882f6SJanis Schoetterl-Glausch 	install_page(root, virt_to_pte_phys(root, pagebuf), 0);
6916c3882f6SJanis Schoetterl-Glausch 	cc = modify_subchannel_key_1(test_device_sid, (struct schib *)0);
6926c3882f6SJanis Schoetterl-Glausch 	install_page(root, 0, 0);
6936c3882f6SJanis Schoetterl-Glausch 	if (!cc) {
6946c3882f6SJanis Schoetterl-Glausch 		WRITE_ONCE(schib->pmcw.intparm, 0);
6956c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6966c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 600, "fetched from SCHIB");
6976c3882f6SJanis Schoetterl-Glausch 	} else {
6986c3882f6SJanis Schoetterl-Glausch 		report_fail("MSCH cc != 0");
6996c3882f6SJanis Schoetterl-Glausch 	}
7006c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
7016c3882f6SJanis Schoetterl-Glausch 
7026c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 0;
7036c3882f6SJanis Schoetterl-Glausch 	if (!msch(test_device_sid, schib)) {
7046c3882f6SJanis Schoetterl-Glausch 		report_prefix_push("fetch-protection override does not apply");
7056c3882f6SJanis Schoetterl-Glausch 		schib->pmcw.intparm = 700;
7066c3882f6SJanis Schoetterl-Glausch 		no_override_schib = (struct schib *)(pagebuf + 2048);
7076c3882f6SJanis Schoetterl-Glausch 		memcpy(no_override_schib, schib, sizeof(struct schib));
7086c3882f6SJanis Schoetterl-Glausch 		set_storage_key(pagebuf, 0x28, 0);
7096c3882f6SJanis Schoetterl-Glausch 		expect_pgm_int();
7106c3882f6SJanis Schoetterl-Glausch 		install_page(root, virt_to_pte_phys(root, pagebuf), 0);
7116c3882f6SJanis Schoetterl-Glausch 		modify_subchannel_key_1(test_device_sid, OPAQUE_PTR(2048));
7126c3882f6SJanis Schoetterl-Glausch 		install_page(root, 0, 0);
7136c3882f6SJanis Schoetterl-Glausch 		check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
7146c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
7156c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 0, "did not modify subchannel");
7166c3882f6SJanis Schoetterl-Glausch 		report_prefix_pop();
7176c3882f6SJanis Schoetterl-Glausch 	} else {
7186c3882f6SJanis Schoetterl-Glausch 		report_fail("could not reset SCHIB");
7196c3882f6SJanis Schoetterl-Glausch 	}
7206c3882f6SJanis Schoetterl-Glausch 
7216c3882f6SJanis Schoetterl-Glausch 	ctl_clear_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE);
722fc1a9e45SClaudio Imbrenda 	lowcore.pgm_new_psw.mask |= PSW_MASK_DAT;
7236c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
7246c3882f6SJanis Schoetterl-Glausch 	set_storage_key(schib, 0x00, 0);
7256c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
7266c3882f6SJanis Schoetterl-Glausch }
7276c3882f6SJanis Schoetterl-Glausch 
main(void)728f6b354fbSJanosch Frank int main(void)
729f6b354fbSJanosch Frank {
730f6b354fbSJanosch Frank 	report_prefix_push("skey");
73147df95c7SJanosch Frank 	if (test_facility(169)) {
73247df95c7SJanosch Frank 		report_skip("storage key removal facility is active");
73347df95c7SJanosch Frank 		goto done;
73447df95c7SJanosch Frank 	}
735f6b354fbSJanosch Frank 	test_priv();
736429c9cc2SDavid Hildenbrand 	test_invalid_address();
737f6b354fbSJanosch Frank 	test_set();
738f6b354fbSJanosch Frank 	test_set_mb();
739f6b354fbSJanosch Frank 	test_chg();
74066abce92SJanis Schoetterl-Glausch 	test_test_protection();
74166abce92SJanis Schoetterl-Glausch 	test_store_cpu_address();
742965e38a0SJanis Schoetterl-Glausch 	test_diag_308();
7436c3882f6SJanis Schoetterl-Glausch 	test_channel_subsystem_call();
74466abce92SJanis Schoetterl-Glausch 
74566abce92SJanis Schoetterl-Glausch 	setup_vm();
74666abce92SJanis Schoetterl-Glausch 	test_set_prefix();
7476c3882f6SJanis Schoetterl-Glausch 	test_msch();
74847df95c7SJanosch Frank done:
749f6b354fbSJanosch Frank 	report_prefix_pop();
750f6b354fbSJanosch Frank 	return report_summary();
751f6b354fbSJanosch Frank }
752