xref: /openbmc/linux/arch/x86/kernel/ksysfs.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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