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