1*09c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2948af1f0SMike Waychison /* 3948af1f0SMike Waychison * dmi-sysfs.c 4948af1f0SMike Waychison * 5948af1f0SMike Waychison * This module exports the DMI tables read-only to userspace through the 6948af1f0SMike Waychison * sysfs file system. 7948af1f0SMike Waychison * 8948af1f0SMike Waychison * Data is currently found below 9948af1f0SMike Waychison * /sys/firmware/dmi/... 10948af1f0SMike Waychison * 11948af1f0SMike Waychison * DMI attributes are presented in attribute files with names 12948af1f0SMike Waychison * formatted using %d-%d, so that the first integer indicates the 13948af1f0SMike Waychison * structure type (0-255), and the second field is the instance of that 14948af1f0SMike Waychison * entry. 15948af1f0SMike Waychison * 16948af1f0SMike Waychison * Copyright 2011 Google, Inc. 17948af1f0SMike Waychison */ 18948af1f0SMike Waychison 19948af1f0SMike Waychison #include <linux/kernel.h> 20948af1f0SMike Waychison #include <linux/init.h> 21948af1f0SMike Waychison #include <linux/module.h> 22948af1f0SMike Waychison #include <linux/types.h> 23948af1f0SMike Waychison #include <linux/kobject.h> 24948af1f0SMike Waychison #include <linux/dmi.h> 25948af1f0SMike Waychison #include <linux/capability.h> 26948af1f0SMike Waychison #include <linux/slab.h> 27948af1f0SMike Waychison #include <linux/list.h> 28948af1f0SMike Waychison #include <linux/io.h> 29f7750a79STom Lendacky #include <asm/dmi.h> 30948af1f0SMike Waychison 31948af1f0SMike Waychison #define MAX_ENTRY_TYPE 255 /* Most of these aren't used, but we consider 32948af1f0SMike Waychison the top entry type is only 8 bits */ 33948af1f0SMike Waychison 34948af1f0SMike Waychison struct dmi_sysfs_entry { 35948af1f0SMike Waychison struct dmi_header dh; 36948af1f0SMike Waychison struct kobject kobj; 37948af1f0SMike Waychison int instance; 38948af1f0SMike Waychison int position; 39948af1f0SMike Waychison struct list_head list; 40925a1da7SMike Waychison struct kobject *child; 41948af1f0SMike Waychison }; 42948af1f0SMike Waychison 43948af1f0SMike Waychison /* 44948af1f0SMike Waychison * Global list of dmi_sysfs_entry. Even though this should only be 45948af1f0SMike Waychison * manipulated at setup and teardown, the lazy nature of the kobject 46948af1f0SMike Waychison * system means we get lazy removes. 47948af1f0SMike Waychison */ 48948af1f0SMike Waychison static LIST_HEAD(entry_list); 49948af1f0SMike Waychison static DEFINE_SPINLOCK(entry_list_lock); 50948af1f0SMike Waychison 51948af1f0SMike Waychison /* dmi_sysfs_attribute - Top level attribute. used by all entries. */ 52948af1f0SMike Waychison struct dmi_sysfs_attribute { 53948af1f0SMike Waychison struct attribute attr; 54948af1f0SMike Waychison ssize_t (*show)(struct dmi_sysfs_entry *entry, char *buf); 55948af1f0SMike Waychison }; 56948af1f0SMike Waychison 57948af1f0SMike Waychison #define DMI_SYSFS_ATTR(_entry, _name) \ 58948af1f0SMike Waychison struct dmi_sysfs_attribute dmi_sysfs_attr_##_entry##_##_name = { \ 59948af1f0SMike Waychison .attr = {.name = __stringify(_name), .mode = 0400}, \ 60948af1f0SMike Waychison .show = dmi_sysfs_##_entry##_##_name, \ 61948af1f0SMike Waychison } 62948af1f0SMike Waychison 63948af1f0SMike Waychison /* 64948af1f0SMike Waychison * dmi_sysfs_mapped_attribute - Attribute where we require the entry be 65948af1f0SMike Waychison * mapped in. Use in conjunction with dmi_sysfs_specialize_attr_ops. 66948af1f0SMike Waychison */ 67948af1f0SMike Waychison struct dmi_sysfs_mapped_attribute { 68948af1f0SMike Waychison struct attribute attr; 69948af1f0SMike Waychison ssize_t (*show)(struct dmi_sysfs_entry *entry, 70948af1f0SMike Waychison const struct dmi_header *dh, 71948af1f0SMike Waychison char *buf); 72948af1f0SMike Waychison }; 73948af1f0SMike Waychison 74948af1f0SMike Waychison #define DMI_SYSFS_MAPPED_ATTR(_entry, _name) \ 75948af1f0SMike Waychison struct dmi_sysfs_mapped_attribute dmi_sysfs_attr_##_entry##_##_name = { \ 76948af1f0SMike Waychison .attr = {.name = __stringify(_name), .mode = 0400}, \ 77948af1f0SMike Waychison .show = dmi_sysfs_##_entry##_##_name, \ 78948af1f0SMike Waychison } 79948af1f0SMike Waychison 80948af1f0SMike Waychison /************************************************* 81948af1f0SMike Waychison * Generic DMI entry support. 82948af1f0SMike Waychison *************************************************/ 83925a1da7SMike Waychison static void dmi_entry_free(struct kobject *kobj) 84925a1da7SMike Waychison { 85925a1da7SMike Waychison kfree(kobj); 86925a1da7SMike Waychison } 87948af1f0SMike Waychison 88948af1f0SMike Waychison static struct dmi_sysfs_entry *to_entry(struct kobject *kobj) 89948af1f0SMike Waychison { 90948af1f0SMike Waychison return container_of(kobj, struct dmi_sysfs_entry, kobj); 91948af1f0SMike Waychison } 92948af1f0SMike Waychison 93948af1f0SMike Waychison static struct dmi_sysfs_attribute *to_attr(struct attribute *attr) 94948af1f0SMike Waychison { 95948af1f0SMike Waychison return container_of(attr, struct dmi_sysfs_attribute, attr); 96948af1f0SMike Waychison } 97948af1f0SMike Waychison 98948af1f0SMike Waychison static ssize_t dmi_sysfs_attr_show(struct kobject *kobj, 99948af1f0SMike Waychison struct attribute *_attr, char *buf) 100948af1f0SMike Waychison { 101948af1f0SMike Waychison struct dmi_sysfs_entry *entry = to_entry(kobj); 102948af1f0SMike Waychison struct dmi_sysfs_attribute *attr = to_attr(_attr); 103948af1f0SMike Waychison 104948af1f0SMike Waychison /* DMI stuff is only ever admin visible */ 105948af1f0SMike Waychison if (!capable(CAP_SYS_ADMIN)) 106948af1f0SMike Waychison return -EACCES; 107948af1f0SMike Waychison 108948af1f0SMike Waychison return attr->show(entry, buf); 109948af1f0SMike Waychison } 110948af1f0SMike Waychison 111948af1f0SMike Waychison static const struct sysfs_ops dmi_sysfs_attr_ops = { 112948af1f0SMike Waychison .show = dmi_sysfs_attr_show, 113948af1f0SMike Waychison }; 114948af1f0SMike Waychison 115948af1f0SMike Waychison typedef ssize_t (*dmi_callback)(struct dmi_sysfs_entry *, 116948af1f0SMike Waychison const struct dmi_header *dh, void *); 117948af1f0SMike Waychison 118948af1f0SMike Waychison struct find_dmi_data { 119948af1f0SMike Waychison struct dmi_sysfs_entry *entry; 120948af1f0SMike Waychison dmi_callback callback; 121948af1f0SMike Waychison void *private; 122948af1f0SMike Waychison int instance_countdown; 123948af1f0SMike Waychison ssize_t ret; 124948af1f0SMike Waychison }; 125948af1f0SMike Waychison 126948af1f0SMike Waychison static void find_dmi_entry_helper(const struct dmi_header *dh, 127948af1f0SMike Waychison void *_data) 128948af1f0SMike Waychison { 129948af1f0SMike Waychison struct find_dmi_data *data = _data; 130948af1f0SMike Waychison struct dmi_sysfs_entry *entry = data->entry; 131948af1f0SMike Waychison 132948af1f0SMike Waychison /* Is this the entry we want? */ 133948af1f0SMike Waychison if (dh->type != entry->dh.type) 134948af1f0SMike Waychison return; 135948af1f0SMike Waychison 136948af1f0SMike Waychison if (data->instance_countdown != 0) { 137948af1f0SMike Waychison /* try the next instance? */ 138948af1f0SMike Waychison data->instance_countdown--; 139948af1f0SMike Waychison return; 140948af1f0SMike Waychison } 141948af1f0SMike Waychison 142948af1f0SMike Waychison /* 143948af1f0SMike Waychison * Don't ever revisit the instance. Short circuit later 144948af1f0SMike Waychison * instances by letting the instance_countdown run negative 145948af1f0SMike Waychison */ 146948af1f0SMike Waychison data->instance_countdown--; 147948af1f0SMike Waychison 148948af1f0SMike Waychison /* Found the entry */ 149948af1f0SMike Waychison data->ret = data->callback(entry, dh, data->private); 150948af1f0SMike Waychison } 151948af1f0SMike Waychison 152948af1f0SMike Waychison /* State for passing the read parameters through dmi_find_entry() */ 153948af1f0SMike Waychison struct dmi_read_state { 154948af1f0SMike Waychison char *buf; 155948af1f0SMike Waychison loff_t pos; 156948af1f0SMike Waychison size_t count; 157948af1f0SMike Waychison }; 158948af1f0SMike Waychison 159948af1f0SMike Waychison static ssize_t find_dmi_entry(struct dmi_sysfs_entry *entry, 160948af1f0SMike Waychison dmi_callback callback, void *private) 161948af1f0SMike Waychison { 162948af1f0SMike Waychison struct find_dmi_data data = { 163948af1f0SMike Waychison .entry = entry, 164948af1f0SMike Waychison .callback = callback, 165948af1f0SMike Waychison .private = private, 166948af1f0SMike Waychison .instance_countdown = entry->instance, 167948af1f0SMike Waychison .ret = -EIO, /* To signal the entry disappeared */ 168948af1f0SMike Waychison }; 169948af1f0SMike Waychison int ret; 170948af1f0SMike Waychison 171948af1f0SMike Waychison ret = dmi_walk(find_dmi_entry_helper, &data); 172948af1f0SMike Waychison /* This shouldn't happen, but just in case. */ 173948af1f0SMike Waychison if (ret) 174948af1f0SMike Waychison return -EINVAL; 175948af1f0SMike Waychison return data.ret; 176948af1f0SMike Waychison } 177948af1f0SMike Waychison 178948af1f0SMike Waychison /* 179948af1f0SMike Waychison * Calculate and return the byte length of the dmi entry identified by 180948af1f0SMike Waychison * dh. This includes both the formatted portion as well as the 181948af1f0SMike Waychison * unformatted string space, including the two trailing nul characters. 182948af1f0SMike Waychison */ 183948af1f0SMike Waychison static size_t dmi_entry_length(const struct dmi_header *dh) 184948af1f0SMike Waychison { 185948af1f0SMike Waychison const char *p = (const char *)dh; 186948af1f0SMike Waychison 187948af1f0SMike Waychison p += dh->length; 188948af1f0SMike Waychison 189948af1f0SMike Waychison while (p[0] || p[1]) 190948af1f0SMike Waychison p++; 191948af1f0SMike Waychison 192948af1f0SMike Waychison return 2 + p - (const char *)dh; 193948af1f0SMike Waychison } 194948af1f0SMike Waychison 195948af1f0SMike Waychison /************************************************* 196925a1da7SMike Waychison * Support bits for specialized DMI entry support 197925a1da7SMike Waychison *************************************************/ 198925a1da7SMike Waychison struct dmi_entry_attr_show_data { 199925a1da7SMike Waychison struct attribute *attr; 200925a1da7SMike Waychison char *buf; 201925a1da7SMike Waychison }; 202925a1da7SMike Waychison 203925a1da7SMike Waychison static ssize_t dmi_entry_attr_show_helper(struct dmi_sysfs_entry *entry, 204925a1da7SMike Waychison const struct dmi_header *dh, 205925a1da7SMike Waychison void *_data) 206925a1da7SMike Waychison { 207925a1da7SMike Waychison struct dmi_entry_attr_show_data *data = _data; 208925a1da7SMike Waychison struct dmi_sysfs_mapped_attribute *attr; 209925a1da7SMike Waychison 210925a1da7SMike Waychison attr = container_of(data->attr, 211925a1da7SMike Waychison struct dmi_sysfs_mapped_attribute, attr); 212925a1da7SMike Waychison return attr->show(entry, dh, data->buf); 213925a1da7SMike Waychison } 214925a1da7SMike Waychison 215925a1da7SMike Waychison static ssize_t dmi_entry_attr_show(struct kobject *kobj, 216925a1da7SMike Waychison struct attribute *attr, 217925a1da7SMike Waychison char *buf) 218925a1da7SMike Waychison { 219925a1da7SMike Waychison struct dmi_entry_attr_show_data data = { 220925a1da7SMike Waychison .attr = attr, 221925a1da7SMike Waychison .buf = buf, 222925a1da7SMike Waychison }; 223925a1da7SMike Waychison /* Find the entry according to our parent and call the 224925a1da7SMike Waychison * normalized show method hanging off of the attribute */ 225925a1da7SMike Waychison return find_dmi_entry(to_entry(kobj->parent), 226925a1da7SMike Waychison dmi_entry_attr_show_helper, &data); 227925a1da7SMike Waychison } 228925a1da7SMike Waychison 229925a1da7SMike Waychison static const struct sysfs_ops dmi_sysfs_specialize_attr_ops = { 230925a1da7SMike Waychison .show = dmi_entry_attr_show, 231925a1da7SMike Waychison }; 232925a1da7SMike Waychison 233925a1da7SMike Waychison /************************************************* 234925a1da7SMike Waychison * Specialized DMI entry support. 235925a1da7SMike Waychison *************************************************/ 236925a1da7SMike Waychison 237925a1da7SMike Waychison /*** Type 15 - System Event Table ***/ 238925a1da7SMike Waychison 239925a1da7SMike Waychison #define DMI_SEL_ACCESS_METHOD_IO8 0x00 240925a1da7SMike Waychison #define DMI_SEL_ACCESS_METHOD_IO2x8 0x01 241925a1da7SMike Waychison #define DMI_SEL_ACCESS_METHOD_IO16 0x02 242925a1da7SMike Waychison #define DMI_SEL_ACCESS_METHOD_PHYS32 0x03 243925a1da7SMike Waychison #define DMI_SEL_ACCESS_METHOD_GPNV 0x04 244925a1da7SMike Waychison 245925a1da7SMike Waychison struct dmi_system_event_log { 246925a1da7SMike Waychison struct dmi_header header; 247925a1da7SMike Waychison u16 area_length; 248925a1da7SMike Waychison u16 header_start_offset; 249925a1da7SMike Waychison u16 data_start_offset; 250925a1da7SMike Waychison u8 access_method; 251925a1da7SMike Waychison u8 status; 252925a1da7SMike Waychison u32 change_token; 253925a1da7SMike Waychison union { 254925a1da7SMike Waychison struct { 255925a1da7SMike Waychison u16 index_addr; 256925a1da7SMike Waychison u16 data_addr; 257925a1da7SMike Waychison } io; 258925a1da7SMike Waychison u32 phys_addr32; 259925a1da7SMike Waychison u16 gpnv_handle; 260925a1da7SMike Waychison u32 access_method_address; 261925a1da7SMike Waychison }; 262925a1da7SMike Waychison u8 header_format; 263925a1da7SMike Waychison u8 type_descriptors_supported_count; 264925a1da7SMike Waychison u8 per_log_type_descriptor_length; 265925a1da7SMike Waychison u8 supported_log_type_descriptos[0]; 266925a1da7SMike Waychison } __packed; 267925a1da7SMike Waychison 268925a1da7SMike Waychison #define DMI_SYSFS_SEL_FIELD(_field) \ 269925a1da7SMike Waychison static ssize_t dmi_sysfs_sel_##_field(struct dmi_sysfs_entry *entry, \ 270925a1da7SMike Waychison const struct dmi_header *dh, \ 271925a1da7SMike Waychison char *buf) \ 272925a1da7SMike Waychison { \ 27366245ad0SMike Waychison struct dmi_system_event_log sel; \ 27466245ad0SMike Waychison if (sizeof(sel) > dmi_entry_length(dh)) \ 275925a1da7SMike Waychison return -EIO; \ 27666245ad0SMike Waychison memcpy(&sel, dh, sizeof(sel)); \ 27766245ad0SMike Waychison return sprintf(buf, "%u\n", sel._field); \ 278925a1da7SMike Waychison } \ 279925a1da7SMike Waychison static DMI_SYSFS_MAPPED_ATTR(sel, _field) 280925a1da7SMike Waychison 281925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(area_length); 282925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(header_start_offset); 283925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(data_start_offset); 284925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(access_method); 285925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(status); 286925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(change_token); 287925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(access_method_address); 288925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(header_format); 289925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(type_descriptors_supported_count); 290925a1da7SMike Waychison DMI_SYSFS_SEL_FIELD(per_log_type_descriptor_length); 291925a1da7SMike Waychison 292925a1da7SMike Waychison static struct attribute *dmi_sysfs_sel_attrs[] = { 293925a1da7SMike Waychison &dmi_sysfs_attr_sel_area_length.attr, 294925a1da7SMike Waychison &dmi_sysfs_attr_sel_header_start_offset.attr, 295925a1da7SMike Waychison &dmi_sysfs_attr_sel_data_start_offset.attr, 296925a1da7SMike Waychison &dmi_sysfs_attr_sel_access_method.attr, 297925a1da7SMike Waychison &dmi_sysfs_attr_sel_status.attr, 298925a1da7SMike Waychison &dmi_sysfs_attr_sel_change_token.attr, 299925a1da7SMike Waychison &dmi_sysfs_attr_sel_access_method_address.attr, 300925a1da7SMike Waychison &dmi_sysfs_attr_sel_header_format.attr, 301925a1da7SMike Waychison &dmi_sysfs_attr_sel_type_descriptors_supported_count.attr, 302925a1da7SMike Waychison &dmi_sysfs_attr_sel_per_log_type_descriptor_length.attr, 303925a1da7SMike Waychison NULL, 304925a1da7SMike Waychison }; 305925a1da7SMike Waychison 306925a1da7SMike Waychison 307925a1da7SMike Waychison static struct kobj_type dmi_system_event_log_ktype = { 308925a1da7SMike Waychison .release = dmi_entry_free, 309925a1da7SMike Waychison .sysfs_ops = &dmi_sysfs_specialize_attr_ops, 310925a1da7SMike Waychison .default_attrs = dmi_sysfs_sel_attrs, 311925a1da7SMike Waychison }; 312925a1da7SMike Waychison 313a3857a5cSMike Waychison typedef u8 (*sel_io_reader)(const struct dmi_system_event_log *sel, 314a3857a5cSMike Waychison loff_t offset); 315a3857a5cSMike Waychison 316a3857a5cSMike Waychison static DEFINE_MUTEX(io_port_lock); 317a3857a5cSMike Waychison 318a3857a5cSMike Waychison static u8 read_sel_8bit_indexed_io(const struct dmi_system_event_log *sel, 319a3857a5cSMike Waychison loff_t offset) 320a3857a5cSMike Waychison { 321a3857a5cSMike Waychison u8 ret; 322a3857a5cSMike Waychison 323a3857a5cSMike Waychison mutex_lock(&io_port_lock); 324a3857a5cSMike Waychison outb((u8)offset, sel->io.index_addr); 325a3857a5cSMike Waychison ret = inb(sel->io.data_addr); 326a3857a5cSMike Waychison mutex_unlock(&io_port_lock); 327a3857a5cSMike Waychison return ret; 328a3857a5cSMike Waychison } 329a3857a5cSMike Waychison 330a3857a5cSMike Waychison static u8 read_sel_2x8bit_indexed_io(const struct dmi_system_event_log *sel, 331a3857a5cSMike Waychison loff_t offset) 332a3857a5cSMike Waychison { 333a3857a5cSMike Waychison u8 ret; 334a3857a5cSMike Waychison 335a3857a5cSMike Waychison mutex_lock(&io_port_lock); 336a3857a5cSMike Waychison outb((u8)offset, sel->io.index_addr); 337a3857a5cSMike Waychison outb((u8)(offset >> 8), sel->io.index_addr + 1); 338a3857a5cSMike Waychison ret = inb(sel->io.data_addr); 339a3857a5cSMike Waychison mutex_unlock(&io_port_lock); 340a3857a5cSMike Waychison return ret; 341a3857a5cSMike Waychison } 342a3857a5cSMike Waychison 343a3857a5cSMike Waychison static u8 read_sel_16bit_indexed_io(const struct dmi_system_event_log *sel, 344a3857a5cSMike Waychison loff_t offset) 345a3857a5cSMike Waychison { 346a3857a5cSMike Waychison u8 ret; 347a3857a5cSMike Waychison 348a3857a5cSMike Waychison mutex_lock(&io_port_lock); 349a3857a5cSMike Waychison outw((u16)offset, sel->io.index_addr); 350a3857a5cSMike Waychison ret = inb(sel->io.data_addr); 351a3857a5cSMike Waychison mutex_unlock(&io_port_lock); 352a3857a5cSMike Waychison return ret; 353a3857a5cSMike Waychison } 354a3857a5cSMike Waychison 355a3857a5cSMike Waychison static sel_io_reader sel_io_readers[] = { 356a3857a5cSMike Waychison [DMI_SEL_ACCESS_METHOD_IO8] = read_sel_8bit_indexed_io, 357a3857a5cSMike Waychison [DMI_SEL_ACCESS_METHOD_IO2x8] = read_sel_2x8bit_indexed_io, 358a3857a5cSMike Waychison [DMI_SEL_ACCESS_METHOD_IO16] = read_sel_16bit_indexed_io, 359a3857a5cSMike Waychison }; 360a3857a5cSMike Waychison 361a3857a5cSMike Waychison static ssize_t dmi_sel_raw_read_io(struct dmi_sysfs_entry *entry, 362a3857a5cSMike Waychison const struct dmi_system_event_log *sel, 363a3857a5cSMike Waychison char *buf, loff_t pos, size_t count) 364a3857a5cSMike Waychison { 365a3857a5cSMike Waychison ssize_t wrote = 0; 366a3857a5cSMike Waychison 367a3857a5cSMike Waychison sel_io_reader io_reader = sel_io_readers[sel->access_method]; 368a3857a5cSMike Waychison 369a3857a5cSMike Waychison while (count && pos < sel->area_length) { 370a3857a5cSMike Waychison count--; 371a3857a5cSMike Waychison *(buf++) = io_reader(sel, pos++); 372a3857a5cSMike Waychison wrote++; 373a3857a5cSMike Waychison } 374a3857a5cSMike Waychison 375a3857a5cSMike Waychison return wrote; 376a3857a5cSMike Waychison } 377a3857a5cSMike Waychison 378a3857a5cSMike Waychison static ssize_t dmi_sel_raw_read_phys32(struct dmi_sysfs_entry *entry, 379a3857a5cSMike Waychison const struct dmi_system_event_log *sel, 380a3857a5cSMike Waychison char *buf, loff_t pos, size_t count) 381a3857a5cSMike Waychison { 382a3857a5cSMike Waychison u8 __iomem *mapped; 383a3857a5cSMike Waychison ssize_t wrote = 0; 384a3857a5cSMike Waychison 385f7750a79STom Lendacky mapped = dmi_remap(sel->access_method_address, sel->area_length); 386a3857a5cSMike Waychison if (!mapped) 387a3857a5cSMike Waychison return -EIO; 388a3857a5cSMike Waychison 389a3857a5cSMike Waychison while (count && pos < sel->area_length) { 390a3857a5cSMike Waychison count--; 391a3857a5cSMike Waychison *(buf++) = readb(mapped + pos++); 392a3857a5cSMike Waychison wrote++; 393a3857a5cSMike Waychison } 394a3857a5cSMike Waychison 395f7750a79STom Lendacky dmi_unmap(mapped); 396a3857a5cSMike Waychison return wrote; 397a3857a5cSMike Waychison } 398a3857a5cSMike Waychison 399a3857a5cSMike Waychison static ssize_t dmi_sel_raw_read_helper(struct dmi_sysfs_entry *entry, 400a3857a5cSMike Waychison const struct dmi_header *dh, 401a3857a5cSMike Waychison void *_state) 402a3857a5cSMike Waychison { 403a3857a5cSMike Waychison struct dmi_read_state *state = _state; 40466245ad0SMike Waychison struct dmi_system_event_log sel; 405a3857a5cSMike Waychison 40666245ad0SMike Waychison if (sizeof(sel) > dmi_entry_length(dh)) 407a3857a5cSMike Waychison return -EIO; 408a3857a5cSMike Waychison 40966245ad0SMike Waychison memcpy(&sel, dh, sizeof(sel)); 41066245ad0SMike Waychison 41166245ad0SMike Waychison switch (sel.access_method) { 412a3857a5cSMike Waychison case DMI_SEL_ACCESS_METHOD_IO8: 413a3857a5cSMike Waychison case DMI_SEL_ACCESS_METHOD_IO2x8: 414a3857a5cSMike Waychison case DMI_SEL_ACCESS_METHOD_IO16: 41566245ad0SMike Waychison return dmi_sel_raw_read_io(entry, &sel, state->buf, 416a3857a5cSMike Waychison state->pos, state->count); 417a3857a5cSMike Waychison case DMI_SEL_ACCESS_METHOD_PHYS32: 41866245ad0SMike Waychison return dmi_sel_raw_read_phys32(entry, &sel, state->buf, 419a3857a5cSMike Waychison state->pos, state->count); 420a3857a5cSMike Waychison case DMI_SEL_ACCESS_METHOD_GPNV: 421a3857a5cSMike Waychison pr_info("dmi-sysfs: GPNV support missing.\n"); 422a3857a5cSMike Waychison return -EIO; 423a3857a5cSMike Waychison default: 424a3857a5cSMike Waychison pr_info("dmi-sysfs: Unknown access method %02x\n", 42566245ad0SMike Waychison sel.access_method); 426a3857a5cSMike Waychison return -EIO; 427a3857a5cSMike Waychison } 428a3857a5cSMike Waychison } 429a3857a5cSMike Waychison 430a3857a5cSMike Waychison static ssize_t dmi_sel_raw_read(struct file *filp, struct kobject *kobj, 431a3857a5cSMike Waychison struct bin_attribute *bin_attr, 432a3857a5cSMike Waychison char *buf, loff_t pos, size_t count) 433a3857a5cSMike Waychison { 434a3857a5cSMike Waychison struct dmi_sysfs_entry *entry = to_entry(kobj->parent); 435a3857a5cSMike Waychison struct dmi_read_state state = { 436a3857a5cSMike Waychison .buf = buf, 437a3857a5cSMike Waychison .pos = pos, 438a3857a5cSMike Waychison .count = count, 439a3857a5cSMike Waychison }; 440a3857a5cSMike Waychison 441a3857a5cSMike Waychison return find_dmi_entry(entry, dmi_sel_raw_read_helper, &state); 442a3857a5cSMike Waychison } 443a3857a5cSMike Waychison 444a3857a5cSMike Waychison static struct bin_attribute dmi_sel_raw_attr = { 445a3857a5cSMike Waychison .attr = {.name = "raw_event_log", .mode = 0400}, 446a3857a5cSMike Waychison .read = dmi_sel_raw_read, 447a3857a5cSMike Waychison }; 448a3857a5cSMike Waychison 449925a1da7SMike Waychison static int dmi_system_event_log(struct dmi_sysfs_entry *entry) 450925a1da7SMike Waychison { 451925a1da7SMike Waychison int ret; 452925a1da7SMike Waychison 453925a1da7SMike Waychison entry->child = kzalloc(sizeof(*entry->child), GFP_KERNEL); 454925a1da7SMike Waychison if (!entry->child) 455925a1da7SMike Waychison return -ENOMEM; 456925a1da7SMike Waychison ret = kobject_init_and_add(entry->child, 457925a1da7SMike Waychison &dmi_system_event_log_ktype, 458925a1da7SMike Waychison &entry->kobj, 459925a1da7SMike Waychison "system_event_log"); 460925a1da7SMike Waychison if (ret) 461925a1da7SMike Waychison goto out_free; 462a3857a5cSMike Waychison 463a3857a5cSMike Waychison ret = sysfs_create_bin_file(entry->child, &dmi_sel_raw_attr); 464a3857a5cSMike Waychison if (ret) 465a3857a5cSMike Waychison goto out_del; 466a3857a5cSMike Waychison 467a3857a5cSMike Waychison return 0; 468a3857a5cSMike Waychison 469a3857a5cSMike Waychison out_del: 470a3857a5cSMike Waychison kobject_del(entry->child); 471925a1da7SMike Waychison out_free: 472925a1da7SMike Waychison kfree(entry->child); 473925a1da7SMike Waychison return ret; 474925a1da7SMike Waychison } 475925a1da7SMike Waychison 476925a1da7SMike Waychison /************************************************* 477948af1f0SMike Waychison * Generic DMI entry support. 478948af1f0SMike Waychison *************************************************/ 479948af1f0SMike Waychison 480948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_length(struct dmi_sysfs_entry *entry, char *buf) 481948af1f0SMike Waychison { 482948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->dh.length); 483948af1f0SMike Waychison } 484948af1f0SMike Waychison 485948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_handle(struct dmi_sysfs_entry *entry, char *buf) 486948af1f0SMike Waychison { 487948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->dh.handle); 488948af1f0SMike Waychison } 489948af1f0SMike Waychison 490948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_type(struct dmi_sysfs_entry *entry, char *buf) 491948af1f0SMike Waychison { 492948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->dh.type); 493948af1f0SMike Waychison } 494948af1f0SMike Waychison 495948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_instance(struct dmi_sysfs_entry *entry, 496948af1f0SMike Waychison char *buf) 497948af1f0SMike Waychison { 498948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->instance); 499948af1f0SMike Waychison } 500948af1f0SMike Waychison 501948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_position(struct dmi_sysfs_entry *entry, 502948af1f0SMike Waychison char *buf) 503948af1f0SMike Waychison { 504948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->position); 505948af1f0SMike Waychison } 506948af1f0SMike Waychison 507948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, length); 508948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, handle); 509948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, type); 510948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, instance); 511948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, position); 512948af1f0SMike Waychison 513948af1f0SMike Waychison static struct attribute *dmi_sysfs_entry_attrs[] = { 514948af1f0SMike Waychison &dmi_sysfs_attr_entry_length.attr, 515948af1f0SMike Waychison &dmi_sysfs_attr_entry_handle.attr, 516948af1f0SMike Waychison &dmi_sysfs_attr_entry_type.attr, 517948af1f0SMike Waychison &dmi_sysfs_attr_entry_instance.attr, 518948af1f0SMike Waychison &dmi_sysfs_attr_entry_position.attr, 519948af1f0SMike Waychison NULL, 520948af1f0SMike Waychison }; 521948af1f0SMike Waychison 522948af1f0SMike Waychison static ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry, 523948af1f0SMike Waychison const struct dmi_header *dh, 524948af1f0SMike Waychison void *_state) 525948af1f0SMike Waychison { 526948af1f0SMike Waychison struct dmi_read_state *state = _state; 527948af1f0SMike Waychison size_t entry_length; 528948af1f0SMike Waychison 529948af1f0SMike Waychison entry_length = dmi_entry_length(dh); 530948af1f0SMike Waychison 531948af1f0SMike Waychison return memory_read_from_buffer(state->buf, state->count, 532948af1f0SMike Waychison &state->pos, dh, entry_length); 533948af1f0SMike Waychison } 534948af1f0SMike Waychison 535948af1f0SMike Waychison static ssize_t dmi_entry_raw_read(struct file *filp, 536948af1f0SMike Waychison struct kobject *kobj, 537948af1f0SMike Waychison struct bin_attribute *bin_attr, 538948af1f0SMike Waychison char *buf, loff_t pos, size_t count) 539948af1f0SMike Waychison { 540948af1f0SMike Waychison struct dmi_sysfs_entry *entry = to_entry(kobj); 541948af1f0SMike Waychison struct dmi_read_state state = { 542948af1f0SMike Waychison .buf = buf, 543948af1f0SMike Waychison .pos = pos, 544948af1f0SMike Waychison .count = count, 545948af1f0SMike Waychison }; 546948af1f0SMike Waychison 547948af1f0SMike Waychison return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state); 548948af1f0SMike Waychison } 549948af1f0SMike Waychison 550948af1f0SMike Waychison static const struct bin_attribute dmi_entry_raw_attr = { 551948af1f0SMike Waychison .attr = {.name = "raw", .mode = 0400}, 552948af1f0SMike Waychison .read = dmi_entry_raw_read, 553948af1f0SMike Waychison }; 554948af1f0SMike Waychison 555948af1f0SMike Waychison static void dmi_sysfs_entry_release(struct kobject *kobj) 556948af1f0SMike Waychison { 557948af1f0SMike Waychison struct dmi_sysfs_entry *entry = to_entry(kobj); 558a61aca28SBjorn Helgaas 559948af1f0SMike Waychison spin_lock(&entry_list_lock); 560948af1f0SMike Waychison list_del(&entry->list); 561948af1f0SMike Waychison spin_unlock(&entry_list_lock); 562948af1f0SMike Waychison kfree(entry); 563948af1f0SMike Waychison } 564948af1f0SMike Waychison 565948af1f0SMike Waychison static struct kobj_type dmi_sysfs_entry_ktype = { 566948af1f0SMike Waychison .release = dmi_sysfs_entry_release, 567948af1f0SMike Waychison .sysfs_ops = &dmi_sysfs_attr_ops, 568948af1f0SMike Waychison .default_attrs = dmi_sysfs_entry_attrs, 569948af1f0SMike Waychison }; 570948af1f0SMike Waychison 571948af1f0SMike Waychison static struct kset *dmi_kset; 572948af1f0SMike Waychison 573948af1f0SMike Waychison /* Global count of all instances seen. Only for setup */ 574948af1f0SMike Waychison static int __initdata instance_counts[MAX_ENTRY_TYPE + 1]; 575948af1f0SMike Waychison 576948af1f0SMike Waychison /* Global positional count of all entries seen. Only for setup */ 577948af1f0SMike Waychison static int __initdata position_count; 578948af1f0SMike Waychison 579948af1f0SMike Waychison static void __init dmi_sysfs_register_handle(const struct dmi_header *dh, 580948af1f0SMike Waychison void *_ret) 581948af1f0SMike Waychison { 582948af1f0SMike Waychison struct dmi_sysfs_entry *entry; 583948af1f0SMike Waychison int *ret = _ret; 584948af1f0SMike Waychison 585948af1f0SMike Waychison /* If a previous entry saw an error, short circuit */ 586948af1f0SMike Waychison if (*ret) 587948af1f0SMike Waychison return; 588948af1f0SMike Waychison 589948af1f0SMike Waychison /* Allocate and register a new entry into the entries set */ 590948af1f0SMike Waychison entry = kzalloc(sizeof(*entry), GFP_KERNEL); 591948af1f0SMike Waychison if (!entry) { 592948af1f0SMike Waychison *ret = -ENOMEM; 593948af1f0SMike Waychison return; 594948af1f0SMike Waychison } 595948af1f0SMike Waychison 596948af1f0SMike Waychison /* Set the key */ 59766245ad0SMike Waychison memcpy(&entry->dh, dh, sizeof(*dh)); 598948af1f0SMike Waychison entry->instance = instance_counts[dh->type]++; 599948af1f0SMike Waychison entry->position = position_count++; 600948af1f0SMike Waychison 601948af1f0SMike Waychison entry->kobj.kset = dmi_kset; 602948af1f0SMike Waychison *ret = kobject_init_and_add(&entry->kobj, &dmi_sysfs_entry_ktype, NULL, 603948af1f0SMike Waychison "%d-%d", dh->type, entry->instance); 604948af1f0SMike Waychison 605948af1f0SMike Waychison if (*ret) { 606948af1f0SMike Waychison kfree(entry); 607948af1f0SMike Waychison return; 608948af1f0SMike Waychison } 609948af1f0SMike Waychison 610948af1f0SMike Waychison /* Thread on the global list for cleanup */ 611948af1f0SMike Waychison spin_lock(&entry_list_lock); 612948af1f0SMike Waychison list_add_tail(&entry->list, &entry_list); 613948af1f0SMike Waychison spin_unlock(&entry_list_lock); 614948af1f0SMike Waychison 615925a1da7SMike Waychison /* Handle specializations by type */ 616925a1da7SMike Waychison switch (dh->type) { 617925a1da7SMike Waychison case DMI_ENTRY_SYSTEM_EVENT_LOG: 618925a1da7SMike Waychison *ret = dmi_system_event_log(entry); 619925a1da7SMike Waychison break; 620925a1da7SMike Waychison default: 621925a1da7SMike Waychison /* No specialization */ 622925a1da7SMike Waychison break; 623925a1da7SMike Waychison } 624925a1da7SMike Waychison if (*ret) 625925a1da7SMike Waychison goto out_err; 626925a1da7SMike Waychison 627948af1f0SMike Waychison /* Create the raw binary file to access the entry */ 628948af1f0SMike Waychison *ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr); 629948af1f0SMike Waychison if (*ret) 630948af1f0SMike Waychison goto out_err; 631948af1f0SMike Waychison 632948af1f0SMike Waychison return; 633948af1f0SMike Waychison out_err: 634925a1da7SMike Waychison kobject_put(entry->child); 635948af1f0SMike Waychison kobject_put(&entry->kobj); 636948af1f0SMike Waychison return; 637948af1f0SMike Waychison } 638948af1f0SMike Waychison 639948af1f0SMike Waychison static void cleanup_entry_list(void) 640948af1f0SMike Waychison { 641948af1f0SMike Waychison struct dmi_sysfs_entry *entry, *next; 642948af1f0SMike Waychison 643948af1f0SMike Waychison /* No locks, we are on our way out */ 644948af1f0SMike Waychison list_for_each_entry_safe(entry, next, &entry_list, list) { 645925a1da7SMike Waychison kobject_put(entry->child); 646948af1f0SMike Waychison kobject_put(&entry->kobj); 647948af1f0SMike Waychison } 648948af1f0SMike Waychison } 649948af1f0SMike Waychison 650948af1f0SMike Waychison static int __init dmi_sysfs_init(void) 651948af1f0SMike Waychison { 652d7f96f97SIvan Khoronzhuk int error; 653948af1f0SMike Waychison int val; 654948af1f0SMike Waychison 655d7f96f97SIvan Khoronzhuk if (!dmi_kobj) { 656a81114d0SArd Biesheuvel pr_debug("dmi-sysfs: dmi entry is absent.\n"); 657d7f96f97SIvan Khoronzhuk error = -ENODATA; 658948af1f0SMike Waychison goto err; 659d7f96f97SIvan Khoronzhuk } 660948af1f0SMike Waychison 661948af1f0SMike Waychison dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); 662d7f96f97SIvan Khoronzhuk if (!dmi_kset) { 663d7f96f97SIvan Khoronzhuk error = -ENOMEM; 664948af1f0SMike Waychison goto err; 665d7f96f97SIvan Khoronzhuk } 666948af1f0SMike Waychison 667948af1f0SMike Waychison val = 0; 668948af1f0SMike Waychison error = dmi_walk(dmi_sysfs_register_handle, &val); 669948af1f0SMike Waychison if (error) 670948af1f0SMike Waychison goto err; 671948af1f0SMike Waychison if (val) { 672948af1f0SMike Waychison error = val; 673948af1f0SMike Waychison goto err; 674948af1f0SMike Waychison } 675948af1f0SMike Waychison 676948af1f0SMike Waychison pr_debug("dmi-sysfs: loaded.\n"); 677948af1f0SMike Waychison 678948af1f0SMike Waychison return 0; 679948af1f0SMike Waychison err: 680948af1f0SMike Waychison cleanup_entry_list(); 681948af1f0SMike Waychison kset_unregister(dmi_kset); 682948af1f0SMike Waychison return error; 683948af1f0SMike Waychison } 684948af1f0SMike Waychison 685948af1f0SMike Waychison /* clean up everything. */ 686948af1f0SMike Waychison static void __exit dmi_sysfs_exit(void) 687948af1f0SMike Waychison { 688948af1f0SMike Waychison pr_debug("dmi-sysfs: unloading.\n"); 689948af1f0SMike Waychison cleanup_entry_list(); 690948af1f0SMike Waychison kset_unregister(dmi_kset); 691948af1f0SMike Waychison } 692948af1f0SMike Waychison 693948af1f0SMike Waychison module_init(dmi_sysfs_init); 694948af1f0SMike Waychison module_exit(dmi_sysfs_exit); 695948af1f0SMike Waychison 696948af1f0SMike Waychison MODULE_AUTHOR("Mike Waychison <mikew@google.com>"); 697948af1f0SMike Waychison MODULE_DESCRIPTION("DMI sysfs support"); 698948af1f0SMike Waychison MODULE_LICENSE("GPL"); 699