s390/dump: streamline oldmem copy functions
Introduce two copy functions for the memory of the dumped system, copy_oldmem_kernel() to copy to the virtual kernel address space and copy_oldmem_user() to copy to user space. Acked-by: Michael Holzheu <holzheu@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
8a07dd02d7
commit
df9694c797
6 changed files with 105 additions and 102 deletions
|
@ -38,7 +38,7 @@ u32 os_info_csum(struct os_info *os_info);
|
||||||
|
|
||||||
#ifdef CONFIG_CRASH_DUMP
|
#ifdef CONFIG_CRASH_DUMP
|
||||||
void *os_info_old_entry(int nr, unsigned long *size);
|
void *os_info_old_entry(int nr, unsigned long *size);
|
||||||
int copy_from_oldmem(void *dest, void *src, size_t count);
|
int copy_oldmem_kernel(void *dst, void *src, size_t count);
|
||||||
#else
|
#else
|
||||||
static inline void *os_info_old_entry(int nr, unsigned long *size)
|
static inline void *os_info_old_entry(int nr, unsigned long *size)
|
||||||
{
|
{
|
||||||
|
|
|
@ -77,7 +77,8 @@ int sclp_chp_read_info(struct sclp_chp_info *info);
|
||||||
void sclp_get_ipl_info(struct sclp_ipl_info *info);
|
void sclp_get_ipl_info(struct sclp_ipl_info *info);
|
||||||
int sclp_pci_configure(u32 fid);
|
int sclp_pci_configure(u32 fid);
|
||||||
int sclp_pci_deconfigure(u32 fid);
|
int sclp_pci_deconfigure(u32 fid);
|
||||||
int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode);
|
int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count);
|
||||||
|
int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count);
|
||||||
void sclp_early_detect(void);
|
void sclp_early_detect(void);
|
||||||
int _sclp_print_early(const char *);
|
int _sclp_print_early(const char *);
|
||||||
|
|
||||||
|
|
|
@ -51,74 +51,85 @@ static inline void *load_real_addr(void *addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy real to virtual or real memory
|
* Copy memory of the old, dumped system to a kernel space virtual address
|
||||||
*/
|
*/
|
||||||
static int copy_from_realmem(void *dest, void *src, size_t count)
|
int copy_oldmem_kernel(void *dst, void *src, size_t count)
|
||||||
{
|
|
||||||
unsigned long size;
|
|
||||||
|
|
||||||
if (!count)
|
|
||||||
return 0;
|
|
||||||
if (!is_vmalloc_or_module_addr(dest))
|
|
||||||
return memcpy_real(dest, src, count);
|
|
||||||
do {
|
|
||||||
size = min(count, PAGE_SIZE - (__pa(dest) & ~PAGE_MASK));
|
|
||||||
if (memcpy_real(load_real_addr(dest), src, size))
|
|
||||||
return -EFAULT;
|
|
||||||
count -= size;
|
|
||||||
dest += size;
|
|
||||||
src += size;
|
|
||||||
} while (count);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy one page from zfcpdump "oldmem"
|
|
||||||
*
|
|
||||||
* For pages below HSA size memory from the HSA is copied. Otherwise
|
|
||||||
* real memory copy is used.
|
|
||||||
*/
|
|
||||||
static ssize_t copy_oldmem_page_zfcpdump(char *buf, size_t csize,
|
|
||||||
unsigned long src, int userbuf)
|
|
||||||
{
|
{
|
||||||
|
unsigned long from, len;
|
||||||
|
void *ra;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (src < sclp.hsa_size) {
|
while (count) {
|
||||||
rc = memcpy_hsa(buf, src, csize, userbuf);
|
from = __pa(src);
|
||||||
|
if (!OLDMEM_BASE && from < sclp.hsa_size) {
|
||||||
|
/* Copy from zfcpdump HSA area */
|
||||||
|
len = min(count, sclp.hsa_size - from);
|
||||||
|
rc = memcpy_hsa_kernel(dst, from, len);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
} else {
|
} else {
|
||||||
if (userbuf)
|
/* Check for swapped kdump oldmem areas */
|
||||||
rc = copy_to_user_real((void __force __user *) buf,
|
if (OLDMEM_BASE && from - OLDMEM_BASE < OLDMEM_SIZE) {
|
||||||
(void *) src, csize);
|
from -= OLDMEM_BASE;
|
||||||
else
|
len = min(count, OLDMEM_SIZE - from);
|
||||||
rc = memcpy_real(buf, (void *) src, csize);
|
} else if (OLDMEM_BASE && from < OLDMEM_SIZE) {
|
||||||
|
len = min(count, OLDMEM_SIZE - from);
|
||||||
|
from += OLDMEM_BASE;
|
||||||
|
} else {
|
||||||
|
len = count;
|
||||||
}
|
}
|
||||||
return rc ? rc : csize;
|
if (is_vmalloc_or_module_addr(dst)) {
|
||||||
|
ra = load_real_addr(dst);
|
||||||
|
len = min(PAGE_SIZE - offset_in_page(ra), len);
|
||||||
|
} else {
|
||||||
|
ra = dst;
|
||||||
|
}
|
||||||
|
if (memcpy_real(ra, (void *) from, len))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
dst += len;
|
||||||
|
src += len;
|
||||||
|
count -= len;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy one page from kdump "oldmem"
|
* Copy memory of the old, dumped system to a user space virtual address
|
||||||
*
|
|
||||||
* For the kdump reserved memory this functions performs a swap operation:
|
|
||||||
* - [OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE] is mapped to [0 - OLDMEM_SIZE].
|
|
||||||
* - [0 - OLDMEM_SIZE] is mapped to [OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE]
|
|
||||||
*/
|
*/
|
||||||
static ssize_t copy_oldmem_page_kdump(char *buf, size_t csize,
|
int copy_oldmem_user(void __user *dst, void *src, size_t count)
|
||||||
unsigned long src, int userbuf)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
|
unsigned long from, len;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (src < OLDMEM_SIZE)
|
while (count) {
|
||||||
src += OLDMEM_BASE;
|
from = __pa(src);
|
||||||
else if (src > OLDMEM_BASE &&
|
if (!OLDMEM_BASE && from < sclp.hsa_size) {
|
||||||
src < OLDMEM_BASE + OLDMEM_SIZE)
|
/* Copy from zfcpdump HSA area */
|
||||||
src -= OLDMEM_BASE;
|
len = min(count, sclp.hsa_size - from);
|
||||||
if (userbuf)
|
rc = memcpy_hsa_user(dst, from, len);
|
||||||
rc = copy_to_user_real((void __force __user *) buf,
|
if (rc)
|
||||||
(void *) src, csize);
|
return rc;
|
||||||
else
|
} else {
|
||||||
rc = copy_from_realmem(buf, (void *) src, csize);
|
/* Check for swapped kdump oldmem areas */
|
||||||
return (rc == 0) ? rc : csize;
|
if (OLDMEM_BASE && from - OLDMEM_BASE < OLDMEM_SIZE) {
|
||||||
|
from -= OLDMEM_BASE;
|
||||||
|
len = min(count, OLDMEM_SIZE - from);
|
||||||
|
} else if (OLDMEM_BASE && from < OLDMEM_SIZE) {
|
||||||
|
len = min(count, OLDMEM_SIZE - from);
|
||||||
|
from += OLDMEM_BASE;
|
||||||
|
} else {
|
||||||
|
len = count;
|
||||||
|
}
|
||||||
|
rc = copy_to_user_real(dst, (void *) from, count);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
dst += len;
|
||||||
|
src += len;
|
||||||
|
count -= len;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -127,15 +138,17 @@ static ssize_t copy_oldmem_page_kdump(char *buf, size_t csize,
|
||||||
ssize_t copy_oldmem_page(unsigned long pfn, char *buf, size_t csize,
|
ssize_t copy_oldmem_page(unsigned long pfn, char *buf, size_t csize,
|
||||||
unsigned long offset, int userbuf)
|
unsigned long offset, int userbuf)
|
||||||
{
|
{
|
||||||
unsigned long src;
|
void *src;
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (!csize)
|
if (!csize)
|
||||||
return 0;
|
return 0;
|
||||||
src = (pfn << PAGE_SHIFT) + offset;
|
src = (void *) (pfn << PAGE_SHIFT) + offset;
|
||||||
if (OLDMEM_BASE)
|
if (userbuf)
|
||||||
return copy_oldmem_page_kdump(buf, csize, src, userbuf);
|
rc = copy_oldmem_user((void __force __user *) buf, src, csize);
|
||||||
else
|
else
|
||||||
return copy_oldmem_page_zfcpdump(buf, csize, src, userbuf);
|
rc = copy_oldmem_kernel((void *) buf, src, csize);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -203,33 +216,6 @@ int remap_oldmem_pfn_range(struct vm_area_struct *vma, unsigned long from,
|
||||||
prot);
|
prot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy memory from old kernel
|
|
||||||
*/
|
|
||||||
int copy_from_oldmem(void *dest, void *src, size_t count)
|
|
||||||
{
|
|
||||||
unsigned long copied = 0;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (OLDMEM_BASE) {
|
|
||||||
if ((unsigned long) src < OLDMEM_SIZE) {
|
|
||||||
copied = min(count, OLDMEM_SIZE - (unsigned long) src);
|
|
||||||
rc = copy_from_realmem(dest, src + OLDMEM_BASE, copied);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unsigned long hsa_end = sclp.hsa_size;
|
|
||||||
if ((unsigned long) src < hsa_end) {
|
|
||||||
copied = min(count, hsa_end - (unsigned long) src);
|
|
||||||
rc = memcpy_hsa(dest, (unsigned long) src, copied, 0);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return copy_from_realmem(dest + copied, src + copied, count - copied);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alloc memory and panic in case of ENOMEM
|
* Alloc memory and panic in case of ENOMEM
|
||||||
*/
|
*/
|
||||||
|
@ -425,17 +411,18 @@ static void *get_vmcoreinfo_old(unsigned long *size)
|
||||||
Elf64_Nhdr note;
|
Elf64_Nhdr note;
|
||||||
void *addr;
|
void *addr;
|
||||||
|
|
||||||
if (copy_from_oldmem(&addr, &S390_lowcore.vmcore_info, sizeof(addr)))
|
if (copy_oldmem_kernel(&addr, &S390_lowcore.vmcore_info, sizeof(addr)))
|
||||||
return NULL;
|
return NULL;
|
||||||
memset(nt_name, 0, sizeof(nt_name));
|
memset(nt_name, 0, sizeof(nt_name));
|
||||||
if (copy_from_oldmem(¬e, addr, sizeof(note)))
|
if (copy_oldmem_kernel(¬e, addr, sizeof(note)))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (copy_from_oldmem(nt_name, addr + sizeof(note), sizeof(nt_name) - 1))
|
if (copy_oldmem_kernel(nt_name, addr + sizeof(note),
|
||||||
|
sizeof(nt_name) - 1))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (strcmp(nt_name, "VMCOREINFO") != 0)
|
if (strcmp(nt_name, "VMCOREINFO") != 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
vmcoreinfo = kzalloc_panic(note.n_descsz);
|
vmcoreinfo = kzalloc_panic(note.n_descsz);
|
||||||
if (copy_from_oldmem(vmcoreinfo, addr + 24, note.n_descsz))
|
if (copy_oldmem_kernel(vmcoreinfo, addr + 24, note.n_descsz))
|
||||||
return NULL;
|
return NULL;
|
||||||
*size = note.n_descsz;
|
*size = note.n_descsz;
|
||||||
return vmcoreinfo;
|
return vmcoreinfo;
|
||||||
|
|
|
@ -89,7 +89,7 @@ static void os_info_old_alloc(int nr, int align)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
buf_align = PTR_ALIGN(buf, align);
|
buf_align = PTR_ALIGN(buf, align);
|
||||||
if (copy_from_oldmem(buf_align, (void *) addr, size)) {
|
if (copy_oldmem_kernel(buf_align, (void *) addr, size)) {
|
||||||
msg = "copy failed";
|
msg = "copy failed";
|
||||||
goto fail_free;
|
goto fail_free;
|
||||||
}
|
}
|
||||||
|
@ -122,14 +122,15 @@ static void os_info_old_init(void)
|
||||||
return;
|
return;
|
||||||
if (!OLDMEM_BASE)
|
if (!OLDMEM_BASE)
|
||||||
goto fail;
|
goto fail;
|
||||||
if (copy_from_oldmem(&addr, &S390_lowcore.os_info, sizeof(addr)))
|
if (copy_oldmem_kernel(&addr, &S390_lowcore.os_info, sizeof(addr)))
|
||||||
goto fail;
|
goto fail;
|
||||||
if (addr == 0 || addr % PAGE_SIZE)
|
if (addr == 0 || addr % PAGE_SIZE)
|
||||||
goto fail;
|
goto fail;
|
||||||
os_info_old = kzalloc(sizeof(*os_info_old), GFP_KERNEL);
|
os_info_old = kzalloc(sizeof(*os_info_old), GFP_KERNEL);
|
||||||
if (!os_info_old)
|
if (!os_info_old)
|
||||||
goto fail;
|
goto fail;
|
||||||
if (copy_from_oldmem(os_info_old, (void *) addr, sizeof(*os_info_old)))
|
if (copy_oldmem_kernel(os_info_old, (void *) addr,
|
||||||
|
sizeof(*os_info_old)))
|
||||||
goto fail_free;
|
goto fail_free;
|
||||||
if (os_info_old->magic != OS_INFO_MAGIC)
|
if (os_info_old->magic != OS_INFO_MAGIC)
|
||||||
goto fail_free;
|
goto fail_free;
|
||||||
|
|
|
@ -546,8 +546,8 @@ static void __init __smp_store_cpu_state(struct save_area_ext *sa_ext,
|
||||||
|
|
||||||
if (is_boot_cpu) {
|
if (is_boot_cpu) {
|
||||||
/* Copy the registers of the boot CPU. */
|
/* Copy the registers of the boot CPU. */
|
||||||
copy_oldmem_page(1, (void *) &sa_ext->sa, sizeof(sa_ext->sa),
|
copy_oldmem_kernel(&sa_ext->sa, (void *) SAVE_AREA_BASE,
|
||||||
SAVE_AREA_BASE - PAGE_SIZE, 0);
|
sizeof(sa_ext->sa));
|
||||||
if (MACHINE_HAS_VX)
|
if (MACHINE_HAS_VX)
|
||||||
save_vx_regs_safe(sa_ext->vx_regs);
|
save_vx_regs_safe(sa_ext->vx_regs);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -64,7 +64,7 @@ static struct ipl_parameter_block *ipl_block;
|
||||||
* @count: Size of buffer, which should be copied
|
* @count: Size of buffer, which should be copied
|
||||||
* @mode: Either TO_KERNEL or TO_USER
|
* @mode: Either TO_KERNEL or TO_USER
|
||||||
*/
|
*/
|
||||||
int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)
|
static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)
|
||||||
{
|
{
|
||||||
int offs, blk_num;
|
int offs, blk_num;
|
||||||
static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
|
static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
|
||||||
|
@ -126,12 +126,26 @@ out:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
|
/*
|
||||||
|
* Copy memory from HSA to user memory (not reentrant):
|
||||||
|
*
|
||||||
|
* @dest: Kernel or user buffer where memory should be copied to
|
||||||
|
* @src: Start address within HSA where data should be copied
|
||||||
|
* @count: Size of buffer, which should be copied
|
||||||
|
*/
|
||||||
|
int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
|
||||||
{
|
{
|
||||||
return memcpy_hsa((void __force *) dest, src, count, TO_USER);
|
return memcpy_hsa((void __force *) dest, src, count, TO_USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
|
/*
|
||||||
|
* Copy memory from HSA to kernel memory (not reentrant):
|
||||||
|
*
|
||||||
|
* @dest: Kernel or user buffer where memory should be copied to
|
||||||
|
* @src: Start address within HSA where data should be copied
|
||||||
|
* @count: Size of buffer, which should be copied
|
||||||
|
*/
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue