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 static int dmi_system_event_log(struct dmi_sysfs_entry *entry) 316 { 317 int ret; 318 319 entry->child = kzalloc(sizeof(*entry->child), GFP_KERNEL); 320 if (!entry->child) 321 return -ENOMEM; 322 ret = kobject_init_and_add(entry->child, 323 &dmi_system_event_log_ktype, 324 &entry->kobj, 325 "system_event_log"); 326 if (ret) 327 goto out_free; 328 out_free: 329 kfree(entry->child); 330 return ret; 331 } 332 333 /************************************************* 334 * Generic DMI entry support. 335 *************************************************/ 336 337 static ssize_t dmi_sysfs_entry_length(struct dmi_sysfs_entry *entry, char *buf) 338 { 339 return sprintf(buf, "%d\n", entry->dh.length); 340 } 341 342 static ssize_t dmi_sysfs_entry_handle(struct dmi_sysfs_entry *entry, char *buf) 343 { 344 return sprintf(buf, "%d\n", entry->dh.handle); 345 } 346 347 static ssize_t dmi_sysfs_entry_type(struct dmi_sysfs_entry *entry, char *buf) 348 { 349 return sprintf(buf, "%d\n", entry->dh.type); 350 } 351 352 static ssize_t dmi_sysfs_entry_instance(struct dmi_sysfs_entry *entry, 353 char *buf) 354 { 355 return sprintf(buf, "%d\n", entry->instance); 356 } 357 358 static ssize_t dmi_sysfs_entry_position(struct dmi_sysfs_entry *entry, 359 char *buf) 360 { 361 return sprintf(buf, "%d\n", entry->position); 362 } 363 364 static DMI_SYSFS_ATTR(entry, length); 365 static DMI_SYSFS_ATTR(entry, handle); 366 static DMI_SYSFS_ATTR(entry, type); 367 static DMI_SYSFS_ATTR(entry, instance); 368 static DMI_SYSFS_ATTR(entry, position); 369 370 static struct attribute *dmi_sysfs_entry_attrs[] = { 371 &dmi_sysfs_attr_entry_length.attr, 372 &dmi_sysfs_attr_entry_handle.attr, 373 &dmi_sysfs_attr_entry_type.attr, 374 &dmi_sysfs_attr_entry_instance.attr, 375 &dmi_sysfs_attr_entry_position.attr, 376 NULL, 377 }; 378 379 static ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry, 380 const struct dmi_header *dh, 381 void *_state) 382 { 383 struct dmi_read_state *state = _state; 384 size_t entry_length; 385 386 entry_length = dmi_entry_length(dh); 387 388 return memory_read_from_buffer(state->buf, state->count, 389 &state->pos, dh, entry_length); 390 } 391 392 static ssize_t dmi_entry_raw_read(struct file *filp, 393 struct kobject *kobj, 394 struct bin_attribute *bin_attr, 395 char *buf, loff_t pos, size_t count) 396 { 397 struct dmi_sysfs_entry *entry = to_entry(kobj); 398 struct dmi_read_state state = { 399 .buf = buf, 400 .pos = pos, 401 .count = count, 402 }; 403 404 return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state); 405 } 406 407 static const struct bin_attribute dmi_entry_raw_attr = { 408 .attr = {.name = "raw", .mode = 0400}, 409 .read = dmi_entry_raw_read, 410 }; 411 412 static void dmi_sysfs_entry_release(struct kobject *kobj) 413 { 414 struct dmi_sysfs_entry *entry = to_entry(kobj); 415 sysfs_remove_bin_file(&entry->kobj, &dmi_entry_raw_attr); 416 spin_lock(&entry_list_lock); 417 list_del(&entry->list); 418 spin_unlock(&entry_list_lock); 419 kfree(entry); 420 } 421 422 static struct kobj_type dmi_sysfs_entry_ktype = { 423 .release = dmi_sysfs_entry_release, 424 .sysfs_ops = &dmi_sysfs_attr_ops, 425 .default_attrs = dmi_sysfs_entry_attrs, 426 }; 427 428 static struct kobject *dmi_kobj; 429 static struct kset *dmi_kset; 430 431 /* Global count of all instances seen. Only for setup */ 432 static int __initdata instance_counts[MAX_ENTRY_TYPE + 1]; 433 434 /* Global positional count of all entries seen. Only for setup */ 435 static int __initdata position_count; 436 437 static void __init dmi_sysfs_register_handle(const struct dmi_header *dh, 438 void *_ret) 439 { 440 struct dmi_sysfs_entry *entry; 441 int *ret = _ret; 442 443 /* If a previous entry saw an error, short circuit */ 444 if (*ret) 445 return; 446 447 /* Allocate and register a new entry into the entries set */ 448 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 449 if (!entry) { 450 *ret = -ENOMEM; 451 return; 452 } 453 454 /* Set the key */ 455 entry->dh = *dh; 456 entry->instance = instance_counts[dh->type]++; 457 entry->position = position_count++; 458 459 entry->kobj.kset = dmi_kset; 460 *ret = kobject_init_and_add(&entry->kobj, &dmi_sysfs_entry_ktype, NULL, 461 "%d-%d", dh->type, entry->instance); 462 463 if (*ret) { 464 kfree(entry); 465 return; 466 } 467 468 /* Thread on the global list for cleanup */ 469 spin_lock(&entry_list_lock); 470 list_add_tail(&entry->list, &entry_list); 471 spin_unlock(&entry_list_lock); 472 473 /* Handle specializations by type */ 474 switch (dh->type) { 475 case DMI_ENTRY_SYSTEM_EVENT_LOG: 476 *ret = dmi_system_event_log(entry); 477 break; 478 default: 479 /* No specialization */ 480 break; 481 } 482 if (*ret) 483 goto out_err; 484 485 /* Create the raw binary file to access the entry */ 486 *ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr); 487 if (*ret) 488 goto out_err; 489 490 return; 491 out_err: 492 kobject_put(entry->child); 493 kobject_put(&entry->kobj); 494 return; 495 } 496 497 static void cleanup_entry_list(void) 498 { 499 struct dmi_sysfs_entry *entry, *next; 500 501 /* No locks, we are on our way out */ 502 list_for_each_entry_safe(entry, next, &entry_list, list) { 503 kobject_put(entry->child); 504 kobject_put(&entry->kobj); 505 } 506 } 507 508 static int __init dmi_sysfs_init(void) 509 { 510 int error = -ENOMEM; 511 int val; 512 513 /* Set up our directory */ 514 dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); 515 if (!dmi_kobj) 516 goto err; 517 518 dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); 519 if (!dmi_kset) 520 goto err; 521 522 val = 0; 523 error = dmi_walk(dmi_sysfs_register_handle, &val); 524 if (error) 525 goto err; 526 if (val) { 527 error = val; 528 goto err; 529 } 530 531 pr_debug("dmi-sysfs: loaded.\n"); 532 533 return 0; 534 err: 535 cleanup_entry_list(); 536 kset_unregister(dmi_kset); 537 kobject_put(dmi_kobj); 538 return error; 539 } 540 541 /* clean up everything. */ 542 static void __exit dmi_sysfs_exit(void) 543 { 544 pr_debug("dmi-sysfs: unloading.\n"); 545 cleanup_entry_list(); 546 kset_unregister(dmi_kset); 547 kobject_put(dmi_kobj); 548 } 549 550 module_init(dmi_sysfs_init); 551 module_exit(dmi_sysfs_exit); 552 553 MODULE_AUTHOR("Mike Waychison <[email protected]>"); 554 MODULE_DESCRIPTION("DMI sysfs support"); 555 MODULE_LICENSE("GPL"); 556