xref: /openbmc/linux/drivers/firmware/dmi-id.c (revision 4f5c791a850e5305a5b1b48d0e4b4de248dc96f9)
1*4f5c791aSLennart Poettering /*
2*4f5c791aSLennart Poettering  * Export SMBIOS/DMI info via sysfs to userspace
3*4f5c791aSLennart Poettering  *
4*4f5c791aSLennart Poettering  * Copyright 2007, Lennart Poettering
5*4f5c791aSLennart Poettering  *
6*4f5c791aSLennart Poettering  * Licensed under GPLv2
7*4f5c791aSLennart Poettering  */
8*4f5c791aSLennart Poettering 
9*4f5c791aSLennart Poettering #include <linux/module.h>
10*4f5c791aSLennart Poettering #include <linux/kernel.h>
11*4f5c791aSLennart Poettering #include <linux/init.h>
12*4f5c791aSLennart Poettering #include <linux/dmi.h>
13*4f5c791aSLennart Poettering #include <linux/device.h>
14*4f5c791aSLennart Poettering #include <linux/autoconf.h>
15*4f5c791aSLennart Poettering 
16*4f5c791aSLennart Poettering #define DEFINE_DMI_ATTR(_name, _mode, _show)		\
17*4f5c791aSLennart Poettering static struct device_attribute sys_dmi_##_name##_attr =	\
18*4f5c791aSLennart Poettering 	__ATTR(_name, _mode, _show, NULL);
19*4f5c791aSLennart Poettering 
20*4f5c791aSLennart Poettering #define DEFINE_DMI_ATTR_WITH_SHOW(_name, _mode, _field)			\
21*4f5c791aSLennart Poettering static ssize_t sys_dmi_##_name##_show(struct device *dev,		\
22*4f5c791aSLennart Poettering 				      struct device_attribute *attr,	\
23*4f5c791aSLennart Poettering 				      char *page)			\
24*4f5c791aSLennart Poettering {									\
25*4f5c791aSLennart Poettering 	ssize_t len;							\
26*4f5c791aSLennart Poettering 	len = scnprintf(page, PAGE_SIZE, "%s\n", dmi_get_system_info(_field)); \
27*4f5c791aSLennart Poettering 	page[len-1] = '\n';						\
28*4f5c791aSLennart Poettering 	return len;							\
29*4f5c791aSLennart Poettering }									\
30*4f5c791aSLennart Poettering DEFINE_DMI_ATTR(_name, _mode, sys_dmi_##_name##_show);
31*4f5c791aSLennart Poettering 
32*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(bios_vendor,		0444, DMI_BIOS_VENDOR);
33*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(bios_version,		0444, DMI_BIOS_VERSION);
34*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(bios_date,		0444, DMI_BIOS_DATE);
35*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(sys_vendor,		0444, DMI_SYS_VENDOR);
36*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_name,		0444, DMI_PRODUCT_NAME);
37*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_version,	0444, DMI_PRODUCT_VERSION);
38*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_serial,	0400, DMI_PRODUCT_SERIAL);
39*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_uuid,		0400, DMI_PRODUCT_UUID);
40*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_vendor,		0444, DMI_BOARD_VENDOR);
41*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_name,		0444, DMI_BOARD_NAME);
42*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_version,	0444, DMI_BOARD_VERSION);
43*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_serial,		0400, DMI_BOARD_SERIAL);
44*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_asset_tag,	0444, DMI_BOARD_ASSET_TAG);
45*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_vendor,	0444, DMI_CHASSIS_VENDOR);
46*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_type,		0444, DMI_CHASSIS_TYPE);
47*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_version,	0444, DMI_CHASSIS_VERSION);
48*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_serial,	0400, DMI_CHASSIS_SERIAL);
49*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_asset_tag,	0444, DMI_CHASSIS_ASSET_TAG);
50*4f5c791aSLennart Poettering 
51*4f5c791aSLennart Poettering static void ascii_filter(char *d, const char *s)
52*4f5c791aSLennart Poettering {
53*4f5c791aSLennart Poettering 	/* Filter out characters we don't want to see in the modalias string */
54*4f5c791aSLennart Poettering 	for (; *s; s++)
55*4f5c791aSLennart Poettering 		if (*s > ' ' && *s < 127 && *s != ':')
56*4f5c791aSLennart Poettering 			*(d++) = *s;
57*4f5c791aSLennart Poettering 
58*4f5c791aSLennart Poettering 	*d = 0;
59*4f5c791aSLennart Poettering }
60*4f5c791aSLennart Poettering 
61*4f5c791aSLennart Poettering static ssize_t get_modalias(char *buffer, size_t buffer_size)
62*4f5c791aSLennart Poettering {
63*4f5c791aSLennart Poettering 	static const struct mafield {
64*4f5c791aSLennart Poettering 		const char *prefix;
65*4f5c791aSLennart Poettering 		int field;
66*4f5c791aSLennart Poettering 	} fields[] = {
67*4f5c791aSLennart Poettering 		{ "bvn", DMI_BIOS_VENDOR },
68*4f5c791aSLennart Poettering 		{ "bvr", DMI_BIOS_VERSION },
69*4f5c791aSLennart Poettering 		{ "bd",  DMI_BIOS_DATE },
70*4f5c791aSLennart Poettering 		{ "svn", DMI_SYS_VENDOR },
71*4f5c791aSLennart Poettering 		{ "pn",  DMI_PRODUCT_NAME },
72*4f5c791aSLennart Poettering 		{ "pvr", DMI_PRODUCT_VERSION },
73*4f5c791aSLennart Poettering 		{ "rvn", DMI_BOARD_VENDOR },
74*4f5c791aSLennart Poettering 		{ "rn",  DMI_BOARD_NAME },
75*4f5c791aSLennart Poettering 		{ "rvr", DMI_BOARD_VERSION },
76*4f5c791aSLennart Poettering 		{ "cvn", DMI_CHASSIS_VENDOR },
77*4f5c791aSLennart Poettering 		{ "ct",  DMI_CHASSIS_TYPE },
78*4f5c791aSLennart Poettering 		{ "cvr", DMI_CHASSIS_VERSION },
79*4f5c791aSLennart Poettering 		{ NULL,  DMI_NONE }
80*4f5c791aSLennart Poettering 	};
81*4f5c791aSLennart Poettering 
82*4f5c791aSLennart Poettering 	ssize_t l, left;
83*4f5c791aSLennart Poettering 	char *p;
84*4f5c791aSLennart Poettering 	const struct mafield *f;
85*4f5c791aSLennart Poettering 
86*4f5c791aSLennart Poettering 	strcpy(buffer, "dmi");
87*4f5c791aSLennart Poettering 	p = buffer + 3; left = buffer_size - 4;
88*4f5c791aSLennart Poettering 
89*4f5c791aSLennart Poettering 	for (f = fields; f->prefix && left > 0; f++) {
90*4f5c791aSLennart Poettering 		const char *c;
91*4f5c791aSLennart Poettering 		char *t;
92*4f5c791aSLennart Poettering 
93*4f5c791aSLennart Poettering 		c = dmi_get_system_info(f->field);
94*4f5c791aSLennart Poettering 		if (!c)
95*4f5c791aSLennart Poettering 			continue;
96*4f5c791aSLennart Poettering 
97*4f5c791aSLennart Poettering 		t = kmalloc(strlen(c) + 1, GFP_KERNEL);
98*4f5c791aSLennart Poettering 		if (!t)
99*4f5c791aSLennart Poettering 			break;
100*4f5c791aSLennart Poettering 		ascii_filter(t, c);
101*4f5c791aSLennart Poettering 		l = scnprintf(p, left, ":%s%s", f->prefix, t);
102*4f5c791aSLennart Poettering 		kfree(t);
103*4f5c791aSLennart Poettering 
104*4f5c791aSLennart Poettering 		p += l;
105*4f5c791aSLennart Poettering 		left -= l;
106*4f5c791aSLennart Poettering 	}
107*4f5c791aSLennart Poettering 
108*4f5c791aSLennart Poettering 	p[0] = ':';
109*4f5c791aSLennart Poettering 	p[1] = 0;
110*4f5c791aSLennart Poettering 
111*4f5c791aSLennart Poettering 	return p - buffer + 1;
112*4f5c791aSLennart Poettering }
113*4f5c791aSLennart Poettering 
114*4f5c791aSLennart Poettering static ssize_t sys_dmi_modalias_show(struct device *dev,
115*4f5c791aSLennart Poettering 				     struct device_attribute *attr, char *page)
116*4f5c791aSLennart Poettering {
117*4f5c791aSLennart Poettering 	ssize_t r;
118*4f5c791aSLennart Poettering 	r = get_modalias(page, PAGE_SIZE-1);
119*4f5c791aSLennart Poettering 	page[r] = '\n';
120*4f5c791aSLennart Poettering 	page[r+1] = 0;
121*4f5c791aSLennart Poettering 	return r+1;
122*4f5c791aSLennart Poettering }
123*4f5c791aSLennart Poettering 
124*4f5c791aSLennart Poettering DEFINE_DMI_ATTR(modalias, 0444, sys_dmi_modalias_show);
125*4f5c791aSLennart Poettering 
126*4f5c791aSLennart Poettering static struct attribute *sys_dmi_attributes[DMI_STRING_MAX+2];
127*4f5c791aSLennart Poettering 
128*4f5c791aSLennart Poettering static struct attribute_group sys_dmi_attribute_group = {
129*4f5c791aSLennart Poettering 	.attrs = sys_dmi_attributes,
130*4f5c791aSLennart Poettering };
131*4f5c791aSLennart Poettering 
132*4f5c791aSLennart Poettering static struct attribute_group* sys_dmi_attribute_groups[] = {
133*4f5c791aSLennart Poettering 	&sys_dmi_attribute_group,
134*4f5c791aSLennart Poettering 	NULL
135*4f5c791aSLennart Poettering };
136*4f5c791aSLennart Poettering 
137*4f5c791aSLennart Poettering static int dmi_dev_uevent(struct device *dev, char **envp,
138*4f5c791aSLennart Poettering 			    int num_envp, char *buffer, int buffer_size)
139*4f5c791aSLennart Poettering {
140*4f5c791aSLennart Poettering 	strcpy(buffer, "MODALIAS=");
141*4f5c791aSLennart Poettering 	get_modalias(buffer+9, buffer_size-9);
142*4f5c791aSLennart Poettering 	envp[0] = buffer;
143*4f5c791aSLennart Poettering 	envp[1] = NULL;
144*4f5c791aSLennart Poettering 
145*4f5c791aSLennart Poettering 	return 0;
146*4f5c791aSLennart Poettering }
147*4f5c791aSLennart Poettering 
148*4f5c791aSLennart Poettering static struct class dmi_class = {
149*4f5c791aSLennart Poettering 	.name = "dmi",
150*4f5c791aSLennart Poettering 	.dev_release = (void(*)(struct device *)) kfree,
151*4f5c791aSLennart Poettering 	.dev_uevent = dmi_dev_uevent,
152*4f5c791aSLennart Poettering };
153*4f5c791aSLennart Poettering 
154*4f5c791aSLennart Poettering static struct device *dmi_dev;
155*4f5c791aSLennart Poettering 
156*4f5c791aSLennart Poettering /* Initialization */
157*4f5c791aSLennart Poettering 
158*4f5c791aSLennart Poettering #define ADD_DMI_ATTR(_name, _field) \
159*4f5c791aSLennart Poettering 	if (dmi_get_system_info(_field)) \
160*4f5c791aSLennart Poettering 		sys_dmi_attributes[i++] = & sys_dmi_##_name##_attr.attr;
161*4f5c791aSLennart Poettering 
162*4f5c791aSLennart Poettering extern int dmi_available;
163*4f5c791aSLennart Poettering 
164*4f5c791aSLennart Poettering static int __init dmi_id_init(void)
165*4f5c791aSLennart Poettering {
166*4f5c791aSLennart Poettering 	int ret, i;
167*4f5c791aSLennart Poettering 
168*4f5c791aSLennart Poettering 	if (!dmi_available)
169*4f5c791aSLennart Poettering 		return -ENODEV;
170*4f5c791aSLennart Poettering 
171*4f5c791aSLennart Poettering 	/* Not necessarily all DMI fields are available on all
172*4f5c791aSLennart Poettering 	 * systems, hence let's built an attribute table of just
173*4f5c791aSLennart Poettering 	 * what's available */
174*4f5c791aSLennart Poettering 	i = 0;
175*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(bios_vendor,       DMI_BIOS_VENDOR);
176*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(bios_version,      DMI_BIOS_VERSION);
177*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(bios_date,         DMI_BIOS_DATE);
178*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(sys_vendor,        DMI_SYS_VENDOR);
179*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(product_name,      DMI_PRODUCT_NAME);
180*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(product_version,   DMI_PRODUCT_VERSION);
181*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(product_serial,    DMI_PRODUCT_SERIAL);
182*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(product_uuid,      DMI_PRODUCT_UUID);
183*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(board_vendor,      DMI_BOARD_VENDOR);
184*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(board_name,        DMI_BOARD_NAME);
185*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(board_version,     DMI_BOARD_VERSION);
186*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(board_serial,      DMI_BOARD_SERIAL);
187*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(board_asset_tag,   DMI_BOARD_ASSET_TAG);
188*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(chassis_vendor,    DMI_CHASSIS_VENDOR);
189*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(chassis_type,      DMI_CHASSIS_TYPE);
190*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(chassis_version,   DMI_CHASSIS_VERSION);
191*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(chassis_serial,    DMI_CHASSIS_SERIAL);
192*4f5c791aSLennart Poettering 	ADD_DMI_ATTR(chassis_asset_tag, DMI_CHASSIS_ASSET_TAG);
193*4f5c791aSLennart Poettering 	sys_dmi_attributes[i++] = &sys_dmi_modalias_attr.attr;
194*4f5c791aSLennart Poettering 
195*4f5c791aSLennart Poettering 	ret = class_register(&dmi_class);
196*4f5c791aSLennart Poettering 	if (ret)
197*4f5c791aSLennart Poettering 		return ret;
198*4f5c791aSLennart Poettering 
199*4f5c791aSLennart Poettering 	dmi_dev = kzalloc(sizeof(*dmi_dev), GFP_KERNEL);
200*4f5c791aSLennart Poettering 	if (!dmi_dev) {
201*4f5c791aSLennart Poettering 		ret = -ENOMEM;
202*4f5c791aSLennart Poettering 		goto fail_class_unregister;
203*4f5c791aSLennart Poettering 	}
204*4f5c791aSLennart Poettering 
205*4f5c791aSLennart Poettering 	dmi_dev->class = &dmi_class;
206*4f5c791aSLennart Poettering 	strcpy(dmi_dev->bus_id, "id");
207*4f5c791aSLennart Poettering 	dmi_dev->groups = sys_dmi_attribute_groups;
208*4f5c791aSLennart Poettering 
209*4f5c791aSLennart Poettering 	ret = device_register(dmi_dev);
210*4f5c791aSLennart Poettering 	if (ret)
211*4f5c791aSLennart Poettering 		goto fail_class_unregister;
212*4f5c791aSLennart Poettering 
213*4f5c791aSLennart Poettering 	return 0;
214*4f5c791aSLennart Poettering 
215*4f5c791aSLennart Poettering fail_class_unregister:
216*4f5c791aSLennart Poettering 
217*4f5c791aSLennart Poettering 	class_unregister(&dmi_class);
218*4f5c791aSLennart Poettering 
219*4f5c791aSLennart Poettering 	return ret;
220*4f5c791aSLennart Poettering }
221*4f5c791aSLennart Poettering 
222*4f5c791aSLennart Poettering arch_initcall(dmi_id_init);
223