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 #include <asm/dmi.h> 29 30 #define MAX_ENTRY_TYPE 255 /* Most of these aren't used, but we consider 31 the top entry type is only 8 bits */ 32 33 struct dmi_sysfs_entry { 34 struct dmi_header dh; 35 struct kobject kobj; 36 int instance; 37 int position; 38 struct list_head list; 39 struct kobject *child; 40 }; 41 42 /* 43 * Global list of dmi_sysfs_entry. Even though this should only be 44 * manipulated at setup and teardown, the lazy nature of the kobject 45 * system means we get lazy removes. 46 */ 47 static LIST_HEAD(entry_list); 48 static DEFINE_SPINLOCK(entry_list_lock); 49 50 /* dmi_sysfs_attribute - Top level attribute. used by all entries. */ 51 struct dmi_sysfs_attribute { 52 struct attribute attr; 53 ssize_t (*show)(struct dmi_sysfs_entry *entry, char *buf); 54 }; 55 56 #define DMI_SYSFS_ATTR(_entry, _name) \ 57 struct dmi_sysfs_attribute dmi_sysfs_attr_##_entry##_##_name = { \ 58 .attr = {.name = __stringify(_name), .mode = 0400}, \ 59 .show = dmi_sysfs_##_entry##_##_name, \ 60 } 61 62 /* 63 * dmi_sysfs_mapped_attribute - Attribute where we require the entry be 64 * mapped in. Use in conjunction with dmi_sysfs_specialize_attr_ops. 65 */ 66 struct dmi_sysfs_mapped_attribute { 67 struct attribute attr; 68 ssize_t (*show)(struct dmi_sysfs_entry *entry, 69 const struct dmi_header *dh, 70 char *buf); 71 }; 72 73 #define DMI_SYSFS_MAPPED_ATTR(_entry, _name) \ 74 struct dmi_sysfs_mapped_attribute dmi_sysfs_attr_##_entry##_##_name = { \ 75 .attr = {.name = __stringify(_name), .mode = 0400}, \ 76 .show = dmi_sysfs_##_entry##_##_name, \ 77 } 78 79 /************************************************* 80 * Generic DMI entry support. 81 *************************************************/ 82 static void dmi_entry_free(struct kobject *kobj) 83 { 84 kfree(kobj); 85 } 86 87 static struct dmi_sysfs_entry *to_entry(struct kobject *kobj) 88 { 89 return container_of(kobj, struct dmi_sysfs_entry, kobj); 90 } 91 92 static struct dmi_sysfs_attribute *to_attr(struct attribute *attr) 93 { 94 return container_of(attr, struct dmi_sysfs_attribute, attr); 95 } 96 97 static ssize_t dmi_sysfs_attr_show(struct kobject *kobj, 98 struct attribute *_attr, char *buf) 99 { 100 struct dmi_sysfs_entry *entry = to_entry(kobj); 101 struct dmi_sysfs_attribute *attr = to_attr(_attr); 102 103 /* DMI stuff is only ever admin visible */ 104 if (!capable(CAP_SYS_ADMIN)) 105 return -EACCES; 106 107 return attr->show(entry, buf); 108 } 109 110 static const struct sysfs_ops dmi_sysfs_attr_ops = { 111 .show = dmi_sysfs_attr_show, 112 }; 113 114 typedef ssize_t (*dmi_callback)(struct dmi_sysfs_entry *, 115 const struct dmi_header *dh, void *); 116 117 struct find_dmi_data { 118 struct dmi_sysfs_entry *entry; 119 dmi_callback callback; 120 void *private; 121 int instance_countdown; 122 ssize_t ret; 123 }; 124 125 static void find_dmi_entry_helper(const struct dmi_header *dh, 126 void *_data) 127 { 128 struct find_dmi_data *data = _data; 129 struct dmi_sysfs_entry *entry = data->entry; 130 131 /* Is this the entry we want? */ 132 if (dh->type != entry->dh.type) 133 return; 134 135 if (data->instance_countdown != 0) { 136 /* try the next instance? */ 137 data->instance_countdown--; 138 return; 139 } 140 141 /* 142 * Don't ever revisit the instance. Short circuit later 143 * instances by letting the instance_countdown run negative 144 */ 145 data->instance_countdown--; 146 147 /* Found the entry */ 148 data->ret = data->callback(entry, dh, data->private); 149 } 150 151 /* State for passing the read parameters through dmi_find_entry() */ 152 struct dmi_read_state { 153 char *buf; 154 loff_t pos; 155 size_t count; 156 }; 157 158 static ssize_t find_dmi_entry(struct dmi_sysfs_entry *entry, 159 dmi_callback callback, void *private) 160 { 161 struct find_dmi_data data = { 162 .entry = entry, 163 .callback = callback, 164 .private = private, 165 .instance_countdown = entry->instance, 166 .ret = -EIO, /* To signal the entry disappeared */ 167 }; 168 int ret; 169 170 ret = dmi_walk(find_dmi_entry_helper, &data); 171 /* This shouldn't happen, but just in case. */ 172 if (ret) 173 return -EINVAL; 174 return data.ret; 175 } 176 177 /* 178 * Calculate and return the byte length of the dmi entry identified by 179 * dh. This includes both the formatted portion as well as the 180 * unformatted string space, including the two trailing nul characters. 181 */ 182 static size_t dmi_entry_length(const struct dmi_header *dh) 183 { 184 const char *p = (const char *)dh; 185 186 p += dh->length; 187 188 while (p[0] || p[1]) 189 p++; 190 191 return 2 + p - (const char *)dh; 192 } 193 194 /************************************************* 195 * Support bits for specialized DMI entry support 196 *************************************************/ 197 struct dmi_entry_attr_show_data { 198 struct attribute *attr; 199 char *buf; 200 }; 201 202 static ssize_t dmi_entry_attr_show_helper(struct dmi_sysfs_entry *entry, 203 const struct dmi_header *dh, 204 void *_data) 205 { 206 struct dmi_entry_attr_show_data *data = _data; 207 struct dmi_sysfs_mapped_attribute *attr; 208 209 attr = container_of(data->attr, 210 struct dmi_sysfs_mapped_attribute, attr); 211 return attr->show(entry, dh, data->buf); 212 } 213 214 static ssize_t dmi_entry_attr_show(struct kobject *kobj, 215 struct attribute *attr, 216 char *buf) 217 { 218 struct dmi_entry_attr_show_data data = { 219 .attr = attr, 220 .buf = buf, 221 }; 222 /* Find the entry according to our parent and call the 223 * normalized show method hanging off of the attribute */ 224 return find_dmi_entry(to_entry(kobj->parent), 225 dmi_entry_attr_show_helper, &data); 226 } 227 228 static const struct sysfs_ops dmi_sysfs_specialize_attr_ops = { 229 .show = dmi_entry_attr_show, 230 }; 231 232 /************************************************* 233 * Specialized DMI entry support. 234 *************************************************/ 235 236 /*** Type 15 - System Event Table ***/ 237 238 #define DMI_SEL_ACCESS_METHOD_IO8 0x00 239 #define DMI_SEL_ACCESS_METHOD_IO2x8 0x01 240 #define DMI_SEL_ACCESS_METHOD_IO16 0x02 241 #define DMI_SEL_ACCESS_METHOD_PHYS32 0x03 242 #define DMI_SEL_ACCESS_METHOD_GPNV 0x04 243 244 struct dmi_system_event_log { 245 struct dmi_header header; 246 u16 area_length; 247 u16 header_start_offset; 248 u16 data_start_offset; 249 u8 access_method; 250 u8 status; 251 u32 change_token; 252 union { 253 struct { 254 u16 index_addr; 255 u16 data_addr; 256 } io; 257 u32 phys_addr32; 258 u16 gpnv_handle; 259 u32 access_method_address; 260 }; 261 u8 header_format; 262 u8 type_descriptors_supported_count; 263 u8 per_log_type_descriptor_length; 264 u8 supported_log_type_descriptos[0]; 265 } __packed; 266 267 #define DMI_SYSFS_SEL_FIELD(_field) \ 268 static ssize_t dmi_sysfs_sel_##_field(struct dmi_sysfs_entry *entry, \ 269 const struct dmi_header *dh, \ 270 char *buf) \ 271 { \ 272 struct dmi_system_event_log sel; \ 273 if (sizeof(sel) > dmi_entry_length(dh)) \ 274 return -EIO; \ 275 memcpy(&sel, dh, sizeof(sel)); \ 276 return sprintf(buf, "%u\n", sel._field); \ 277 } \ 278 static DMI_SYSFS_MAPPED_ATTR(sel, _field) 279 280 DMI_SYSFS_SEL_FIELD(area_length); 281 DMI_SYSFS_SEL_FIELD(header_start_offset); 282 DMI_SYSFS_SEL_FIELD(data_start_offset); 283 DMI_SYSFS_SEL_FIELD(access_method); 284 DMI_SYSFS_SEL_FIELD(status); 285 DMI_SYSFS_SEL_FIELD(change_token); 286 DMI_SYSFS_SEL_FIELD(access_method_address); 287 DMI_SYSFS_SEL_FIELD(header_format); 288 DMI_SYSFS_SEL_FIELD(type_descriptors_supported_count); 289 DMI_SYSFS_SEL_FIELD(per_log_type_descriptor_length); 290 291 static struct attribute *dmi_sysfs_sel_attrs[] = { 292 &dmi_sysfs_attr_sel_area_length.attr, 293 &dmi_sysfs_attr_sel_header_start_offset.attr, 294 &dmi_sysfs_attr_sel_data_start_offset.attr, 295 &dmi_sysfs_attr_sel_access_method.attr, 296 &dmi_sysfs_attr_sel_status.attr, 297 &dmi_sysfs_attr_sel_change_token.attr, 298 &dmi_sysfs_attr_sel_access_method_address.attr, 299 &dmi_sysfs_attr_sel_header_format.attr, 300 &dmi_sysfs_attr_sel_type_descriptors_supported_count.attr, 301 &dmi_sysfs_attr_sel_per_log_type_descriptor_length.attr, 302 NULL, 303 }; 304 305 306 static struct kobj_type dmi_system_event_log_ktype = { 307 .release = dmi_entry_free, 308 .sysfs_ops = &dmi_sysfs_specialize_attr_ops, 309 .default_attrs = dmi_sysfs_sel_attrs, 310 }; 311 312 typedef u8 (*sel_io_reader)(const struct dmi_system_event_log *sel, 313 loff_t offset); 314 315 static DEFINE_MUTEX(io_port_lock); 316 317 static u8 read_sel_8bit_indexed_io(const struct dmi_system_event_log *sel, 318 loff_t offset) 319 { 320 u8 ret; 321 322 mutex_lock(&io_port_lock); 323 outb((u8)offset, sel->io.index_addr); 324 ret = inb(sel->io.data_addr); 325 mutex_unlock(&io_port_lock); 326 return ret; 327 } 328 329 static u8 read_sel_2x8bit_indexed_io(const struct dmi_system_event_log *sel, 330 loff_t offset) 331 { 332 u8 ret; 333 334 mutex_lock(&io_port_lock); 335 outb((u8)offset, sel->io.index_addr); 336 outb((u8)(offset >> 8), sel->io.index_addr + 1); 337 ret = inb(sel->io.data_addr); 338 mutex_unlock(&io_port_lock); 339 return ret; 340 } 341 342 static u8 read_sel_16bit_indexed_io(const struct dmi_system_event_log *sel, 343 loff_t offset) 344 { 345 u8 ret; 346 347 mutex_lock(&io_port_lock); 348 outw((u16)offset, sel->io.index_addr); 349 ret = inb(sel->io.data_addr); 350 mutex_unlock(&io_port_lock); 351 return ret; 352 } 353 354 static sel_io_reader sel_io_readers[] = { 355 [DMI_SEL_ACCESS_METHOD_IO8] = read_sel_8bit_indexed_io, 356 [DMI_SEL_ACCESS_METHOD_IO2x8] = read_sel_2x8bit_indexed_io, 357 [DMI_SEL_ACCESS_METHOD_IO16] = read_sel_16bit_indexed_io, 358 }; 359 360 static ssize_t dmi_sel_raw_read_io(struct dmi_sysfs_entry *entry, 361 const struct dmi_system_event_log *sel, 362 char *buf, loff_t pos, size_t count) 363 { 364 ssize_t wrote = 0; 365 366 sel_io_reader io_reader = sel_io_readers[sel->access_method]; 367 368 while (count && pos < sel->area_length) { 369 count--; 370 *(buf++) = io_reader(sel, pos++); 371 wrote++; 372 } 373 374 return wrote; 375 } 376 377 static ssize_t dmi_sel_raw_read_phys32(struct dmi_sysfs_entry *entry, 378 const struct dmi_system_event_log *sel, 379 char *buf, loff_t pos, size_t count) 380 { 381 u8 __iomem *mapped; 382 ssize_t wrote = 0; 383 384 mapped = dmi_remap(sel->access_method_address, sel->area_length); 385 if (!mapped) 386 return -EIO; 387 388 while (count && pos < sel->area_length) { 389 count--; 390 *(buf++) = readb(mapped + pos++); 391 wrote++; 392 } 393 394 dmi_unmap(mapped); 395 return wrote; 396 } 397 398 static ssize_t dmi_sel_raw_read_helper(struct dmi_sysfs_entry *entry, 399 const struct dmi_header *dh, 400 void *_state) 401 { 402 struct dmi_read_state *state = _state; 403 struct dmi_system_event_log sel; 404 405 if (sizeof(sel) > dmi_entry_length(dh)) 406 return -EIO; 407 408 memcpy(&sel, dh, sizeof(sel)); 409 410 switch (sel.access_method) { 411 case DMI_SEL_ACCESS_METHOD_IO8: 412 case DMI_SEL_ACCESS_METHOD_IO2x8: 413 case DMI_SEL_ACCESS_METHOD_IO16: 414 return dmi_sel_raw_read_io(entry, &sel, state->buf, 415 state->pos, state->count); 416 case DMI_SEL_ACCESS_METHOD_PHYS32: 417 return dmi_sel_raw_read_phys32(entry, &sel, state->buf, 418 state->pos, state->count); 419 case DMI_SEL_ACCESS_METHOD_GPNV: 420 pr_info("dmi-sysfs: GPNV support missing.\n"); 421 return -EIO; 422 default: 423 pr_info("dmi-sysfs: Unknown access method %02x\n", 424 sel.access_method); 425 return -EIO; 426 } 427 } 428 429 static ssize_t dmi_sel_raw_read(struct file *filp, struct kobject *kobj, 430 struct bin_attribute *bin_attr, 431 char *buf, loff_t pos, size_t count) 432 { 433 struct dmi_sysfs_entry *entry = to_entry(kobj->parent); 434 struct dmi_read_state state = { 435 .buf = buf, 436 .pos = pos, 437 .count = count, 438 }; 439 440 return find_dmi_entry(entry, dmi_sel_raw_read_helper, &state); 441 } 442 443 static struct bin_attribute dmi_sel_raw_attr = { 444 .attr = {.name = "raw_event_log", .mode = 0400}, 445 .read = dmi_sel_raw_read, 446 }; 447 448 static int dmi_system_event_log(struct dmi_sysfs_entry *entry) 449 { 450 int ret; 451 452 entry->child = kzalloc(sizeof(*entry->child), GFP_KERNEL); 453 if (!entry->child) 454 return -ENOMEM; 455 ret = kobject_init_and_add(entry->child, 456 &dmi_system_event_log_ktype, 457 &entry->kobj, 458 "system_event_log"); 459 if (ret) 460 goto out_free; 461 462 ret = sysfs_create_bin_file(entry->child, &dmi_sel_raw_attr); 463 if (ret) 464 goto out_del; 465 466 return 0; 467 468 out_del: 469 kobject_del(entry->child); 470 out_free: 471 kfree(entry->child); 472 return ret; 473 } 474 475 /************************************************* 476 * Generic DMI entry support. 477 *************************************************/ 478 479 static ssize_t dmi_sysfs_entry_length(struct dmi_sysfs_entry *entry, char *buf) 480 { 481 return sprintf(buf, "%d\n", entry->dh.length); 482 } 483 484 static ssize_t dmi_sysfs_entry_handle(struct dmi_sysfs_entry *entry, char *buf) 485 { 486 return sprintf(buf, "%d\n", entry->dh.handle); 487 } 488 489 static ssize_t dmi_sysfs_entry_type(struct dmi_sysfs_entry *entry, char *buf) 490 { 491 return sprintf(buf, "%d\n", entry->dh.type); 492 } 493 494 static ssize_t dmi_sysfs_entry_instance(struct dmi_sysfs_entry *entry, 495 char *buf) 496 { 497 return sprintf(buf, "%d\n", entry->instance); 498 } 499 500 static ssize_t dmi_sysfs_entry_position(struct dmi_sysfs_entry *entry, 501 char *buf) 502 { 503 return sprintf(buf, "%d\n", entry->position); 504 } 505 506 static DMI_SYSFS_ATTR(entry, length); 507 static DMI_SYSFS_ATTR(entry, handle); 508 static DMI_SYSFS_ATTR(entry, type); 509 static DMI_SYSFS_ATTR(entry, instance); 510 static DMI_SYSFS_ATTR(entry, position); 511 512 static struct attribute *dmi_sysfs_entry_attrs[] = { 513 &dmi_sysfs_attr_entry_length.attr, 514 &dmi_sysfs_attr_entry_handle.attr, 515 &dmi_sysfs_attr_entry_type.attr, 516 &dmi_sysfs_attr_entry_instance.attr, 517 &dmi_sysfs_attr_entry_position.attr, 518 NULL, 519 }; 520 521 static ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry, 522 const struct dmi_header *dh, 523 void *_state) 524 { 525 struct dmi_read_state *state = _state; 526 size_t entry_length; 527 528 entry_length = dmi_entry_length(dh); 529 530 return memory_read_from_buffer(state->buf, state->count, 531 &state->pos, dh, entry_length); 532 } 533 534 static ssize_t dmi_entry_raw_read(struct file *filp, 535 struct kobject *kobj, 536 struct bin_attribute *bin_attr, 537 char *buf, loff_t pos, size_t count) 538 { 539 struct dmi_sysfs_entry *entry = to_entry(kobj); 540 struct dmi_read_state state = { 541 .buf = buf, 542 .pos = pos, 543 .count = count, 544 }; 545 546 return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state); 547 } 548 549 static const struct bin_attribute dmi_entry_raw_attr = { 550 .attr = {.name = "raw", .mode = 0400}, 551 .read = dmi_entry_raw_read, 552 }; 553 554 static void dmi_sysfs_entry_release(struct kobject *kobj) 555 { 556 struct dmi_sysfs_entry *entry = to_entry(kobj); 557 558 spin_lock(&entry_list_lock); 559 list_del(&entry->list); 560 spin_unlock(&entry_list_lock); 561 kfree(entry); 562 } 563 564 static struct kobj_type dmi_sysfs_entry_ktype = { 565 .release = dmi_sysfs_entry_release, 566 .sysfs_ops = &dmi_sysfs_attr_ops, 567 .default_attrs = dmi_sysfs_entry_attrs, 568 }; 569 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) { 616 case DMI_ENTRY_SYSTEM_EVENT_LOG: 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; 652 int val; 653 654 if (!dmi_kobj) { 655 pr_debug("dmi-sysfs: dmi entry is absent.\n"); 656 error = -ENODATA; 657 goto err; 658 } 659 660 dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); 661 if (!dmi_kset) { 662 error = -ENOMEM; 663 goto err; 664 } 665 666 val = 0; 667 error = dmi_walk(dmi_sysfs_register_handle, &val); 668 if (error) 669 goto err; 670 if (val) { 671 error = val; 672 goto err; 673 } 674 675 pr_debug("dmi-sysfs: loaded.\n"); 676 677 return 0; 678 err: 679 cleanup_entry_list(); 680 kset_unregister(dmi_kset); 681 return error; 682 } 683 684 /* clean up everything. */ 685 static void __exit dmi_sysfs_exit(void) 686 { 687 pr_debug("dmi-sysfs: unloading.\n"); 688 cleanup_entry_list(); 689 kset_unregister(dmi_kset); 690 } 691 692 module_init(dmi_sysfs_init); 693 module_exit(dmi_sysfs_exit); 694 695 MODULE_AUTHOR("Mike Waychison <mikew@google.com>"); 696 MODULE_DESCRIPTION("DMI sysfs support"); 697 MODULE_LICENSE("GPL"); 698