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 static const struct dmi_system_event_log *to_sel(const struct dmi_header *dh) 267 { 268 return (const struct dmi_system_event_log *)dh; 269 } 270 271 #define DMI_SYSFS_SEL_FIELD(_field) \ 272 static ssize_t dmi_sysfs_sel_##_field(struct dmi_sysfs_entry *entry, \ 273 const struct dmi_header *dh, \ 274 char *buf) \ 275 { \ 276 const struct dmi_system_event_log *sel = to_sel(dh); \ 277 if (sizeof(*sel) > dmi_entry_length(dh)) \ 278 return -EIO; \ 279 return sprintf(buf, "%u\n", sel->_field); \ 280 } \ 281 static DMI_SYSFS_MAPPED_ATTR(sel, _field) 282 283 DMI_SYSFS_SEL_FIELD(area_length); 284 DMI_SYSFS_SEL_FIELD(header_start_offset); 285 DMI_SYSFS_SEL_FIELD(data_start_offset); 286 DMI_SYSFS_SEL_FIELD(access_method); 287 DMI_SYSFS_SEL_FIELD(status); 288 DMI_SYSFS_SEL_FIELD(change_token); 289 DMI_SYSFS_SEL_FIELD(access_method_address); 290 DMI_SYSFS_SEL_FIELD(header_format); 291 DMI_SYSFS_SEL_FIELD(type_descriptors_supported_count); 292 DMI_SYSFS_SEL_FIELD(per_log_type_descriptor_length); 293 294 static struct attribute *dmi_sysfs_sel_attrs[] = { 295 &dmi_sysfs_attr_sel_area_length.attr, 296 &dmi_sysfs_attr_sel_header_start_offset.attr, 297 &dmi_sysfs_attr_sel_data_start_offset.attr, 298 &dmi_sysfs_attr_sel_access_method.attr, 299 &dmi_sysfs_attr_sel_status.attr, 300 &dmi_sysfs_attr_sel_change_token.attr, 301 &dmi_sysfs_attr_sel_access_method_address.attr, 302 &dmi_sysfs_attr_sel_header_format.attr, 303 &dmi_sysfs_attr_sel_type_descriptors_supported_count.attr, 304 &dmi_sysfs_attr_sel_per_log_type_descriptor_length.attr, 305 NULL, 306 }; 307 308 309 static struct kobj_type dmi_system_event_log_ktype = { 310 .release = dmi_entry_free, 311 .sysfs_ops = &dmi_sysfs_specialize_attr_ops, 312 .default_attrs = dmi_sysfs_sel_attrs, 313 }; 314 315 typedef u8 (*sel_io_reader)(const struct dmi_system_event_log *sel, 316 loff_t offset); 317 318 static DEFINE_MUTEX(io_port_lock); 319 320 static u8 read_sel_8bit_indexed_io(const struct dmi_system_event_log *sel, 321 loff_t offset) 322 { 323 u8 ret; 324 325 mutex_lock(&io_port_lock); 326 outb((u8)offset, sel->io.index_addr); 327 ret = inb(sel->io.data_addr); 328 mutex_unlock(&io_port_lock); 329 return ret; 330 } 331 332 static u8 read_sel_2x8bit_indexed_io(const struct dmi_system_event_log *sel, 333 loff_t offset) 334 { 335 u8 ret; 336 337 mutex_lock(&io_port_lock); 338 outb((u8)offset, sel->io.index_addr); 339 outb((u8)(offset >> 8), sel->io.index_addr + 1); 340 ret = inb(sel->io.data_addr); 341 mutex_unlock(&io_port_lock); 342 return ret; 343 } 344 345 static u8 read_sel_16bit_indexed_io(const struct dmi_system_event_log *sel, 346 loff_t offset) 347 { 348 u8 ret; 349 350 mutex_lock(&io_port_lock); 351 outw((u16)offset, sel->io.index_addr); 352 ret = inb(sel->io.data_addr); 353 mutex_unlock(&io_port_lock); 354 return ret; 355 } 356 357 static sel_io_reader sel_io_readers[] = { 358 [DMI_SEL_ACCESS_METHOD_IO8] = read_sel_8bit_indexed_io, 359 [DMI_SEL_ACCESS_METHOD_IO2x8] = read_sel_2x8bit_indexed_io, 360 [DMI_SEL_ACCESS_METHOD_IO16] = read_sel_16bit_indexed_io, 361 }; 362 363 static ssize_t dmi_sel_raw_read_io(struct dmi_sysfs_entry *entry, 364 const struct dmi_system_event_log *sel, 365 char *buf, loff_t pos, size_t count) 366 { 367 ssize_t wrote = 0; 368 369 sel_io_reader io_reader = sel_io_readers[sel->access_method]; 370 371 while (count && pos < sel->area_length) { 372 count--; 373 *(buf++) = io_reader(sel, pos++); 374 wrote++; 375 } 376 377 return wrote; 378 } 379 380 static ssize_t dmi_sel_raw_read_phys32(struct dmi_sysfs_entry *entry, 381 const struct dmi_system_event_log *sel, 382 char *buf, loff_t pos, size_t count) 383 { 384 u8 __iomem *mapped; 385 ssize_t wrote = 0; 386 387 mapped = ioremap(sel->access_method_address, sel->area_length); 388 if (!mapped) 389 return -EIO; 390 391 while (count && pos < sel->area_length) { 392 count--; 393 *(buf++) = readb(mapped + pos++); 394 wrote++; 395 } 396 397 iounmap(mapped); 398 return wrote; 399 } 400 401 static ssize_t dmi_sel_raw_read_helper(struct dmi_sysfs_entry *entry, 402 const struct dmi_header *dh, 403 void *_state) 404 { 405 struct dmi_read_state *state = _state; 406 const struct dmi_system_event_log *sel = to_sel(dh); 407 408 if (sizeof(*sel) > dmi_entry_length(dh)) 409 return -EIO; 410 411 switch (sel->access_method) { 412 case DMI_SEL_ACCESS_METHOD_IO8: 413 case DMI_SEL_ACCESS_METHOD_IO2x8: 414 case DMI_SEL_ACCESS_METHOD_IO16: 415 return dmi_sel_raw_read_io(entry, sel, state->buf, 416 state->pos, state->count); 417 case DMI_SEL_ACCESS_METHOD_PHYS32: 418 return dmi_sel_raw_read_phys32(entry, sel, state->buf, 419 state->pos, state->count); 420 case DMI_SEL_ACCESS_METHOD_GPNV: 421 pr_info("dmi-sysfs: GPNV support missing.\n"); 422 return -EIO; 423 default: 424 pr_info("dmi-sysfs: Unknown access method %02x\n", 425 sel->access_method); 426 return -EIO; 427 } 428 } 429 430 static ssize_t dmi_sel_raw_read(struct file *filp, struct kobject *kobj, 431 struct bin_attribute *bin_attr, 432 char *buf, loff_t pos, size_t count) 433 { 434 struct dmi_sysfs_entry *entry = to_entry(kobj->parent); 435 struct dmi_read_state state = { 436 .buf = buf, 437 .pos = pos, 438 .count = count, 439 }; 440 441 return find_dmi_entry(entry, dmi_sel_raw_read_helper, &state); 442 } 443 444 static struct bin_attribute dmi_sel_raw_attr = { 445 .attr = {.name = "raw_event_log", .mode = 0400}, 446 .read = dmi_sel_raw_read, 447 }; 448 449 static int dmi_system_event_log(struct dmi_sysfs_entry *entry) 450 { 451 int ret; 452 453 entry->child = kzalloc(sizeof(*entry->child), GFP_KERNEL); 454 if (!entry->child) 455 return -ENOMEM; 456 ret = kobject_init_and_add(entry->child, 457 &dmi_system_event_log_ktype, 458 &entry->kobj, 459 "system_event_log"); 460 if (ret) 461 goto out_free; 462 463 ret = sysfs_create_bin_file(entry->child, &dmi_sel_raw_attr); 464 if (ret) 465 goto out_del; 466 467 return 0; 468 469 out_del: 470 kobject_del(entry->child); 471 out_free: 472 kfree(entry->child); 473 return ret; 474 } 475 476 /************************************************* 477 * Generic DMI entry support. 478 *************************************************/ 479 480 static ssize_t dmi_sysfs_entry_length(struct dmi_sysfs_entry *entry, char *buf) 481 { 482 return sprintf(buf, "%d\n", entry->dh.length); 483 } 484 485 static ssize_t dmi_sysfs_entry_handle(struct dmi_sysfs_entry *entry, char *buf) 486 { 487 return sprintf(buf, "%d\n", entry->dh.handle); 488 } 489 490 static ssize_t dmi_sysfs_entry_type(struct dmi_sysfs_entry *entry, char *buf) 491 { 492 return sprintf(buf, "%d\n", entry->dh.type); 493 } 494 495 static ssize_t dmi_sysfs_entry_instance(struct dmi_sysfs_entry *entry, 496 char *buf) 497 { 498 return sprintf(buf, "%d\n", entry->instance); 499 } 500 501 static ssize_t dmi_sysfs_entry_position(struct dmi_sysfs_entry *entry, 502 char *buf) 503 { 504 return sprintf(buf, "%d\n", entry->position); 505 } 506 507 static DMI_SYSFS_ATTR(entry, length); 508 static DMI_SYSFS_ATTR(entry, handle); 509 static DMI_SYSFS_ATTR(entry, type); 510 static DMI_SYSFS_ATTR(entry, instance); 511 static DMI_SYSFS_ATTR(entry, position); 512 513 static struct attribute *dmi_sysfs_entry_attrs[] = { 514 &dmi_sysfs_attr_entry_length.attr, 515 &dmi_sysfs_attr_entry_handle.attr, 516 &dmi_sysfs_attr_entry_type.attr, 517 &dmi_sysfs_attr_entry_instance.attr, 518 &dmi_sysfs_attr_entry_position.attr, 519 NULL, 520 }; 521 522 static ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry, 523 const struct dmi_header *dh, 524 void *_state) 525 { 526 struct dmi_read_state *state = _state; 527 size_t entry_length; 528 529 entry_length = dmi_entry_length(dh); 530 531 return memory_read_from_buffer(state->buf, state->count, 532 &state->pos, dh, entry_length); 533 } 534 535 static ssize_t dmi_entry_raw_read(struct file *filp, 536 struct kobject *kobj, 537 struct bin_attribute *bin_attr, 538 char *buf, loff_t pos, size_t count) 539 { 540 struct dmi_sysfs_entry *entry = to_entry(kobj); 541 struct dmi_read_state state = { 542 .buf = buf, 543 .pos = pos, 544 .count = count, 545 }; 546 547 return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state); 548 } 549 550 static const struct bin_attribute dmi_entry_raw_attr = { 551 .attr = {.name = "raw", .mode = 0400}, 552 .read = dmi_entry_raw_read, 553 }; 554 555 static void dmi_sysfs_entry_release(struct kobject *kobj) 556 { 557 struct dmi_sysfs_entry *entry = to_entry(kobj); 558 sysfs_remove_bin_file(&entry->kobj, &dmi_entry_raw_attr); 559 spin_lock(&entry_list_lock); 560 list_del(&entry->list); 561 spin_unlock(&entry_list_lock); 562 kfree(entry); 563 } 564 565 static struct kobj_type dmi_sysfs_entry_ktype = { 566 .release = dmi_sysfs_entry_release, 567 .sysfs_ops = &dmi_sysfs_attr_ops, 568 .default_attrs = dmi_sysfs_entry_attrs, 569 }; 570 571 static struct kobject *dmi_kobj; 572 static struct kset *dmi_kset; 573 574 /* Global count of all instances seen. Only for setup */ 575 static int __initdata instance_counts[MAX_ENTRY_TYPE + 1]; 576 577 /* Global positional count of all entries seen. Only for setup */ 578 static int __initdata position_count; 579 580 static void __init dmi_sysfs_register_handle(const struct dmi_header *dh, 581 void *_ret) 582 { 583 struct dmi_sysfs_entry *entry; 584 int *ret = _ret; 585 586 /* If a previous entry saw an error, short circuit */ 587 if (*ret) 588 return; 589 590 /* Allocate and register a new entry into the entries set */ 591 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 592 if (!entry) { 593 *ret = -ENOMEM; 594 return; 595 } 596 597 /* Set the key */ 598 entry->dh = *dh; 599 entry->instance = instance_counts[dh->type]++; 600 entry->position = position_count++; 601 602 entry->kobj.kset = dmi_kset; 603 *ret = kobject_init_and_add(&entry->kobj, &dmi_sysfs_entry_ktype, NULL, 604 "%d-%d", dh->type, entry->instance); 605 606 if (*ret) { 607 kfree(entry); 608 return; 609 } 610 611 /* Thread on the global list for cleanup */ 612 spin_lock(&entry_list_lock); 613 list_add_tail(&entry->list, &entry_list); 614 spin_unlock(&entry_list_lock); 615 616 /* Handle specializations by type */ 617 switch (dh->type) { 618 case DMI_ENTRY_SYSTEM_EVENT_LOG: 619 *ret = dmi_system_event_log(entry); 620 break; 621 default: 622 /* No specialization */ 623 break; 624 } 625 if (*ret) 626 goto out_err; 627 628 /* Create the raw binary file to access the entry */ 629 *ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr); 630 if (*ret) 631 goto out_err; 632 633 return; 634 out_err: 635 kobject_put(entry->child); 636 kobject_put(&entry->kobj); 637 return; 638 } 639 640 static void cleanup_entry_list(void) 641 { 642 struct dmi_sysfs_entry *entry, *next; 643 644 /* No locks, we are on our way out */ 645 list_for_each_entry_safe(entry, next, &entry_list, list) { 646 kobject_put(entry->child); 647 kobject_put(&entry->kobj); 648 } 649 } 650 651 static int __init dmi_sysfs_init(void) 652 { 653 int error = -ENOMEM; 654 int val; 655 656 /* Set up our directory */ 657 dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); 658 if (!dmi_kobj) 659 goto err; 660 661 dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); 662 if (!dmi_kset) 663 goto err; 664 665 val = 0; 666 error = dmi_walk(dmi_sysfs_register_handle, &val); 667 if (error) 668 goto err; 669 if (val) { 670 error = val; 671 goto err; 672 } 673 674 pr_debug("dmi-sysfs: loaded.\n"); 675 676 return 0; 677 err: 678 cleanup_entry_list(); 679 kset_unregister(dmi_kset); 680 kobject_put(dmi_kobj); 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 kobject_put(dmi_kobj); 691 } 692 693 module_init(dmi_sysfs_init); 694 module_exit(dmi_sysfs_exit); 695 696 MODULE_AUTHOR("Mike Waychison <[email protected]>"); 697 MODULE_DESCRIPTION("DMI sysfs support"); 698 MODULE_LICENSE("GPL"); 699