18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * dmi-sysfs.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This module exports the DMI tables read-only to userspace through the 68c2ecf20Sopenharmony_ci * sysfs file system. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Data is currently found below 98c2ecf20Sopenharmony_ci * /sys/firmware/dmi/... 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * DMI attributes are presented in attribute files with names 128c2ecf20Sopenharmony_ci * formatted using %d-%d, so that the first integer indicates the 138c2ecf20Sopenharmony_ci * structure type (0-255), and the second field is the instance of that 148c2ecf20Sopenharmony_ci * entry. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Copyright 2011 Google, Inc. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/types.h> 238c2ecf20Sopenharmony_ci#include <linux/kobject.h> 248c2ecf20Sopenharmony_ci#include <linux/dmi.h> 258c2ecf20Sopenharmony_ci#include <linux/capability.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <linux/list.h> 288c2ecf20Sopenharmony_ci#include <linux/io.h> 298c2ecf20Sopenharmony_ci#include <asm/dmi.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define MAX_ENTRY_TYPE 255 /* Most of these aren't used, but we consider 328c2ecf20Sopenharmony_ci the top entry type is only 8 bits */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct dmi_sysfs_entry { 358c2ecf20Sopenharmony_ci struct dmi_header dh; 368c2ecf20Sopenharmony_ci struct kobject kobj; 378c2ecf20Sopenharmony_ci int instance; 388c2ecf20Sopenharmony_ci int position; 398c2ecf20Sopenharmony_ci struct list_head list; 408c2ecf20Sopenharmony_ci struct kobject *child; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * Global list of dmi_sysfs_entry. Even though this should only be 458c2ecf20Sopenharmony_ci * manipulated at setup and teardown, the lazy nature of the kobject 468c2ecf20Sopenharmony_ci * system means we get lazy removes. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistatic LIST_HEAD(entry_list); 498c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(entry_list_lock); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* dmi_sysfs_attribute - Top level attribute. used by all entries. */ 528c2ecf20Sopenharmony_cistruct dmi_sysfs_attribute { 538c2ecf20Sopenharmony_ci struct attribute attr; 548c2ecf20Sopenharmony_ci ssize_t (*show)(struct dmi_sysfs_entry *entry, char *buf); 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define DMI_SYSFS_ATTR(_entry, _name) \ 588c2ecf20Sopenharmony_cistruct dmi_sysfs_attribute dmi_sysfs_attr_##_entry##_##_name = { \ 598c2ecf20Sopenharmony_ci .attr = {.name = __stringify(_name), .mode = 0400}, \ 608c2ecf20Sopenharmony_ci .show = dmi_sysfs_##_entry##_##_name, \ 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * dmi_sysfs_mapped_attribute - Attribute where we require the entry be 658c2ecf20Sopenharmony_ci * mapped in. Use in conjunction with dmi_sysfs_specialize_attr_ops. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistruct dmi_sysfs_mapped_attribute { 688c2ecf20Sopenharmony_ci struct attribute attr; 698c2ecf20Sopenharmony_ci ssize_t (*show)(struct dmi_sysfs_entry *entry, 708c2ecf20Sopenharmony_ci const struct dmi_header *dh, 718c2ecf20Sopenharmony_ci char *buf); 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define DMI_SYSFS_MAPPED_ATTR(_entry, _name) \ 758c2ecf20Sopenharmony_cistruct dmi_sysfs_mapped_attribute dmi_sysfs_attr_##_entry##_##_name = { \ 768c2ecf20Sopenharmony_ci .attr = {.name = __stringify(_name), .mode = 0400}, \ 778c2ecf20Sopenharmony_ci .show = dmi_sysfs_##_entry##_##_name, \ 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/************************************************* 818c2ecf20Sopenharmony_ci * Generic DMI entry support. 828c2ecf20Sopenharmony_ci *************************************************/ 838c2ecf20Sopenharmony_cistatic void dmi_entry_free(struct kobject *kobj) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci kfree(kobj); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic struct dmi_sysfs_entry *to_entry(struct kobject *kobj) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci return container_of(kobj, struct dmi_sysfs_entry, kobj); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic struct dmi_sysfs_attribute *to_attr(struct attribute *attr) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci return container_of(attr, struct dmi_sysfs_attribute, attr); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic ssize_t dmi_sysfs_attr_show(struct kobject *kobj, 998c2ecf20Sopenharmony_ci struct attribute *_attr, char *buf) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct dmi_sysfs_entry *entry = to_entry(kobj); 1028c2ecf20Sopenharmony_ci struct dmi_sysfs_attribute *attr = to_attr(_attr); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* DMI stuff is only ever admin visible */ 1058c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 1068c2ecf20Sopenharmony_ci return -EACCES; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return attr->show(entry, buf); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const struct sysfs_ops dmi_sysfs_attr_ops = { 1128c2ecf20Sopenharmony_ci .show = dmi_sysfs_attr_show, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_citypedef ssize_t (*dmi_callback)(struct dmi_sysfs_entry *, 1168c2ecf20Sopenharmony_ci const struct dmi_header *dh, void *); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistruct find_dmi_data { 1198c2ecf20Sopenharmony_ci struct dmi_sysfs_entry *entry; 1208c2ecf20Sopenharmony_ci dmi_callback callback; 1218c2ecf20Sopenharmony_ci void *private; 1228c2ecf20Sopenharmony_ci int instance_countdown; 1238c2ecf20Sopenharmony_ci ssize_t ret; 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void find_dmi_entry_helper(const struct dmi_header *dh, 1278c2ecf20Sopenharmony_ci void *_data) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct find_dmi_data *data = _data; 1308c2ecf20Sopenharmony_ci struct dmi_sysfs_entry *entry = data->entry; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Is this the entry we want? */ 1338c2ecf20Sopenharmony_ci if (dh->type != entry->dh.type) 1348c2ecf20Sopenharmony_ci return; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (data->instance_countdown != 0) { 1378c2ecf20Sopenharmony_ci /* try the next instance? */ 1388c2ecf20Sopenharmony_ci data->instance_countdown--; 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * Don't ever revisit the instance. Short circuit later 1448c2ecf20Sopenharmony_ci * instances by letting the instance_countdown run negative 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci data->instance_countdown--; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* Found the entry */ 1498c2ecf20Sopenharmony_ci data->ret = data->callback(entry, dh, data->private); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* State for passing the read parameters through dmi_find_entry() */ 1538c2ecf20Sopenharmony_cistruct dmi_read_state { 1548c2ecf20Sopenharmony_ci char *buf; 1558c2ecf20Sopenharmony_ci loff_t pos; 1568c2ecf20Sopenharmony_ci size_t count; 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic ssize_t find_dmi_entry(struct dmi_sysfs_entry *entry, 1608c2ecf20Sopenharmony_ci dmi_callback callback, void *private) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct find_dmi_data data = { 1638c2ecf20Sopenharmony_ci .entry = entry, 1648c2ecf20Sopenharmony_ci .callback = callback, 1658c2ecf20Sopenharmony_ci .private = private, 1668c2ecf20Sopenharmony_ci .instance_countdown = entry->instance, 1678c2ecf20Sopenharmony_ci .ret = -EIO, /* To signal the entry disappeared */ 1688c2ecf20Sopenharmony_ci }; 1698c2ecf20Sopenharmony_ci int ret; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ret = dmi_walk(find_dmi_entry_helper, &data); 1728c2ecf20Sopenharmony_ci /* This shouldn't happen, but just in case. */ 1738c2ecf20Sopenharmony_ci if (ret) 1748c2ecf20Sopenharmony_ci return -EINVAL; 1758c2ecf20Sopenharmony_ci return data.ret; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* 1798c2ecf20Sopenharmony_ci * Calculate and return the byte length of the dmi entry identified by 1808c2ecf20Sopenharmony_ci * dh. This includes both the formatted portion as well as the 1818c2ecf20Sopenharmony_ci * unformatted string space, including the two trailing nul characters. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_cistatic size_t dmi_entry_length(const struct dmi_header *dh) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci const char *p = (const char *)dh; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci p += dh->length; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci while (p[0] || p[1]) 1908c2ecf20Sopenharmony_ci p++; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 2 + p - (const char *)dh; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/************************************************* 1968c2ecf20Sopenharmony_ci * Support bits for specialized DMI entry support 1978c2ecf20Sopenharmony_ci *************************************************/ 1988c2ecf20Sopenharmony_cistruct dmi_entry_attr_show_data { 1998c2ecf20Sopenharmony_ci struct attribute *attr; 2008c2ecf20Sopenharmony_ci char *buf; 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic ssize_t dmi_entry_attr_show_helper(struct dmi_sysfs_entry *entry, 2048c2ecf20Sopenharmony_ci const struct dmi_header *dh, 2058c2ecf20Sopenharmony_ci void *_data) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct dmi_entry_attr_show_data *data = _data; 2088c2ecf20Sopenharmony_ci struct dmi_sysfs_mapped_attribute *attr; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci attr = container_of(data->attr, 2118c2ecf20Sopenharmony_ci struct dmi_sysfs_mapped_attribute, attr); 2128c2ecf20Sopenharmony_ci return attr->show(entry, dh, data->buf); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic ssize_t dmi_entry_attr_show(struct kobject *kobj, 2168c2ecf20Sopenharmony_ci struct attribute *attr, 2178c2ecf20Sopenharmony_ci char *buf) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct dmi_entry_attr_show_data data = { 2208c2ecf20Sopenharmony_ci .attr = attr, 2218c2ecf20Sopenharmony_ci .buf = buf, 2228c2ecf20Sopenharmony_ci }; 2238c2ecf20Sopenharmony_ci /* Find the entry according to our parent and call the 2248c2ecf20Sopenharmony_ci * normalized show method hanging off of the attribute */ 2258c2ecf20Sopenharmony_ci return find_dmi_entry(to_entry(kobj->parent), 2268c2ecf20Sopenharmony_ci dmi_entry_attr_show_helper, &data); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic const struct sysfs_ops dmi_sysfs_specialize_attr_ops = { 2308c2ecf20Sopenharmony_ci .show = dmi_entry_attr_show, 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/************************************************* 2348c2ecf20Sopenharmony_ci * Specialized DMI entry support. 2358c2ecf20Sopenharmony_ci *************************************************/ 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/*** Type 15 - System Event Table ***/ 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci#define DMI_SEL_ACCESS_METHOD_IO8 0x00 2408c2ecf20Sopenharmony_ci#define DMI_SEL_ACCESS_METHOD_IO2x8 0x01 2418c2ecf20Sopenharmony_ci#define DMI_SEL_ACCESS_METHOD_IO16 0x02 2428c2ecf20Sopenharmony_ci#define DMI_SEL_ACCESS_METHOD_PHYS32 0x03 2438c2ecf20Sopenharmony_ci#define DMI_SEL_ACCESS_METHOD_GPNV 0x04 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistruct dmi_system_event_log { 2468c2ecf20Sopenharmony_ci struct dmi_header header; 2478c2ecf20Sopenharmony_ci u16 area_length; 2488c2ecf20Sopenharmony_ci u16 header_start_offset; 2498c2ecf20Sopenharmony_ci u16 data_start_offset; 2508c2ecf20Sopenharmony_ci u8 access_method; 2518c2ecf20Sopenharmony_ci u8 status; 2528c2ecf20Sopenharmony_ci u32 change_token; 2538c2ecf20Sopenharmony_ci union { 2548c2ecf20Sopenharmony_ci struct { 2558c2ecf20Sopenharmony_ci u16 index_addr; 2568c2ecf20Sopenharmony_ci u16 data_addr; 2578c2ecf20Sopenharmony_ci } io; 2588c2ecf20Sopenharmony_ci u32 phys_addr32; 2598c2ecf20Sopenharmony_ci u16 gpnv_handle; 2608c2ecf20Sopenharmony_ci u32 access_method_address; 2618c2ecf20Sopenharmony_ci }; 2628c2ecf20Sopenharmony_ci u8 header_format; 2638c2ecf20Sopenharmony_ci u8 type_descriptors_supported_count; 2648c2ecf20Sopenharmony_ci u8 per_log_type_descriptor_length; 2658c2ecf20Sopenharmony_ci u8 supported_log_type_descriptos[]; 2668c2ecf20Sopenharmony_ci} __packed; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci#define DMI_SYSFS_SEL_FIELD(_field) \ 2698c2ecf20Sopenharmony_cistatic ssize_t dmi_sysfs_sel_##_field(struct dmi_sysfs_entry *entry, \ 2708c2ecf20Sopenharmony_ci const struct dmi_header *dh, \ 2718c2ecf20Sopenharmony_ci char *buf) \ 2728c2ecf20Sopenharmony_ci{ \ 2738c2ecf20Sopenharmony_ci struct dmi_system_event_log sel; \ 2748c2ecf20Sopenharmony_ci if (sizeof(sel) > dmi_entry_length(dh)) \ 2758c2ecf20Sopenharmony_ci return -EIO; \ 2768c2ecf20Sopenharmony_ci memcpy(&sel, dh, sizeof(sel)); \ 2778c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", sel._field); \ 2788c2ecf20Sopenharmony_ci} \ 2798c2ecf20Sopenharmony_cistatic DMI_SYSFS_MAPPED_ATTR(sel, _field) 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ciDMI_SYSFS_SEL_FIELD(area_length); 2828c2ecf20Sopenharmony_ciDMI_SYSFS_SEL_FIELD(header_start_offset); 2838c2ecf20Sopenharmony_ciDMI_SYSFS_SEL_FIELD(data_start_offset); 2848c2ecf20Sopenharmony_ciDMI_SYSFS_SEL_FIELD(access_method); 2858c2ecf20Sopenharmony_ciDMI_SYSFS_SEL_FIELD(status); 2868c2ecf20Sopenharmony_ciDMI_SYSFS_SEL_FIELD(change_token); 2878c2ecf20Sopenharmony_ciDMI_SYSFS_SEL_FIELD(access_method_address); 2888c2ecf20Sopenharmony_ciDMI_SYSFS_SEL_FIELD(header_format); 2898c2ecf20Sopenharmony_ciDMI_SYSFS_SEL_FIELD(type_descriptors_supported_count); 2908c2ecf20Sopenharmony_ciDMI_SYSFS_SEL_FIELD(per_log_type_descriptor_length); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic struct attribute *dmi_sysfs_sel_attrs[] = { 2938c2ecf20Sopenharmony_ci &dmi_sysfs_attr_sel_area_length.attr, 2948c2ecf20Sopenharmony_ci &dmi_sysfs_attr_sel_header_start_offset.attr, 2958c2ecf20Sopenharmony_ci &dmi_sysfs_attr_sel_data_start_offset.attr, 2968c2ecf20Sopenharmony_ci &dmi_sysfs_attr_sel_access_method.attr, 2978c2ecf20Sopenharmony_ci &dmi_sysfs_attr_sel_status.attr, 2988c2ecf20Sopenharmony_ci &dmi_sysfs_attr_sel_change_token.attr, 2998c2ecf20Sopenharmony_ci &dmi_sysfs_attr_sel_access_method_address.attr, 3008c2ecf20Sopenharmony_ci &dmi_sysfs_attr_sel_header_format.attr, 3018c2ecf20Sopenharmony_ci &dmi_sysfs_attr_sel_type_descriptors_supported_count.attr, 3028c2ecf20Sopenharmony_ci &dmi_sysfs_attr_sel_per_log_type_descriptor_length.attr, 3038c2ecf20Sopenharmony_ci NULL, 3048c2ecf20Sopenharmony_ci}; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic struct kobj_type dmi_system_event_log_ktype = { 3088c2ecf20Sopenharmony_ci .release = dmi_entry_free, 3098c2ecf20Sopenharmony_ci .sysfs_ops = &dmi_sysfs_specialize_attr_ops, 3108c2ecf20Sopenharmony_ci .default_attrs = dmi_sysfs_sel_attrs, 3118c2ecf20Sopenharmony_ci}; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_citypedef u8 (*sel_io_reader)(const struct dmi_system_event_log *sel, 3148c2ecf20Sopenharmony_ci loff_t offset); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(io_port_lock); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic u8 read_sel_8bit_indexed_io(const struct dmi_system_event_log *sel, 3198c2ecf20Sopenharmony_ci loff_t offset) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci u8 ret; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci mutex_lock(&io_port_lock); 3248c2ecf20Sopenharmony_ci outb((u8)offset, sel->io.index_addr); 3258c2ecf20Sopenharmony_ci ret = inb(sel->io.data_addr); 3268c2ecf20Sopenharmony_ci mutex_unlock(&io_port_lock); 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic u8 read_sel_2x8bit_indexed_io(const struct dmi_system_event_log *sel, 3318c2ecf20Sopenharmony_ci loff_t offset) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci u8 ret; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci mutex_lock(&io_port_lock); 3368c2ecf20Sopenharmony_ci outb((u8)offset, sel->io.index_addr); 3378c2ecf20Sopenharmony_ci outb((u8)(offset >> 8), sel->io.index_addr + 1); 3388c2ecf20Sopenharmony_ci ret = inb(sel->io.data_addr); 3398c2ecf20Sopenharmony_ci mutex_unlock(&io_port_lock); 3408c2ecf20Sopenharmony_ci return ret; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic u8 read_sel_16bit_indexed_io(const struct dmi_system_event_log *sel, 3448c2ecf20Sopenharmony_ci loff_t offset) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci u8 ret; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci mutex_lock(&io_port_lock); 3498c2ecf20Sopenharmony_ci outw((u16)offset, sel->io.index_addr); 3508c2ecf20Sopenharmony_ci ret = inb(sel->io.data_addr); 3518c2ecf20Sopenharmony_ci mutex_unlock(&io_port_lock); 3528c2ecf20Sopenharmony_ci return ret; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic sel_io_reader sel_io_readers[] = { 3568c2ecf20Sopenharmony_ci [DMI_SEL_ACCESS_METHOD_IO8] = read_sel_8bit_indexed_io, 3578c2ecf20Sopenharmony_ci [DMI_SEL_ACCESS_METHOD_IO2x8] = read_sel_2x8bit_indexed_io, 3588c2ecf20Sopenharmony_ci [DMI_SEL_ACCESS_METHOD_IO16] = read_sel_16bit_indexed_io, 3598c2ecf20Sopenharmony_ci}; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic ssize_t dmi_sel_raw_read_io(struct dmi_sysfs_entry *entry, 3628c2ecf20Sopenharmony_ci const struct dmi_system_event_log *sel, 3638c2ecf20Sopenharmony_ci char *buf, loff_t pos, size_t count) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci ssize_t wrote = 0; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci sel_io_reader io_reader = sel_io_readers[sel->access_method]; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci while (count && pos < sel->area_length) { 3708c2ecf20Sopenharmony_ci count--; 3718c2ecf20Sopenharmony_ci *(buf++) = io_reader(sel, pos++); 3728c2ecf20Sopenharmony_ci wrote++; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci return wrote; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic ssize_t dmi_sel_raw_read_phys32(struct dmi_sysfs_entry *entry, 3798c2ecf20Sopenharmony_ci const struct dmi_system_event_log *sel, 3808c2ecf20Sopenharmony_ci char *buf, loff_t pos, size_t count) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci u8 __iomem *mapped; 3838c2ecf20Sopenharmony_ci ssize_t wrote = 0; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci mapped = dmi_remap(sel->access_method_address, sel->area_length); 3868c2ecf20Sopenharmony_ci if (!mapped) 3878c2ecf20Sopenharmony_ci return -EIO; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci while (count && pos < sel->area_length) { 3908c2ecf20Sopenharmony_ci count--; 3918c2ecf20Sopenharmony_ci *(buf++) = readb(mapped + pos++); 3928c2ecf20Sopenharmony_ci wrote++; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci dmi_unmap(mapped); 3968c2ecf20Sopenharmony_ci return wrote; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic ssize_t dmi_sel_raw_read_helper(struct dmi_sysfs_entry *entry, 4008c2ecf20Sopenharmony_ci const struct dmi_header *dh, 4018c2ecf20Sopenharmony_ci void *_state) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct dmi_read_state *state = _state; 4048c2ecf20Sopenharmony_ci struct dmi_system_event_log sel; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (sizeof(sel) > dmi_entry_length(dh)) 4078c2ecf20Sopenharmony_ci return -EIO; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci memcpy(&sel, dh, sizeof(sel)); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci switch (sel.access_method) { 4128c2ecf20Sopenharmony_ci case DMI_SEL_ACCESS_METHOD_IO8: 4138c2ecf20Sopenharmony_ci case DMI_SEL_ACCESS_METHOD_IO2x8: 4148c2ecf20Sopenharmony_ci case DMI_SEL_ACCESS_METHOD_IO16: 4158c2ecf20Sopenharmony_ci return dmi_sel_raw_read_io(entry, &sel, state->buf, 4168c2ecf20Sopenharmony_ci state->pos, state->count); 4178c2ecf20Sopenharmony_ci case DMI_SEL_ACCESS_METHOD_PHYS32: 4188c2ecf20Sopenharmony_ci return dmi_sel_raw_read_phys32(entry, &sel, state->buf, 4198c2ecf20Sopenharmony_ci state->pos, state->count); 4208c2ecf20Sopenharmony_ci case DMI_SEL_ACCESS_METHOD_GPNV: 4218c2ecf20Sopenharmony_ci pr_info("dmi-sysfs: GPNV support missing.\n"); 4228c2ecf20Sopenharmony_ci return -EIO; 4238c2ecf20Sopenharmony_ci default: 4248c2ecf20Sopenharmony_ci pr_info("dmi-sysfs: Unknown access method %02x\n", 4258c2ecf20Sopenharmony_ci sel.access_method); 4268c2ecf20Sopenharmony_ci return -EIO; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic ssize_t dmi_sel_raw_read(struct file *filp, struct kobject *kobj, 4318c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, 4328c2ecf20Sopenharmony_ci char *buf, loff_t pos, size_t count) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct dmi_sysfs_entry *entry = to_entry(kobj->parent); 4358c2ecf20Sopenharmony_ci struct dmi_read_state state = { 4368c2ecf20Sopenharmony_ci .buf = buf, 4378c2ecf20Sopenharmony_ci .pos = pos, 4388c2ecf20Sopenharmony_ci .count = count, 4398c2ecf20Sopenharmony_ci }; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return find_dmi_entry(entry, dmi_sel_raw_read_helper, &state); 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic struct bin_attribute dmi_sel_raw_attr = { 4458c2ecf20Sopenharmony_ci .attr = {.name = "raw_event_log", .mode = 0400}, 4468c2ecf20Sopenharmony_ci .read = dmi_sel_raw_read, 4478c2ecf20Sopenharmony_ci}; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int dmi_system_event_log(struct dmi_sysfs_entry *entry) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci int ret; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci entry->child = kzalloc(sizeof(*entry->child), GFP_KERNEL); 4548c2ecf20Sopenharmony_ci if (!entry->child) 4558c2ecf20Sopenharmony_ci return -ENOMEM; 4568c2ecf20Sopenharmony_ci ret = kobject_init_and_add(entry->child, 4578c2ecf20Sopenharmony_ci &dmi_system_event_log_ktype, 4588c2ecf20Sopenharmony_ci &entry->kobj, 4598c2ecf20Sopenharmony_ci "system_event_log"); 4608c2ecf20Sopenharmony_ci if (ret) 4618c2ecf20Sopenharmony_ci goto out_free; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci ret = sysfs_create_bin_file(entry->child, &dmi_sel_raw_attr); 4648c2ecf20Sopenharmony_ci if (ret) 4658c2ecf20Sopenharmony_ci goto out_del; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ciout_del: 4708c2ecf20Sopenharmony_ci kobject_del(entry->child); 4718c2ecf20Sopenharmony_ciout_free: 4728c2ecf20Sopenharmony_ci kfree(entry->child); 4738c2ecf20Sopenharmony_ci return ret; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci/************************************************* 4778c2ecf20Sopenharmony_ci * Generic DMI entry support. 4788c2ecf20Sopenharmony_ci *************************************************/ 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic ssize_t dmi_sysfs_entry_length(struct dmi_sysfs_entry *entry, char *buf) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", entry->dh.length); 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic ssize_t dmi_sysfs_entry_handle(struct dmi_sysfs_entry *entry, char *buf) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", entry->dh.handle); 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic ssize_t dmi_sysfs_entry_type(struct dmi_sysfs_entry *entry, char *buf) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", entry->dh.type); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic ssize_t dmi_sysfs_entry_instance(struct dmi_sysfs_entry *entry, 4968c2ecf20Sopenharmony_ci char *buf) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", entry->instance); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic ssize_t dmi_sysfs_entry_position(struct dmi_sysfs_entry *entry, 5028c2ecf20Sopenharmony_ci char *buf) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", entry->position); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic DMI_SYSFS_ATTR(entry, length); 5088c2ecf20Sopenharmony_cistatic DMI_SYSFS_ATTR(entry, handle); 5098c2ecf20Sopenharmony_cistatic DMI_SYSFS_ATTR(entry, type); 5108c2ecf20Sopenharmony_cistatic DMI_SYSFS_ATTR(entry, instance); 5118c2ecf20Sopenharmony_cistatic DMI_SYSFS_ATTR(entry, position); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic struct attribute *dmi_sysfs_entry_attrs[] = { 5148c2ecf20Sopenharmony_ci &dmi_sysfs_attr_entry_length.attr, 5158c2ecf20Sopenharmony_ci &dmi_sysfs_attr_entry_handle.attr, 5168c2ecf20Sopenharmony_ci &dmi_sysfs_attr_entry_type.attr, 5178c2ecf20Sopenharmony_ci &dmi_sysfs_attr_entry_instance.attr, 5188c2ecf20Sopenharmony_ci &dmi_sysfs_attr_entry_position.attr, 5198c2ecf20Sopenharmony_ci NULL, 5208c2ecf20Sopenharmony_ci}; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry, 5238c2ecf20Sopenharmony_ci const struct dmi_header *dh, 5248c2ecf20Sopenharmony_ci void *_state) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct dmi_read_state *state = _state; 5278c2ecf20Sopenharmony_ci size_t entry_length; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci entry_length = dmi_entry_length(dh); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return memory_read_from_buffer(state->buf, state->count, 5328c2ecf20Sopenharmony_ci &state->pos, dh, entry_length); 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic ssize_t dmi_entry_raw_read(struct file *filp, 5368c2ecf20Sopenharmony_ci struct kobject *kobj, 5378c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, 5388c2ecf20Sopenharmony_ci char *buf, loff_t pos, size_t count) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct dmi_sysfs_entry *entry = to_entry(kobj); 5418c2ecf20Sopenharmony_ci struct dmi_read_state state = { 5428c2ecf20Sopenharmony_ci .buf = buf, 5438c2ecf20Sopenharmony_ci .pos = pos, 5448c2ecf20Sopenharmony_ci .count = count, 5458c2ecf20Sopenharmony_ci }; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic const struct bin_attribute dmi_entry_raw_attr = { 5518c2ecf20Sopenharmony_ci .attr = {.name = "raw", .mode = 0400}, 5528c2ecf20Sopenharmony_ci .read = dmi_entry_raw_read, 5538c2ecf20Sopenharmony_ci}; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic void dmi_sysfs_entry_release(struct kobject *kobj) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct dmi_sysfs_entry *entry = to_entry(kobj); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci spin_lock(&entry_list_lock); 5608c2ecf20Sopenharmony_ci list_del(&entry->list); 5618c2ecf20Sopenharmony_ci spin_unlock(&entry_list_lock); 5628c2ecf20Sopenharmony_ci kfree(entry); 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic struct kobj_type dmi_sysfs_entry_ktype = { 5668c2ecf20Sopenharmony_ci .release = dmi_sysfs_entry_release, 5678c2ecf20Sopenharmony_ci .sysfs_ops = &dmi_sysfs_attr_ops, 5688c2ecf20Sopenharmony_ci .default_attrs = dmi_sysfs_entry_attrs, 5698c2ecf20Sopenharmony_ci}; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic struct kset *dmi_kset; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci/* Global count of all instances seen. Only for setup */ 5748c2ecf20Sopenharmony_cistatic int __initdata instance_counts[MAX_ENTRY_TYPE + 1]; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/* Global positional count of all entries seen. Only for setup */ 5778c2ecf20Sopenharmony_cistatic int __initdata position_count; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic void __init dmi_sysfs_register_handle(const struct dmi_header *dh, 5808c2ecf20Sopenharmony_ci void *_ret) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct dmi_sysfs_entry *entry; 5838c2ecf20Sopenharmony_ci int *ret = _ret; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* If a previous entry saw an error, short circuit */ 5868c2ecf20Sopenharmony_ci if (*ret) 5878c2ecf20Sopenharmony_ci return; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* Allocate and register a new entry into the entries set */ 5908c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 5918c2ecf20Sopenharmony_ci if (!entry) { 5928c2ecf20Sopenharmony_ci *ret = -ENOMEM; 5938c2ecf20Sopenharmony_ci return; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* Set the key */ 5978c2ecf20Sopenharmony_ci memcpy(&entry->dh, dh, sizeof(*dh)); 5988c2ecf20Sopenharmony_ci entry->instance = instance_counts[dh->type]++; 5998c2ecf20Sopenharmony_ci entry->position = position_count++; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci entry->kobj.kset = dmi_kset; 6028c2ecf20Sopenharmony_ci *ret = kobject_init_and_add(&entry->kobj, &dmi_sysfs_entry_ktype, NULL, 6038c2ecf20Sopenharmony_ci "%d-%d", dh->type, entry->instance); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (*ret) { 6068c2ecf20Sopenharmony_ci kobject_put(&entry->kobj); 6078c2ecf20Sopenharmony_ci return; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* Thread on the global list for cleanup */ 6118c2ecf20Sopenharmony_ci spin_lock(&entry_list_lock); 6128c2ecf20Sopenharmony_ci list_add_tail(&entry->list, &entry_list); 6138c2ecf20Sopenharmony_ci spin_unlock(&entry_list_lock); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* Handle specializations by type */ 6168c2ecf20Sopenharmony_ci switch (dh->type) { 6178c2ecf20Sopenharmony_ci case DMI_ENTRY_SYSTEM_EVENT_LOG: 6188c2ecf20Sopenharmony_ci *ret = dmi_system_event_log(entry); 6198c2ecf20Sopenharmony_ci break; 6208c2ecf20Sopenharmony_ci default: 6218c2ecf20Sopenharmony_ci /* No specialization */ 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci if (*ret) 6258c2ecf20Sopenharmony_ci goto out_err; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* Create the raw binary file to access the entry */ 6288c2ecf20Sopenharmony_ci *ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr); 6298c2ecf20Sopenharmony_ci if (*ret) 6308c2ecf20Sopenharmony_ci goto out_err; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return; 6338c2ecf20Sopenharmony_ciout_err: 6348c2ecf20Sopenharmony_ci kobject_put(entry->child); 6358c2ecf20Sopenharmony_ci kobject_put(&entry->kobj); 6368c2ecf20Sopenharmony_ci return; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic void cleanup_entry_list(void) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct dmi_sysfs_entry *entry, *next; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* No locks, we are on our way out */ 6448c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, next, &entry_list, list) { 6458c2ecf20Sopenharmony_ci kobject_put(entry->child); 6468c2ecf20Sopenharmony_ci kobject_put(&entry->kobj); 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic int __init dmi_sysfs_init(void) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci int error; 6538c2ecf20Sopenharmony_ci int val; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (!dmi_kobj) { 6568c2ecf20Sopenharmony_ci pr_debug("dmi-sysfs: dmi entry is absent.\n"); 6578c2ecf20Sopenharmony_ci error = -ENODATA; 6588c2ecf20Sopenharmony_ci goto err; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); 6628c2ecf20Sopenharmony_ci if (!dmi_kset) { 6638c2ecf20Sopenharmony_ci error = -ENOMEM; 6648c2ecf20Sopenharmony_ci goto err; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci val = 0; 6688c2ecf20Sopenharmony_ci error = dmi_walk(dmi_sysfs_register_handle, &val); 6698c2ecf20Sopenharmony_ci if (error) 6708c2ecf20Sopenharmony_ci goto err; 6718c2ecf20Sopenharmony_ci if (val) { 6728c2ecf20Sopenharmony_ci error = val; 6738c2ecf20Sopenharmony_ci goto err; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci pr_debug("dmi-sysfs: loaded.\n"); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_cierr: 6808c2ecf20Sopenharmony_ci cleanup_entry_list(); 6818c2ecf20Sopenharmony_ci kset_unregister(dmi_kset); 6828c2ecf20Sopenharmony_ci return error; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci/* clean up everything. */ 6868c2ecf20Sopenharmony_cistatic void __exit dmi_sysfs_exit(void) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci pr_debug("dmi-sysfs: unloading.\n"); 6898c2ecf20Sopenharmony_ci cleanup_entry_list(); 6908c2ecf20Sopenharmony_ci kset_unregister(dmi_kset); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cimodule_init(dmi_sysfs_init); 6948c2ecf20Sopenharmony_cimodule_exit(dmi_sysfs_exit); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mike Waychison <mikew@google.com>"); 6978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DMI sysfs support"); 6988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 699