diff options
Diffstat (limited to 'arch/x86/kernel/ksysfs.c')
| -rw-r--r-- | arch/x86/kernel/ksysfs.c | 340 | 
1 files changed, 340 insertions, 0 deletions
diff --git a/arch/x86/kernel/ksysfs.c b/arch/x86/kernel/ksysfs.c new file mode 100644 index 00000000000..c2bedaea11f --- /dev/null +++ b/arch/x86/kernel/ksysfs.c @@ -0,0 +1,340 @@ +/* + * 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> + +#include <asm/io.h> +#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);  | 
