diff options
Diffstat (limited to 'drivers/s390/char/zcore.c')
| -rw-r--r-- | drivers/s390/char/zcore.c | 210 | 
1 files changed, 113 insertions, 97 deletions
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 3b94044027c..1884653e447 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -5,7 +5,7 @@   *   * For more information please refer to Documentation/s390/zfcpdump.txt   * - * Copyright IBM Corp. 2003,2008 + * Copyright IBM Corp. 2003, 2008   * Author(s): Michael Holzheu   */ @@ -16,11 +16,13 @@  #include <linux/slab.h>  #include <linux/miscdevice.h>  #include <linux/debugfs.h> +#include <linux/module.h> +#include <linux/memblock.h> +  #include <asm/asm-offsets.h>  #include <asm/ipl.h>  #include <asm/sclp.h>  #include <asm/setup.h> -#include <asm/sigp.h>  #include <asm/uaccess.h>  #include <asm/debug.h>  #include <asm/processor.h> @@ -30,8 +32,8 @@  #define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) -#define TO_USER		0 -#define TO_KERNEL	1 +#define TO_USER		1 +#define TO_KERNEL	0  #define CHUNK_INFO_SIZE	34 /* 2 16-byte char, each followed by blank */  enum arch_id { @@ -62,6 +64,7 @@ static struct dentry *zcore_dir;  static struct dentry *zcore_file;  static struct dentry *zcore_memmap_file;  static struct dentry *zcore_reipl_file; +static struct dentry *zcore_hsa_file;  static struct ipl_parameter_block *ipl_block;  /* @@ -72,11 +75,13 @@ static struct ipl_parameter_block *ipl_block;   * @count: Size of buffer, which should be copied   * @mode:  Either TO_KERNEL or TO_USER   */ -static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode) +int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)  {  	int offs, blk_num;  	static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +	if (!hsa_available) +		return -ENODATA;  	if (count == 0)  		return 0; @@ -124,7 +129,7 @@ static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)  	}  	if (mode == TO_USER) {  		if (copy_to_user((__force __user void*) dest + offs, buf, -				 PAGE_SIZE)) +				 count - offs))  			return -EFAULT;  	} else  		memcpy(dest + offs, buf, count - offs); @@ -142,29 +147,13 @@ 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_user(void __user *dest, unsigned long src, size_t count) -{ -	static char buf[4096]; -	int offs = 0, size; - -	while (offs < count) { -		size = min(sizeof(buf), count - offs); -		if (memcpy_real(buf, (void *) src + offs, size)) -			return -EFAULT; -		if (copy_to_user(dest + offs, buf, size)) -			return -EFAULT; -		offs += size; -	} -	return 0; -} -  static int __init init_cpu_info(enum arch_id arch)  {  	struct save_area *sa;  	/* get info for boot cpu from lowcore, stored in the HSA */ -	sa = kmalloc(sizeof(*sa), GFP_KERNEL); +	sa = dump_save_area_create(0);  	if (!sa)  		return -ENOMEM;  	if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) { @@ -172,7 +161,6 @@ static int __init init_cpu_info(enum arch_id arch)  		kfree(sa);  		return -EIO;  	} -	zfcpdump_save_areas[0] = sa;  	return 0;  } @@ -259,24 +247,25 @@ static int copy_lc(void __user *buf, void *sa, int sa_off, int len)  static int zcore_add_lc(char __user *buf, unsigned long start, size_t count)  {  	unsigned long end; -	int i = 0; +	int i;  	if (count == 0)  		return 0;  	end = start + count; -	while (zfcpdump_save_areas[i]) { +	for (i = 0; i < dump_save_areas.count; i++) {  		unsigned long cp_start, cp_end; /* copy range */  		unsigned long sa_start, sa_end; /* save area range */  		unsigned long prefix;  		unsigned long sa_off, len, buf_off; +		struct save_area *save_area = dump_save_areas.areas[i]; -		prefix = zfcpdump_save_areas[i]->pref_reg; +		prefix = save_area->pref_reg;  		sa_start = prefix + sys_info.sa_base;  		sa_end = prefix + sys_info.sa_base + sys_info.sa_size;  		if ((end < sa_start) || (start > sa_end)) -			goto next; +			continue;  		cp_start = max(start, sa_start);  		cp_end = min(end, sa_end); @@ -285,15 +274,22 @@ static int zcore_add_lc(char __user *buf, unsigned long start, size_t count)  		len = cp_end - cp_start;  		TRACE("copy_lc for: %lx\n", start); -		if (copy_lc(buf + buf_off, zfcpdump_save_areas[i], sa_off, len)) +		if (copy_lc(buf + buf_off, save_area, sa_off, len))  			return -EFAULT; -next: -		i++;  	}  	return 0;  }  /* + * Release the HSA + */ +static void release_hsa(void) +{ +	diag308(DIAG308_REL_HSA, NULL); +	hsa_available = 0; +} + +/*   * Read routine for zcore character device   * First 4K are dump header   * Next 32MB are HSA Memory @@ -334,9 +330,9 @@ static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,  	mem_offs = 0;  	/* Copy from HSA data */ -	if (*ppos < (ZFCPDUMP_HSA_SIZE + HEADER_SIZE)) { -		size = min((count - hdr_count), (size_t) (ZFCPDUMP_HSA_SIZE -			   - mem_start)); +	if (*ppos < sclp_get_hsa_size() + HEADER_SIZE) { +		size = min((count - hdr_count), +			   (size_t) (sclp_get_hsa_size() - mem_start));  		rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);  		if (rc)  			goto fail; @@ -346,8 +342,8 @@ static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,  	/* Copy from real mem */  	size = count - mem_offs - hdr_count; -	rc = memcpy_real_user(buf + hdr_count + mem_offs, mem_start + mem_offs, -			      size); +	rc = copy_to_user_real(buf + hdr_count + mem_offs, +			       (void *) mem_start + mem_offs, size);  	if (rc)  		goto fail; @@ -379,8 +375,8 @@ static int zcore_open(struct inode *inode, struct file *filp)  static int zcore_release(struct inode *inode, struct file *filep)  { -	diag308(DIAG308_REL_HSA, NULL); -	hsa_available = 0; +	if (hsa_available) +		release_hsa();  	return 0;  } @@ -417,33 +413,24 @@ static ssize_t zcore_memmap_read(struct file *filp, char __user *buf,  				 size_t count, loff_t *ppos)  {  	return simple_read_from_buffer(buf, count, ppos, filp->private_data, -				       MEMORY_CHUNKS * CHUNK_INFO_SIZE); +				       memblock.memory.cnt * CHUNK_INFO_SIZE);  }  static int zcore_memmap_open(struct inode *inode, struct file *filp)  { -	int i; +	struct memblock_region *reg;  	char *buf; -	struct mem_chunk *chunk_array; +	int i = 0; -	chunk_array = kzalloc(MEMORY_CHUNKS * sizeof(struct mem_chunk), -			      GFP_KERNEL); -	if (!chunk_array) -		return -ENOMEM; -	detect_memory_layout(chunk_array); -	buf = kzalloc(MEMORY_CHUNKS * CHUNK_INFO_SIZE, GFP_KERNEL); +	buf = kzalloc(memblock.memory.cnt * CHUNK_INFO_SIZE, GFP_KERNEL);  	if (!buf) { -		kfree(chunk_array);  		return -ENOMEM;  	} -	for (i = 0; i < MEMORY_CHUNKS; i++) { -		sprintf(buf + (i * CHUNK_INFO_SIZE), "%016llx %016llx ", -			(unsigned long long) chunk_array[i].addr, -			(unsigned long long) chunk_array[i].size); -		if (chunk_array[i].size == 0) -			break; +	for_each_memblock(memory, reg) { +		sprintf(buf + (i++ * CHUNK_INFO_SIZE), "%016llx %016llx ", +			(unsigned long long) reg->base, +			(unsigned long long) reg->size);  	} -	kfree(chunk_array);  	filp->private_data = buf;  	return nonseekable_open(inode, filp);  } @@ -490,6 +477,41 @@ static const struct file_operations zcore_reipl_fops = {  	.llseek		= no_llseek,  }; +static ssize_t zcore_hsa_read(struct file *filp, char __user *buf, +			      size_t count, loff_t *ppos) +{ +	static char str[18]; + +	if (hsa_available) +		snprintf(str, sizeof(str), "%lx\n", sclp_get_hsa_size()); +	else +		snprintf(str, sizeof(str), "0\n"); +	return simple_read_from_buffer(buf, count, ppos, str, strlen(str)); +} + +static ssize_t zcore_hsa_write(struct file *filp, const char __user *buf, +			       size_t count, loff_t *ppos) +{ +	char value; + +	if (*ppos != 0) +		return -EPIPE; +	if (copy_from_user(&value, buf, 1)) +		return -EFAULT; +	if (value != '0') +		return -EINVAL; +	release_hsa(); +	return count; +} + +static const struct file_operations zcore_hsa_fops = { +	.owner		= THIS_MODULE, +	.write		= zcore_hsa_write, +	.read		= zcore_hsa_read, +	.open		= nonseekable_open, +	.llseek		= no_llseek, +}; +  #ifdef CONFIG_32BIT  static void __init set_lc_mask(struct save_area *map) @@ -526,7 +548,7 @@ static void __init set_lc_mask(struct save_area *map)  /*   * Initialize dump globals for a given architecture   */ -static int __init sys_info_init(enum arch_id arch) +static int __init sys_info_init(enum arch_id arch, unsigned long mem_end)  {  	int rc; @@ -548,75 +570,55 @@ static int __init sys_info_init(enum arch_id arch)  	rc = init_cpu_info(arch);  	if (rc)  		return rc; -	sys_info.mem_size = real_memory_size; +	sys_info.mem_size = mem_end;  	return 0;  }  static int __init check_sdias(void)  { -	int rc, act_hsa_size; - -	rc = sclp_sdias_blk_count(); -	if (rc < 0) { +	if (!sclp_get_hsa_size()) {  		TRACE("Could not determine HSA size\n"); -		return rc; -	} -	act_hsa_size = (rc - 1) * PAGE_SIZE; -	if (act_hsa_size < ZFCPDUMP_HSA_SIZE) { -		TRACE("HSA size too small: %i\n", act_hsa_size); -		return -EINVAL; +		return -ENODEV;  	}  	return 0;  } -static int __init get_mem_size(unsigned long *mem) +static int __init get_mem_info(unsigned long *mem, unsigned long *end)  { -	int i; -	struct mem_chunk *chunk_array; +	struct memblock_region *reg; -	chunk_array = kzalloc(MEMORY_CHUNKS * sizeof(struct mem_chunk), -			      GFP_KERNEL); -	if (!chunk_array) -		return -ENOMEM; -	detect_memory_layout(chunk_array); -	for (i = 0; i < MEMORY_CHUNKS; i++) { -		if (chunk_array[i].size == 0) -			break; -		*mem += chunk_array[i].size; +	for_each_memblock(memory, reg) { +		*mem += reg->size; +		*end = max_t(unsigned long, *end, reg->base + reg->size);  	} -	kfree(chunk_array);  	return 0;  } -static int __init zcore_header_init(int arch, struct zcore_header *hdr) +static void __init zcore_header_init(int arch, struct zcore_header *hdr, +				     unsigned long mem_size)  { -	int rc, i; -	unsigned long memory = 0;  	u32 prefix; +	int i;  	if (arch == ARCH_S390X)  		hdr->arch_id = DUMP_ARCH_S390X;  	else  		hdr->arch_id = DUMP_ARCH_S390; -	rc = get_mem_size(&memory); -	if (rc) -		return rc; -	hdr->mem_size = memory; -	hdr->rmem_size = memory; +	hdr->mem_size = mem_size; +	hdr->rmem_size = mem_size;  	hdr->mem_end = sys_info.mem_size; -	hdr->num_pages = memory / PAGE_SIZE; -	hdr->tod = get_clock(); +	hdr->num_pages = mem_size / PAGE_SIZE; +	hdr->tod = get_tod_clock();  	get_cpu_id(&hdr->cpu_id); -	for (i = 0; zfcpdump_save_areas[i]; i++) { -		prefix = zfcpdump_save_areas[i]->pref_reg; +	for (i = 0; i < dump_save_areas.count; i++) { +		prefix = dump_save_areas.areas[i]->pref_reg;  		hdr->real_cpu_cnt++;  		if (!prefix)  			continue;  		hdr->lc_vec[hdr->cpu_cnt] = prefix;  		hdr->cpu_cnt++;  	} -	return 0;  }  /* @@ -636,7 +638,7 @@ static int __init zcore_reipl_init(void)  	ipl_block = (void *) __get_free_page(GFP_KERNEL);  	if (!ipl_block)  		return -ENOMEM; -	if (ipib_info.ipib < ZFCPDUMP_HSA_SIZE) +	if (ipib_info.ipib < sclp_get_hsa_size())  		rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE);  	else  		rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE); @@ -651,11 +653,15 @@ static int __init zcore_reipl_init(void)  static int __init zcore_init(void)  { +	unsigned long mem_size, mem_end;  	unsigned char arch;  	int rc; +	mem_size = mem_end = 0;  	if (ipl_info.type != IPL_TYPE_FCP_DUMP)  		return -ENODATA; +	if (OLDMEM_BASE) +		return -ENODATA;  	zcore_dbf = debug_register("zcore", 4, 1, 4 * sizeof(long));  	debug_register_view(zcore_dbf, &debug_sprintf_view); @@ -672,6 +678,7 @@ static int __init zcore_init(void)  	rc = check_sdias();  	if (rc)  		goto fail; +	hsa_available = 1;  	rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1);  	if (rc) @@ -693,13 +700,14 @@ static int __init zcore_init(void)  	}  #endif /* CONFIG_64BIT */ -	rc = sys_info_init(arch); +	rc = get_mem_info(&mem_size, &mem_end);  	if (rc)  		goto fail; -	rc = zcore_header_init(arch, &zcore_header); +	rc = sys_info_init(arch, mem_end);  	if (rc)  		goto fail; +	zcore_header_init(arch, &zcore_header, mem_size);  	rc = zcore_reipl_init();  	if (rc) @@ -728,9 +736,16 @@ static int __init zcore_init(void)  		rc = -ENOMEM;  		goto fail_memmap_file;  	} -	hsa_available = 1; +	zcore_hsa_file = debugfs_create_file("hsa", S_IRUSR|S_IWUSR, zcore_dir, +					     NULL, &zcore_hsa_fops); +	if (!zcore_hsa_file) { +		rc = -ENOMEM; +		goto fail_reipl_file; +	}  	return 0; +fail_reipl_file: +	debugfs_remove(zcore_reipl_file);  fail_memmap_file:  	debugfs_remove(zcore_memmap_file);  fail_file: @@ -747,6 +762,7 @@ static void __exit zcore_exit(void)  	debug_unregister(zcore_dbf);  	sclp_sdias_exit();  	free_page((unsigned long) ipl_block); +	debugfs_remove(zcore_hsa_file);  	debugfs_remove(zcore_reipl_file);  	debugfs_remove(zcore_memmap_file);  	debugfs_remove(zcore_file);  | 
