| /* | |
| * 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 __ro_after_init = { | |
| .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); |