Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 92fe3132 authored by Michael Holzheu's avatar Michael Holzheu Committed by Martin Schwidefsky
Browse files

[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: default avatarMichael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent ae6be51e
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -110,6 +110,7 @@ extern void pfault_fini(void);
#endif /* CONFIG_PFAULT */
#endif /* CONFIG_PFAULT */


extern void cmma_init(void);
extern void cmma_init(void);
extern int memcpy_real(void *, void *, size_t);


#define finish_arch_switch(prev) do {					     \
#define finish_arch_switch(prev) do {					     \
	set_fs(current->thread.mm_segment);				     \
	set_fs(current->thread.mm_segment);				     \
+3 −3
Original line number Original line Diff line number Diff line
@@ -292,7 +292,7 @@ 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);
	zfcpdump_save_areas[cpu] = kmalloc(sizeof(struct save_area), GFP_KERNEL);
	while (raw_sigp(phy_cpu, sigp_stop_and_store_status) == sigp_busy)
	while (raw_sigp(phy_cpu, sigp_stop_and_store_status) == sigp_busy)
		cpu_relax();
		cpu_relax();
	memcpy(zfcpdump_save_areas[cpu],
	memcpy_real(zfcpdump_save_areas[cpu],
		    (void *)(unsigned long) store_prefix() + SAVE_AREA_BASE,
		    (void *)(unsigned long) store_prefix() + SAVE_AREA_BASE,
		    sizeof(struct save_area));
		    sizeof(struct save_area));
}
}
+26 −0
Original line number Original line Diff line number Diff line
@@ -59,3 +59,29 @@ long probe_kernel_write(void *dst, void *src, size_t size)
	}
	}
	return copied < 0 ? -EFAULT : 0;
	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;
}
+2 −29
Original line number Original line Diff line number Diff line
@@ -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);
	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 int memcpy_real_user(void __user *dest, unsigned long src, size_t count)
{
{
	static char buf[4096];
	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) {
	while (offs < count) {
		size = min(sizeof(buf), count - offs);
		size = min(sizeof(buf), count - offs);
		if (memcpy_real(buf, src + offs, size))
		if (memcpy_real(buf, (void *) src + offs, size))
			return -EFAULT;
			return -EFAULT;
		if (copy_to_user(dest + offs, buf, size))
		if (copy_to_user(dest + offs, buf, size))
			return -EFAULT;
			return -EFAULT;
@@ -663,7 +636,7 @@ static int __init zcore_reipl_init(void)
	if (ipib_info.ipib < ZFCPDUMP_HSA_SIZE)
	if (ipib_info.ipib < ZFCPDUMP_HSA_SIZE)
		rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE);
		rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE);
	else
	else
		rc = memcpy_real(ipl_block, ipib_info.ipib, PAGE_SIZE);
		rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE);
	if (rc) {
	if (rc) {
		free_page((unsigned long) ipl_block);
		free_page((unsigned long) ipl_block);
		return rc;
		return rc;