diff options
Diffstat (limited to 'arch/s390/kernel/crash_dump.c')
| -rw-r--r-- | arch/s390/kernel/crash_dump.c | 51 | 
1 files changed, 47 insertions, 4 deletions
diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c index f703d91bf720..d8f355657171 100644 --- a/arch/s390/kernel/crash_dump.c +++ b/arch/s390/kernel/crash_dump.c @@ -21,6 +21,48 @@  #define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))  #define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y)))) + +/* + * Return physical address for virtual address + */ +static inline void *load_real_addr(void *addr) +{ +	unsigned long real_addr; + +	asm volatile( +		   "	lra     %0,0(%1)\n" +		   "	jz	0f\n" +		   "	la	%0,0\n" +		   "0:" +		   : "=a" (real_addr) : "a" (addr) : "cc"); +	return (void *)real_addr; +} + +/* + * Copy up to one page to vmalloc or real memory + */ +static ssize_t copy_page_real(void *buf, void *src, size_t csize) +{ +	size_t size; + +	if (is_vmalloc_addr(buf)) { +		BUG_ON(csize >= PAGE_SIZE); +		/* If buf is not page aligned, copy first part */ +		size = min(roundup(__pa(buf), PAGE_SIZE) - __pa(buf), csize); +		if (size) { +			if (memcpy_real(load_real_addr(buf), src, size)) +				return -EFAULT; +			buf += size; +			src += size; +		} +		/* Copy second part */ +		size = csize - size; +		return (size) ? memcpy_real(load_real_addr(buf), src, size) : 0; +	} else { +		return memcpy_real(buf, src, csize); +	} +} +  /*   * Copy one page from "oldmem"   * @@ -32,6 +74,7 @@ ssize_t copy_oldmem_page(unsigned long pfn, char *buf,  			 size_t csize, unsigned long offset, int userbuf)  {  	unsigned long src; +	int rc;  	if (!csize)  		return 0; @@ -43,11 +86,11 @@ ssize_t copy_oldmem_page(unsigned long pfn, char *buf,  		 src < OLDMEM_BASE + OLDMEM_SIZE)  		src -= OLDMEM_BASE;  	if (userbuf) -		copy_to_user_real((void __force __user *) buf, (void *) src, -				  csize); +		rc = copy_to_user_real((void __force __user *) buf, +				       (void *) src, csize);  	else -		memcpy_real(buf, (void *) src, csize); -	return csize; +		rc = copy_page_real(buf, (void *) src, csize); +	return (rc == 0) ? csize : rc;  }  /*  | 
