forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
7.1 KiB
356 lines
7.1 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* Architecture specific sysfs attributes in /sys/kernel |
|
* |
|
* Copyright (C) 2007, Intel Corp. |
|
* Huang Ying <[email protected]> |
|
* Copyright (C) 2013, 2013 Red Hat, Inc. |
|
* Dave Young <[email protected]> |
|
*/ |
|
|
|
#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 <linux/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 const 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 = memremap(pa_data, sizeof(*data), MEMREMAP_WB); |
|
if (!data) |
|
return -ENOMEM; |
|
|
|
pa_data = data->next; |
|
memunmap(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 = memremap(pa_data, sizeof(*data), MEMREMAP_WB); |
|
if (!data) |
|
return -ENOMEM; |
|
if (nr == i) { |
|
if (data->type == SETUP_INDIRECT && |
|
((struct setup_indirect *)data->data)->type != SETUP_INDIRECT) |
|
*size = ((struct setup_indirect *)data->data)->len; |
|
else |
|
*size = data->len; |
|
|
|
memunmap(data); |
|
return 0; |
|
} |
|
|
|
pa_data = data->next; |
|
memunmap(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 = memremap(paddr, sizeof(*data), MEMREMAP_WB); |
|
if (!data) |
|
return -ENOMEM; |
|
|
|
if (data->type == SETUP_INDIRECT) |
|
ret = sprintf(buf, "0x%x\n", ((struct setup_indirect *)data->data)->type); |
|
else |
|
ret = sprintf(buf, "0x%x\n", data->type); |
|
memunmap(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, len; |
|
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 = memremap(paddr, sizeof(*data), MEMREMAP_WB); |
|
if (!data) |
|
return -ENOMEM; |
|
|
|
if (data->type == SETUP_INDIRECT && |
|
((struct setup_indirect *)data->data)->type != SETUP_INDIRECT) { |
|
paddr = ((struct setup_indirect *)data->data)->addr; |
|
len = ((struct setup_indirect *)data->data)->len; |
|
} else { |
|
paddr += sizeof(*data); |
|
len = data->len; |
|
} |
|
|
|
if (off > len) { |
|
ret = -EINVAL; |
|
goto out; |
|
} |
|
|
|
if (count > len - off) |
|
count = len - off; |
|
|
|
if (!count) |
|
goto out; |
|
|
|
ret = count; |
|
p = memremap(paddr, len, MEMREMAP_WB); |
|
if (!p) { |
|
ret = -ENOMEM; |
|
goto out; |
|
} |
|
memcpy(buf, p + off, count); |
|
memunmap(p); |
|
out: |
|
memunmap(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 const 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 = memremap(pa_data, sizeof(*data), MEMREMAP_WB); |
|
if (!data) { |
|
ret = -ENOMEM; |
|
goto out; |
|
} |
|
pa_data = data->next; |
|
memunmap(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_array(nr, sizeof(*kobjp), 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);
|
|
|