xref: /kvm-unit-tests/s390x/skey.c (revision 965e38a097b2ed0676690a5fa1f2cb94fd9a58c5)
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 
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 
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 
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 
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 
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 
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 
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  */
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 
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 
306*965e38a0SJanis Schoetterl-Glausch static void test_diag_308(void)
307*965e38a0SJanis Schoetterl-Glausch {
308*965e38a0SJanis Schoetterl-Glausch 	uint16_t response;
309*965e38a0SJanis Schoetterl-Glausch 	uint32_t *ipib = (uint32_t *)pagebuf;
310*965e38a0SJanis Schoetterl-Glausch 
311*965e38a0SJanis Schoetterl-Glausch 	report_prefix_push("DIAG 308");
312*965e38a0SJanis Schoetterl-Glausch 	WRITE_ONCE(ipib[0], 0); /* Invalid length */
313*965e38a0SJanis Schoetterl-Glausch 	set_storage_key(ipib, 0x28, 0);
314*965e38a0SJanis Schoetterl-Glausch 	/* key-controlled protection does not apply */
315*965e38a0SJanis Schoetterl-Glausch 	asm volatile (
316*965e38a0SJanis Schoetterl-Glausch 		"lr	%%r2,%[ipib]\n\t"
317*965e38a0SJanis Schoetterl-Glausch 		"spka	0x10\n\t"
318*965e38a0SJanis Schoetterl-Glausch 		"diag	%%r2,%[code],0x308\n\t"
319*965e38a0SJanis Schoetterl-Glausch 		"spka	0\n\t"
320*965e38a0SJanis Schoetterl-Glausch 		"lr	%[response],%%r3\n"
321*965e38a0SJanis Schoetterl-Glausch 		: [response] "=d" (response)
322*965e38a0SJanis Schoetterl-Glausch 		: [ipib] "d" (ipib),
323*965e38a0SJanis Schoetterl-Glausch 		  [code] "d" (5)
324*965e38a0SJanis Schoetterl-Glausch 		: "%r2", "%r3"
325*965e38a0SJanis Schoetterl-Glausch 	);
326*965e38a0SJanis Schoetterl-Glausch 	report(response == 0x402, "no exception on fetch, response: invalid IPIB");
327*965e38a0SJanis Schoetterl-Glausch 	set_storage_key(ipib, 0x00, 0);
328*965e38a0SJanis Schoetterl-Glausch 	report_prefix_pop();
329*965e38a0SJanis Schoetterl-Glausch }
330*965e38a0SJanis 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  */
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";
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 
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  */
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 /*
45566abce92SJanis Schoetterl-Glausch  * We remapped page 0, making the lowcore inaccessible, which breaks the normal
45666abce92SJanis Schoetterl-Glausch  * handler and breaks skipping the faulting instruction.
45766abce92SJanis Schoetterl-Glausch  * Just disable dynamic address translation to make things work.
45866abce92SJanis Schoetterl-Glausch  */
45966abce92SJanis Schoetterl-Glausch static void dat_fixup_pgm_int(void)
46066abce92SJanis Schoetterl-Glausch {
46166abce92SJanis Schoetterl-Glausch 	uint64_t psw_mask = extract_psw_mask();
46266abce92SJanis Schoetterl-Glausch 
46366abce92SJanis Schoetterl-Glausch 	psw_mask &= ~PSW_MASK_DAT;
46466abce92SJanis Schoetterl-Glausch 	load_psw_mask(psw_mask);
46566abce92SJanis Schoetterl-Glausch }
46666abce92SJanis Schoetterl-Glausch 
46766abce92SJanis Schoetterl-Glausch #define PREFIX_AREA_SIZE (PAGE_SIZE * 2)
46866abce92SJanis Schoetterl-Glausch static char lowcore_tmp[PREFIX_AREA_SIZE] __attribute__((aligned(PREFIX_AREA_SIZE)));
46966abce92SJanis Schoetterl-Glausch 
47066abce92SJanis Schoetterl-Glausch /*
47166abce92SJanis Schoetterl-Glausch  * Test accessibility of the operand to SET PREFIX given different configurations
47266abce92SJanis Schoetterl-Glausch  * with regards to storage keys. That is, check the accessibility of the location
47366abce92SJanis Schoetterl-Glausch  * holding the new prefix, not that of the new prefix area. The new prefix area
47466abce92SJanis Schoetterl-Glausch  * is a valid lowcore, so that the test does not crash on failure.
47566abce92SJanis Schoetterl-Glausch  */
47666abce92SJanis Schoetterl-Glausch static void test_set_prefix(void)
47766abce92SJanis Schoetterl-Glausch {
47866abce92SJanis Schoetterl-Glausch 	uint32_t *prefix_ptr = (uint32_t *)pagebuf;
47966abce92SJanis Schoetterl-Glausch 	uint32_t *no_override_prefix_ptr;
48066abce92SJanis Schoetterl-Glausch 	uint32_t old_prefix;
48166abce92SJanis Schoetterl-Glausch 	pgd_t *root;
48266abce92SJanis Schoetterl-Glausch 
48366abce92SJanis Schoetterl-Glausch 	report_prefix_push("SET PREFIX");
48466abce92SJanis Schoetterl-Glausch 	root = (pgd_t *)(stctg(1) & PAGE_MASK);
48566abce92SJanis Schoetterl-Glausch 	old_prefix = get_prefix();
48666abce92SJanis Schoetterl-Glausch 	memcpy(lowcore_tmp, 0, sizeof(lowcore_tmp));
48766abce92SJanis Schoetterl-Glausch 	assert(((uint64_t)&lowcore_tmp >> 31) == 0);
48866abce92SJanis Schoetterl-Glausch 	*prefix_ptr = (uint32_t)(uint64_t)&lowcore_tmp;
48966abce92SJanis Schoetterl-Glausch 
49066abce92SJanis Schoetterl-Glausch 	report_prefix_push("zero key");
49166abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
49266abce92SJanis Schoetterl-Glausch 	set_storage_key(prefix_ptr, 0x20, 0);
49366abce92SJanis Schoetterl-Glausch 	set_prefix(*prefix_ptr);
49466abce92SJanis Schoetterl-Glausch 	report(get_prefix() == *prefix_ptr, "set prefix");
49566abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
49666abce92SJanis Schoetterl-Glausch 
49766abce92SJanis Schoetterl-Glausch 	report_prefix_push("matching key");
49866abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
49966abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x10, 0);
50066abce92SJanis Schoetterl-Glausch 	set_prefix_key_1(prefix_ptr);
50166abce92SJanis Schoetterl-Glausch 	report(get_prefix() == *prefix_ptr, "set prefix");
50266abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
50366abce92SJanis Schoetterl-Glausch 
50466abce92SJanis Schoetterl-Glausch 	report_prefix_push("mismatching key");
50566abce92SJanis Schoetterl-Glausch 
50666abce92SJanis Schoetterl-Glausch 	report_prefix_push("no fetch protection");
50766abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
50866abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x20, 0);
50966abce92SJanis Schoetterl-Glausch 	set_prefix_key_1(prefix_ptr);
51066abce92SJanis Schoetterl-Glausch 	report(get_prefix() == *prefix_ptr, "set prefix");
51166abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
51266abce92SJanis Schoetterl-Glausch 
51366abce92SJanis Schoetterl-Glausch 	report_prefix_push("fetch protection");
51466abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
51566abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x28, 0);
51666abce92SJanis Schoetterl-Glausch 	expect_pgm_int();
51766abce92SJanis Schoetterl-Glausch 	set_prefix_key_1(prefix_ptr);
518b02598aeSJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
51966abce92SJanis Schoetterl-Glausch 	report(get_prefix() == old_prefix, "did not set prefix");
52066abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
52166abce92SJanis Schoetterl-Glausch 
52266abce92SJanis Schoetterl-Glausch 	register_pgm_cleanup_func(dat_fixup_pgm_int);
52366abce92SJanis Schoetterl-Glausch 
52466abce92SJanis Schoetterl-Glausch 	report_prefix_push("remapped page, fetch protection");
52566abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
52666abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x28, 0);
52766abce92SJanis Schoetterl-Glausch 	expect_pgm_int();
52866abce92SJanis Schoetterl-Glausch 	install_page(root, virt_to_pte_phys(root, pagebuf), 0);
52966abce92SJanis Schoetterl-Glausch 	set_prefix_key_1((uint32_t *)0);
53066abce92SJanis Schoetterl-Glausch 	install_page(root, 0, 0);
531b02598aeSJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
53266abce92SJanis Schoetterl-Glausch 	report(get_prefix() == old_prefix, "did not set prefix");
53366abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
53466abce92SJanis Schoetterl-Glausch 
53566abce92SJanis Schoetterl-Glausch 	ctl_set_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE);
53666abce92SJanis Schoetterl-Glausch 
53766abce92SJanis Schoetterl-Glausch 	report_prefix_push("fetch protection override applies");
53866abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
53966abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x28, 0);
54066abce92SJanis Schoetterl-Glausch 	install_page(root, virt_to_pte_phys(root, pagebuf), 0);
54166abce92SJanis Schoetterl-Glausch 	set_prefix_key_1((uint32_t *)0);
54266abce92SJanis Schoetterl-Glausch 	install_page(root, 0, 0);
54366abce92SJanis Schoetterl-Glausch 	report(get_prefix() == *prefix_ptr, "set prefix");
54466abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
54566abce92SJanis Schoetterl-Glausch 
54666abce92SJanis Schoetterl-Glausch 	no_override_prefix_ptr = (uint32_t *)(pagebuf + 2048);
54766abce92SJanis Schoetterl-Glausch 	WRITE_ONCE(*no_override_prefix_ptr, (uint32_t)(uint64_t)&lowcore_tmp);
54866abce92SJanis Schoetterl-Glausch 	report_prefix_push("fetch protection override does not apply");
54966abce92SJanis Schoetterl-Glausch 	set_prefix(old_prefix);
55066abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x28, 0);
55166abce92SJanis Schoetterl-Glausch 	expect_pgm_int();
55266abce92SJanis Schoetterl-Glausch 	install_page(root, virt_to_pte_phys(root, pagebuf), 0);
5535af83aa8SJanis Schoetterl-Glausch 	set_prefix_key_1(OPAQUE_PTR(2048));
55466abce92SJanis Schoetterl-Glausch 	install_page(root, 0, 0);
555b02598aeSJanis Schoetterl-Glausch 	check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
55666abce92SJanis Schoetterl-Glausch 	report(get_prefix() == old_prefix, "did not set prefix");
55766abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
55866abce92SJanis Schoetterl-Glausch 
55966abce92SJanis Schoetterl-Glausch 	ctl_clear_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE);
56066abce92SJanis Schoetterl-Glausch 	register_pgm_cleanup_func(NULL);
56166abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
56266abce92SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x00, 0);
56366abce92SJanis Schoetterl-Glausch 	report_prefix_pop();
56466abce92SJanis Schoetterl-Glausch }
56566abce92SJanis Schoetterl-Glausch 
5666c3882f6SJanis Schoetterl-Glausch /*
5676c3882f6SJanis Schoetterl-Glausch  * Perform MODIFY SUBCHANNEL (MSCH) instruction while temporarily executing
5686c3882f6SJanis Schoetterl-Glausch  * with access key 1.
5696c3882f6SJanis Schoetterl-Glausch  */
5706c3882f6SJanis Schoetterl-Glausch static uint32_t modify_subchannel_key_1(uint32_t sid, struct schib *schib)
5716c3882f6SJanis Schoetterl-Glausch {
5726c3882f6SJanis Schoetterl-Glausch 	uint32_t program_mask;
5736c3882f6SJanis Schoetterl-Glausch 
5746c3882f6SJanis Schoetterl-Glausch 	asm volatile (
5756c3882f6SJanis Schoetterl-Glausch 		"lr %%r1,%[sid]\n\t"
5766c3882f6SJanis Schoetterl-Glausch 		"spka	0x10\n\t"
5776c3882f6SJanis Schoetterl-Glausch 		"msch	%[schib]\n\t"
5786c3882f6SJanis Schoetterl-Glausch 		"spka	0\n\t"
5796c3882f6SJanis Schoetterl-Glausch 		"ipm	%[program_mask]\n"
5806c3882f6SJanis Schoetterl-Glausch 		: [program_mask] "=d" (program_mask)
5816c3882f6SJanis Schoetterl-Glausch 		: [sid] "d" (sid),
5826c3882f6SJanis Schoetterl-Glausch 		  [schib] "Q" (*schib)
5836c3882f6SJanis Schoetterl-Glausch 		: "%r1"
5846c3882f6SJanis Schoetterl-Glausch 	);
5856c3882f6SJanis Schoetterl-Glausch 	return program_mask >> 28;
5866c3882f6SJanis Schoetterl-Glausch }
5876c3882f6SJanis Schoetterl-Glausch 
5886c3882f6SJanis Schoetterl-Glausch static void test_msch(void)
5896c3882f6SJanis Schoetterl-Glausch {
5906c3882f6SJanis Schoetterl-Glausch 	struct schib *schib = (struct schib *)pagebuf;
5916c3882f6SJanis Schoetterl-Glausch 	struct schib *no_override_schib;
5926c3882f6SJanis Schoetterl-Glausch 	int test_device_sid;
5936c3882f6SJanis Schoetterl-Glausch 	pgd_t *root;
5946c3882f6SJanis Schoetterl-Glausch 	int cc;
5956c3882f6SJanis Schoetterl-Glausch 
5966c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("MSCH");
5976c3882f6SJanis Schoetterl-Glausch 	root = (pgd_t *)(stctg(1) & PAGE_MASK);
5986c3882f6SJanis Schoetterl-Glausch 	test_device_sid = css_enumerate();
5996c3882f6SJanis Schoetterl-Glausch 
6006c3882f6SJanis Schoetterl-Glausch 	if (!(test_device_sid & SCHID_ONE)) {
6016c3882f6SJanis Schoetterl-Glausch 		report_fail("no I/O device found");
6026c3882f6SJanis Schoetterl-Glausch 		return;
6036c3882f6SJanis Schoetterl-Glausch 	}
6046c3882f6SJanis Schoetterl-Glausch 
6056c3882f6SJanis Schoetterl-Glausch 	cc = stsch(test_device_sid, schib);
6066c3882f6SJanis Schoetterl-Glausch 	if (cc) {
6076c3882f6SJanis Schoetterl-Glausch 		report_fail("could not store SCHIB");
6086c3882f6SJanis Schoetterl-Glausch 		return;
6096c3882f6SJanis Schoetterl-Glausch 	}
6106c3882f6SJanis Schoetterl-Glausch 
6116c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("zero key");
6126c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 100;
6136c3882f6SJanis Schoetterl-Glausch 	set_storage_key(schib, 0x28, 0);
6146c3882f6SJanis Schoetterl-Glausch 	cc = msch(test_device_sid, schib);
6156c3882f6SJanis Schoetterl-Glausch 	if (!cc) {
6166c3882f6SJanis Schoetterl-Glausch 		WRITE_ONCE(schib->pmcw.intparm, 0);
6176c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6186c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 100, "fetched from SCHIB");
6196c3882f6SJanis Schoetterl-Glausch 	} else {
6206c3882f6SJanis Schoetterl-Glausch 		report_fail("MSCH cc != 0");
6216c3882f6SJanis Schoetterl-Glausch 	}
6226c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
6236c3882f6SJanis Schoetterl-Glausch 
6246c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("matching key");
6256c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 200;
6266c3882f6SJanis Schoetterl-Glausch 	set_storage_key(schib, 0x18, 0);
6276c3882f6SJanis Schoetterl-Glausch 	cc = modify_subchannel_key_1(test_device_sid, schib);
6286c3882f6SJanis Schoetterl-Glausch 	if (!cc) {
6296c3882f6SJanis Schoetterl-Glausch 		WRITE_ONCE(schib->pmcw.intparm, 0);
6306c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6316c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 200, "fetched from SCHIB");
6326c3882f6SJanis Schoetterl-Glausch 	} else {
6336c3882f6SJanis Schoetterl-Glausch 		report_fail("MSCH cc != 0");
6346c3882f6SJanis Schoetterl-Glausch 	}
6356c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
6366c3882f6SJanis Schoetterl-Glausch 
6376c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("mismatching key");
6386c3882f6SJanis Schoetterl-Glausch 
6396c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("no fetch protection");
6406c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 300;
6416c3882f6SJanis Schoetterl-Glausch 	set_storage_key(schib, 0x20, 0);
6426c3882f6SJanis Schoetterl-Glausch 	cc = modify_subchannel_key_1(test_device_sid, schib);
6436c3882f6SJanis Schoetterl-Glausch 	if (!cc) {
6446c3882f6SJanis Schoetterl-Glausch 		WRITE_ONCE(schib->pmcw.intparm, 0);
6456c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6466c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 300, "fetched from SCHIB");
6476c3882f6SJanis Schoetterl-Glausch 	} else {
6486c3882f6SJanis Schoetterl-Glausch 		report_fail("MSCH cc != 0");
6496c3882f6SJanis Schoetterl-Glausch 	}
6506c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
6516c3882f6SJanis Schoetterl-Glausch 
6526c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 0;
6536c3882f6SJanis Schoetterl-Glausch 	if (!msch(test_device_sid, schib)) {
6546c3882f6SJanis Schoetterl-Glausch 		report_prefix_push("fetch protection");
6556c3882f6SJanis Schoetterl-Glausch 		schib->pmcw.intparm = 400;
6566c3882f6SJanis Schoetterl-Glausch 		set_storage_key(schib, 0x28, 0);
6576c3882f6SJanis Schoetterl-Glausch 		expect_pgm_int();
6586c3882f6SJanis Schoetterl-Glausch 		modify_subchannel_key_1(test_device_sid, schib);
6596c3882f6SJanis Schoetterl-Glausch 		check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
6606c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6616c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 0, "did not modify subchannel");
6626c3882f6SJanis Schoetterl-Glausch 		report_prefix_pop();
6636c3882f6SJanis Schoetterl-Glausch 	} else {
6646c3882f6SJanis Schoetterl-Glausch 		report_fail("could not reset SCHIB");
6656c3882f6SJanis Schoetterl-Glausch 	}
6666c3882f6SJanis Schoetterl-Glausch 
6676c3882f6SJanis Schoetterl-Glausch 	register_pgm_cleanup_func(dat_fixup_pgm_int);
6686c3882f6SJanis Schoetterl-Glausch 
6696c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 0;
6706c3882f6SJanis Schoetterl-Glausch 	if (!msch(test_device_sid, schib)) {
6716c3882f6SJanis Schoetterl-Glausch 		report_prefix_push("remapped page, fetch protection");
6726c3882f6SJanis Schoetterl-Glausch 		schib->pmcw.intparm = 500;
6736c3882f6SJanis Schoetterl-Glausch 		set_storage_key(pagebuf, 0x28, 0);
6746c3882f6SJanis Schoetterl-Glausch 		expect_pgm_int();
6756c3882f6SJanis Schoetterl-Glausch 		install_page(root, virt_to_pte_phys(root, pagebuf), 0);
6766c3882f6SJanis Schoetterl-Glausch 		modify_subchannel_key_1(test_device_sid, (struct schib *)0);
6776c3882f6SJanis Schoetterl-Glausch 		install_page(root, 0, 0);
6786c3882f6SJanis Schoetterl-Glausch 		check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
6796c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6806c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 0, "did not modify subchannel");
6816c3882f6SJanis Schoetterl-Glausch 		report_prefix_pop();
6826c3882f6SJanis Schoetterl-Glausch 	} else {
6836c3882f6SJanis Schoetterl-Glausch 		report_fail("could not reset SCHIB");
6846c3882f6SJanis Schoetterl-Glausch 	}
6856c3882f6SJanis Schoetterl-Glausch 
6866c3882f6SJanis Schoetterl-Glausch 	ctl_set_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE);
6876c3882f6SJanis Schoetterl-Glausch 
6886c3882f6SJanis Schoetterl-Glausch 	report_prefix_push("fetch-protection override applies");
6896c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 600;
6906c3882f6SJanis Schoetterl-Glausch 	set_storage_key(pagebuf, 0x28, 0);
6916c3882f6SJanis Schoetterl-Glausch 	install_page(root, virt_to_pte_phys(root, pagebuf), 0);
6926c3882f6SJanis Schoetterl-Glausch 	cc = modify_subchannel_key_1(test_device_sid, (struct schib *)0);
6936c3882f6SJanis Schoetterl-Glausch 	install_page(root, 0, 0);
6946c3882f6SJanis Schoetterl-Glausch 	if (!cc) {
6956c3882f6SJanis Schoetterl-Glausch 		WRITE_ONCE(schib->pmcw.intparm, 0);
6966c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
6976c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 600, "fetched from SCHIB");
6986c3882f6SJanis Schoetterl-Glausch 	} else {
6996c3882f6SJanis Schoetterl-Glausch 		report_fail("MSCH cc != 0");
7006c3882f6SJanis Schoetterl-Glausch 	}
7016c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
7026c3882f6SJanis Schoetterl-Glausch 
7036c3882f6SJanis Schoetterl-Glausch 	schib->pmcw.intparm = 0;
7046c3882f6SJanis Schoetterl-Glausch 	if (!msch(test_device_sid, schib)) {
7056c3882f6SJanis Schoetterl-Glausch 		report_prefix_push("fetch-protection override does not apply");
7066c3882f6SJanis Schoetterl-Glausch 		schib->pmcw.intparm = 700;
7076c3882f6SJanis Schoetterl-Glausch 		no_override_schib = (struct schib *)(pagebuf + 2048);
7086c3882f6SJanis Schoetterl-Glausch 		memcpy(no_override_schib, schib, sizeof(struct schib));
7096c3882f6SJanis Schoetterl-Glausch 		set_storage_key(pagebuf, 0x28, 0);
7106c3882f6SJanis Schoetterl-Glausch 		expect_pgm_int();
7116c3882f6SJanis Schoetterl-Glausch 		install_page(root, virt_to_pte_phys(root, pagebuf), 0);
7126c3882f6SJanis Schoetterl-Glausch 		modify_subchannel_key_1(test_device_sid, OPAQUE_PTR(2048));
7136c3882f6SJanis Schoetterl-Glausch 		install_page(root, 0, 0);
7146c3882f6SJanis Schoetterl-Glausch 		check_key_prot_exc(ACC_FETCH, PROT_FETCH_STORE);
7156c3882f6SJanis Schoetterl-Glausch 		cc = stsch(test_device_sid, schib);
7166c3882f6SJanis Schoetterl-Glausch 		report(!cc && schib->pmcw.intparm == 0, "did not modify subchannel");
7176c3882f6SJanis Schoetterl-Glausch 		report_prefix_pop();
7186c3882f6SJanis Schoetterl-Glausch 	} else {
7196c3882f6SJanis Schoetterl-Glausch 		report_fail("could not reset SCHIB");
7206c3882f6SJanis Schoetterl-Glausch 	}
7216c3882f6SJanis Schoetterl-Glausch 
7226c3882f6SJanis Schoetterl-Glausch 	ctl_clear_bit(0, CTL0_FETCH_PROTECTION_OVERRIDE);
7236c3882f6SJanis Schoetterl-Glausch 	register_pgm_cleanup_func(NULL);
7246c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
7256c3882f6SJanis Schoetterl-Glausch 	set_storage_key(schib, 0x00, 0);
7266c3882f6SJanis Schoetterl-Glausch 	report_prefix_pop();
7276c3882f6SJanis Schoetterl-Glausch }
7286c3882f6SJanis Schoetterl-Glausch 
729f6b354fbSJanosch Frank int main(void)
730f6b354fbSJanosch Frank {
731f6b354fbSJanosch Frank 	report_prefix_push("skey");
73247df95c7SJanosch Frank 	if (test_facility(169)) {
73347df95c7SJanosch Frank 		report_skip("storage key removal facility is active");
73447df95c7SJanosch Frank 		goto done;
73547df95c7SJanosch Frank 	}
736f6b354fbSJanosch Frank 	test_priv();
737429c9cc2SDavid Hildenbrand 	test_invalid_address();
738f6b354fbSJanosch Frank 	test_set();
739f6b354fbSJanosch Frank 	test_set_mb();
740f6b354fbSJanosch Frank 	test_chg();
74166abce92SJanis Schoetterl-Glausch 	test_test_protection();
74266abce92SJanis Schoetterl-Glausch 	test_store_cpu_address();
743*965e38a0SJanis Schoetterl-Glausch 	test_diag_308();
7446c3882f6SJanis Schoetterl-Glausch 	test_channel_subsystem_call();
74566abce92SJanis Schoetterl-Glausch 
74666abce92SJanis Schoetterl-Glausch 	setup_vm();
74766abce92SJanis Schoetterl-Glausch 	test_set_prefix();
7486c3882f6SJanis Schoetterl-Glausch 	test_msch();
74947df95c7SJanosch Frank done:
750f6b354fbSJanosch Frank 	report_prefix_pop();
751f6b354fbSJanosch Frank 	return report_summary();
752f6b354fbSJanosch Frank }
753