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 ctlreg cr1, cr7; 21 22 local_ctl_store(1, &cr1); 23 local_ctl_store(7, &cr7); 24 if (cr1.val == get_lowcore()->kernel_asce.val && cr7.val == get_lowcore()->user_asce.val) 25 return; 26 panic("incorrect ASCE on kernel %s\n" 27 "cr1: %016lx cr7: %016lx\n" 28 "kernel: %016lx user: %016lx\n", 29 exit ? "exit" : "entry", cr1.val, cr7.val, 30 get_lowcore()->kernel_asce.val, get_lowcore()->user_asce.val); 31 } 32 #endif /*CONFIG_DEBUG_ENTRY */ 33 34 union oac { 35 unsigned int val; 36 struct { 37 struct { 38 unsigned short key : 4; 39 unsigned short : 4; 40 unsigned short as : 2; 41 unsigned short : 4; 42 unsigned short k : 1; 43 unsigned short a : 1; 44 } oac1; 45 struct { 46 unsigned short key : 4; 47 unsigned short : 4; 48 unsigned short as : 2; 49 unsigned short : 4; 50 unsigned short k : 1; 51 unsigned short a : 1; 52 } oac2; 53 }; 54 }; 55 56 static uaccess_kmsan_or_inline __must_check unsigned long 57 raw_copy_from_user_key(void *to, const void __user *from, unsigned long size, unsigned long key) 58 { 59 unsigned long osize; 60 union oac spec = { 61 .oac2.key = key, 62 .oac2.as = PSW_BITS_AS_SECONDARY, 63 .oac2.k = 1, 64 .oac2.a = 1, 65 }; 66 int cc; 67 68 while (1) { 69 osize = size; 70 asm_inline volatile( 71 " lr %%r0,%[spec]\n" 72 "0: mvcos %[to],%[from],%[size]\n" 73 "1: nopr %%r7\n" 74 CC_IPM(cc) 75 EX_TABLE_UA_MVCOS_FROM(0b, 0b) 76 EX_TABLE_UA_MVCOS_FROM(1b, 0b) 77 : CC_OUT(cc, cc), [size] "+d" (size), [to] "=Q" (*(char *)to) 78 : [spec] "d" (spec.val), [from] "Q" (*(const char __user *)from) 79 : CC_CLOBBER_LIST("memory", "0")); 80 if (CC_TRANSFORM(cc) == 0) 81 return osize - size; 82 size -= 4096; 83 to += 4096; 84 from += 4096; 85 } 86 } 87 88 unsigned long _copy_from_user_key(void *to, const void __user *from, 89 unsigned long n, unsigned long key) 90 { 91 unsigned long res = n; 92 93 might_fault(); 94 if (!should_fail_usercopy()) { 95 instrument_copy_from_user_before(to, from, n); 96 res = raw_copy_from_user_key(to, from, n, key); 97 instrument_copy_from_user_after(to, from, n, res); 98 } 99 if (unlikely(res)) 100 memset(to + (n - res), 0, res); 101 return res; 102 } 103 EXPORT_SYMBOL(_copy_from_user_key); 104 105 static uaccess_kmsan_or_inline __must_check unsigned long 106 raw_copy_to_user_key(void __user *to, const void *from, unsigned long size, unsigned long key) 107 { 108 unsigned long osize; 109 union oac spec = { 110 .oac1.key = key, 111 .oac1.as = PSW_BITS_AS_SECONDARY, 112 .oac1.k = 1, 113 .oac1.a = 1, 114 }; 115 int cc; 116 117 while (1) { 118 osize = size; 119 asm_inline volatile( 120 " lr %%r0,%[spec]\n" 121 "0: mvcos %[to],%[from],%[size]\n" 122 "1: nopr %%r7\n" 123 CC_IPM(cc) 124 EX_TABLE_UA_MVCOS_TO(0b, 0b) 125 EX_TABLE_UA_MVCOS_TO(1b, 0b) 126 : CC_OUT(cc, cc), [size] "+d" (size), [to] "=Q" (*(char __user *)to) 127 : [spec] "d" (spec.val), [from] "Q" (*(const char *)from) 128 : CC_CLOBBER_LIST("memory", "0")); 129 if (CC_TRANSFORM(cc) == 0) 130 return osize - size; 131 size -= 4096; 132 to += 4096; 133 from += 4096; 134 } 135 } 136 137 unsigned long _copy_to_user_key(void __user *to, const void *from, 138 unsigned long n, unsigned long key) 139 { 140 might_fault(); 141 if (should_fail_usercopy()) 142 return n; 143 instrument_copy_to_user(to, from, n); 144 return raw_copy_to_user_key(to, from, n, key); 145 } 146 EXPORT_SYMBOL(_copy_to_user_key); 147