1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Standard user space access functions based on mvcp/mvcs and doing 4 * interesting things in the secondary space mode. 5 * 6 * Copyright IBM Corp. 2006,2014 7 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), 8 * Gerald Schaefer (gerald.schaefer@de.ibm.com) 9 */ 10 11 #include <linux/uaccess.h> 12 #include <linux/export.h> 13 #include <linux/mm.h> 14 #include <asm/asm-extable.h> 15 #include <asm/ctlreg.h> 16 17 #ifdef CONFIG_DEBUG_ENTRY 18 void debug_user_asce(int exit) 19 { 20 struct lowcore *lc = get_lowcore(); 21 struct ctlreg cr1, cr7; 22 23 local_ctl_store(1, &cr1); 24 local_ctl_store(7, &cr7); 25 if (cr1.val == lc->user_asce.val && cr7.val == lc->user_asce.val) 26 return; 27 panic("incorrect ASCE on kernel %s\n" 28 "cr1: %016lx cr7: %016lx\n" 29 "kernel: %016lx user: %016lx\n", 30 exit ? "exit" : "entry", cr1.val, cr7.val, 31 lc->kernel_asce.val, lc->user_asce.val); 32 } 33 #endif /*CONFIG_DEBUG_ENTRY */ 34 35 union oac { 36 unsigned int val; 37 struct { 38 struct { 39 unsigned short key : 4; 40 unsigned short : 4; 41 unsigned short as : 2; 42 unsigned short : 4; 43 unsigned short k : 1; 44 unsigned short a : 1; 45 } oac1; 46 struct { 47 unsigned short key : 4; 48 unsigned short : 4; 49 unsigned short as : 2; 50 unsigned short : 4; 51 unsigned short k : 1; 52 unsigned short a : 1; 53 } oac2; 54 }; 55 }; 56 57 static uaccess_kmsan_or_inline __must_check unsigned long 58 raw_copy_from_user_key(void *to, const void __user *from, unsigned long size, unsigned long key) 59 { 60 unsigned long osize; 61 union oac spec = { 62 .oac2.key = key, 63 .oac2.as = PSW_BITS_AS_SECONDARY, 64 .oac2.k = 1, 65 .oac2.a = 1, 66 }; 67 int cc; 68 69 while (1) { 70 osize = size; 71 asm_inline volatile( 72 " lr %%r0,%[spec]\n" 73 "0: mvcos %[to],%[from],%[size]\n" 74 "1: nopr %%r7\n" 75 CC_IPM(cc) 76 EX_TABLE_UA_MVCOS_FROM(0b, 0b) 77 EX_TABLE_UA_MVCOS_FROM(1b, 0b) 78 : CC_OUT(cc, cc), [size] "+d" (size), [to] "=Q" (*(char *)to) 79 : [spec] "d" (spec.val), [from] "Q" (*(const char __user *)from) 80 : CC_CLOBBER_LIST("memory", "0")); 81 if (CC_TRANSFORM(cc) == 0) 82 return osize - size; 83 size -= 4096; 84 to += 4096; 85 from += 4096; 86 } 87 } 88 89 unsigned long _copy_from_user_key(void *to, const void __user *from, 90 unsigned long n, unsigned long key) 91 { 92 unsigned long res = n; 93 94 might_fault(); 95 if (!should_fail_usercopy()) { 96 instrument_copy_from_user_before(to, from, n); 97 res = raw_copy_from_user_key(to, from, n, key); 98 instrument_copy_from_user_after(to, from, n, res); 99 } 100 if (unlikely(res)) 101 memset(to + (n - res), 0, res); 102 return res; 103 } 104 EXPORT_SYMBOL(_copy_from_user_key); 105 106 static uaccess_kmsan_or_inline __must_check unsigned long 107 raw_copy_to_user_key(void __user *to, const void *from, unsigned long size, unsigned long key) 108 { 109 unsigned long osize; 110 union oac spec = { 111 .oac1.key = key, 112 .oac1.as = PSW_BITS_AS_SECONDARY, 113 .oac1.k = 1, 114 .oac1.a = 1, 115 }; 116 int cc; 117 118 while (1) { 119 osize = size; 120 asm_inline volatile( 121 " lr %%r0,%[spec]\n" 122 "0: mvcos %[to],%[from],%[size]\n" 123 "1: nopr %%r7\n" 124 CC_IPM(cc) 125 EX_TABLE_UA_MVCOS_TO(0b, 0b) 126 EX_TABLE_UA_MVCOS_TO(1b, 0b) 127 : CC_OUT(cc, cc), [size] "+d" (size), [to] "=Q" (*(char __user *)to) 128 : [spec] "d" (spec.val), [from] "Q" (*(const char *)from) 129 : CC_CLOBBER_LIST("memory", "0")); 130 if (CC_TRANSFORM(cc) == 0) 131 return osize - size; 132 size -= 4096; 133 to += 4096; 134 from += 4096; 135 } 136 } 137 138 unsigned long _copy_to_user_key(void __user *to, const void *from, 139 unsigned long n, unsigned long key) 140 { 141 might_fault(); 142 if (should_fail_usercopy()) 143 return n; 144 instrument_copy_to_user(to, from, n); 145 return raw_copy_to_user_key(to, from, n, key); 146 } 147 EXPORT_SYMBOL(_copy_to_user_key); 148