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 32 struct dmi_sysfs_entry { 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. */ 50 struct dmi_sysfs_attribute { 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 */ 65 struct dmi_sysfs_mapped_attribute { 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 }; 112 113 typedef ssize_t (*dmi_callback)(struct dmi_sysfs_entry *, 114 const struct dmi_header *dh, void *); 115 116 struct find_dmi_data { 117 struct dmi_sysfs_entry *entry; 118 dmi_callback callback; 119 void *private; 120 int instance_countdown; 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() */ 151 struct dmi_read_state { 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 *************************************************/ 196 struct dmi_entry_attr_show_data { 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; 206 struct dmi_sysfs_mapped_attribute *attr; 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 ***/ 236 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 242 243 struct dmi_system_event_log { 244 struct dmi_header header; 245 u16 area_length; 246 u16 header_start_offset; 247 u16 data_start_offset; 248 u8 access_method; 249 u8 status; 250 u32 change_token; 251 union { 252 struct { 253 u16 index_addr; 254 u16 data_addr; 255 } io; 256 u32 phys_addr32; 257 u16 gpnv_handle; 258 u32 access_method_address; 259 }; 260 u8 header_format; 261 u8 type_descriptors_supported_count; 262 u8 per_log_type_descriptor_length; 263 u8 supported_log_type_descriptos[0]; 264 } __packed; 265 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); 282 DMI_SYSFS_SEL_FIELD(access_method); 283 DMI_SYSFS_SEL_FIELD(status); 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 }; 310 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) { 410 case DMI_SEL_ACCESS_METHOD_IO8: 411 case DMI_SEL_ACCESS_METHOD_IO2x8: 412 case DMI_SEL_ACCESS_METHOD_IO16: 413 return dmi_sel_raw_read_io(entry, &sel, state->buf, 414 state->pos, state->count); 415 case DMI_SEL_ACCESS_METHOD_PHYS32: 416 return dmi_sel_raw_read_phys32(entry, &sel, state->buf, 417 state->pos, state->count); 418 case DMI_SEL_ACCESS_METHOD_GPNV: 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) { 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 = -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 <mikew@google.com>"); 695 MODULE_DESCRIPTION("DMI sysfs support"); 696 MODULE_LICENSE("GPL"); 697