xref: /linux/drivers/platform/x86/dell/dell-smbios-base.c (revision b226d019836fbab759be8f62818851ee5cb0d9de)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  Common functions for kernel modules using Dell SMBIOS
4  *
5  *  Copyright (c) Red Hat <mjg@redhat.com>
6  *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
7  *  Copyright (c) 2014 Pali Rohár <pali@kernel.org>
8  *
9  *  Based on documentation in the libsmbios package:
10  *  Copyright (C) 2005-2014 Dell Inc.
11  */
12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13 
14 #include <linux/container_of.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/capability.h>
18 #include <linux/dmi.h>
19 #include <linux/err.h>
20 #include <linux/mutex.h>
21 #include <linux/platform_device.h>
22 #include <linux/slab.h>
23 #include "dell-smbios.h"
24 
25 static u32 da_supported_commands;
26 static int da_num_tokens;
27 static struct platform_device *platform_device;
28 static struct calling_interface_token *da_tokens;
29 static struct token_sysfs_data *token_entries;
30 static struct attribute **token_attrs;
31 static DEFINE_MUTEX(smbios_mutex);
32 
33 struct token_sysfs_data {
34 	struct device_attribute location_attr;
35 	struct device_attribute value_attr;
36 	struct calling_interface_token *token;
37 };
38 
39 struct smbios_device {
40 	struct list_head list;
41 	struct device *device;
42 	int (*call_fn)(struct calling_interface_buffer *arg);
43 };
44 
45 struct smbios_call {
46 	u32 need_capability;
47 	int cmd_class;
48 	int cmd_select;
49 };
50 
51 /* calls that are whitelisted for given capabilities */
52 static struct smbios_call call_whitelist[] = {
53 	/* generally tokens are allowed, but may be further filtered or
54 	 * restricted by token blacklist or whitelist
55 	 */
56 	{CAP_SYS_ADMIN,	CLASS_TOKEN_READ,	SELECT_TOKEN_STD},
57 	{CAP_SYS_ADMIN,	CLASS_TOKEN_READ,	SELECT_TOKEN_AC},
58 	{CAP_SYS_ADMIN,	CLASS_TOKEN_READ,	SELECT_TOKEN_BAT},
59 	{CAP_SYS_ADMIN,	CLASS_TOKEN_WRITE,	SELECT_TOKEN_STD},
60 	{CAP_SYS_ADMIN,	CLASS_TOKEN_WRITE,	SELECT_TOKEN_AC},
61 	{CAP_SYS_ADMIN,	CLASS_TOKEN_WRITE,	SELECT_TOKEN_BAT},
62 	/* used by userspace: fwupdate */
63 	{CAP_SYS_ADMIN, CLASS_ADMIN_PROP,	SELECT_ADMIN_PROP},
64 	/* used by userspace: fwupd */
65 	{CAP_SYS_ADMIN,	CLASS_INFO,		SELECT_DOCK},
66 	{CAP_SYS_ADMIN,	CLASS_FLASH_INTERFACE,	SELECT_FLASH_INTERFACE},
67 };
68 
69 /* calls that are explicitly blacklisted */
70 static struct smbios_call call_blacklist[] = {
71 	{0x0000,  1,  7}, /* manufacturing use */
72 	{0x0000,  6,  5}, /* manufacturing use */
73 	{0x0000, 11,  3}, /* write once */
74 	{0x0000, 11,  7}, /* write once */
75 	{0x0000, 11, 11}, /* write once */
76 	{0x0000, 19, -1}, /* diagnostics */
77 	/* handled by kernel: dell-laptop */
78 	{0x0000, CLASS_INFO, SELECT_RFKILL},
79 	{0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT},
80 	{0x0000, CLASS_INFO, SELECT_THERMAL_MANAGEMENT},
81 };
82 
83 struct token_range {
84 	u32 need_capability;
85 	u16 min;
86 	u16 max;
87 };
88 
89 /* tokens that are whitelisted for given capabilities */
90 static struct token_range token_whitelist[] = {
91 	/* used by userspace: fwupdate */
92 	{CAP_SYS_ADMIN,	CAPSULE_EN_TOKEN,	CAPSULE_DIS_TOKEN},
93 	/* can indicate to userspace that WMI is needed */
94 	{0x0000,	WSMT_EN_TOKEN,		WSMT_DIS_TOKEN}
95 };
96 
97 /* tokens that are explicitly blacklisted */
98 static struct token_range token_blacklist[] = {
99 	{0x0000, 0x0058, 0x0059}, /* ME use */
100 	{0x0000, 0x00CD, 0x00D0}, /* raid shadow copy */
101 	{0x0000, 0x013A, 0x01FF}, /* sata shadow copy */
102 	{0x0000, 0x0175, 0x0176}, /* write once */
103 	{0x0000, 0x0195, 0x0197}, /* diagnostics */
104 	{0x0000, 0x01DC, 0x01DD}, /* manufacturing use */
105 	{0x0000, 0x027D, 0x0284}, /* diagnostics */
106 	{0x0000, 0x02E3, 0x02E3}, /* manufacturing use */
107 	{0x0000, 0x02FF, 0x02FF}, /* manufacturing use */
108 	{0x0000, 0x0300, 0x0302}, /* manufacturing use */
109 	{0x0000, 0x0325, 0x0326}, /* manufacturing use */
110 	{0x0000, 0x0332, 0x0335}, /* fan control */
111 	{0x0000, 0x0350, 0x0350}, /* manufacturing use */
112 	{0x0000, 0x0363, 0x0363}, /* manufacturing use */
113 	{0x0000, 0x0368, 0x0368}, /* manufacturing use */
114 	{0x0000, 0x03F6, 0x03F7}, /* manufacturing use */
115 	{0x0000, 0x049E, 0x049F}, /* manufacturing use */
116 	{0x0000, 0x04A0, 0x04A3}, /* disagnostics */
117 	{0x0000, 0x04E6, 0x04E7}, /* manufacturing use */
118 	{0x0000, 0x4000, 0x7FFF}, /* internal BIOS use */
119 	{0x0000, 0x9000, 0x9001}, /* internal BIOS use */
120 	{0x0000, 0xA000, 0xBFFF}, /* write only */
121 	{0x0000, 0xEFF0, 0xEFFF}, /* internal BIOS use */
122 	/* handled by kernel: dell-laptop */
123 	{0x0000, BRIGHTNESS_TOKEN,	BRIGHTNESS_TOKEN},
124 	{0x0000, KBD_LED_OFF_TOKEN,	KBD_LED_AUTO_TOKEN},
125 	{0x0000, KBD_LED_AC_TOKEN,	KBD_LED_AC_TOKEN},
126 	{0x0000, KBD_LED_AUTO_25_TOKEN,	KBD_LED_AUTO_75_TOKEN},
127 	{0x0000, KBD_LED_AUTO_100_TOKEN,	KBD_LED_AUTO_100_TOKEN},
128 	{0x0000, GLOBAL_MIC_MUTE_ENABLE,	GLOBAL_MIC_MUTE_DISABLE},
129 };
130 
131 static LIST_HEAD(smbios_device_list);
132 
dell_smbios_error(int value)133 int dell_smbios_error(int value)
134 {
135 	switch (value) {
136 	case 0: /* Completed successfully */
137 		return 0;
138 	case -1: /* Completed with error */
139 		return -EIO;
140 	case -2: /* Function not supported */
141 		return -ENXIO;
142 	default: /* Unknown error */
143 		return -EINVAL;
144 	}
145 }
146 EXPORT_SYMBOL_GPL(dell_smbios_error);
147 
dell_smbios_register_device(struct device * d,void * call_fn)148 int dell_smbios_register_device(struct device *d, void *call_fn)
149 {
150 	struct smbios_device *priv;
151 
152 	priv = devm_kzalloc(d, sizeof(struct smbios_device), GFP_KERNEL);
153 	if (!priv)
154 		return -ENOMEM;
155 	get_device(d);
156 	priv->device = d;
157 	priv->call_fn = call_fn;
158 	mutex_lock(&smbios_mutex);
159 	list_add_tail(&priv->list, &smbios_device_list);
160 	mutex_unlock(&smbios_mutex);
161 	dev_dbg(d, "Added device: %s\n", d->driver->name);
162 	return 0;
163 }
164 EXPORT_SYMBOL_GPL(dell_smbios_register_device);
165 
dell_smbios_unregister_device(struct device * d)166 void dell_smbios_unregister_device(struct device *d)
167 {
168 	struct smbios_device *priv;
169 
170 	mutex_lock(&smbios_mutex);
171 	list_for_each_entry(priv, &smbios_device_list, list) {
172 		if (priv->device == d) {
173 			list_del(&priv->list);
174 			put_device(d);
175 			break;
176 		}
177 	}
178 	mutex_unlock(&smbios_mutex);
179 	dev_dbg(d, "Remove device: %s\n", d->driver->name);
180 }
181 EXPORT_SYMBOL_GPL(dell_smbios_unregister_device);
182 
dell_smbios_call_filter(struct device * d,struct calling_interface_buffer * buffer)183 int dell_smbios_call_filter(struct device *d,
184 			    struct calling_interface_buffer *buffer)
185 {
186 	u16 t = 0;
187 	int i;
188 
189 	/* can't make calls over 30 */
190 	if (buffer->cmd_class > 30) {
191 		dev_dbg(d, "class too big: %u\n", buffer->cmd_class);
192 		return -EINVAL;
193 	}
194 
195 	/* supported calls on the particular system */
196 	if (!(da_supported_commands & (1 << buffer->cmd_class))) {
197 		dev_dbg(d, "invalid command, supported commands: 0x%8x\n",
198 			da_supported_commands);
199 		return -EINVAL;
200 	}
201 
202 	/* match against call blacklist  */
203 	for (i = 0; i < ARRAY_SIZE(call_blacklist); i++) {
204 		if (buffer->cmd_class != call_blacklist[i].cmd_class)
205 			continue;
206 		if (buffer->cmd_select != call_blacklist[i].cmd_select &&
207 		    call_blacklist[i].cmd_select != -1)
208 			continue;
209 		dev_dbg(d, "blacklisted command: %u/%u\n",
210 			buffer->cmd_class, buffer->cmd_select);
211 		return -EINVAL;
212 	}
213 
214 	/* if a token call, find token ID */
215 
216 	if ((buffer->cmd_class == CLASS_TOKEN_READ ||
217 	     buffer->cmd_class == CLASS_TOKEN_WRITE) &&
218 	     buffer->cmd_select < 3) {
219 		/* tokens enabled ? */
220 		if (!da_tokens) {
221 			dev_dbg(d, "no token support on this system\n");
222 			return -EINVAL;
223 		}
224 
225 		/* find the matching token ID */
226 		for (i = 0; i < da_num_tokens; i++) {
227 			if (da_tokens[i].location != buffer->input[0])
228 				continue;
229 			t = da_tokens[i].tokenID;
230 			break;
231 		}
232 
233 		/* token call; but token didn't exist */
234 		if (!t) {
235 			dev_dbg(d, "token at location %04x doesn't exist\n",
236 				buffer->input[0]);
237 			return -EINVAL;
238 		}
239 
240 		/* match against token blacklist */
241 		for (i = 0; i < ARRAY_SIZE(token_blacklist); i++) {
242 			if (!token_blacklist[i].min || !token_blacklist[i].max)
243 				continue;
244 			if (t >= token_blacklist[i].min &&
245 			    t <= token_blacklist[i].max)
246 				return -EINVAL;
247 		}
248 
249 		/* match against token whitelist */
250 		for (i = 0; i < ARRAY_SIZE(token_whitelist); i++) {
251 			if (!token_whitelist[i].min || !token_whitelist[i].max)
252 				continue;
253 			if (t < token_whitelist[i].min ||
254 			    t > token_whitelist[i].max)
255 				continue;
256 			if (!token_whitelist[i].need_capability ||
257 			    capable(token_whitelist[i].need_capability)) {
258 				dev_dbg(d, "whitelisted token: %x\n", t);
259 				return 0;
260 			}
261 
262 		}
263 	}
264 	/* match against call whitelist */
265 	for (i = 0; i < ARRAY_SIZE(call_whitelist); i++) {
266 		if (buffer->cmd_class != call_whitelist[i].cmd_class)
267 			continue;
268 		if (buffer->cmd_select != call_whitelist[i].cmd_select)
269 			continue;
270 		if (!call_whitelist[i].need_capability ||
271 		    capable(call_whitelist[i].need_capability)) {
272 			dev_dbg(d, "whitelisted capable command: %u/%u\n",
273 			buffer->cmd_class, buffer->cmd_select);
274 			return 0;
275 		}
276 		dev_dbg(d, "missing capability %d for %u/%u\n",
277 			call_whitelist[i].need_capability,
278 			buffer->cmd_class, buffer->cmd_select);
279 
280 	}
281 
282 	/* not in a whitelist, only allow processes with capabilities */
283 	if (capable(CAP_SYS_RAWIO)) {
284 		dev_dbg(d, "Allowing %u/%u due to CAP_SYS_RAWIO\n",
285 			buffer->cmd_class, buffer->cmd_select);
286 		return 0;
287 	}
288 
289 	return -EACCES;
290 }
291 EXPORT_SYMBOL_GPL(dell_smbios_call_filter);
292 
dell_smbios_call(struct calling_interface_buffer * buffer)293 int dell_smbios_call(struct calling_interface_buffer *buffer)
294 {
295 	int (*call_fn)(struct calling_interface_buffer *) = NULL;
296 	struct device *selected_dev = NULL;
297 	struct smbios_device *priv;
298 	int ret;
299 
300 	mutex_lock(&smbios_mutex);
301 	list_for_each_entry(priv, &smbios_device_list, list) {
302 		if (!selected_dev || priv->device->id >= selected_dev->id) {
303 			dev_dbg(priv->device, "Trying device ID: %d\n",
304 				priv->device->id);
305 			call_fn = priv->call_fn;
306 			selected_dev = priv->device;
307 		}
308 	}
309 
310 	if (!selected_dev) {
311 		ret = -ENODEV;
312 		pr_err("No dell-smbios drivers are loaded\n");
313 		goto out_smbios_call;
314 	}
315 
316 	ret = call_fn(buffer);
317 
318 out_smbios_call:
319 	mutex_unlock(&smbios_mutex);
320 	return ret;
321 }
322 EXPORT_SYMBOL_GPL(dell_smbios_call);
323 
dell_fill_request(struct calling_interface_buffer * buffer,u32 arg0,u32 arg1,u32 arg2,u32 arg3)324 void dell_fill_request(struct calling_interface_buffer *buffer,
325 			       u32 arg0, u32 arg1, u32 arg2, u32 arg3)
326 {
327 	memset(buffer, 0, sizeof(struct calling_interface_buffer));
328 	buffer->input[0] = arg0;
329 	buffer->input[1] = arg1;
330 	buffer->input[2] = arg2;
331 	buffer->input[3] = arg3;
332 }
333 EXPORT_SYMBOL_GPL(dell_fill_request);
334 
dell_send_request(struct calling_interface_buffer * buffer,u16 class,u16 select)335 int dell_send_request(struct calling_interface_buffer *buffer,
336 			     u16 class, u16 select)
337 {
338 	int ret;
339 
340 	buffer->cmd_class = class;
341 	buffer->cmd_select = select;
342 	ret = dell_smbios_call(buffer);
343 	if (ret != 0)
344 		return ret;
345 	return dell_smbios_error(buffer->output[0]);
346 }
347 EXPORT_SYMBOL_GPL(dell_send_request);
348 
dell_smbios_find_token(int tokenid)349 struct calling_interface_token *dell_smbios_find_token(int tokenid)
350 {
351 	int i;
352 
353 	if (!da_tokens)
354 		return NULL;
355 
356 	for (i = 0; i < da_num_tokens; i++) {
357 		if (da_tokens[i].tokenID == tokenid)
358 			return &da_tokens[i];
359 	}
360 
361 	return NULL;
362 }
363 EXPORT_SYMBOL_GPL(dell_smbios_find_token);
364 
365 static BLOCKING_NOTIFIER_HEAD(dell_laptop_chain_head);
366 
dell_laptop_register_notifier(struct notifier_block * nb)367 int dell_laptop_register_notifier(struct notifier_block *nb)
368 {
369 	return blocking_notifier_chain_register(&dell_laptop_chain_head, nb);
370 }
371 EXPORT_SYMBOL_GPL(dell_laptop_register_notifier);
372 
dell_laptop_unregister_notifier(struct notifier_block * nb)373 int dell_laptop_unregister_notifier(struct notifier_block *nb)
374 {
375 	return blocking_notifier_chain_unregister(&dell_laptop_chain_head, nb);
376 }
377 EXPORT_SYMBOL_GPL(dell_laptop_unregister_notifier);
378 
dell_laptop_call_notifier(unsigned long action,void * data)379 void dell_laptop_call_notifier(unsigned long action, void *data)
380 {
381 	blocking_notifier_call_chain(&dell_laptop_chain_head, action, data);
382 }
383 EXPORT_SYMBOL_GPL(dell_laptop_call_notifier);
384 
dell_smbios_class_is_supported(u16 class)385 bool dell_smbios_class_is_supported(u16 class)
386 {
387 	/* Classes over 30 always unsupported */
388 	if (class > 30)
389 		return false;
390 	return da_supported_commands & (1 << class);
391 }
392 EXPORT_SYMBOL_GPL(dell_smbios_class_is_supported);
393 
parse_da_table(const struct dmi_header * dm)394 static void __init parse_da_table(const struct dmi_header *dm)
395 {
396 	/* Final token is a terminator, so we don't want to copy it */
397 	int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
398 	struct calling_interface_token *new_da_tokens;
399 	struct calling_interface_structure *table =
400 		container_of(dm, struct calling_interface_structure, header);
401 
402 	/*
403 	 * 4 bytes of table header, plus 7 bytes of Dell header
404 	 * plus at least 6 bytes of entry
405 	 */
406 
407 	if (dm->length < 17)
408 		return;
409 
410 	da_supported_commands = table->supportedCmds;
411 
412 	new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
413 				 sizeof(struct calling_interface_token),
414 				 GFP_KERNEL);
415 
416 	if (!new_da_tokens)
417 		return;
418 	da_tokens = new_da_tokens;
419 
420 	memcpy(da_tokens+da_num_tokens, table->tokens,
421 	       sizeof(struct calling_interface_token) * tokens);
422 
423 	da_num_tokens += tokens;
424 }
425 
zero_duplicates(struct device * dev)426 static void zero_duplicates(struct device *dev)
427 {
428 	int i, j;
429 
430 	for (i = 0; i < da_num_tokens; i++) {
431 		if (da_tokens[i].tokenID == 0)
432 			continue;
433 		for (j = i+1; j < da_num_tokens; j++) {
434 			if (da_tokens[j].tokenID == 0)
435 				continue;
436 			if (da_tokens[i].tokenID == da_tokens[j].tokenID) {
437 				dev_dbg(dev, "Zeroing dup token ID %x(%x/%x)\n",
438 					da_tokens[j].tokenID,
439 					da_tokens[j].location,
440 					da_tokens[j].value);
441 				da_tokens[j].tokenID = 0;
442 			}
443 		}
444 	}
445 }
446 
find_tokens(const struct dmi_header * dm,void * dummy)447 static void __init find_tokens(const struct dmi_header *dm, void *dummy)
448 {
449 	switch (dm->type) {
450 	case 0xd4: /* Indexed IO */
451 	case 0xd5: /* Protected Area Type 1 */
452 	case 0xd6: /* Protected Area Type 2 */
453 		break;
454 	case 0xda: /* Calling interface */
455 		parse_da_table(dm);
456 		break;
457 	}
458 }
459 
location_show(struct device * dev,struct device_attribute * attr,char * buf)460 static ssize_t location_show(struct device *dev,
461 			     struct device_attribute *attr, char *buf)
462 {
463 	struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, location_attr);
464 
465 	if (!capable(CAP_SYS_ADMIN))
466 		return -EPERM;
467 
468 	return sysfs_emit(buf, "%08x", data->token->location);
469 }
470 
value_show(struct device * dev,struct device_attribute * attr,char * buf)471 static ssize_t value_show(struct device *dev,
472 			  struct device_attribute *attr, char *buf)
473 {
474 	struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, value_attr);
475 
476 	if (!capable(CAP_SYS_ADMIN))
477 		return -EPERM;
478 
479 	return sysfs_emit(buf, "%08x", data->token->value);
480 }
481 
482 static struct attribute_group smbios_attribute_group = {
483 	.name = "tokens"
484 };
485 
486 static struct platform_driver platform_driver = {
487 	.driver = {
488 		.name = "dell-smbios",
489 	},
490 };
491 
build_tokens_sysfs(struct platform_device * dev)492 static int build_tokens_sysfs(struct platform_device *dev)
493 {
494 	char *location_name;
495 	char *value_name;
496 	int ret;
497 	int i, j;
498 
499 	token_entries = kcalloc(da_num_tokens, sizeof(*token_entries), GFP_KERNEL);
500 	if (!token_entries)
501 		return -ENOMEM;
502 
503 	/* need to store both location and value + terminator*/
504 	token_attrs = kcalloc((2 * da_num_tokens) + 1, sizeof(*token_attrs), GFP_KERNEL);
505 	if (!token_attrs)
506 		goto out_allocate_attrs;
507 
508 	for (i = 0, j = 0; i < da_num_tokens; i++) {
509 		/* skip empty */
510 		if (da_tokens[i].tokenID == 0)
511 			continue;
512 
513 		token_entries[i].token = &da_tokens[i];
514 
515 		/* add location */
516 		location_name = kasprintf(GFP_KERNEL, "%04x_location",
517 					  da_tokens[i].tokenID);
518 		if (location_name == NULL)
519 			goto out_unwind_strings;
520 
521 		sysfs_attr_init(&token_entries[i].location_attr.attr);
522 		token_entries[i].location_attr.attr.name = location_name;
523 		token_entries[i].location_attr.attr.mode = 0444;
524 		token_entries[i].location_attr.show = location_show;
525 		token_attrs[j++] = &token_entries[i].location_attr.attr;
526 
527 		/* add value */
528 		value_name = kasprintf(GFP_KERNEL, "%04x_value",
529 				       da_tokens[i].tokenID);
530 		if (!value_name) {
531 			kfree(location_name);
532 			goto out_unwind_strings;
533 		}
534 
535 		sysfs_attr_init(&token_entries[i].value_attr.attr);
536 		token_entries[i].value_attr.attr.name = value_name;
537 		token_entries[i].value_attr.attr.mode = 0444;
538 		token_entries[i].value_attr.show = value_show;
539 		token_attrs[j++] = &token_entries[i].value_attr.attr;
540 	}
541 	smbios_attribute_group.attrs = token_attrs;
542 
543 	ret = sysfs_create_group(&dev->dev.kobj, &smbios_attribute_group);
544 	if (ret)
545 		goto out_unwind_strings;
546 	return 0;
547 
548 out_unwind_strings:
549 	while (i--) {
550 		kfree(token_entries[i].location_attr.attr.name);
551 		kfree(token_entries[i].value_attr.attr.name);
552 	}
553 	kfree(token_attrs);
554 out_allocate_attrs:
555 	kfree(token_entries);
556 
557 	return -ENOMEM;
558 }
559 
free_group(struct platform_device * pdev)560 static void free_group(struct platform_device *pdev)
561 {
562 	int i;
563 
564 	sysfs_remove_group(&pdev->dev.kobj,
565 				&smbios_attribute_group);
566 	for (i = 0; i < da_num_tokens; i++) {
567 		kfree(token_entries[i].location_attr.attr.name);
568 		kfree(token_entries[i].value_attr.attr.name);
569 	}
570 	kfree(token_attrs);
571 	kfree(token_entries);
572 }
573 
dell_smbios_init(void)574 static int __init dell_smbios_init(void)
575 {
576 	int ret, wmi, smm;
577 
578 	if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) &&
579 	    !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Alienware", NULL) &&
580 	    !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) {
581 		pr_err("Unable to run on non-Dell system\n");
582 		return -ENODEV;
583 	}
584 
585 	dmi_walk(find_tokens, NULL);
586 
587 	ret = platform_driver_register(&platform_driver);
588 	if (ret)
589 		goto fail_platform_driver;
590 
591 	platform_device = platform_device_alloc("dell-smbios", 0);
592 	if (!platform_device) {
593 		ret = -ENOMEM;
594 		goto fail_platform_device_alloc;
595 	}
596 	ret = platform_device_add(platform_device);
597 	if (ret)
598 		goto fail_platform_device_add;
599 
600 	/* register backends */
601 	wmi = init_dell_smbios_wmi();
602 	if (wmi)
603 		pr_debug("Failed to initialize WMI backend: %d\n", wmi);
604 	smm = init_dell_smbios_smm();
605 	if (smm)
606 		pr_debug("Failed to initialize SMM backend: %d\n", smm);
607 	if (wmi && smm) {
608 		pr_err("No SMBIOS backends available (wmi: %d, smm: %d)\n",
609 			wmi, smm);
610 		ret = -ENODEV;
611 		goto fail_create_group;
612 	}
613 
614 	if (da_tokens)  {
615 		/* duplicate tokens will cause problems building sysfs files */
616 		zero_duplicates(&platform_device->dev);
617 
618 		ret = build_tokens_sysfs(platform_device);
619 		if (ret)
620 			goto fail_sysfs;
621 	}
622 
623 	return 0;
624 
625 fail_sysfs:
626 	if (!wmi)
627 		exit_dell_smbios_wmi();
628 	if (!smm)
629 		exit_dell_smbios_smm();
630 
631 fail_create_group:
632 	platform_device_del(platform_device);
633 
634 fail_platform_device_add:
635 	platform_device_put(platform_device);
636 
637 fail_platform_device_alloc:
638 	platform_driver_unregister(&platform_driver);
639 
640 fail_platform_driver:
641 	kfree(da_tokens);
642 	return ret;
643 }
644 
dell_smbios_exit(void)645 static void __exit dell_smbios_exit(void)
646 {
647 	exit_dell_smbios_wmi();
648 	exit_dell_smbios_smm();
649 	mutex_lock(&smbios_mutex);
650 	if (platform_device) {
651 		if (da_tokens)
652 			free_group(platform_device);
653 		platform_device_unregister(platform_device);
654 		platform_driver_unregister(&platform_driver);
655 	}
656 	kfree(da_tokens);
657 	mutex_unlock(&smbios_mutex);
658 }
659 
660 module_init(dell_smbios_init);
661 module_exit(dell_smbios_exit);
662 
663 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
664 MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
665 MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
666 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
667 MODULE_DESCRIPTION("Common functions for kernel modules using Dell SMBIOS");
668 MODULE_LICENSE("GPL");
669