155716d26SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25039e316SDave Young /*
35039e316SDave Young * Architecture specific sysfs attributes in /sys/kernel
45039e316SDave Young *
55039e316SDave Young * Copyright (C) 2007, Intel Corp.
65039e316SDave Young * Huang Ying <ying.huang@intel.com>
75039e316SDave Young * Copyright (C) 2013, 2013 Red Hat, Inc.
85039e316SDave Young * Dave Young <dyoung@redhat.com>
95039e316SDave Young */
105039e316SDave Young
115039e316SDave Young #include <linux/kobject.h>
125039e316SDave Young #include <linux/string.h>
135039e316SDave Young #include <linux/sysfs.h>
145039e316SDave Young #include <linux/init.h>
155039e316SDave Young #include <linux/stat.h>
165039e316SDave Young #include <linux/slab.h>
175039e316SDave Young #include <linux/mm.h>
18f7750a79STom Lendacky #include <linux/io.h>
195039e316SDave Young
205039e316SDave Young #include <asm/setup.h>
215039e316SDave Young
version_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)225039e316SDave Young static ssize_t version_show(struct kobject *kobj,
235039e316SDave Young struct kobj_attribute *attr, char *buf)
245039e316SDave Young {
255039e316SDave Young return sprintf(buf, "0x%04x\n", boot_params.hdr.version);
265039e316SDave Young }
275039e316SDave Young
285039e316SDave Young static struct kobj_attribute boot_params_version_attr = __ATTR_RO(version);
295039e316SDave Young
boot_params_data_read(struct file * fp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)305039e316SDave Young static ssize_t boot_params_data_read(struct file *fp, struct kobject *kobj,
315039e316SDave Young struct bin_attribute *bin_attr,
325039e316SDave Young char *buf, loff_t off, size_t count)
335039e316SDave Young {
345039e316SDave Young memcpy(buf, (void *)&boot_params + off, count);
355039e316SDave Young return count;
365039e316SDave Young }
375039e316SDave Young
385039e316SDave Young static struct bin_attribute boot_params_data_attr = {
395039e316SDave Young .attr = {
405039e316SDave Young .name = "data",
415039e316SDave Young .mode = S_IRUGO,
425039e316SDave Young },
435039e316SDave Young .read = boot_params_data_read,
445039e316SDave Young .size = sizeof(boot_params),
455039e316SDave Young };
465039e316SDave Young
475039e316SDave Young static struct attribute *boot_params_version_attrs[] = {
485039e316SDave Young &boot_params_version_attr.attr,
495039e316SDave Young NULL,
505039e316SDave Young };
515039e316SDave Young
525039e316SDave Young static struct bin_attribute *boot_params_data_attrs[] = {
535039e316SDave Young &boot_params_data_attr,
545039e316SDave Young NULL,
555039e316SDave Young };
565039e316SDave Young
5745bd07adSArvind Yadav static const struct attribute_group boot_params_attr_group = {
585039e316SDave Young .attrs = boot_params_version_attrs,
595039e316SDave Young .bin_attrs = boot_params_data_attrs,
605039e316SDave Young };
615039e316SDave Young
kobj_to_setup_data_nr(struct kobject * kobj,int * nr)625039e316SDave Young static int kobj_to_setup_data_nr(struct kobject *kobj, int *nr)
635039e316SDave Young {
645039e316SDave Young const char *name;
655039e316SDave Young
665039e316SDave Young name = kobject_name(kobj);
675039e316SDave Young return kstrtoint(name, 10, nr);
685039e316SDave Young }
695039e316SDave Young
get_setup_data_paddr(int nr,u64 * paddr)705039e316SDave Young static int get_setup_data_paddr(int nr, u64 *paddr)
715039e316SDave Young {
725039e316SDave Young int i = 0;
735039e316SDave Young struct setup_data *data;
745039e316SDave Young u64 pa_data = boot_params.hdr.setup_data;
755039e316SDave Young
765039e316SDave Young while (pa_data) {
775039e316SDave Young if (nr == i) {
785039e316SDave Young *paddr = pa_data;
795039e316SDave Young return 0;
805039e316SDave Young }
81f7750a79STom Lendacky data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
825039e316SDave Young if (!data)
835039e316SDave Young return -ENOMEM;
845039e316SDave Young
855039e316SDave Young pa_data = data->next;
86f7750a79STom Lendacky memunmap(data);
875039e316SDave Young i++;
885039e316SDave Young }
895039e316SDave Young return -EINVAL;
905039e316SDave Young }
915039e316SDave Young
get_setup_data_size(int nr,size_t * size)925039e316SDave Young static int __init get_setup_data_size(int nr, size_t *size)
935039e316SDave Young {
94*7228918bSRoss Philipson u64 pa_data = boot_params.hdr.setup_data, pa_next;
95*7228918bSRoss Philipson struct setup_indirect *indirect;
965039e316SDave Young struct setup_data *data;
97*7228918bSRoss Philipson int i = 0;
98*7228918bSRoss Philipson u32 len;
995039e316SDave Young
1005039e316SDave Young while (pa_data) {
101f7750a79STom Lendacky data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
1025039e316SDave Young if (!data)
1035039e316SDave Young return -ENOMEM;
104*7228918bSRoss Philipson pa_next = data->next;
105*7228918bSRoss Philipson
1065039e316SDave Young if (nr == i) {
107*7228918bSRoss Philipson if (data->type == SETUP_INDIRECT) {
108*7228918bSRoss Philipson len = sizeof(*data) + data->len;
109*7228918bSRoss Philipson memunmap(data);
110*7228918bSRoss Philipson data = memremap(pa_data, len, MEMREMAP_WB);
111*7228918bSRoss Philipson if (!data)
112*7228918bSRoss Philipson return -ENOMEM;
113*7228918bSRoss Philipson
114*7228918bSRoss Philipson indirect = (struct setup_indirect *)data->data;
115*7228918bSRoss Philipson
116*7228918bSRoss Philipson if (indirect->type != SETUP_INDIRECT)
117*7228918bSRoss Philipson *size = indirect->len;
118b3c72fc9SDaniel Kiper else
1195039e316SDave Young *size = data->len;
120*7228918bSRoss Philipson } else {
121*7228918bSRoss Philipson *size = data->len;
122*7228918bSRoss Philipson }
123b3c72fc9SDaniel Kiper
124f7750a79STom Lendacky memunmap(data);
1255039e316SDave Young return 0;
1265039e316SDave Young }
1275039e316SDave Young
128*7228918bSRoss Philipson pa_data = pa_next;
129f7750a79STom Lendacky memunmap(data);
1305039e316SDave Young i++;
1315039e316SDave Young }
1325039e316SDave Young return -EINVAL;
1335039e316SDave Young }
1345039e316SDave Young
type_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1355039e316SDave Young static ssize_t type_show(struct kobject *kobj,
1365039e316SDave Young struct kobj_attribute *attr, char *buf)
1375039e316SDave Young {
138*7228918bSRoss Philipson struct setup_indirect *indirect;
139*7228918bSRoss Philipson struct setup_data *data;
1405039e316SDave Young int nr, ret;
1415039e316SDave Young u64 paddr;
142*7228918bSRoss Philipson u32 len;
1435039e316SDave Young
1445039e316SDave Young ret = kobj_to_setup_data_nr(kobj, &nr);
1455039e316SDave Young if (ret)
1465039e316SDave Young return ret;
1475039e316SDave Young
1485039e316SDave Young ret = get_setup_data_paddr(nr, &paddr);
1495039e316SDave Young if (ret)
1505039e316SDave Young return ret;
151f7750a79STom Lendacky data = memremap(paddr, sizeof(*data), MEMREMAP_WB);
1525039e316SDave Young if (!data)
1535039e316SDave Young return -ENOMEM;
1545039e316SDave Young
155*7228918bSRoss Philipson if (data->type == SETUP_INDIRECT) {
156*7228918bSRoss Philipson len = sizeof(*data) + data->len;
157*7228918bSRoss Philipson memunmap(data);
158*7228918bSRoss Philipson data = memremap(paddr, len, MEMREMAP_WB);
159*7228918bSRoss Philipson if (!data)
160*7228918bSRoss Philipson return -ENOMEM;
161*7228918bSRoss Philipson
162*7228918bSRoss Philipson indirect = (struct setup_indirect *)data->data;
163*7228918bSRoss Philipson
164*7228918bSRoss Philipson ret = sprintf(buf, "0x%x\n", indirect->type);
165*7228918bSRoss Philipson } else {
1665039e316SDave Young ret = sprintf(buf, "0x%x\n", data->type);
167*7228918bSRoss Philipson }
168*7228918bSRoss Philipson
169f7750a79STom Lendacky memunmap(data);
1705039e316SDave Young return ret;
1715039e316SDave Young }
1725039e316SDave Young
setup_data_data_read(struct file * fp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)1735039e316SDave Young static ssize_t setup_data_data_read(struct file *fp,
1745039e316SDave Young struct kobject *kobj,
1755039e316SDave Young struct bin_attribute *bin_attr,
1765039e316SDave Young char *buf,
1775039e316SDave Young loff_t off, size_t count)
1785039e316SDave Young {
179*7228918bSRoss Philipson struct setup_indirect *indirect;
180*7228918bSRoss Philipson struct setup_data *data;
1815039e316SDave Young int nr, ret = 0;
182b3c72fc9SDaniel Kiper u64 paddr, len;
1835039e316SDave Young void *p;
1845039e316SDave Young
1855039e316SDave Young ret = kobj_to_setup_data_nr(kobj, &nr);
1865039e316SDave Young if (ret)
1875039e316SDave Young return ret;
1885039e316SDave Young
1895039e316SDave Young ret = get_setup_data_paddr(nr, &paddr);
1905039e316SDave Young if (ret)
1915039e316SDave Young return ret;
192f7750a79STom Lendacky data = memremap(paddr, sizeof(*data), MEMREMAP_WB);
1935039e316SDave Young if (!data)
1945039e316SDave Young return -ENOMEM;
1955039e316SDave Young
196*7228918bSRoss Philipson if (data->type == SETUP_INDIRECT) {
197*7228918bSRoss Philipson len = sizeof(*data) + data->len;
198*7228918bSRoss Philipson memunmap(data);
199*7228918bSRoss Philipson data = memremap(paddr, len, MEMREMAP_WB);
200*7228918bSRoss Philipson if (!data)
201*7228918bSRoss Philipson return -ENOMEM;
202*7228918bSRoss Philipson
203*7228918bSRoss Philipson indirect = (struct setup_indirect *)data->data;
204*7228918bSRoss Philipson
205*7228918bSRoss Philipson if (indirect->type != SETUP_INDIRECT) {
206*7228918bSRoss Philipson paddr = indirect->addr;
207*7228918bSRoss Philipson len = indirect->len;
208*7228918bSRoss Philipson } else {
209*7228918bSRoss Philipson /*
210*7228918bSRoss Philipson * Even though this is technically undefined, return
211*7228918bSRoss Philipson * the data as though it is a normal setup_data struct.
212*7228918bSRoss Philipson * This will at least allow it to be inspected.
213*7228918bSRoss Philipson */
214*7228918bSRoss Philipson paddr += sizeof(*data);
215*7228918bSRoss Philipson len = data->len;
216*7228918bSRoss Philipson }
217b3c72fc9SDaniel Kiper } else {
218b3c72fc9SDaniel Kiper paddr += sizeof(*data);
219b3c72fc9SDaniel Kiper len = data->len;
220b3c72fc9SDaniel Kiper }
221b3c72fc9SDaniel Kiper
222b3c72fc9SDaniel Kiper if (off > len) {
2235039e316SDave Young ret = -EINVAL;
2245039e316SDave Young goto out;
2255039e316SDave Young }
2265039e316SDave Young
227b3c72fc9SDaniel Kiper if (count > len - off)
228b3c72fc9SDaniel Kiper count = len - off;
2295039e316SDave Young
2305039e316SDave Young if (!count)
2315039e316SDave Young goto out;
2325039e316SDave Young
2335039e316SDave Young ret = count;
234b3c72fc9SDaniel Kiper p = memremap(paddr, len, MEMREMAP_WB);
2355039e316SDave Young if (!p) {
2365039e316SDave Young ret = -ENOMEM;
2375039e316SDave Young goto out;
2385039e316SDave Young }
2395039e316SDave Young memcpy(buf, p + off, count);
240f7750a79STom Lendacky memunmap(p);
2415039e316SDave Young out:
242f7750a79STom Lendacky memunmap(data);
2435039e316SDave Young return ret;
2445039e316SDave Young }
2455039e316SDave Young
2465039e316SDave Young static struct kobj_attribute type_attr = __ATTR_RO(type);
2475039e316SDave Young
248404f6aacSKees Cook static struct bin_attribute data_attr __ro_after_init = {
2495039e316SDave Young .attr = {
2505039e316SDave Young .name = "data",
2515039e316SDave Young .mode = S_IRUGO,
2525039e316SDave Young },
2535039e316SDave Young .read = setup_data_data_read,
2545039e316SDave Young };
2555039e316SDave Young
2565039e316SDave Young static struct attribute *setup_data_type_attrs[] = {
2575039e316SDave Young &type_attr.attr,
2585039e316SDave Young NULL,
2595039e316SDave Young };
2605039e316SDave Young
2615039e316SDave Young static struct bin_attribute *setup_data_data_attrs[] = {
2625039e316SDave Young &data_attr,
2635039e316SDave Young NULL,
2645039e316SDave Young };
2655039e316SDave Young
26645bd07adSArvind Yadav static const struct attribute_group setup_data_attr_group = {
2675039e316SDave Young .attrs = setup_data_type_attrs,
2685039e316SDave Young .bin_attrs = setup_data_data_attrs,
2695039e316SDave Young };
2705039e316SDave Young
create_setup_data_node(struct kobject * parent,struct kobject ** kobjp,int nr)2715039e316SDave Young static int __init create_setup_data_node(struct kobject *parent,
2725039e316SDave Young struct kobject **kobjp, int nr)
2735039e316SDave Young {
2745039e316SDave Young int ret = 0;
2755039e316SDave Young size_t size;
2765039e316SDave Young struct kobject *kobj;
2775039e316SDave Young char name[16]; /* should be enough for setup_data nodes numbers */
2785039e316SDave Young snprintf(name, 16, "%d", nr);
2795039e316SDave Young
2805039e316SDave Young kobj = kobject_create_and_add(name, parent);
2815039e316SDave Young if (!kobj)
2825039e316SDave Young return -ENOMEM;
2835039e316SDave Young
2845039e316SDave Young ret = get_setup_data_size(nr, &size);
2855039e316SDave Young if (ret)
2865039e316SDave Young goto out_kobj;
2875039e316SDave Young
2885039e316SDave Young data_attr.size = size;
2895039e316SDave Young ret = sysfs_create_group(kobj, &setup_data_attr_group);
2905039e316SDave Young if (ret)
2915039e316SDave Young goto out_kobj;
2925039e316SDave Young *kobjp = kobj;
2935039e316SDave Young
2945039e316SDave Young return 0;
2955039e316SDave Young out_kobj:
2965039e316SDave Young kobject_put(kobj);
2975039e316SDave Young return ret;
2985039e316SDave Young }
2995039e316SDave Young
cleanup_setup_data_node(struct kobject * kobj)3005039e316SDave Young static void __init cleanup_setup_data_node(struct kobject *kobj)
3015039e316SDave Young {
3025039e316SDave Young sysfs_remove_group(kobj, &setup_data_attr_group);
3035039e316SDave Young kobject_put(kobj);
3045039e316SDave Young }
3055039e316SDave Young
get_setup_data_total_num(u64 pa_data,int * nr)3065039e316SDave Young static int __init get_setup_data_total_num(u64 pa_data, int *nr)
3075039e316SDave Young {
3085039e316SDave Young int ret = 0;
3095039e316SDave Young struct setup_data *data;
3105039e316SDave Young
3115039e316SDave Young *nr = 0;
3125039e316SDave Young while (pa_data) {
3135039e316SDave Young *nr += 1;
314f7750a79STom Lendacky data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
3155039e316SDave Young if (!data) {
3165039e316SDave Young ret = -ENOMEM;
3175039e316SDave Young goto out;
3185039e316SDave Young }
3195039e316SDave Young pa_data = data->next;
320f7750a79STom Lendacky memunmap(data);
3215039e316SDave Young }
3225039e316SDave Young
3235039e316SDave Young out:
3245039e316SDave Young return ret;
3255039e316SDave Young }
3265039e316SDave Young
create_setup_data_nodes(struct kobject * parent)3275039e316SDave Young static int __init create_setup_data_nodes(struct kobject *parent)
3285039e316SDave Young {
3295039e316SDave Young struct kobject *setup_data_kobj, **kobjp;
3305039e316SDave Young u64 pa_data;
3315039e316SDave Young int i, j, nr, ret = 0;
3325039e316SDave Young
3335039e316SDave Young pa_data = boot_params.hdr.setup_data;
3345039e316SDave Young if (!pa_data)
3355039e316SDave Young return 0;
3365039e316SDave Young
3375039e316SDave Young setup_data_kobj = kobject_create_and_add("setup_data", parent);
3385039e316SDave Young if (!setup_data_kobj) {
3395039e316SDave Young ret = -ENOMEM;
3405039e316SDave Young goto out;
3415039e316SDave Young }
3425039e316SDave Young
3435039e316SDave Young ret = get_setup_data_total_num(pa_data, &nr);
3445039e316SDave Young if (ret)
3455039e316SDave Young goto out_setup_data_kobj;
3465039e316SDave Young
3476da2ec56SKees Cook kobjp = kmalloc_array(nr, sizeof(*kobjp), GFP_KERNEL);
3485039e316SDave Young if (!kobjp) {
3495039e316SDave Young ret = -ENOMEM;
3505039e316SDave Young goto out_setup_data_kobj;
3515039e316SDave Young }
3525039e316SDave Young
3535039e316SDave Young for (i = 0; i < nr; i++) {
3545039e316SDave Young ret = create_setup_data_node(setup_data_kobj, kobjp + i, i);
3555039e316SDave Young if (ret)
3565039e316SDave Young goto out_clean_nodes;
3575039e316SDave Young }
3585039e316SDave Young
3595039e316SDave Young kfree(kobjp);
3605039e316SDave Young return 0;
3615039e316SDave Young
3625039e316SDave Young out_clean_nodes:
3637d709943SSean Fu for (j = i - 1; j >= 0; j--)
3645039e316SDave Young cleanup_setup_data_node(*(kobjp + j));
3655039e316SDave Young kfree(kobjp);
3665039e316SDave Young out_setup_data_kobj:
3675039e316SDave Young kobject_put(setup_data_kobj);
3685039e316SDave Young out:
3695039e316SDave Young return ret;
3705039e316SDave Young }
3715039e316SDave Young
boot_params_ksysfs_init(void)3725039e316SDave Young static int __init boot_params_ksysfs_init(void)
3735039e316SDave Young {
3745039e316SDave Young int ret;
3755039e316SDave Young struct kobject *boot_params_kobj;
3765039e316SDave Young
3775039e316SDave Young boot_params_kobj = kobject_create_and_add("boot_params",
3785039e316SDave Young kernel_kobj);
3795039e316SDave Young if (!boot_params_kobj) {
3805039e316SDave Young ret = -ENOMEM;
3815039e316SDave Young goto out;
3825039e316SDave Young }
3835039e316SDave Young
3845039e316SDave Young ret = sysfs_create_group(boot_params_kobj, &boot_params_attr_group);
3855039e316SDave Young if (ret)
3865039e316SDave Young goto out_boot_params_kobj;
3875039e316SDave Young
3885039e316SDave Young ret = create_setup_data_nodes(boot_params_kobj);
3895039e316SDave Young if (ret)
3905039e316SDave Young goto out_create_group;
3915039e316SDave Young
3925039e316SDave Young return 0;
3935039e316SDave Young out_create_group:
3945039e316SDave Young sysfs_remove_group(boot_params_kobj, &boot_params_attr_group);
3955039e316SDave Young out_boot_params_kobj:
3965039e316SDave Young kobject_put(boot_params_kobj);
3975039e316SDave Young out:
3985039e316SDave Young return ret;
3995039e316SDave Young }
4005039e316SDave Young
4015039e316SDave Young arch_initcall(boot_params_ksysfs_init);
402