|
12 | 12 | #include <linux/errno.h> |
13 | 13 | #include <linux/gfp.h> |
14 | 14 | #include <linux/cpu.h> |
| 15 | +#include <linux/uio.h> |
15 | 16 | #include <asm/asm-extable.h> |
16 | 17 | #include <asm/ctl_reg.h> |
17 | 18 | #include <asm/io.h> |
18 | 19 | #include <asm/abs_lowcore.h> |
19 | 20 | #include <asm/stacktrace.h> |
20 | 21 |
|
| 22 | +unsigned long __bootdata_preserved(__memcpy_real_area); |
| 23 | +static __ro_after_init pte_t *memcpy_real_ptep; |
| 24 | +static DEFINE_MUTEX(memcpy_real_mutex); |
| 25 | + |
21 | 26 | static notrace long s390_kernel_write_odd(void *dst, const void *src, size_t size) |
22 | 27 | { |
23 | 28 | unsigned long aligned, offset, count; |
@@ -77,75 +82,55 @@ notrace void *s390_kernel_write(void *dst, const void *src, size_t size) |
77 | 82 | return dst; |
78 | 83 | } |
79 | 84 |
|
80 | | -static int __no_sanitize_address __memcpy_real(void *dest, void *src, size_t count) |
| 85 | +void __init memcpy_real_init(void) |
81 | 86 | { |
82 | | - union register_pair _dst, _src; |
83 | | - int rc = -EFAULT; |
84 | | - |
85 | | - _dst.even = (unsigned long) dest; |
86 | | - _dst.odd = (unsigned long) count; |
87 | | - _src.even = (unsigned long) src; |
88 | | - _src.odd = (unsigned long) count; |
89 | | - asm volatile ( |
90 | | - "0: mvcle %[dst],%[src],0\n" |
91 | | - "1: jo 0b\n" |
92 | | - " lhi %[rc],0\n" |
93 | | - "2:\n" |
94 | | - EX_TABLE(1b,2b) |
95 | | - : [rc] "+&d" (rc), [dst] "+&d" (_dst.pair), [src] "+&d" (_src.pair) |
96 | | - : : "cc", "memory"); |
97 | | - return rc; |
| 87 | + memcpy_real_ptep = vmem_get_alloc_pte(__memcpy_real_area, true); |
| 88 | + if (!memcpy_real_ptep) |
| 89 | + panic("Couldn't setup memcpy real area"); |
98 | 90 | } |
99 | 91 |
|
100 | | -static unsigned long __no_sanitize_address _memcpy_real(unsigned long dest, |
101 | | - unsigned long src, |
102 | | - unsigned long count) |
| 92 | +size_t memcpy_real_iter(struct iov_iter *iter, unsigned long src, size_t count) |
103 | 93 | { |
104 | | - int irqs_disabled, rc; |
105 | | - unsigned long flags; |
106 | | - |
107 | | - if (!count) |
108 | | - return 0; |
109 | | - flags = arch_local_irq_save(); |
110 | | - irqs_disabled = arch_irqs_disabled_flags(flags); |
111 | | - if (!irqs_disabled) |
112 | | - trace_hardirqs_off(); |
113 | | - __arch_local_irq_stnsm(0xf8); // disable DAT |
114 | | - rc = __memcpy_real((void *) dest, (void *) src, (size_t) count); |
115 | | - if (flags & PSW_MASK_DAT) |
116 | | - __arch_local_irq_stosm(0x04); // enable DAT |
117 | | - if (!irqs_disabled) |
118 | | - trace_hardirqs_on(); |
119 | | - __arch_local_irq_ssm(flags); |
120 | | - return rc; |
| 94 | + size_t len, copied, res = 0; |
| 95 | + unsigned long phys, offset; |
| 96 | + void *chunk; |
| 97 | + pte_t pte; |
| 98 | + |
| 99 | + while (count) { |
| 100 | + phys = src & PAGE_MASK; |
| 101 | + offset = src & ~PAGE_MASK; |
| 102 | + chunk = (void *)(__memcpy_real_area + offset); |
| 103 | + len = min(count, PAGE_SIZE - offset); |
| 104 | + pte = mk_pte_phys(phys, PAGE_KERNEL_RO); |
| 105 | + |
| 106 | + mutex_lock(&memcpy_real_mutex); |
| 107 | + if (pte_val(pte) != pte_val(*memcpy_real_ptep)) { |
| 108 | + __ptep_ipte(__memcpy_real_area, memcpy_real_ptep, 0, 0, IPTE_GLOBAL); |
| 109 | + set_pte(memcpy_real_ptep, pte); |
| 110 | + } |
| 111 | + copied = copy_to_iter(chunk, len, iter); |
| 112 | + mutex_unlock(&memcpy_real_mutex); |
| 113 | + |
| 114 | + count -= copied; |
| 115 | + src += copied; |
| 116 | + res += copied; |
| 117 | + if (copied < len) |
| 118 | + break; |
| 119 | + } |
| 120 | + return res; |
121 | 121 | } |
122 | 122 |
|
123 | | -/* |
124 | | - * Copy memory in real mode (kernel to kernel) |
125 | | - */ |
126 | 123 | int memcpy_real(void *dest, unsigned long src, size_t count) |
127 | 124 | { |
128 | | - unsigned long _dest = (unsigned long)dest; |
129 | | - unsigned long _src = (unsigned long)src; |
130 | | - unsigned long _count = (unsigned long)count; |
131 | | - int rc; |
132 | | - |
133 | | - if (S390_lowcore.nodat_stack != 0) { |
134 | | - preempt_disable(); |
135 | | - rc = call_on_stack(3, S390_lowcore.nodat_stack, |
136 | | - unsigned long, _memcpy_real, |
137 | | - unsigned long, _dest, |
138 | | - unsigned long, _src, |
139 | | - unsigned long, _count); |
140 | | - preempt_enable(); |
141 | | - return rc; |
142 | | - } |
143 | | - /* |
144 | | - * This is a really early memcpy_real call, the stacks are |
145 | | - * not set up yet. Just call _memcpy_real on the early boot |
146 | | - * stack |
147 | | - */ |
148 | | - return _memcpy_real(_dest, _src, _count); |
| 125 | + struct iov_iter iter; |
| 126 | + struct kvec kvec; |
| 127 | + |
| 128 | + kvec.iov_base = dest; |
| 129 | + kvec.iov_len = count; |
| 130 | + iov_iter_kvec(&iter, WRITE, &kvec, 1, count); |
| 131 | + if (memcpy_real_iter(&iter, src, count) < count) |
| 132 | + return -EFAULT; |
| 133 | + return 0; |
149 | 134 | } |
150 | 135 |
|
151 | 136 | /* |
|
0 commit comments