mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
[S390] zcore: CPU registers are not saved under LPAR
To save the registers for all CPUs a sigp "store status" is done that stores the registers to address absolute zero. To access storage at absolute zero, normally the address of the prefix register of the accessing CPU has to be used. This does not work when large pages are active (currently only under LPAR). In order to fix that problem, instead of memcpy memcpy_real is used, which switches to real mode where prefixing works. Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
ae6be51ed0
commit
92fe31329c
@ -110,6 +110,7 @@ extern void pfault_fini(void);
|
||||
#endif /* CONFIG_PFAULT */
|
||||
|
||||
extern void cmma_init(void);
|
||||
extern int memcpy_real(void *, void *, size_t);
|
||||
|
||||
#define finish_arch_switch(prev) do { \
|
||||
set_fs(current->thread.mm_segment); \
|
||||
|
@ -292,9 +292,9 @@ static void __init smp_get_save_area(unsigned int cpu, unsigned int phy_cpu)
|
||||
zfcpdump_save_areas[cpu] = kmalloc(sizeof(struct save_area), GFP_KERNEL);
|
||||
while (raw_sigp(phy_cpu, sigp_stop_and_store_status) == sigp_busy)
|
||||
cpu_relax();
|
||||
memcpy(zfcpdump_save_areas[cpu],
|
||||
(void *)(unsigned long) store_prefix() + SAVE_AREA_BASE,
|
||||
sizeof(struct save_area));
|
||||
memcpy_real(zfcpdump_save_areas[cpu],
|
||||
(void *)(unsigned long) store_prefix() + SAVE_AREA_BASE,
|
||||
sizeof(struct save_area));
|
||||
}
|
||||
|
||||
struct save_area *zfcpdump_save_areas[NR_CPUS + 1];
|
||||
|
@ -59,3 +59,29 @@ long probe_kernel_write(void *dst, void *src, size_t size)
|
||||
}
|
||||
return copied < 0 ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
int memcpy_real(void *dest, void *src, size_t count)
|
||||
{
|
||||
register unsigned long _dest asm("2") = (unsigned long) dest;
|
||||
register unsigned long _len1 asm("3") = (unsigned long) count;
|
||||
register unsigned long _src asm("4") = (unsigned long) src;
|
||||
register unsigned long _len2 asm("5") = (unsigned long) count;
|
||||
unsigned long flags;
|
||||
int rc = -EFAULT;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
flags = __raw_local_irq_stnsm(0xf8UL);
|
||||
asm volatile (
|
||||
"0: mvcle %1,%2,0x0\n"
|
||||
"1: jo 0b\n"
|
||||
" lhi %0,0x0\n"
|
||||
"2:\n"
|
||||
EX_TABLE(1b,2b)
|
||||
: "+d" (rc), "+d" (_dest), "+d" (_src), "+d" (_len1),
|
||||
"+d" (_len2), "=m" (*((long *) dest))
|
||||
: "m" (*((long *) src))
|
||||
: "cc", "memory");
|
||||
__raw_local_irq_ssm(flags);
|
||||
return rc;
|
||||
}
|
||||
|
@ -141,33 +141,6 @@ static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
|
||||
return memcpy_hsa(dest, src, count, TO_KERNEL);
|
||||
}
|
||||
|
||||
static int memcpy_real(void *dest, unsigned long src, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
int rc = -EFAULT;
|
||||
register unsigned long _dest asm("2") = (unsigned long) dest;
|
||||
register unsigned long _len1 asm("3") = (unsigned long) count;
|
||||
register unsigned long _src asm("4") = src;
|
||||
register unsigned long _len2 asm("5") = (unsigned long) count;
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
flags = __raw_local_irq_stnsm(0xf8UL); /* switch to real mode */
|
||||
asm volatile (
|
||||
"0: mvcle %1,%2,0x0\n"
|
||||
"1: jo 0b\n"
|
||||
" lhi %0,0x0\n"
|
||||
"2:\n"
|
||||
EX_TABLE(1b,2b)
|
||||
: "+d" (rc), "+d" (_dest), "+d" (_src), "+d" (_len1),
|
||||
"+d" (_len2), "=m" (*((long*)dest))
|
||||
: "m" (*((long*)src))
|
||||
: "cc", "memory");
|
||||
__raw_local_irq_ssm(flags);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int memcpy_real_user(void __user *dest, unsigned long src, size_t count)
|
||||
{
|
||||
static char buf[4096];
|
||||
@ -175,7 +148,7 @@ static int memcpy_real_user(void __user *dest, unsigned long src, size_t count)
|
||||
|
||||
while (offs < count) {
|
||||
size = min(sizeof(buf), count - offs);
|
||||
if (memcpy_real(buf, src + offs, size))
|
||||
if (memcpy_real(buf, (void *) src + offs, size))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(dest + offs, buf, size))
|
||||
return -EFAULT;
|
||||
@ -663,7 +636,7 @@ static int __init zcore_reipl_init(void)
|
||||
if (ipib_info.ipib < ZFCPDUMP_HSA_SIZE)
|
||||
rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE);
|
||||
else
|
||||
rc = memcpy_real(ipl_block, ipib_info.ipib, PAGE_SIZE);
|
||||
rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE);
|
||||
if (rc) {
|
||||
free_page((unsigned long) ipl_block);
|
||||
return rc;
|
||||
|
Loading…
Reference in New Issue
Block a user