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