Skip to content

Commit 2f0e8aa

Browse files
Alexander GordeevVasily Gorbik
authored andcommitted
s390/mm: rework memcpy_real() to avoid DAT-off mode
Function memcpy_real() is an univeral data mover that does not require DAT mode to be able reading from a physical address. Its advantage is an ability to read from any address, even those for which no kernel virtual mapping exists. Although memcpy_real() is interrupt-safe, there are no handlers that make use of this function. The compiler instrumentation have to be disabled and separate no-DAT stack used to allow execution of the function once DAT mode is disabled. Rework memcpy_real() to overcome these shortcomings. As result, data copying (which is primarily reading out a crashed system memory by a user process) is executed on a regular stack with enabled interrupts. Also, use of memcpy_real_buf swap buffer becomes unnecessary and the swapping is eliminated. The above is achieved by using a fixed virtual address range that spans a single page and remaps that page repeatedly when memcpy_real() is called for a particular physical address. Reviewed-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
1 parent 14a3a26 commit 2f0e8aa

9 files changed

Lines changed: 71 additions & 89 deletions

File tree

arch/s390/boot/startup.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
unsigned long __bootdata_preserved(__kaslr_offset);
1919
unsigned long __bootdata_preserved(__abs_lowcore);
20+
unsigned long __bootdata_preserved(__memcpy_real_area);
2021
unsigned long __bootdata(__amode31_base);
2122
unsigned long __bootdata_preserved(VMALLOC_START);
2223
unsigned long __bootdata_preserved(VMALLOC_END);
@@ -182,7 +183,9 @@ static void setup_kernel_memory_layout(void)
182183
/* force vmalloc and modules below kasan shadow */
183184
vmax = min(vmax, KASAN_SHADOW_START);
184185
#endif
185-
__abs_lowcore = round_down(vmax - ABS_LOWCORE_MAP_SIZE, sizeof(struct lowcore));
186+
__memcpy_real_area = round_down(vmax - PAGE_SIZE, PAGE_SIZE);
187+
__abs_lowcore = round_down(__memcpy_real_area - ABS_LOWCORE_MAP_SIZE,
188+
sizeof(struct lowcore));
186189
MODULES_END = round_down(__abs_lowcore, _SEGMENT_SIZE);
187190
MODULES_VADDR = MODULES_END - MODULES_LEN;
188191
VMALLOC_END = MODULES_VADDR;

arch/s390/include/asm/maccess.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __ASM_S390_MACCESS_H
3+
#define __ASM_S390_MACCESS_H
4+
5+
#include <linux/types.h>
6+
7+
struct iov_iter;
8+
9+
extern unsigned long __memcpy_real_area;
10+
void memcpy_real_init(void);
11+
size_t memcpy_real_iter(struct iov_iter *iter, unsigned long src, size_t count);
12+
int memcpy_real(void *dest, unsigned long src, size_t count);
13+
14+
#endif /* __ASM_S390_MACCESS_H */

arch/s390/include/asm/pgtable.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,6 +1780,7 @@ extern void vmem_remove_mapping(unsigned long start, unsigned long size);
17801780
extern int __vmem_map_4k_page(unsigned long addr, unsigned long phys, pgprot_t prot, bool alloc);
17811781
extern int vmem_map_4k_page(unsigned long addr, unsigned long phys, pgprot_t prot);
17821782
extern void vmem_unmap_4k_page(unsigned long addr);
1783+
extern pte_t *vmem_get_alloc_pte(unsigned long addr, bool alloc);
17831784
extern int s390_enable_sie(void);
17841785
extern int s390_enable_skey(void);
17851786
extern void s390_reset_cmma(struct mm_struct *mm);

arch/s390/include/asm/processor.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,6 @@ static __always_inline void __noreturn disabled_wait(void)
306306

307307
#define ARCH_LOW_ADDRESS_LIMIT 0x7fffffffUL
308308

309-
extern int memcpy_real(void *, unsigned long, size_t);
310-
311309
extern int s390_isolate_bp(void);
312310
extern int s390_isolate_bp_guest(void);
313311

arch/s390/kernel/crash_dump.c

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <asm/elf.h>
2222
#include <asm/ipl.h>
2323
#include <asm/sclp.h>
24+
#include <asm/maccess.h>
2425

2526
#define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))
2627
#define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
@@ -53,8 +54,6 @@ struct save_area {
5354
};
5455

