| 
									
										
										
										
											2013-12-20 18:02:21 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Architecture specific sysfs attributes in /sys/kernel | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2007, Intel Corp. | 
					
						
							|  |  |  |  *      Huang Ying <ying.huang@intel.com> | 
					
						
							|  |  |  |  * Copyright (C) 2013, 2013 Red Hat, Inc. | 
					
						
							|  |  |  |  *      Dave Young <dyoung@redhat.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This file is released under the GPLv2 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kobject.h>
 | 
					
						
							|  |  |  | #include <linux/string.h>
 | 
					
						
							|  |  |  | #include <linux/sysfs.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/stat.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/mm.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-03 11:54:31 +08:00
										 |  |  | #include <asm/io.h>
 | 
					
						
							| 
									
										
										
										
											2013-12-20 18:02:21 +08:00
										 |  |  | #include <asm/setup.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ssize_t version_show(struct kobject *kobj, | 
					
						
							|  |  |  | 			    struct kobj_attribute *attr, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return sprintf(buf, "0x%04x\n", boot_params.hdr.version); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct kobj_attribute boot_params_version_attr = __ATTR_RO(version); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ssize_t boot_params_data_read(struct file *fp, struct kobject *kobj, | 
					
						
							|  |  |  | 				     struct bin_attribute *bin_attr, | 
					
						
							|  |  |  | 				     char *buf, loff_t off, size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	memcpy(buf, (void *)&boot_params + off, count); | 
					
						
							|  |  |  | 	return count; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct bin_attribute boot_params_data_attr = { | 
					
						
							|  |  |  | 	.attr = { | 
					
						
							|  |  |  | 		.name = "data", | 
					
						
							|  |  |  | 		.mode = S_IRUGO, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	.read = boot_params_data_read, | 
					
						
							|  |  |  | 	.size = sizeof(boot_params), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct attribute *boot_params_version_attrs[] = { | 
					
						
							|  |  |  | 	&boot_params_version_attr.attr, | 
					
						
							|  |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct bin_attribute *boot_params_data_attrs[] = { | 
					
						
							|  |  |  | 	&boot_params_data_attr, | 
					
						
							|  |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct attribute_group boot_params_attr_group = { | 
					
						
							|  |  |  | 	.attrs = boot_params_version_attrs, | 
					
						
							|  |  |  | 	.bin_attrs = boot_params_data_attrs, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int kobj_to_setup_data_nr(struct kobject *kobj, int *nr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const char *name; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	name = kobject_name(kobj); | 
					
						
							|  |  |  | 	return kstrtoint(name, 10, nr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int get_setup_data_paddr(int nr, u64 *paddr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i = 0; | 
					
						
							|  |  |  | 	struct setup_data *data; | 
					
						
							|  |  |  | 	u64 pa_data = boot_params.hdr.setup_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (pa_data) { | 
					
						
							|  |  |  | 		if (nr == i) { | 
					
						
							|  |  |  | 			*paddr = pa_data; | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		data = ioremap_cache(pa_data, sizeof(*data)); | 
					
						
							|  |  |  | 		if (!data) | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		pa_data = data->next; | 
					
						
							|  |  |  | 		iounmap(data); | 
					
						
							|  |  |  | 		i++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return -EINVAL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init get_setup_data_size(int nr, size_t *size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i = 0; | 
					
						
							|  |  |  | 	struct setup_data *data; | 
					
						
							|  |  |  | 	u64 pa_data = boot_params.hdr.setup_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (pa_data) { | 
					
						
							|  |  |  | 		data = ioremap_cache(pa_data, sizeof(*data)); | 
					
						
							|  |  |  | 		if (!data) | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 		if (nr == i) { | 
					
						
							|  |  |  | 			*size = data->len; | 
					
						
							|  |  |  | 			iounmap(data); | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		pa_data = data->next; | 
					
						
							|  |  |  | 		iounmap(data); | 
					
						
							|  |  |  | 		i++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return -EINVAL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ssize_t type_show(struct kobject *kobj, | 
					
						
							|  |  |  | 			 struct kobj_attribute *attr, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int nr, ret; | 
					
						
							|  |  |  | 	u64 paddr; | 
					
						
							|  |  |  | 	struct setup_data *data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = kobj_to_setup_data_nr(kobj, &nr); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = get_setup_data_paddr(nr, &paddr); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	data = ioremap_cache(paddr, sizeof(*data)); | 
					
						
							|  |  |  | 	if (!data) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = sprintf(buf, "0x%x\n", data->type); | 
					
						
							|  |  |  | 	iounmap(data); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ssize_t setup_data_data_read(struct file *fp, | 
					
						
							|  |  |  | 				    struct kobject *kobj, | 
					
						
							|  |  |  | 				    struct bin_attribute *bin_attr, | 
					
						
							|  |  |  | 				    char *buf, | 
					
						
							|  |  |  | 				    loff_t off, size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int nr, ret = 0; | 
					
						
							|  |  |  | 	u64 paddr; | 
					
						
							|  |  |  | 	struct setup_data *data; | 
					
						
							|  |  |  | 	void *p; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = kobj_to_setup_data_nr(kobj, &nr); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = get_setup_data_paddr(nr, &paddr); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	data = ioremap_cache(paddr, sizeof(*data)); | 
					
						
							|  |  |  | 	if (!data) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (off > data->len) { | 
					
						
							|  |  |  | 		ret = -EINVAL; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (count > data->len - off) | 
					
						
							|  |  |  | 		count = data->len - off; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!count) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = count; | 
					
						
							|  |  |  | 	p = ioremap_cache(paddr + sizeof(*data), data->len); | 
					
						
							|  |  |  | 	if (!p) { | 
					
						
							|  |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	memcpy(buf, p + off, count); | 
					
						
							|  |  |  | 	iounmap(p); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	iounmap(data); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct kobj_attribute type_attr = __ATTR_RO(type); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct bin_attribute data_attr = { | 
					
						
							|  |  |  | 	.attr = { | 
					
						
							|  |  |  | 		.name = "data", | 
					
						
							|  |  |  | 		.mode = S_IRUGO, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	.read = setup_data_data_read, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct attribute *setup_data_type_attrs[] = { | 
					
						
							|  |  |  | 	&type_attr.attr, | 
					
						
							|  |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct bin_attribute *setup_data_data_attrs[] = { | 
					
						
							|  |  |  | 	&data_attr, | 
					
						
							|  |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct attribute_group setup_data_attr_group = { | 
					
						
							|  |  |  | 	.attrs = setup_data_type_attrs, | 
					
						
							|  |  |  | 	.bin_attrs = setup_data_data_attrs, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init create_setup_data_node(struct kobject *parent, | 
					
						
							|  |  |  | 					 struct kobject **kobjp, int nr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 	size_t size; | 
					
						
							|  |  |  | 	struct kobject *kobj; | 
					
						
							|  |  |  | 	char name[16]; /* should be enough for setup_data nodes numbers */ | 
					
						
							|  |  |  | 	snprintf(name, 16, "%d", nr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	kobj = kobject_create_and_add(name, parent); | 
					
						
							|  |  |  | 	if (!kobj) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = get_setup_data_size(nr, &size); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto out_kobj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data_attr.size = size; | 
					
						
							|  |  |  | 	ret = sysfs_create_group(kobj, &setup_data_attr_group); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto out_kobj; | 
					
						
							|  |  |  | 	*kobjp = kobj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | out_kobj: | 
					
						
							|  |  |  | 	kobject_put(kobj); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init cleanup_setup_data_node(struct kobject *kobj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	sysfs_remove_group(kobj, &setup_data_attr_group); | 
					
						
							|  |  |  | 	kobject_put(kobj); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init get_setup_data_total_num(u64 pa_data, int *nr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 	struct setup_data *data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*nr = 0; | 
					
						
							|  |  |  | 	while (pa_data) { | 
					
						
							|  |  |  | 		*nr += 1; | 
					
						
							|  |  |  | 		data = ioremap_cache(pa_data, sizeof(*data)); | 
					
						
							|  |  |  | 		if (!data) { | 
					
						
							|  |  |  | 			ret = -ENOMEM; | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		pa_data = data->next; | 
					
						
							|  |  |  | 		iounmap(data); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init create_setup_data_nodes(struct kobject *parent) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct kobject *setup_data_kobj, **kobjp; | 
					
						
							|  |  |  | 	u64 pa_data; | 
					
						
							|  |  |  | 	int i, j, nr, ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pa_data = boot_params.hdr.setup_data; | 
					
						
							|  |  |  | 	if (!pa_data) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setup_data_kobj = kobject_create_and_add("setup_data", parent); | 
					
						
							|  |  |  | 	if (!setup_data_kobj) { | 
					
						
							|  |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = get_setup_data_total_num(pa_data, &nr); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto out_setup_data_kobj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	kobjp = kmalloc(sizeof(*kobjp) * nr, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!kobjp) { | 
					
						
							|  |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto out_setup_data_kobj; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < nr; i++) { | 
					
						
							|  |  |  | 		ret = create_setup_data_node(setup_data_kobj, kobjp + i, i); | 
					
						
							|  |  |  | 		if (ret) | 
					
						
							|  |  |  | 			goto out_clean_nodes; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	kfree(kobjp); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out_clean_nodes: | 
					
						
							|  |  |  | 	for (j = i - 1; j > 0; j--) | 
					
						
							|  |  |  | 		cleanup_setup_data_node(*(kobjp + j)); | 
					
						
							|  |  |  | 	kfree(kobjp); | 
					
						
							|  |  |  | out_setup_data_kobj: | 
					
						
							|  |  |  | 	kobject_put(setup_data_kobj); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init boot_params_ksysfs_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 	struct kobject *boot_params_kobj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	boot_params_kobj = kobject_create_and_add("boot_params", | 
					
						
							|  |  |  | 						  kernel_kobj); | 
					
						
							|  |  |  | 	if (!boot_params_kobj) { | 
					
						
							|  |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = sysfs_create_group(boot_params_kobj, &boot_params_attr_group); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto out_boot_params_kobj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = create_setup_data_nodes(boot_params_kobj); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto out_create_group; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | out_create_group: | 
					
						
							|  |  |  | 	sysfs_remove_group(boot_params_kobj, &boot_params_attr_group); | 
					
						
							|  |  |  | out_boot_params_kobj: | 
					
						
							|  |  |  | 	kobject_put(boot_params_kobj); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | arch_initcall(boot_params_ksysfs_init); |