1948af1f0SMike Waychison /* 2948af1f0SMike Waychison * dmi-sysfs.c 3948af1f0SMike Waychison * 4948af1f0SMike Waychison * This module exports the DMI tables read-only to userspace through the 5948af1f0SMike Waychison * sysfs file system. 6948af1f0SMike Waychison * 7948af1f0SMike Waychison * Data is currently found below 8948af1f0SMike Waychison * /sys/firmware/dmi/... 9948af1f0SMike Waychison * 10948af1f0SMike Waychison * DMI attributes are presented in attribute files with names 11948af1f0SMike Waychison * formatted using %d-%d, so that the first integer indicates the 12948af1f0SMike Waychison * structure type (0-255), and the second field is the instance of that 13948af1f0SMike Waychison * entry. 14948af1f0SMike Waychison * 15948af1f0SMike Waychison * Copyright 2011 Google, Inc. 16948af1f0SMike Waychison */ 17948af1f0SMike Waychison 18948af1f0SMike Waychison #include <linux/kernel.h> 19948af1f0SMike Waychison #include <linux/init.h> 20948af1f0SMike Waychison #include <linux/module.h> 21948af1f0SMike Waychison #include <linux/types.h> 22948af1f0SMike Waychison #include <linux/kobject.h> 23948af1f0SMike Waychison #include <linux/dmi.h> 24948af1f0SMike Waychison #include <linux/capability.h> 25948af1f0SMike Waychison #include <linux/slab.h> 26948af1f0SMike Waychison #include <linux/list.h> 27948af1f0SMike Waychison #include <linux/io.h> 28948af1f0SMike Waychison 29948af1f0SMike Waychison #define MAX_ENTRY_TYPE 255 /* Most of these aren't used, but we consider 30948af1f0SMike Waychison the top entry type is only 8 bits */ 31948af1f0SMike Waychison 32948af1f0SMike Waychison struct dmi_sysfs_entry { 33948af1f0SMike Waychison struct dmi_header dh; 34948af1f0SMike Waychison struct kobject kobj; 35948af1f0SMike Waychison int instance; 36948af1f0SMike Waychison int position; 37948af1f0SMike Waychison struct list_head list; 38*925a1da7SMike Waychison struct kobject *child; 39948af1f0SMike Waychison }; 40948af1f0SMike Waychison 41948af1f0SMike Waychison /* 42948af1f0SMike Waychison * Global list of dmi_sysfs_entry. Even though this should only be 43948af1f0SMike Waychison * manipulated at setup and teardown, the lazy nature of the kobject 44948af1f0SMike Waychison * system means we get lazy removes. 45948af1f0SMike Waychison */ 46948af1f0SMike Waychison static LIST_HEAD(entry_list); 47948af1f0SMike Waychison static DEFINE_SPINLOCK(entry_list_lock); 48948af1f0SMike Waychison 49948af1f0SMike Waychison /* dmi_sysfs_attribute - Top level attribute. used by all entries. */ 50948af1f0SMike Waychison struct dmi_sysfs_attribute { 51948af1f0SMike Waychison struct attribute attr; 52948af1f0SMike Waychison ssize_t (*show)(struct dmi_sysfs_entry *entry, char *buf); 53948af1f0SMike Waychison }; 54948af1f0SMike Waychison 55948af1f0SMike Waychison #define DMI_SYSFS_ATTR(_entry, _name) \ 56948af1f0SMike Waychison struct dmi_sysfs_attribute dmi_sysfs_attr_##_entry##_##_name = { \ 57948af1f0SMike Waychison .attr = {.name = __stringify(_name), .mode = 0400}, \ 58948af1f0SMike Waychison .show = dmi_sysfs_##_entry##_##_name, \ 59948af1f0SMike Waychison } 60948af1f0SMike Waychison 61948af1f0SMike Waychison /* 62948af1f0SMike Waychison * dmi_sysfs_mapped_attribute - Attribute where we require the entry be 63948af1f0SMike Waychison * mapped in. Use in conjunction with dmi_sysfs_specialize_attr_ops. 64948af1f0SMike Waychison */ 65948af1f0SMike Waychison struct dmi_sysfs_mapped_attribute { 66948af1f0SMike Waychison struct attribute attr; 67948af1f0SMike Waychison ssize_t (*show)(struct dmi_sysfs_entry *entry, 68948af1f0SMike Waychison const struct dmi_header *dh, 69948af1f0SMike Waychison char *buf); 70948af1f0SMike Waychison }; 71948af1f0SMike Waychison 72948af1f0SMike Waychison #define DMI_SYSFS_MAPPED_ATTR(_entry, _name) \ 73948af1f0SMike Waychison struct dmi_sysfs_mapped_attribute dmi_sysfs_attr_##_entry##_##_name = { \ 74948af1f0SMike Waychison .attr = {.name = __stringify(_name), .mode = 0400}, \ 75948af1f0SMike Waychison .show = dmi_sysfs_##_entry##_##_name, \ 76948af1f0SMike Waychison } 77948af1f0SMike Waychison 78948af1f0SMike Waychison /************************************************* 79948af1f0SMike Waychison * Generic DMI entry support. 80948af1f0SMike Waychison *************************************************/ 81*925a1da7SMike Waychison static void dmi_entry_free(struct kobject *kobj) 82*925a1da7SMike Waychison { 83*925a1da7SMike Waychison kfree(kobj); 84*925a1da7SMike Waychison } 85948af1f0SMike Waychison 86948af1f0SMike Waychison static struct dmi_sysfs_entry *to_entry(struct kobject *kobj) 87948af1f0SMike Waychison { 88948af1f0SMike Waychison return container_of(kobj, struct dmi_sysfs_entry, kobj); 89948af1f0SMike Waychison } 90948af1f0SMike Waychison 91948af1f0SMike Waychison static struct dmi_sysfs_attribute *to_attr(struct attribute *attr) 92948af1f0SMike Waychison { 93948af1f0SMike Waychison return container_of(attr, struct dmi_sysfs_attribute, attr); 94948af1f0SMike Waychison } 95948af1f0SMike Waychison 96948af1f0SMike Waychison static ssize_t dmi_sysfs_attr_show(struct kobject *kobj, 97948af1f0SMike Waychison struct attribute *_attr, char *buf) 98948af1f0SMike Waychison { 99948af1f0SMike Waychison struct dmi_sysfs_entry *entry = to_entry(kobj); 100948af1f0SMike Waychison struct dmi_sysfs_attribute *attr = to_attr(_attr); 101948af1f0SMike Waychison 102948af1f0SMike Waychison /* DMI stuff is only ever admin visible */ 103948af1f0SMike Waychison if (!capable(CAP_SYS_ADMIN)) 104948af1f0SMike Waychison return -EACCES; 105948af1f0SMike Waychison 106948af1f0SMike Waychison return attr->show(entry, buf); 107948af1f0SMike Waychison } 108948af1f0SMike Waychison 109948af1f0SMike Waychison static const struct sysfs_ops dmi_sysfs_attr_ops = { 110948af1f0SMike Waychison .show = dmi_sysfs_attr_show, 111948af1f0SMike Waychison }; 112948af1f0SMike Waychison 113948af1f0SMike Waychison typedef ssize_t (*dmi_callback)(struct dmi_sysfs_entry *, 114948af1f0SMike Waychison const struct dmi_header *dh, void *); 115948af1f0SMike Waychison 116948af1f0SMike Waychison struct find_dmi_data { 117948af1f0SMike Waychison struct dmi_sysfs_entry *entry; 118948af1f0SMike Waychison dmi_callback callback; 119948af1f0SMike Waychison void *private; 120948af1f0SMike Waychison int instance_countdown; 121948af1f0SMike Waychison ssize_t ret; 122948af1f0SMike Waychison }; 123948af1f0SMike Waychison 124948af1f0SMike Waychison static void find_dmi_entry_helper(const struct dmi_header *dh, 125948af1f0SMike Waychison void *_data) 126948af1f0SMike Waychison { 127948af1f0SMike Waychison struct find_dmi_data *data = _data; 128948af1f0SMike Waychison struct dmi_sysfs_entry *entry = data->entry; 129948af1f0SMike Waychison 130948af1f0SMike Waychison /* Is this the entry we want? */ 131948af1f0SMike Waychison if (dh->type != entry->dh.type) 132948af1f0SMike Waychison return; 133948af1f0SMike Waychison 134948af1f0SMike Waychison if (data->instance_countdown != 0) { 135948af1f0SMike Waychison /* try the next instance? */ 136948af1f0SMike Waychison data->instance_countdown--; 137948af1f0SMike Waychison return; 138948af1f0SMike Waychison } 139948af1f0SMike Waychison 140948af1f0SMike Waychison /* 141948af1f0SMike Waychison * Don't ever revisit the instance. Short circuit later 142948af1f0SMike Waychison * instances by letting the instance_countdown run negative 143948af1f0SMike Waychison */ 144948af1f0SMike Waychison data->instance_countdown--; 145948af1f0SMike Waychison 146948af1f0SMike Waychison /* Found the entry */ 147948af1f0SMike Waychison data->ret = data->callback(entry, dh, data->private); 148948af1f0SMike Waychison } 149948af1f0SMike Waychison 150948af1f0SMike Waychison /* State for passing the read parameters through dmi_find_entry() */ 151948af1f0SMike Waychison struct dmi_read_state { 152948af1f0SMike Waychison char *buf; 153948af1f0SMike Waychison loff_t pos; 154948af1f0SMike Waychison size_t count; 155948af1f0SMike Waychison }; 156948af1f0SMike Waychison 157948af1f0SMike Waychison static ssize_t find_dmi_entry(struct dmi_sysfs_entry *entry, 158948af1f0SMike Waychison dmi_callback callback, void *private) 159948af1f0SMike Waychison { 160948af1f0SMike Waychison struct find_dmi_data data = { 161948af1f0SMike Waychison .entry = entry, 162948af1f0SMike Waychison .callback = callback, 163948af1f0SMike Waychison .private = private, 164948af1f0SMike Waychison .instance_countdown = entry->instance, 165948af1f0SMike Waychison .ret = -EIO, /* To signal the entry disappeared */ 166948af1f0SMike Waychison }; 167948af1f0SMike Waychison int ret; 168948af1f0SMike Waychison 169948af1f0SMike Waychison ret = dmi_walk(find_dmi_entry_helper, &data); 170948af1f0SMike Waychison /* This shouldn't happen, but just in case. */ 171948af1f0SMike Waychison if (ret) 172948af1f0SMike Waychison return -EINVAL; 173948af1f0SMike Waychison return data.ret; 174948af1f0SMike Waychison } 175948af1f0SMike Waychison 176948af1f0SMike Waychison /* 177948af1f0SMike Waychison * Calculate and return the byte length of the dmi entry identified by 178948af1f0SMike Waychison * dh. This includes both the formatted portion as well as the 179948af1f0SMike Waychison * unformatted string space, including the two trailing nul characters. 180948af1f0SMike Waychison */ 181948af1f0SMike Waychison static size_t dmi_entry_length(const struct dmi_header *dh) 182948af1f0SMike Waychison { 183948af1f0SMike Waychison const char *p = (const char *)dh; 184948af1f0SMike Waychison 185948af1f0SMike Waychison p += dh->length; 186948af1f0SMike Waychison 187948af1f0SMike Waychison while (p[0] || p[1]) 188948af1f0SMike Waychison p++; 189948af1f0SMike Waychison 190948af1f0SMike Waychison return 2 + p - (const char *)dh; 191948af1f0SMike Waychison } 192948af1f0SMike Waychison 193948af1f0SMike Waychison /************************************************* 194*925a1da7SMike Waychison * Support bits for specialized DMI entry support 195*925a1da7SMike Waychison *************************************************/ 196*925a1da7SMike Waychison struct dmi_entry_attr_show_data { 197*925a1da7SMike Waychison struct attribute *attr; 198*925a1da7SMike Waychison char *buf; 199*925a1da7SMike Waychison }; 200*925a1da7SMike Waychison 201*925a1da7SMike Waychison static ssize_t dmi_entry_attr_show_helper(struct dmi_sysfs_entry *entry, 202*925a1da7SMike Waychison const struct dmi_header *dh, 203*925a1da7SMike Waychison void *_data) 204*925a1da7SMike Waychison { 205*925a1da7SMike Waychison struct dmi_entry_attr_show_data *data = _data; 206*925a1da7SMike Waychison struct dmi_sysfs_mapped_attribute *attr; 207*925a1da7SMike Waychison 208*925a1da7SMike Waychison attr = container_of(data->attr, 209*925a1da7SMike Waychison struct dmi_sysfs_mapped_attribute, attr); 210*925a1da7SMike Waychison return attr->show(entry, dh, data->buf); 211*925a1da7SMike Waychison } 212*925a1da7SMike Waychison 213*925a1da7SMike Waychison static ssize_t dmi_entry_attr_show(struct kobject *kobj, 214*925a1da7SMike Waychison struct attribute *attr, 215*925a1da7SMike Waychison char *buf) 216*925a1da7SMike Waychison { 217*925a1da7SMike Waychison struct dmi_entry_attr_show_data data = { 218*925a1da7SMike Waychison .attr = attr, 219*925a1da7SMike Waychison .buf = buf, 220*925a1da7SMike Waychison }; 221*925a1da7SMike Waychison /* Find the entry according to our parent and call the 222*925a1da7SMike Waychison * normalized show method hanging off of the attribute */ 223*925a1da7SMike Waychison return find_dmi_entry(to_entry(kobj->parent), 224*925a1da7SMike Waychison dmi_entry_attr_show_helper, &data); 225*925a1da7SMike Waychison } 226*925a1da7SMike Waychison 227*925a1da7SMike Waychison static const struct sysfs_ops dmi_sysfs_specialize_attr_ops = { 228*925a1da7SMike Waychison .show = dmi_entry_attr_show, 229*925a1da7SMike Waychison }; 230*925a1da7SMike Waychison 231*925a1da7SMike Waychison /************************************************* 232*925a1da7SMike Waychison * Specialized DMI entry support. 233*925a1da7SMike Waychison *************************************************/ 234*925a1da7SMike Waychison 235*925a1da7SMike Waychison /*** Type 15 - System Event Table ***/ 236*925a1da7SMike Waychison 237*925a1da7SMike Waychison #define DMI_SEL_ACCESS_METHOD_IO8 0x00 238*925a1da7SMike Waychison #define DMI_SEL_ACCESS_METHOD_IO2x8 0x01 239*925a1da7SMike Waychison #define DMI_SEL_ACCESS_METHOD_IO16 0x02 240*925a1da7SMike Waychison #define DMI_SEL_ACCESS_METHOD_PHYS32 0x03 241*925a1da7SMike Waychison #define DMI_SEL_ACCESS_METHOD_GPNV 0x04 242*925a1da7SMike Waychison 243*925a1da7SMike Waychison struct dmi_system_event_log { 244*925a1da7SMike Waychison struct dmi_header header; 245*925a1da7SMike Waychison u16 area_length; 246*925a1da7SMike Waychison u16 header_start_offset; 247*925a1da7SMike Waychison u16 data_start_offset; 248*925a1da7SMike Waychison u8 access_method; 249*925a1da7SMike Waychison u8 status; 250*925a1da7SMike Waychison u32 change_token; 251*925a1da7SMike Waychison union { 252*925a1da7SMike Waychison struct { 253*925a1da7SMike Waychison u16 index_addr; 254*925a1da7SMike Waychison u16 data_addr; 255*925a1da7SMike Waychison } io; 256*925a1da7SMike Waychison u32 phys_addr32; 257*925a1da7SMike Waychison u16 gpnv_handle; 258*925a1da7SMike Waychison u32 access_method_address; 259*925a1da7SMike Waychison }; 260*925a1da7SMike Waychison u8 header_format; 261*925a1da7SMike Waychison u8 type_descriptors_supported_count; 262*925a1da7SMike Waychison u8 per_log_type_descriptor_length; 263*925a1da7SMike Waychison u8 supported_log_type_descriptos[0]; 264*925a1da7SMike Waychison } __packed; 265*925a1da7SMike Waychison 266*925a1da7SMike Waychison static const struct dmi_system_event_log *to_sel(const struct dmi_header *dh) 267*925a1da7SMike Waychison { 268*925a1da7SMike Waychison return (const struct dmi_system_event_log *)dh; 269*925a1da7SMike Waychison } 270*925a1da7SMike Waychison 271*925a1da7SMike Waychison #define DMI_SYSFS_SEL_FIELD(_field) \ 272*925a1da7SMike Waychison static ssize_t dmi_sysfs_sel_##_field(struct dmi_sysfs_entry *entry, \ 273*925a1da7SMike Waychison const struct dmi_header *dh, \ 274*925a1da7SMike Waychison char *buf) \ 275*925a1da7SMike Waychison { \ 276*925a1da7SMike Waychison const struct dmi_system_event_log *sel = to_sel(dh); \ 277*925a1da7SMike Waychison if (sizeof(*sel) > dmi_entry_length(dh)) \ 278*925a1da7SMike Waychison return -EIO; \ 279*925a1da7SMike Waychison return sprintf(buf, "%u\n", sel->_field); \ 280*925a1da7SMike Waychison } \ 281*925a1da7SMike Waychison static DMI_SYSFS_MAPPED_ATTR(sel, _field) 282*925a1da7SMike Waychison 283*925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(area_length); 284*925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(header_start_offset); 285*925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(data_start_offset); 286*925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(access_method); 287*925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(status); 288*925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(change_token); 289*925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(access_method_address); 290*925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(header_format); 291*925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(type_descriptors_supported_count); 292*925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(per_log_type_descriptor_length); 293*925a1da7SMike Waychison 294*925a1da7SMike Waychison static struct attribute *dmi_sysfs_sel_attrs[] = { 295*925a1da7SMike Waychison &dmi_sysfs_attr_sel_area_length.attr, 296*925a1da7SMike Waychison &dmi_sysfs_attr_sel_header_start_offset.attr, 297*925a1da7SMike Waychison &dmi_sysfs_attr_sel_data_start_offset.attr, 298*925a1da7SMike Waychison &dmi_sysfs_attr_sel_access_method.attr, 299*925a1da7SMike Waychison &dmi_sysfs_attr_sel_status.attr, 300*925a1da7SMike Waychison &dmi_sysfs_attr_sel_change_token.attr, 301*925a1da7SMike Waychison &dmi_sysfs_attr_sel_access_method_address.attr, 302*925a1da7SMike Waychison &dmi_sysfs_attr_sel_header_format.attr, 303*925a1da7SMike Waychison &dmi_sysfs_attr_sel_type_descriptors_supported_count.attr, 304*925a1da7SMike Waychison &dmi_sysfs_attr_sel_per_log_type_descriptor_length.attr, 305*925a1da7SMike Waychison NULL, 306*925a1da7SMike Waychison }; 307*925a1da7SMike Waychison 308*925a1da7SMike Waychison 309*925a1da7SMike Waychison static struct kobj_type dmi_system_event_log_ktype = { 310*925a1da7SMike Waychison .release = dmi_entry_free, 311*925a1da7SMike Waychison .sysfs_ops = &dmi_sysfs_specialize_attr_ops, 312*925a1da7SMike Waychison .default_attrs = dmi_sysfs_sel_attrs, 313*925a1da7SMike Waychison }; 314*925a1da7SMike Waychison 315*925a1da7SMike Waychison static int dmi_system_event_log(struct dmi_sysfs_entry *entry) 316*925a1da7SMike Waychison { 317*925a1da7SMike Waychison int ret; 318*925a1da7SMike Waychison 319*925a1da7SMike Waychison entry->child = kzalloc(sizeof(*entry->child), GFP_KERNEL); 320*925a1da7SMike Waychison if (!entry->child) 321*925a1da7SMike Waychison return -ENOMEM; 322*925a1da7SMike Waychison ret = kobject_init_and_add(entry->child, 323*925a1da7SMike Waychison &dmi_system_event_log_ktype, 324*925a1da7SMike Waychison &entry->kobj, 325*925a1da7SMike Waychison "system_event_log"); 326*925a1da7SMike Waychison if (ret) 327*925a1da7SMike Waychison goto out_free; 328*925a1da7SMike Waychison out_free: 329*925a1da7SMike Waychison kfree(entry->child); 330*925a1da7SMike Waychison return ret; 331*925a1da7SMike Waychison } 332*925a1da7SMike Waychison 333*925a1da7SMike Waychison /************************************************* 334948af1f0SMike Waychison * Generic DMI entry support. 335948af1f0SMike Waychison *************************************************/ 336948af1f0SMike Waychison 337948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_length(struct dmi_sysfs_entry *entry, char *buf) 338948af1f0SMike Waychison { 339948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->dh.length); 340948af1f0SMike Waychison } 341948af1f0SMike Waychison 342948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_handle(struct dmi_sysfs_entry *entry, char *buf) 343948af1f0SMike Waychison { 344948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->dh.handle); 345948af1f0SMike Waychison } 346948af1f0SMike Waychison 347948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_type(struct dmi_sysfs_entry *entry, char *buf) 348948af1f0SMike Waychison { 349948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->dh.type); 350948af1f0SMike Waychison } 351948af1f0SMike Waychison 352948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_instance(struct dmi_sysfs_entry *entry, 353948af1f0SMike Waychison char *buf) 354948af1f0SMike Waychison { 355948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->instance); 356948af1f0SMike Waychison } 357948af1f0SMike Waychison 358948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_position(struct dmi_sysfs_entry *entry, 359948af1f0SMike Waychison char *buf) 360948af1f0SMike Waychison { 361948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->position); 362948af1f0SMike Waychison } 363948af1f0SMike Waychison 364948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, length); 365948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, handle); 366948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, type); 367948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, instance); 368948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, position); 369948af1f0SMike Waychison 370948af1f0SMike Waychison static struct attribute *dmi_sysfs_entry_attrs[] = { 371948af1f0SMike Waychison &dmi_sysfs_attr_entry_length.attr, 372948af1f0SMike Waychison &dmi_sysfs_attr_entry_handle.attr, 373948af1f0SMike Waychison &dmi_sysfs_attr_entry_type.attr, 374948af1f0SMike Waychison &dmi_sysfs_attr_entry_instance.attr, 375948af1f0SMike Waychison &dmi_sysfs_attr_entry_position.attr, 376948af1f0SMike Waychison NULL, 377948af1f0SMike Waychison }; 378948af1f0SMike Waychison 379948af1f0SMike Waychison static ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry, 380948af1f0SMike Waychison const struct dmi_header *dh, 381948af1f0SMike Waychison void *_state) 382948af1f0SMike Waychison { 383948af1f0SMike Waychison struct dmi_read_state *state = _state; 384948af1f0SMike Waychison size_t entry_length; 385948af1f0SMike Waychison 386948af1f0SMike Waychison entry_length = dmi_entry_length(dh); 387948af1f0SMike Waychison 388948af1f0SMike Waychison return memory_read_from_buffer(state->buf, state->count, 389948af1f0SMike Waychison &state->pos, dh, entry_length); 390948af1f0SMike Waychison } 391948af1f0SMike Waychison 392948af1f0SMike Waychison static ssize_t dmi_entry_raw_read(struct file *filp, 393948af1f0SMike Waychison struct kobject *kobj, 394948af1f0SMike Waychison struct bin_attribute *bin_attr, 395948af1f0SMike Waychison char *buf, loff_t pos, size_t count) 396948af1f0SMike Waychison { 397948af1f0SMike Waychison struct dmi_sysfs_entry *entry = to_entry(kobj); 398948af1f0SMike Waychison struct dmi_read_state state = { 399948af1f0SMike Waychison .buf = buf, 400948af1f0SMike Waychison .pos = pos, 401948af1f0SMike Waychison .count = count, 402948af1f0SMike Waychison }; 403948af1f0SMike Waychison 404948af1f0SMike Waychison return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state); 405948af1f0SMike Waychison } 406948af1f0SMike Waychison 407948af1f0SMike Waychison static const struct bin_attribute dmi_entry_raw_attr = { 408948af1f0SMike Waychison .attr = {.name = "raw", .mode = 0400}, 409948af1f0SMike Waychison .read = dmi_entry_raw_read, 410948af1f0SMike Waychison }; 411948af1f0SMike Waychison 412948af1f0SMike Waychison static void dmi_sysfs_entry_release(struct kobject *kobj) 413948af1f0SMike Waychison { 414948af1f0SMike Waychison struct dmi_sysfs_entry *entry = to_entry(kobj); 415948af1f0SMike Waychison sysfs_remove_bin_file(&entry->kobj, &dmi_entry_raw_attr); 416948af1f0SMike Waychison spin_lock(&entry_list_lock); 417948af1f0SMike Waychison list_del(&entry->list); 418948af1f0SMike Waychison spin_unlock(&entry_list_lock); 419948af1f0SMike Waychison kfree(entry); 420948af1f0SMike Waychison } 421948af1f0SMike Waychison 422948af1f0SMike Waychison static struct kobj_type dmi_sysfs_entry_ktype = { 423948af1f0SMike Waychison .release = dmi_sysfs_entry_release, 424948af1f0SMike Waychison .sysfs_ops = &dmi_sysfs_attr_ops, 425948af1f0SMike Waychison .default_attrs = dmi_sysfs_entry_attrs, 426948af1f0SMike Waychison }; 427948af1f0SMike Waychison 428948af1f0SMike Waychison static struct kobject *dmi_kobj; 429948af1f0SMike Waychison static struct kset *dmi_kset; 430948af1f0SMike Waychison 431948af1f0SMike Waychison /* Global count of all instances seen. Only for setup */ 432948af1f0SMike Waychison static int __initdata instance_counts[MAX_ENTRY_TYPE + 1]; 433948af1f0SMike Waychison 434948af1f0SMike Waychison /* Global positional count of all entries seen. Only for setup */ 435948af1f0SMike Waychison static int __initdata position_count; 436948af1f0SMike Waychison 437948af1f0SMike Waychison static void __init dmi_sysfs_register_handle(const struct dmi_header *dh, 438948af1f0SMike Waychison void *_ret) 439948af1f0SMike Waychison { 440948af1f0SMike Waychison struct dmi_sysfs_entry *entry; 441948af1f0SMike Waychison int *ret = _ret; 442948af1f0SMike Waychison 443948af1f0SMike Waychison /* If a previous entry saw an error, short circuit */ 444948af1f0SMike Waychison if (*ret) 445948af1f0SMike Waychison return; 446948af1f0SMike Waychison 447948af1f0SMike Waychison /* Allocate and register a new entry into the entries set */ 448948af1f0SMike Waychison entry = kzalloc(sizeof(*entry), GFP_KERNEL); 449948af1f0SMike Waychison if (!entry) { 450948af1f0SMike Waychison *ret = -ENOMEM; 451948af1f0SMike Waychison return; 452948af1f0SMike Waychison } 453948af1f0SMike Waychison 454948af1f0SMike Waychison /* Set the key */ 455948af1f0SMike Waychison entry->dh = *dh; 456948af1f0SMike Waychison entry->instance = instance_counts[dh->type]++; 457948af1f0SMike Waychison entry->position = position_count++; 458948af1f0SMike Waychison 459948af1f0SMike Waychison entry->kobj.kset = dmi_kset; 460948af1f0SMike Waychison *ret = kobject_init_and_add(&entry->kobj, &dmi_sysfs_entry_ktype, NULL, 461948af1f0SMike Waychison "%d-%d", dh->type, entry->instance); 462948af1f0SMike Waychison 463948af1f0SMike Waychison if (*ret) { 464948af1f0SMike Waychison kfree(entry); 465948af1f0SMike Waychison return; 466948af1f0SMike Waychison } 467948af1f0SMike Waychison 468948af1f0SMike Waychison /* Thread on the global list for cleanup */ 469948af1f0SMike Waychison spin_lock(&entry_list_lock); 470948af1f0SMike Waychison list_add_tail(&entry->list, &entry_list); 471948af1f0SMike Waychison spin_unlock(&entry_list_lock); 472948af1f0SMike Waychison 473*925a1da7SMike Waychison /* Handle specializations by type */ 474*925a1da7SMike Waychison switch (dh->type) { 475*925a1da7SMike Waychison case DMI_ENTRY_SYSTEM_EVENT_LOG: 476*925a1da7SMike Waychison *ret = dmi_system_event_log(entry); 477*925a1da7SMike Waychison break; 478*925a1da7SMike Waychison default: 479*925a1da7SMike Waychison /* No specialization */ 480*925a1da7SMike Waychison break; 481*925a1da7SMike Waychison } 482*925a1da7SMike Waychison if (*ret) 483*925a1da7SMike Waychison goto out_err; 484*925a1da7SMike Waychison 485948af1f0SMike Waychison /* Create the raw binary file to access the entry */ 486948af1f0SMike Waychison *ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr); 487948af1f0SMike Waychison if (*ret) 488948af1f0SMike Waychison goto out_err; 489948af1f0SMike Waychison 490948af1f0SMike Waychison return; 491948af1f0SMike Waychison out_err: 492*925a1da7SMike Waychison kobject_put(entry->child); 493948af1f0SMike Waychison kobject_put(&entry->kobj); 494948af1f0SMike Waychison return; 495948af1f0SMike Waychison } 496948af1f0SMike Waychison 497948af1f0SMike Waychison static void cleanup_entry_list(void) 498948af1f0SMike Waychison { 499948af1f0SMike Waychison struct dmi_sysfs_entry *entry, *next; 500948af1f0SMike Waychison 501948af1f0SMike Waychison /* No locks, we are on our way out */ 502948af1f0SMike Waychison list_for_each_entry_safe(entry, next, &entry_list, list) { 503*925a1da7SMike Waychison kobject_put(entry->child); 504948af1f0SMike Waychison kobject_put(&entry->kobj); 505948af1f0SMike Waychison } 506948af1f0SMike Waychison } 507948af1f0SMike Waychison 508948af1f0SMike Waychison static int __init dmi_sysfs_init(void) 509948af1f0SMike Waychison { 510948af1f0SMike Waychison int error = -ENOMEM; 511948af1f0SMike Waychison int val; 512948af1f0SMike Waychison 513948af1f0SMike Waychison /* Set up our directory */ 514948af1f0SMike Waychison dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); 515948af1f0SMike Waychison if (!dmi_kobj) 516948af1f0SMike Waychison goto err; 517948af1f0SMike Waychison 518948af1f0SMike Waychison dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); 519948af1f0SMike Waychison if (!dmi_kset) 520948af1f0SMike Waychison goto err; 521948af1f0SMike Waychison 522948af1f0SMike Waychison val = 0; 523948af1f0SMike Waychison error = dmi_walk(dmi_sysfs_register_handle, &val); 524948af1f0SMike Waychison if (error) 525948af1f0SMike Waychison goto err; 526948af1f0SMike Waychison if (val) { 527948af1f0SMike Waychison error = val; 528948af1f0SMike Waychison goto err; 529948af1f0SMike Waychison } 530948af1f0SMike Waychison 531948af1f0SMike Waychison pr_debug("dmi-sysfs: loaded.\n"); 532948af1f0SMike Waychison 533948af1f0SMike Waychison return 0; 534948af1f0SMike Waychison err: 535948af1f0SMike Waychison cleanup_entry_list(); 536948af1f0SMike Waychison kset_unregister(dmi_kset); 537948af1f0SMike Waychison kobject_put(dmi_kobj); 538948af1f0SMike Waychison return error; 539948af1f0SMike Waychison } 540948af1f0SMike Waychison 541948af1f0SMike Waychison /* clean up everything. */ 542948af1f0SMike Waychison static void __exit dmi_sysfs_exit(void) 543948af1f0SMike Waychison { 544948af1f0SMike Waychison pr_debug("dmi-sysfs: unloading.\n"); 545948af1f0SMike Waychison cleanup_entry_list(); 546948af1f0SMike Waychison kset_unregister(dmi_kset); 547948af1f0SMike Waychison kobject_put(dmi_kobj); 548948af1f0SMike Waychison } 549948af1f0SMike Waychison 550948af1f0SMike Waychison module_init(dmi_sysfs_init); 551948af1f0SMike Waychison module_exit(dmi_sysfs_exit); 552948af1f0SMike Waychison 553948af1f0SMike Waychison MODULE_AUTHOR("Mike Waychison <mikew@google.com>"); 554948af1f0SMike Waychison MODULE_DESCRIPTION("DMI sysfs support"); 555948af1f0SMike Waychison MODULE_LICENSE("GPL"); 556