5556
static LIST_HEAD(dump_save_areas);
56-
static DEFINE_MUTEX(memcpy_real_mutex);
57-
static char memcpy_real_buf[PAGE_SIZE];
5857

5958
/*
6059
* Allocate a save area
@@ -116,26 +115,6 @@ void __init save_area_add_vxrs(struct save_area *sa, __vector128 *vxrs)
116115
memcpy(sa->vxrs_high, vxrs + 16, 16 * sizeof(__vector128));
117116
}
118117

119-
static size_t copy_to_iter_real(struct iov_iter *iter, unsigned long src, size_t count)
120-
{
121-
size_t len, copied, res = 0;
122-
123-
mutex_lock(&memcpy_real_mutex);
124-
while (count) {
125-
len = min(PAGE_SIZE, count);
126-
if (memcpy_real(memcpy_real_buf, src, len))
127-
break;
128-
copied = copy_to_iter(memcpy_real_buf, len, iter);
129-
count -= copied;
130-
src += copied;
131-
res += copied;
132-
if (copied < len)
133-
break;
134-
}
135-
mutex_unlock(&memcpy_real_mutex);
136-
return res;
137-
}
138-
139118
size_t copy_oldmem_iter(struct iov_iter *iter, unsigned long src, size_t count)
140119
{
141120
size_t len, copied, res = 0;
@@ -156,7 +135,7 @@ size_t copy_oldmem_iter(struct iov_iter *iter, unsigned long src, size_t count)
156135
} else {
157136
len = count;
158137
}
159-
copied = copy_to_iter_real(iter, src, len);
138+
copied = memcpy_real_iter(iter, src, len);
160139
}
161140
count -= copied;
162141
src += copied;

arch/s390/kernel/setup.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
#include <asm/alternative.h>
7575
#include <asm/nospec-branch.h>
7676
#include <asm/mem_detect.h>
77+
#include <asm/maccess.h>
7778
#include <asm/uv.h>
7879
#include <asm/asm-offsets.h>
7980
#include "entry.h"
@@ -1050,7 +1051,7 @@ void __init setup_arch(char **cmdline_p)
10501051
* Create kernel page tables and switch to virtual addressing.
10511052
*/
10521053
paging_init();
1053-
1054+
memcpy_real_init();
10541055
/*
10551056
* After paging_init created the kernel page table, the new PSWs
10561057
* in lowcore can now run with DAT enabled.

arch/s390/mm/maccess.c

Lines changed: 46 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,17 @@
1212
#include <linux/errno.h>
1313
#include <linux/gfp.h>
1414
#include <linux/cpu.h>
15+
#include <linux/uio.h>
1516
#include <asm/asm-extable.h>
1617
#include <asm/ctl_reg.h>
1718
#include <asm/io.h>
1819
#include <asm/abs_lowcore.h>
1920
#include <asm/stacktrace.h>
2021

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+
2126
static notrace long s390_kernel_write_odd(void *dst, const void *src, size_t size)
2227
{
2328
unsigned long aligned, offset, count;
@@ -77,75 +82,55 @@ notrace void *s390_kernel_write(void *dst, const void *src, size_t size)
7782
return dst;
7883
}
7984

80-
static int __no_sanitize_address __memcpy_real(void *dest, void *src, size_t count)
85+
void __init memcpy_real_init(void)
8186
{
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");
9890
}
9991

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)
10393
{
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;
121121
}
122122

123-
/*
124-
* Copy memory in real mode (kernel to kernel)
125-
*/
126123
int memcpy_real(void *dest, unsigned long src, size_t count)
127124
{
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;
149134
}
150135

151136
/*

arch/s390/mm/vmem.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ int vmem_add_mapping(unsigned long start, unsigned long size)
567567
* while traversing is an error, since the function is expected to be
568568
* called against virtual regions reserverd for 4KB mappings only.
569569
*/
570-
static pte_t *vmem_get_alloc_pte(unsigned long addr, bool alloc)
570+
pte_t *vmem_get_alloc_pte(unsigned long addr, bool alloc)
571571
{
572572
pte_t *ptep = NULL;
573573
pgd_t *pgd;

drivers/s390/char/zcore.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <asm/checksum.h>
3131
#include <asm/os_info.h>
3232
#include <asm/switch_to.h>
33+
#include <asm/maccess.h>
3334
#include "sclp.h"
3435

3536
#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x)

0 commit comments

Comments
 (0)