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 32struct 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 */ 46static LIST_HEAD(entry_list); 47static DEFINE_SPINLOCK(entry_list_lock); 48 49/* dmi_sysfs_attribute - Top level attribute. used by all entries. */ 50struct 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) \ 56struct 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 */ 65struct 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) \ 73struct 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 *************************************************/ 81static void dmi_entry_free(struct kobject *kobj) 82{ 83 kfree(kobj); 84} 85 86static struct dmi_sysfs_entry *to_entry(struct kobject *kobj) 87{ 88 return container_of(kobj, struct dmi_sysfs_entry, kobj); 89} 90 91static struct dmi_sysfs_attribute *to_attr(struct attribute *attr) 92{ 93 return container_of(attr, struct dmi_sysfs_attribute, attr); 94} 95 96static 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 109static const struct sysfs_ops dmi_sysfs_attr_ops = { 110 .show = dmi_sysfs_attr_show, 111}; 112 113typedef ssize_t (*dmi_callback)(struct dmi_sysfs_entry *, 114 const struct dmi_header *dh, void *); 115 116struct 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 124static 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() */ 151struct dmi_read_state { 152 char *buf; 153 loff_t pos; 154 size_t count; 155}; 156 157static 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 */ 181static 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 *************************************************/ 196struct dmi_entry_attr_show_data { 197 struct attribute *attr; 198 char *buf; 199}; 200 201static 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 213static 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 227static 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 243struct 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) \ 267static 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} \ 277static DMI_SYSFS_MAPPED_ATTR(sel, _field) 278 279DMI_SYSFS_SEL_FIELD(area_length); 280DMI_SYSFS_SEL_FIELD(header_start_offset); 281DMI_SYSFS_SEL_FIELD(data_start_offset); 282DMI_SYSFS_SEL_FIELD(access_method); 283DMI_SYSFS_SEL_FIELD(status); 284DMI_SYSFS_SEL_FIELD(change_token); 285DMI_SYSFS_SEL_FIELD(access_method_address); 286DMI_SYSFS_SEL_FIELD(header_format); 287DMI_SYSFS_SEL_FIELD(type_descriptors_supported_count); 288DMI_SYSFS_SEL_FIELD(per_log_type_descriptor_length); 289 290static 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 305static 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 311typedef u8 (*sel_io_reader)(const struct dmi_system_event_log *sel, 312 loff_t offset); 313 314static DEFINE_MUTEX(io_port_lock); 315 316static 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 328static 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 341static 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 353static 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 359static 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 376static 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 397static 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 428static 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 442static struct bin_attribute dmi_sel_raw_attr = { 443 .attr = {.name = "raw_event_log", .mode = 0400}, 444 .read = dmi_sel_raw_read, 445}; 446 447static 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 467out_del: 468 kobject_del(entry->child); 469out_free: 470 kfree(entry->child); 471 return ret; 472} 473 474/************************************************* 475 * Generic DMI entry support. 476 *************************************************/ 477 478static 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 483static 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 488static 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 493static 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 499static 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 505static DMI_SYSFS_ATTR(entry, length); 506static DMI_SYSFS_ATTR(entry, handle); 507static DMI_SYSFS_ATTR(entry, type); 508static DMI_SYSFS_ATTR(entry, instance); 509static DMI_SYSFS_ATTR(entry, position); 510 511static 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 520static 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 533static 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 548static const struct bin_attribute dmi_entry_raw_attr = { 549 .attr = {.name = "raw", .mode = 0400}, 550 .read = dmi_entry_raw_read, 551}; 552 553static void dmi_sysfs_entry_release(struct kobject *kobj) 554{ 555 struct dmi_sysfs_entry *entry = to_entry(kobj); 556 557 spin_lock(&entry_list_lock); 558 list_del(&entry->list); 559 spin_unlock(&entry_list_lock); 560 kfree(entry); 561} 562 563static 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 569static struct kset *dmi_kset; 570 571/* Global count of all instances seen. Only for setup */ 572static int __initdata instance_counts[MAX_ENTRY_TYPE + 1]; 573 574/* Global positional count of all entries seen. Only for setup */ 575static int __initdata position_count; 576 577static void __init dmi_sysfs_register_handle(const struct dmi_header *dh, 578 void *_ret) 579{ 580 struct dmi_sysfs_entry *entry; 581 int *ret = _ret; 582 583 /* If a previous entry saw an error, short circuit */ 584 if (*ret) 585 return; 586 587 /* Allocate and register a new entry into the entries set */ 588 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 589 if (!entry) { 590 *ret = -ENOMEM; 591 return; 592 } 593 594 /* Set the key */ 595 memcpy(&entry->dh, dh, sizeof(*dh)); 596 entry->instance = instance_counts[dh->type]++; 597 entry->position = position_count++; 598 599 entry->kobj.kset = dmi_kset; 600 *ret = kobject_init_and_add(&entry->kobj, &dmi_sysfs_entry_ktype, NULL, 601 "%d-%d", dh->type, entry->instance); 602 603 if (*ret) { 604 kfree(entry); 605 return; 606 } 607 608 /* Thread on the global list for cleanup */ 609 spin_lock(&entry_list_lock); 610 list_add_tail(&entry->list, &entry_list); 611 spin_unlock(&entry_list_lock); 612 613 /* Handle specializations by type */ 614 switch (dh->type) { 615 case DMI_ENTRY_SYSTEM_EVENT_LOG: 616 *ret = dmi_system_event_log(entry); 617 break; 618 default: 619 /* No specialization */ 620 break; 621 } 622 if (*ret) 623 goto out_err; 624 625 /* Create the raw binary file to access the entry */ 626 *ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr); 627 if (*ret) 628 goto out_err; 629 630 return; 631out_err: 632 kobject_put(entry->child); 633 kobject_put(&entry->kobj); 634 return; 635} 636 637static void cleanup_entry_list(void) 638{ 639 struct dmi_sysfs_entry *entry, *next; 640 641 /* No locks, we are on our way out */ 642 list_for_each_entry_safe(entry, next, &entry_list, list) { 643 kobject_put(entry->child); 644 kobject_put(&entry->kobj); 645 } 646} 647 648static int __init dmi_sysfs_init(void) 649{ 650 int error; 651 int val; 652 653 if (!dmi_kobj) { 654 pr_err("dmi-sysfs: dmi entry is absent.\n"); 655 error = -ENODATA; 656 goto err; 657 } 658 659 dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); 660 if (!dmi_kset) { 661 error = -ENOMEM; 662 goto err; 663 } 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; 677err: 678 cleanup_entry_list(); 679 kset_unregister(dmi_kset); 680 return error; 681} 682 683/* clean up everything. */ 684static void __exit dmi_sysfs_exit(void) 685{ 686 pr_debug("dmi-sysfs: unloading.\n"); 687 cleanup_entry_list(); 688 kset_unregister(dmi_kset); 689} 690 691module_init(dmi_sysfs_init); 692module_exit(dmi_sysfs_exit); 693 694MODULE_AUTHOR("Mike Waychison <mikew@google.com>"); 695MODULE_DESCRIPTION("DMI sysfs support"); 696MODULE_LICENSE("GPL"); 697