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