Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dmi-sysfs.c
Go to the documentation of this file.
1 /*
2  * dmi-sysfs.c
3  *
4  * This module exports the DMI tables read-only to userspace through the
5  * sysfs file system.
6  *
7  * Data is currently found below
8  * /sys/firmware/dmi/...
9  *
10  * DMI attributes are presented in attribute files with names
11  * formatted using %d-%d, so that the first integer indicates the
12  * structure type (0-255), and the second field is the instance of that
13  * entry.
14  *
15  * Copyright 2011 Google, Inc.
16  */
17 
18 #include <linux/kernel.h>
19 #include <linux/init.h>
20 #include <linux/module.h>
21 #include <linux/types.h>
22 #include <linux/kobject.h>
23 #include <linux/dmi.h>
24 #include <linux/capability.h>
25 #include <linux/slab.h>
26 #include <linux/list.h>
27 #include <linux/io.h>
28 
29 #define MAX_ENTRY_TYPE 255 /* Most of these aren't used, but we consider
30  the top entry type is only 8 bits */
31 
33  struct dmi_header dh;
34  struct kobject kobj;
35  int instance;
36  int position;
37  struct list_head list;
38  struct kobject *child;
39 };
40 
41 /*
42  * Global list of dmi_sysfs_entry. Even though this should only be
43  * manipulated at setup and teardown, the lazy nature of the kobject
44  * system means we get lazy removes.
45  */
46 static LIST_HEAD(entry_list);
47 static DEFINE_SPINLOCK(entry_list_lock);
48 
49 /* dmi_sysfs_attribute - Top level attribute. used by all entries. */
51  struct attribute attr;
52  ssize_t (*show)(struct dmi_sysfs_entry *entry, char *buf);
53 };
54 
55 #define DMI_SYSFS_ATTR(_entry, _name) \
56 struct dmi_sysfs_attribute dmi_sysfs_attr_##_entry##_##_name = { \
57  .attr = {.name = __stringify(_name), .mode = 0400}, \
58  .show = dmi_sysfs_##_entry##_##_name, \
59 }
60 
61 /*
62  * dmi_sysfs_mapped_attribute - Attribute where we require the entry be
63  * mapped in. Use in conjunction with dmi_sysfs_specialize_attr_ops.
64  */
66  struct attribute attr;
67  ssize_t (*show)(struct dmi_sysfs_entry *entry,
68  const struct dmi_header *dh,
69  char *buf);
70 };
71 
72 #define DMI_SYSFS_MAPPED_ATTR(_entry, _name) \
73 struct dmi_sysfs_mapped_attribute dmi_sysfs_attr_##_entry##_##_name = { \
74  .attr = {.name = __stringify(_name), .mode = 0400}, \
75  .show = dmi_sysfs_##_entry##_##_name, \
76 }
77 
78 /*************************************************
79  * Generic DMI entry support.
80  *************************************************/
81 static void dmi_entry_free(struct kobject *kobj)
82 {
83  kfree(kobj);
84 }
85 
86 static struct dmi_sysfs_entry *to_entry(struct kobject *kobj)
87 {
88  return container_of(kobj, struct dmi_sysfs_entry, kobj);
89 }
90 
91 static struct dmi_sysfs_attribute *to_attr(struct attribute *attr)
92 {
93  return container_of(attr, struct dmi_sysfs_attribute, attr);
94 }
95 
96 static ssize_t dmi_sysfs_attr_show(struct kobject *kobj,
97  struct attribute *_attr, char *buf)
98 {
99  struct dmi_sysfs_entry *entry = to_entry(kobj);
100  struct dmi_sysfs_attribute *attr = to_attr(_attr);
101 
102  /* DMI stuff is only ever admin visible */
103  if (!capable(CAP_SYS_ADMIN))
104  return -EACCES;
105 
106  return attr->show(entry, buf);
107 }
108 
109 static const struct sysfs_ops dmi_sysfs_attr_ops = {
110  .show = dmi_sysfs_attr_show,
111 };
113 typedef ssize_t (*dmi_callback)(struct dmi_sysfs_entry *,
114  const struct dmi_header *dh, void *);
119  void *private;
121  ssize_t ret;
122 };
123 
124 static void find_dmi_entry_helper(const struct dmi_header *dh,
125  void *_data)
126 {
127  struct find_dmi_data *data = _data;
128  struct dmi_sysfs_entry *entry = data->entry;
129 
130  /* Is this the entry we want? */
131  if (dh->type != entry->dh.type)
132  return;
133 
134  if (data->instance_countdown != 0) {
135  /* try the next instance? */
136  data->instance_countdown--;
137  return;
138  }
139 
140  /*
141  * Don't ever revisit the instance. Short circuit later
142  * instances by letting the instance_countdown run negative
143  */
144  data->instance_countdown--;
145 
146  /* Found the entry */
147  data->ret = data->callback(entry, dh, data->private);
148 }
149 
150 /* State for passing the read parameters through dmi_find_entry() */
152  char *buf;
153  loff_t pos;
154  size_t count;
155 };
156 
157 static ssize_t find_dmi_entry(struct dmi_sysfs_entry *entry,
158  dmi_callback callback, void *private)
159 {
160  struct find_dmi_data data = {
161  .entry = entry,
162  .callback = callback,
163  .private = private,
164  .instance_countdown = entry->instance,
165  .ret = -EIO, /* To signal the entry disappeared */
166  };
167  int ret;
168 
169  ret = dmi_walk(find_dmi_entry_helper, &data);
170  /* This shouldn't happen, but just in case. */
171  if (ret)
172  return -EINVAL;
173  return data.ret;
174 }
175 
176 /*
177  * Calculate and return the byte length of the dmi entry identified by
178  * dh. This includes both the formatted portion as well as the
179  * unformatted string space, including the two trailing nul characters.
180  */
181 static size_t dmi_entry_length(const struct dmi_header *dh)
182 {
183  const char *p = (const char *)dh;
184 
185  p += dh->length;
186 
187  while (p[0] || p[1])
188  p++;
189 
190  return 2 + p - (const char *)dh;
191 }
192 
193 /*************************************************
194  * Support bits for specialized DMI entry support
195  *************************************************/
197  struct attribute *attr;
198  char *buf;
199 };
200 
201 static ssize_t dmi_entry_attr_show_helper(struct dmi_sysfs_entry *entry,
202  const struct dmi_header *dh,
203  void *_data)
204 {
205  struct dmi_entry_attr_show_data *data = _data;
207 
208  attr = container_of(data->attr,
209  struct dmi_sysfs_mapped_attribute, attr);
210  return attr->show(entry, dh, data->buf);
211 }
212 
213 static ssize_t dmi_entry_attr_show(struct kobject *kobj,
214  struct attribute *attr,
215  char *buf)
216 {
217  struct dmi_entry_attr_show_data data = {
218  .attr = attr,
219  .buf = buf,
220  };
221  /* Find the entry according to our parent and call the
222  * normalized show method hanging off of the attribute */
223  return find_dmi_entry(to_entry(kobj->parent),
224  dmi_entry_attr_show_helper, &data);
225 }
226 
227 static const struct sysfs_ops dmi_sysfs_specialize_attr_ops = {
228  .show = dmi_entry_attr_show,
229 };
230 
231 /*************************************************
232  * Specialized DMI entry support.
233  *************************************************/
234 
235 /*** Type 15 - System Event Table ***/
237 #define DMI_SEL_ACCESS_METHOD_IO8 0x00
238 #define DMI_SEL_ACCESS_METHOD_IO2x8 0x01
239 #define DMI_SEL_ACCESS_METHOD_IO16 0x02
240 #define DMI_SEL_ACCESS_METHOD_PHYS32 0x03
241 #define DMI_SEL_ACCESS_METHOD_GPNV 0x04
251  union {
252  struct {
254  u16 data_addr;
255  } io;
259  };
264 } __packed;
266 #define DMI_SYSFS_SEL_FIELD(_field) \
267 static ssize_t dmi_sysfs_sel_##_field(struct dmi_sysfs_entry *entry, \
268  const struct dmi_header *dh, \
269  char *buf) \
270 { \
271  struct dmi_system_event_log sel; \
272  if (sizeof(sel) > dmi_entry_length(dh)) \
273  return -EIO; \
274  memcpy(&sel, dh, sizeof(sel)); \
275  return sprintf(buf, "%u\n", sel._field); \
276 } \
277 static DMI_SYSFS_MAPPED_ATTR(sel, _field)
278 
279 DMI_SYSFS_SEL_FIELD(area_length);
280 DMI_SYSFS_SEL_FIELD(header_start_offset);
281 DMI_SYSFS_SEL_FIELD(data_start_offset);
284 DMI_SYSFS_SEL_FIELD(change_token);
285 DMI_SYSFS_SEL_FIELD(access_method_address);
286 DMI_SYSFS_SEL_FIELD(header_format);
287 DMI_SYSFS_SEL_FIELD(type_descriptors_supported_count);
288 DMI_SYSFS_SEL_FIELD(per_log_type_descriptor_length);
289 
290 static struct attribute *dmi_sysfs_sel_attrs[] = {
291  &dmi_sysfs_attr_sel_area_length.attr,
292  &dmi_sysfs_attr_sel_header_start_offset.attr,
293  &dmi_sysfs_attr_sel_data_start_offset.attr,
294  &dmi_sysfs_attr_sel_access_method.attr,
295  &dmi_sysfs_attr_sel_status.attr,
296  &dmi_sysfs_attr_sel_change_token.attr,
297  &dmi_sysfs_attr_sel_access_method_address.attr,
298  &dmi_sysfs_attr_sel_header_format.attr,
299  &dmi_sysfs_attr_sel_type_descriptors_supported_count.attr,
300  &dmi_sysfs_attr_sel_per_log_type_descriptor_length.attr,
301  NULL,
302 };
303 
304 
305 static struct kobj_type dmi_system_event_log_ktype = {
306  .release = dmi_entry_free,
307  .sysfs_ops = &dmi_sysfs_specialize_attr_ops,
308  .default_attrs = dmi_sysfs_sel_attrs,
309 };
311 typedef u8 (*sel_io_reader)(const struct dmi_system_event_log *sel,
312  loff_t offset);
313 
314 static DEFINE_MUTEX(io_port_lock);
315 
316 static u8 read_sel_8bit_indexed_io(const struct dmi_system_event_log *sel,
317  loff_t offset)
318 {
319  u8 ret;
320 
321  mutex_lock(&io_port_lock);
322  outb((u8)offset, sel->io.index_addr);
323  ret = inb(sel->io.data_addr);
324  mutex_unlock(&io_port_lock);
325  return ret;
326 }
327 
328 static u8 read_sel_2x8bit_indexed_io(const struct dmi_system_event_log *sel,
329  loff_t offset)
330 {
331  u8 ret;
332 
333  mutex_lock(&io_port_lock);
334  outb((u8)offset, sel->io.index_addr);
335  outb((u8)(offset >> 8), sel->io.index_addr + 1);
336  ret = inb(sel->io.data_addr);
337  mutex_unlock(&io_port_lock);
338  return ret;
339 }
340 
341 static u8 read_sel_16bit_indexed_io(const struct dmi_system_event_log *sel,
342  loff_t offset)
343 {
344  u8 ret;
345 
346  mutex_lock(&io_port_lock);
347  outw((u16)offset, sel->io.index_addr);
348  ret = inb(sel->io.data_addr);
349  mutex_unlock(&io_port_lock);
350  return ret;
351 }
352 
353 static sel_io_reader sel_io_readers[] = {
354  [DMI_SEL_ACCESS_METHOD_IO8] = read_sel_8bit_indexed_io,
355  [DMI_SEL_ACCESS_METHOD_IO2x8] = read_sel_2x8bit_indexed_io,
356  [DMI_SEL_ACCESS_METHOD_IO16] = read_sel_16bit_indexed_io,
357 };
358 
359 static ssize_t dmi_sel_raw_read_io(struct dmi_sysfs_entry *entry,
360  const struct dmi_system_event_log *sel,
361  char *buf, loff_t pos, size_t count)
362 {
363  ssize_t wrote = 0;
364 
365  sel_io_reader io_reader = sel_io_readers[sel->access_method];
366 
367  while (count && pos < sel->area_length) {
368  count--;
369  *(buf++) = io_reader(sel, pos++);
370  wrote++;
371  }
372 
373  return wrote;
374 }
375 
376 static ssize_t dmi_sel_raw_read_phys32(struct dmi_sysfs_entry *entry,
377  const struct dmi_system_event_log *sel,
378  char *buf, loff_t pos, size_t count)
379 {
380  u8 __iomem *mapped;
381  ssize_t wrote = 0;
382 
383  mapped = ioremap(sel->access_method_address, sel->area_length);
384  if (!mapped)
385  return -EIO;
386 
387  while (count && pos < sel->area_length) {
388  count--;
389  *(buf++) = readb(mapped + pos++);
390  wrote++;
391  }
392 
393  iounmap(mapped);
394  return wrote;
395 }
396 
397 static ssize_t dmi_sel_raw_read_helper(struct dmi_sysfs_entry *entry,
398  const struct dmi_header *dh,
399  void *_state)
400 {
401  struct dmi_read_state *state = _state;
402  struct dmi_system_event_log sel;
403 
404  if (sizeof(sel) > dmi_entry_length(dh))
405  return -EIO;
406 
407  memcpy(&sel, dh, sizeof(sel));
408 
409  switch (sel.access_method) {
413  return dmi_sel_raw_read_io(entry, &sel, state->buf,
414  state->pos, state->count);
416  return dmi_sel_raw_read_phys32(entry, &sel, state->buf,
417  state->pos, state->count);
419  pr_info("dmi-sysfs: GPNV support missing.\n");
420  return -EIO;
421  default:
422  pr_info("dmi-sysfs: Unknown access method %02x\n",
423  sel.access_method);
424  return -EIO;
425  }
426 }
427 
428 static ssize_t dmi_sel_raw_read(struct file *filp, struct kobject *kobj,
429  struct bin_attribute *bin_attr,
430  char *buf, loff_t pos, size_t count)
431 {
432  struct dmi_sysfs_entry *entry = to_entry(kobj->parent);
433  struct dmi_read_state state = {
434  .buf = buf,
435  .pos = pos,
436  .count = count,
437  };
438 
439  return find_dmi_entry(entry, dmi_sel_raw_read_helper, &state);
440 }
441 
442 static struct bin_attribute dmi_sel_raw_attr = {
443  .attr = {.name = "raw_event_log", .mode = 0400},
444  .read = dmi_sel_raw_read,
445 };
446 
447 static int dmi_system_event_log(struct dmi_sysfs_entry *entry)
448 {
449  int ret;
450 
451  entry->child = kzalloc(sizeof(*entry->child), GFP_KERNEL);
452  if (!entry->child)
453  return -ENOMEM;
454  ret = kobject_init_and_add(entry->child,
455  &dmi_system_event_log_ktype,
456  &entry->kobj,
457  "system_event_log");
458  if (ret)
459  goto out_free;
460 
461  ret = sysfs_create_bin_file(entry->child, &dmi_sel_raw_attr);
462  if (ret)
463  goto out_del;
464 
465  return 0;
466 
467 out_del:
468  kobject_del(entry->child);
469 out_free:
470  kfree(entry->child);
471  return ret;
472 }
473 
474 /*************************************************
475  * Generic DMI entry support.
476  *************************************************/
477 
478 static ssize_t dmi_sysfs_entry_length(struct dmi_sysfs_entry *entry, char *buf)
479 {
480  return sprintf(buf, "%d\n", entry->dh.length);
481 }
482 
483 static ssize_t dmi_sysfs_entry_handle(struct dmi_sysfs_entry *entry, char *buf)
484 {
485  return sprintf(buf, "%d\n", entry->dh.handle);
486 }
487 
488 static ssize_t dmi_sysfs_entry_type(struct dmi_sysfs_entry *entry, char *buf)
489 {
490  return sprintf(buf, "%d\n", entry->dh.type);
491 }
492 
493 static ssize_t dmi_sysfs_entry_instance(struct dmi_sysfs_entry *entry,
494  char *buf)
495 {
496  return sprintf(buf, "%d\n", entry->instance);
497 }
498 
499 static ssize_t dmi_sysfs_entry_position(struct dmi_sysfs_entry *entry,
500  char *buf)
501 {
502  return sprintf(buf, "%d\n", entry->position);
503 }
504 
505 static DMI_SYSFS_ATTR(entry, length);
506 static DMI_SYSFS_ATTR(entry, handle);
507 static DMI_SYSFS_ATTR(entry, type);
508 static DMI_SYSFS_ATTR(entry, instance);
509 static DMI_SYSFS_ATTR(entry, position);
510 
511 static struct attribute *dmi_sysfs_entry_attrs[] = {
512  &dmi_sysfs_attr_entry_length.attr,
513  &dmi_sysfs_attr_entry_handle.attr,
514  &dmi_sysfs_attr_entry_type.attr,
515  &dmi_sysfs_attr_entry_instance.attr,
516  &dmi_sysfs_attr_entry_position.attr,
517  NULL,
518 };
519 
520 static ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry,
521  const struct dmi_header *dh,
522  void *_state)
523 {
524  struct dmi_read_state *state = _state;
525  size_t entry_length;
526 
527  entry_length = dmi_entry_length(dh);
528 
529  return memory_read_from_buffer(state->buf, state->count,
530  &state->pos, dh, entry_length);
531 }
532 
533 static ssize_t dmi_entry_raw_read(struct file *filp,
534  struct kobject *kobj,
535  struct bin_attribute *bin_attr,
536  char *buf, loff_t pos, size_t count)
537 {
538  struct dmi_sysfs_entry *entry = to_entry(kobj);
539  struct dmi_read_state state = {
540  .buf = buf,
541  .pos = pos,
542  .count = count,
543  };
544 
545  return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state);
546 }
547 
548 static const struct bin_attribute dmi_entry_raw_attr = {
549  .attr = {.name = "raw", .mode = 0400},
550  .read = dmi_entry_raw_read,
551 };
552 
553 static void dmi_sysfs_entry_release(struct kobject *kobj)
554 {
555  struct dmi_sysfs_entry *entry = to_entry(kobj);
556  sysfs_remove_bin_file(&entry->kobj, &dmi_entry_raw_attr);
557  spin_lock(&entry_list_lock);
558  list_del(&entry->list);
559  spin_unlock(&entry_list_lock);
560  kfree(entry);
561 }
562 
563 static struct kobj_type dmi_sysfs_entry_ktype = {
564  .release = dmi_sysfs_entry_release,
565  .sysfs_ops = &dmi_sysfs_attr_ops,
566  .default_attrs = dmi_sysfs_entry_attrs,
567 };
568 
569 static struct kobject *dmi_kobj;
570 static struct kset *dmi_kset;
571 
572 /* Global count of all instances seen. Only for setup */
573 static int __initdata instance_counts[MAX_ENTRY_TYPE + 1];
574 
575 /* Global positional count of all entries seen. Only for setup */
576 static int __initdata position_count;
577 
578 static void __init dmi_sysfs_register_handle(const struct dmi_header *dh,
579  void *_ret)
580 {
581  struct dmi_sysfs_entry *entry;
582  int *ret = _ret;
583 
584  /* If a previous entry saw an error, short circuit */
585  if (*ret)
586  return;
587 
588  /* Allocate and register a new entry into the entries set */
589  entry = kzalloc(sizeof(*entry), GFP_KERNEL);
590  if (!entry) {
591  *ret = -ENOMEM;
592  return;
593  }
594 
595  /* Set the key */
596  memcpy(&entry->dh, dh, sizeof(*dh));
597  entry->instance = instance_counts[dh->type]++;
598  entry->position = position_count++;
599 
600  entry->kobj.kset = dmi_kset;
601  *ret = kobject_init_and_add(&entry->kobj, &dmi_sysfs_entry_ktype, NULL,
602  "%d-%d", dh->type, entry->instance);
603 
604  if (*ret) {
605  kfree(entry);
606  return;
607  }
608 
609  /* Thread on the global list for cleanup */
610  spin_lock(&entry_list_lock);
611  list_add_tail(&entry->list, &entry_list);
612  spin_unlock(&entry_list_lock);
613 
614  /* Handle specializations by type */
615  switch (dh->type) {
617  *ret = dmi_system_event_log(entry);
618  break;
619  default:
620  /* No specialization */
621  break;
622  }
623  if (*ret)
624  goto out_err;
625 
626  /* Create the raw binary file to access the entry */
627  *ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr);
628  if (*ret)
629  goto out_err;
630 
631  return;
632 out_err:
633  kobject_put(entry->child);
634  kobject_put(&entry->kobj);
635  return;
636 }
637 
638 static void cleanup_entry_list(void)
639 {
640  struct dmi_sysfs_entry *entry, *next;
641 
642  /* No locks, we are on our way out */
643  list_for_each_entry_safe(entry, next, &entry_list, list) {
644  kobject_put(entry->child);
645  kobject_put(&entry->kobj);
646  }
647 }
648 
649 static int __init dmi_sysfs_init(void)
650 {
651  int error = -ENOMEM;
652  int val;
653 
654  /* Set up our directory */
655  dmi_kobj = kobject_create_and_add("dmi", firmware_kobj);
656  if (!dmi_kobj)
657  goto err;
658 
659  dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj);
660  if (!dmi_kset)
661  goto err;
662 
663  val = 0;
664  error = dmi_walk(dmi_sysfs_register_handle, &val);
665  if (error)
666  goto err;
667  if (val) {
668  error = val;
669  goto err;
670  }
671 
672  pr_debug("dmi-sysfs: loaded.\n");
673 
674  return 0;
675 err:
676  cleanup_entry_list();
677  kset_unregister(dmi_kset);
678  kobject_put(dmi_kobj);
679  return error;
680 }
681 
682 /* clean up everything. */
683 static void __exit dmi_sysfs_exit(void)
684 {
685  pr_debug("dmi-sysfs: unloading.\n");
686  cleanup_entry_list();
687  kset_unregister(dmi_kset);
688  kobject_put(dmi_kobj);
689 }
690 
691 module_init(dmi_sysfs_init);
692 module_exit(dmi_sysfs_exit);
693 
694 MODULE_AUTHOR("Mike Waychison <[email protected]>");
695 MODULE_DESCRIPTION("DMI sysfs support");
696 MODULE_LICENSE("GPL");