1
2
3
4
5
6
7
8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9
10#include <linux/fs.h>
11#include <linux/dmi.h>
12#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/wmi.h>
15#include "dell-wmi-sysman.h"
16#include "../firmware_attributes_class.h"
17
18#define MAX_TYPES 4
19#include <linux/nls.h>
20
21struct wmi_sysman_priv wmi_priv = {
22 .mutex = __MUTEX_INITIALIZER(wmi_priv.mutex),
23};
24
25
26static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"};
27static int reset_option = -1;
28static struct class *fw_attr_class;
29
30
31
32
33
34
35
36
37ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str)
38{
39 u16 *length = (u16 *)buffer;
40 u16 *target = length + 1;
41 int ret;
42
43 ret = utf8s_to_utf16s(str, strlen(str), UTF16_HOST_ENDIAN,
44 target, buffer_len - sizeof(u16));
45 if (ret < 0) {
46 dev_err(wmi_priv.class_dev, "UTF16 conversion failed\n");
47 return ret;
48 }
49
50 if ((ret * sizeof(u16)) > U16_MAX) {
51 dev_err(wmi_priv.class_dev, "Error string too long\n");
52 return -ERANGE;
53 }
54
55 *length = ret * sizeof(u16);
56 return sizeof(u16) + *length;
57}
58
59
60
61
62
63
64size_t calculate_string_buffer(const char *str)
65{
66
67 return sizeof(u16) + strlen(str) * sizeof(u16);
68}
69
70
71
72
73
74
75
76size_t calculate_security_buffer(char *authentication)
77{
78 if (strlen(authentication) > 0) {
79 return (sizeof(u32) * 2) + strlen(authentication) +
80 strlen(authentication) % 2;
81 }
82 return sizeof(u32) * 2;
83}
84
85
86
87
88
89
90
91
92void populate_security_buffer(char *buffer, char *authentication)
93{
94 char *auth = buffer + sizeof(u32) * 2;
95 u32 *sectype = (u32 *) buffer;
96 u32 *seclen = sectype + 1;
97
98 *sectype = strlen(authentication) > 0 ? 1 : 0;
99 *seclen = strlen(authentication);
100
101
102 if (strlen(authentication) > 0)
103 memcpy(auth, authentication, *seclen);
104}
105
106
107
108
109
110int map_wmi_error(int error_code)
111{
112 switch (error_code) {
113 case 0:
114
115 return 0;
116 case 1:
117
118 return -EIO;
119 case 2:
120
121 return -EINVAL;
122 case 3:
123
124 return -EACCES;
125 case 4:
126
127 return -EOPNOTSUPP;
128 case 5:
129
130 return -ENOMEM;
131 case 6:
132
133 return -EPROTO;
134 }
135
136 return -EIO;
137}
138
139
140
141
142
143
144
145static ssize_t reset_bios_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
146{
147 char *start = buf;
148 int i;
149
150 for (i = 0; i < MAX_TYPES; i++) {
151 if (i == reset_option)
152 buf += sprintf(buf, "[%s] ", reset_types[i]);
153 else
154 buf += sprintf(buf, "%s ", reset_types[i]);
155 }
156 buf += sprintf(buf, "\n");
157 return buf-start;
158}
159
160
161
162
163
164
165
166
167static ssize_t reset_bios_store(struct kobject *kobj,
168 struct kobj_attribute *attr, const char *buf, size_t count)
169{
170 int type = sysfs_match_string(reset_types, buf);
171 int ret;
172
173 if (type < 0)
174 return type;
175
176 ret = set_bios_defaults(type);
177 pr_debug("reset all attributes request type %d: %d\n", type, ret);
178 if (!ret) {
179 reset_option = type;
180 ret = count;
181 }
182
183 return ret;
184}
185
186
187
188
189
190
191
192
193
194
195static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr,
196 char *buf)
197{
198 return sprintf(buf, "%d\n", wmi_priv.pending_changes);
199}
200
201static struct kobj_attribute reset_bios = __ATTR_RW(reset_bios);
202static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
203
204
205
206
207
208
209static int create_attributes_level_sysfs_files(void)
210{
211 int ret = sysfs_create_file(&wmi_priv.main_dir_kset->kobj, &reset_bios.attr);
212
213 if (ret) {
214 pr_debug("could not create reset_bios file\n");
215 return ret;
216 }
217
218 ret = sysfs_create_file(&wmi_priv.main_dir_kset->kobj, &pending_reboot.attr);
219 if (ret) {
220 pr_debug("could not create changing_pending_reboot file\n");
221 sysfs_remove_file(&wmi_priv.main_dir_kset->kobj, &reset_bios.attr);
222 }
223 return ret;
224}
225
226static void release_reset_bios_data(void)
227{
228 sysfs_remove_file(&wmi_priv.main_dir_kset->kobj, &reset_bios.attr);
229 sysfs_remove_file(&wmi_priv.main_dir_kset->kobj, &pending_reboot.attr);
230}
231
232static ssize_t wmi_sysman_attr_show(struct kobject *kobj, struct attribute *attr,
233 char *buf)
234{
235 struct kobj_attribute *kattr;
236 ssize_t ret = -EIO;
237
238 kattr = container_of(attr, struct kobj_attribute, attr);
239 if (kattr->show)
240 ret = kattr->show(kobj, kattr, buf);
241 return ret;
242}
243
244static ssize_t wmi_sysman_attr_store(struct kobject *kobj, struct attribute *attr,
245 const char *buf, size_t count)
246{
247 struct kobj_attribute *kattr;
248 ssize_t ret = -EIO;
249
250 kattr = container_of(attr, struct kobj_attribute, attr);
251 if (kattr->store)
252 ret = kattr->store(kobj, kattr, buf, count);
253 return ret;
254}
255
256static const struct sysfs_ops wmi_sysman_kobj_sysfs_ops = {
257 .show = wmi_sysman_attr_show,
258 .store = wmi_sysman_attr_store,
259};
260
261static void attr_name_release(struct kobject *kobj)
262{
263 kfree(kobj);
264}
265
266static struct kobj_type attr_name_ktype = {
267 .release = attr_name_release,
268 .sysfs_ops = &wmi_sysman_kobj_sysfs_ops,
269};
270
271
272
273
274
275
276void strlcpy_attr(char *dest, char *src)
277{
278 size_t len = strlen(src) + 1;
279
280 if (len > 1 && len <= MAX_BUFF)
281 strlcpy(dest, src, len);
282
283
284
285
286 if (len > MAX_BUFF)
287 pr_err("Source string returned from BIOS is out of bound!\n");
288}
289
290
291
292
293
294
295
296
297
298union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string)
299{
300 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
301 acpi_status status;
302
303 status = wmi_query_block(guid_string, instance_id, &out);
304
305 return ACPI_SUCCESS(status) ? (union acpi_object *)out.pointer : NULL;
306}
307
308
309
310
311
312int get_instance_count(const char *guid_string)
313{
314 union acpi_object *wmi_obj = NULL;
315 int i = 0;
316
317 do {
318 kfree(wmi_obj);
319 wmi_obj = get_wmiobj_pointer(i, guid_string);
320 i++;
321 } while (wmi_obj);
322
323 return (i-1);
324}
325
326
327
328
329
330static int alloc_attributes_data(int attr_type)
331{
332 int retval = 0;
333
334 switch (attr_type) {
335 case ENUM:
336 retval = alloc_enum_data();
337 break;
338 case INT:
339 retval = alloc_int_data();
340 break;
341 case STR:
342 retval = alloc_str_data();
343 break;
344 case PO:
345 retval = alloc_po_data();
346 break;
347 default:
348 break;
349 }
350
351 return retval;
352}
353
354
355
356
357
358
359
360static void destroy_attribute_objs(struct kset *kset)
361{
362 struct kobject *pos, *next;
363
364 list_for_each_entry_safe(pos, next, &kset->list, entry) {
365 kobject_put(pos);
366 }
367}
368
369
370
371
372static void release_attributes_data(void)
373{
374 release_reset_bios_data();
375
376 mutex_lock(&wmi_priv.mutex);
377 exit_enum_attributes();
378 exit_int_attributes();
379 exit_str_attributes();
380 exit_po_attributes();
381 if (wmi_priv.authentication_dir_kset) {
382 destroy_attribute_objs(wmi_priv.authentication_dir_kset);
383 kset_unregister(wmi_priv.authentication_dir_kset);
384 wmi_priv.authentication_dir_kset = NULL;
385 }
386 if (wmi_priv.main_dir_kset) {
387 destroy_attribute_objs(wmi_priv.main_dir_kset);
388 kset_unregister(wmi_priv.main_dir_kset);
389 }
390 mutex_unlock(&wmi_priv.mutex);
391
392}
393
394
395
396
397
398
399
400
401
402static int init_bios_attributes(int attr_type, const char *guid)
403{
404 struct kobject *attr_name_kobj;
405 union acpi_object *obj = NULL;
406 union acpi_object *elements;
407 struct kset *tmp_set;
408
409
410
411
412 int instance_id = 0;
413 int retval = 0;
414
415 retval = alloc_attributes_data(attr_type);
416 if (retval)
417 return retval;
418
419 obj = get_wmiobj_pointer(instance_id, guid);
420 if (!obj || obj->type != ACPI_TYPE_PACKAGE)
421 return -ENODEV;
422 elements = obj->package.elements;
423
424 mutex_lock(&wmi_priv.mutex);
425 while (elements) {
426
427 if (elements[ATTR_NAME].type != ACPI_TYPE_STRING) {
428 pr_debug("incorrect element type\n");
429 goto nextobj;
430 }
431 if (strlen(elements[ATTR_NAME].string.pointer) == 0) {
432 pr_debug("empty attribute found\n");
433 goto nextobj;
434 }
435 if (attr_type == PO)
436 tmp_set = wmi_priv.authentication_dir_kset;
437 else
438 tmp_set = wmi_priv.main_dir_kset;
439
440 if (kset_find_obj(tmp_set, elements[ATTR_NAME].string.pointer)) {
441 pr_debug("duplicate attribute name found - %s\n",
442 elements[ATTR_NAME].string.pointer);
443 goto nextobj;
444 }
445
446
447 attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL);
448 if (!attr_name_kobj) {
449 retval = -ENOMEM;
450 goto err_attr_init;
451 }
452
453 attr_name_kobj->kset = tmp_set;
454
455 retval = kobject_init_and_add(attr_name_kobj, &attr_name_ktype, NULL, "%s",
456 elements[ATTR_NAME].string.pointer);
457 if (retval) {
458 kobject_put(attr_name_kobj);
459 goto err_attr_init;
460 }
461
462
463 switch (attr_type) {
464 case ENUM:
465 retval = populate_enum_data(elements, instance_id, attr_name_kobj);
466 break;
467 case INT:
468 retval = populate_int_data(elements, instance_id, attr_name_kobj);
469 break;
470 case STR:
471 retval = populate_str_data(elements, instance_id, attr_name_kobj);
472 break;
473 case PO:
474 retval = populate_po_data(elements, instance_id, attr_name_kobj);
475 break;
476 default:
477 break;
478 }
479
480 if (retval) {
481 pr_debug("failed to populate %s\n",
482 elements[ATTR_NAME].string.pointer);
483 goto err_attr_init;
484 }
485
486nextobj:
487 kfree(obj);
488 instance_id++;
489 obj = get_wmiobj_pointer(instance_id, guid);
490 elements = obj ? obj->package.elements : NULL;
491 }
492
493 mutex_unlock(&wmi_priv.mutex);
494 return 0;
495
496err_attr_init:
497 mutex_unlock(&wmi_priv.mutex);
498 release_attributes_data();
499 kfree(obj);
500 return retval;
501}
502
503static int __init sysman_init(void)
504{
505 int ret = 0;
506
507 if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) &&
508 !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) {
509 pr_err("Unable to run on non-Dell system\n");
510 return -ENODEV;
511 }
512
513 ret = init_bios_attr_set_interface();
514 if (ret || !wmi_priv.bios_attr_wdev) {
515 pr_debug("failed to initialize set interface\n");
516 goto fail_set_interface;
517 }
518
519 ret = init_bios_attr_pass_interface();
520 if (ret || !wmi_priv.password_attr_wdev) {
521 pr_debug("failed to initialize pass interface\n");
522 goto fail_pass_interface;
523 }
524
525 ret = fw_attributes_class_get(&fw_attr_class);
526 if (ret)
527 goto fail_class;
528
529 wmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
530 NULL, "%s", DRIVER_NAME);
531 if (IS_ERR(wmi_priv.class_dev)) {
532 ret = PTR_ERR(wmi_priv.class_dev);
533 goto fail_classdev;
534 }
535
536 wmi_priv.main_dir_kset = kset_create_and_add("attributes", NULL,
537 &wmi_priv.class_dev->kobj);
538 if (!wmi_priv.main_dir_kset) {
539 ret = -ENOMEM;
540 goto fail_main_kset;
541 }
542
543 wmi_priv.authentication_dir_kset = kset_create_and_add("authentication", NULL,
544 &wmi_priv.class_dev->kobj);
545 if (!wmi_priv.authentication_dir_kset) {
546 ret = -ENOMEM;
547 goto fail_authentication_kset;
548 }
549
550 ret = create_attributes_level_sysfs_files();
551 if (ret) {
552 pr_debug("could not create reset BIOS attribute\n");
553 goto fail_reset_bios;
554 }
555
556 ret = init_bios_attributes(ENUM, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID);
557 if (ret) {
558 pr_debug("failed to populate enumeration type attributes\n");
559 goto fail_create_group;
560 }
561
562 ret = init_bios_attributes(INT, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID);
563 if (ret) {
564 pr_debug("failed to populate integer type attributes\n");
565 goto fail_create_group;
566 }
567
568 ret = init_bios_attributes(STR, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID);
569 if (ret) {
570 pr_debug("failed to populate string type attributes\n");
571 goto fail_create_group;
572 }
573
574 ret = init_bios_attributes(PO, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID);
575 if (ret) {
576 pr_debug("failed to populate pass object type attributes\n");
577 goto fail_create_group;
578 }
579
580 return 0;
581
582fail_create_group:
583 release_attributes_data();
584
585fail_reset_bios:
586 if (wmi_priv.authentication_dir_kset) {
587 kset_unregister(wmi_priv.authentication_dir_kset);
588 wmi_priv.authentication_dir_kset = NULL;
589 }
590
591fail_authentication_kset:
592 if (wmi_priv.main_dir_kset) {
593 kset_unregister(wmi_priv.main_dir_kset);
594 wmi_priv.main_dir_kset = NULL;
595 }
596
597fail_main_kset:
598 device_destroy(fw_attr_class, MKDEV(0, 0));
599
600fail_classdev:
601 fw_attributes_class_put();
602
603fail_class:
604 exit_bios_attr_pass_interface();
605
606fail_pass_interface:
607 exit_bios_attr_set_interface();
608
609fail_set_interface:
610 return ret;
611}
612
613static void __exit sysman_exit(void)
614{
615 release_attributes_data();
616 device_destroy(fw_attr_class, MKDEV(0, 0));
617 fw_attributes_class_put();
618 exit_bios_attr_set_interface();
619 exit_bios_attr_pass_interface();
620}
621
622module_init(sysman_init);
623module_exit(sysman_exit);
624
625MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
626MODULE_AUTHOR("Prasanth Ksr <prasanth.ksr@dell.com>");
627MODULE_AUTHOR("Divya Bharathi <divya.bharathi@dell.com>");
628MODULE_DESCRIPTION("Dell platform setting control interface");
629MODULE_LICENSE("GPL");
630