109c434b8SThomas 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 *************************************************/
dmi_entry_free(struct kobject * kobj)83925a1da7SMike Waychison static void dmi_entry_free(struct kobject *kobj)
84925a1da7SMike Waychison {
85925a1da7SMike Waychison kfree(kobj);
86925a1da7SMike Waychison }
87948af1f0SMike Waychison
to_entry(struct kobject * kobj)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
to_attr(struct attribute * attr)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
dmi_sysfs_attr_show(struct kobject * kobj,struct attribute * _attr,char * buf)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
find_dmi_entry_helper(const struct dmi_header * dh,void * _data)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
find_dmi_entry(struct dmi_sysfs_entry * entry,dmi_callback callback,void * private)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 */
dmi_entry_length(const struct dmi_header * dh)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
dmi_entry_attr_show_helper(struct dmi_sysfs_entry * entry,const struct dmi_header * dh,void * _data)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
dmi_entry_attr_show(struct kobject * kobj,struct attribute * attr,char * buf)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;
2658e849a41SGustavo A. R. Silva u8 supported_log_type_descriptos[];
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 };
305ab6d0f57SGreg Kroah-Hartman ATTRIBUTE_GROUPS(dmi_sysfs_sel);
306925a1da7SMike Waychison
307*81773c98SThomas Weißschuh static const struct kobj_type dmi_system_event_log_ktype = {
308925a1da7SMike Waychison .release = dmi_entry_free,
309925a1da7SMike Waychison .sysfs_ops = &dmi_sysfs_specialize_attr_ops,
310ab6d0f57SGreg Kroah-Hartman .default_groups = dmi_sysfs_sel_groups,
311925a1da7SMike Waychison };
312925a1da7SMike Waychison
313a3857a5cSMike Waychison #ifdef CONFIG_HAS_IOPORT
314a3857a5cSMike Waychison typedef u8 (*sel_io_reader)(const struct dmi_system_event_log *sel,
315a3857a5cSMike Waychison loff_t offset);
316a3857a5cSMike Waychison
317a3857a5cSMike Waychison static DEFINE_MUTEX(io_port_lock);
318a3857a5cSMike Waychison
read_sel_8bit_indexed_io(const struct dmi_system_event_log * sel,loff_t offset)319a3857a5cSMike Waychison static u8 read_sel_8bit_indexed_io(const struct dmi_system_event_log *sel,
320a3857a5cSMike Waychison loff_t offset)
321a3857a5cSMike Waychison {
322a3857a5cSMike Waychison u8 ret;
323a3857a5cSMike Waychison
324a3857a5cSMike Waychison mutex_lock(&io_port_lock);
325a3857a5cSMike Waychison outb((u8)offset, sel->io.index_addr);
326a3857a5cSMike Waychison ret = inb(sel->io.data_addr);
327a3857a5cSMike Waychison mutex_unlock(&io_port_lock);
328a3857a5cSMike Waychison return ret;
329a3857a5cSMike Waychison }
330a3857a5cSMike Waychison
read_sel_2x8bit_indexed_io(const struct dmi_system_event_log * sel,loff_t offset)331a3857a5cSMike Waychison static u8 read_sel_2x8bit_indexed_io(const struct dmi_system_event_log *sel,
332a3857a5cSMike Waychison loff_t offset)
333a3857a5cSMike Waychison {
334a3857a5cSMike Waychison u8 ret;
335a3857a5cSMike Waychison
336a3857a5cSMike Waychison mutex_lock(&io_port_lock);
337a3857a5cSMike Waychison outb((u8)offset, sel->io.index_addr);
338a3857a5cSMike Waychison outb((u8)(offset >> 8), sel->io.index_addr + 1);
339a3857a5cSMike Waychison ret = inb(sel->io.data_addr);
340a3857a5cSMike Waychison mutex_unlock(&io_port_lock);
341a3857a5cSMike Waychison return ret;
342a3857a5cSMike Waychison }
343a3857a5cSMike Waychison
read_sel_16bit_indexed_io(const struct dmi_system_event_log * sel,loff_t offset)344a3857a5cSMike Waychison static u8 read_sel_16bit_indexed_io(const struct dmi_system_event_log *sel,
345a3857a5cSMike Waychison loff_t offset)
346a3857a5cSMike Waychison {
347a3857a5cSMike Waychison u8 ret;
348a3857a5cSMike Waychison
349a3857a5cSMike Waychison mutex_lock(&io_port_lock);
350a3857a5cSMike Waychison outw((u16)offset, sel->io.index_addr);
351a3857a5cSMike Waychison ret = inb(sel->io.data_addr);
352a3857a5cSMike Waychison mutex_unlock(&io_port_lock);
353a3857a5cSMike Waychison return ret;
354a3857a5cSMike Waychison }
355a3857a5cSMike Waychison
356a3857a5cSMike Waychison static sel_io_reader sel_io_readers[] = {
357a3857a5cSMike Waychison [DMI_SEL_ACCESS_METHOD_IO8] = read_sel_8bit_indexed_io,
358a3857a5cSMike Waychison [DMI_SEL_ACCESS_METHOD_IO2x8] = read_sel_2x8bit_indexed_io,
359a3857a5cSMike Waychison [DMI_SEL_ACCESS_METHOD_IO16] = read_sel_16bit_indexed_io,
360a3857a5cSMike Waychison };
361a3857a5cSMike Waychison
dmi_sel_raw_read_io(struct dmi_sysfs_entry * entry,const struct dmi_system_event_log * sel,char * buf,loff_t pos,size_t count)362a3857a5cSMike Waychison static ssize_t dmi_sel_raw_read_io(struct dmi_sysfs_entry *entry,
363a3857a5cSMike Waychison const struct dmi_system_event_log *sel,
364a3857a5cSMike Waychison char *buf, loff_t pos, size_t count)
365a3857a5cSMike Waychison {
366a3857a5cSMike Waychison ssize_t wrote = 0;
367a3857a5cSMike Waychison
368a3857a5cSMike Waychison sel_io_reader io_reader = sel_io_readers[sel->access_method];
369a3857a5cSMike Waychison
370a3857a5cSMike Waychison while (count && pos < sel->area_length) {
371a3857a5cSMike Waychison count--;
372a3857a5cSMike Waychison *(buf++) = io_reader(sel, pos++);
373a3857a5cSMike Waychison wrote++;
374a3857a5cSMike Waychison }
375a3857a5cSMike Waychison
376a3857a5cSMike Waychison return wrote;
377a3857a5cSMike Waychison }
378a3857a5cSMike Waychison #endif
379a3857a5cSMike Waychison
dmi_sel_raw_read_phys32(struct dmi_sysfs_entry * entry,const struct dmi_system_event_log * sel,char * buf,loff_t pos,size_t count)380a3857a5cSMike Waychison static ssize_t dmi_sel_raw_read_phys32(struct dmi_sysfs_entry *entry,
381a3857a5cSMike Waychison const struct dmi_system_event_log *sel,
382a3857a5cSMike Waychison char *buf, loff_t pos, size_t count)
383a3857a5cSMike Waychison {
384a3857a5cSMike Waychison u8 __iomem *mapped;
385f7750a79STom Lendacky ssize_t wrote = 0;
386a3857a5cSMike Waychison
387a3857a5cSMike Waychison mapped = dmi_remap(sel->access_method_address, sel->area_length);
388a3857a5cSMike Waychison if (!mapped)
389a3857a5cSMike Waychison return -EIO;
390a3857a5cSMike Waychison
391a3857a5cSMike Waychison while (count && pos < sel->area_length) {
392a3857a5cSMike Waychison count--;
393a3857a5cSMike Waychison *(buf++) = readb(mapped + pos++);
394a3857a5cSMike Waychison wrote++;
395f7750a79STom Lendacky }
396a3857a5cSMike Waychison
397a3857a5cSMike Waychison dmi_unmap(mapped);
398a3857a5cSMike Waychison return wrote;
399a3857a5cSMike Waychison }
400a3857a5cSMike Waychison
dmi_sel_raw_read_helper(struct dmi_sysfs_entry * entry,const struct dmi_header * dh,void * _state)401a3857a5cSMike Waychison static ssize_t dmi_sel_raw_read_helper(struct dmi_sysfs_entry *entry,
402a3857a5cSMike Waychison const struct dmi_header *dh,
403a3857a5cSMike Waychison void *_state)
40466245ad0SMike Waychison {
405a3857a5cSMike Waychison struct dmi_read_state *state = _state;
40666245ad0SMike Waychison struct dmi_system_event_log sel;
407a3857a5cSMike Waychison
408a3857a5cSMike Waychison if (sizeof(sel) > dmi_entry_length(dh))
40966245ad0SMike Waychison return -EIO;
41066245ad0SMike Waychison
41166245ad0SMike Waychison memcpy(&sel, dh, sizeof(sel));
412a3857a5cSMike Waychison
413a3857a5cSMike Waychison switch (sel.access_method) {
414a3857a5cSMike Waychison #ifdef CONFIG_HAS_IOPORT
41566245ad0SMike Waychison case DMI_SEL_ACCESS_METHOD_IO8:
416a3857a5cSMike Waychison case DMI_SEL_ACCESS_METHOD_IO2x8:
417a3857a5cSMike Waychison case DMI_SEL_ACCESS_METHOD_IO16:
41866245ad0SMike Waychison return dmi_sel_raw_read_io(entry, &sel, state->buf,
419a3857a5cSMike Waychison state->pos, state->count);
420a3857a5cSMike Waychison #endif
4217d12970fSColin Ian King case DMI_SEL_ACCESS_METHOD_PHYS32:
422a3857a5cSMike Waychison return dmi_sel_raw_read_phys32(entry, &sel, state->buf,
423a3857a5cSMike Waychison state->pos, state->count);
4247d12970fSColin Ian King case DMI_SEL_ACCESS_METHOD_GPNV:
42566245ad0SMike Waychison pr_info_ratelimited("dmi-sysfs: GPNV support missing.\n");
426a3857a5cSMike Waychison return -EIO;
427a3857a5cSMike Waychison default:
428a3857a5cSMike Waychison pr_info_ratelimited("dmi-sysfs: Unknown access method %02x\n",
429a3857a5cSMike Waychison sel.access_method);
430a3857a5cSMike Waychison return -EIO;
431a3857a5cSMike Waychison }
432a3857a5cSMike Waychison }
433a3857a5cSMike Waychison
dmi_sel_raw_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t pos,size_t count)434a3857a5cSMike Waychison static ssize_t dmi_sel_raw_read(struct file *filp, struct kobject *kobj,
435a3857a5cSMike Waychison struct bin_attribute *bin_attr,
436a3857a5cSMike Waychison char *buf, loff_t pos, size_t count)
437a3857a5cSMike Waychison {
438a3857a5cSMike Waychison struct dmi_sysfs_entry *entry = to_entry(kobj->parent);
439a3857a5cSMike Waychison struct dmi_read_state state = {
440a3857a5cSMike Waychison .buf = buf,
441a3857a5cSMike Waychison .pos = pos,
442a3857a5cSMike Waychison .count = count,
443a3857a5cSMike Waychison };
444a3857a5cSMike Waychison
445a3857a5cSMike Waychison return find_dmi_entry(entry, dmi_sel_raw_read_helper, &state);
446a3857a5cSMike Waychison }
447a3857a5cSMike Waychison
448a3857a5cSMike Waychison static struct bin_attribute dmi_sel_raw_attr = {
449925a1da7SMike Waychison .attr = {.name = "raw_event_log", .mode = 0400},
450925a1da7SMike Waychison .read = dmi_sel_raw_read,
451925a1da7SMike Waychison };
452925a1da7SMike Waychison
dmi_system_event_log(struct dmi_sysfs_entry * entry)453925a1da7SMike Waychison static int dmi_system_event_log(struct dmi_sysfs_entry *entry)
454925a1da7SMike Waychison {
455925a1da7SMike Waychison int ret;
456925a1da7SMike Waychison
457925a1da7SMike Waychison entry->child = kzalloc(sizeof(*entry->child), GFP_KERNEL);
458925a1da7SMike Waychison if (!entry->child)
459925a1da7SMike Waychison return -ENOMEM;
460925a1da7SMike Waychison ret = kobject_init_and_add(entry->child,
461925a1da7SMike Waychison &dmi_system_event_log_ktype,
462a3857a5cSMike Waychison &entry->kobj,
463a3857a5cSMike Waychison "system_event_log");
464a3857a5cSMike Waychison if (ret)
465a3857a5cSMike Waychison goto out_free;
466a3857a5cSMike Waychison
467a3857a5cSMike Waychison ret = sysfs_create_bin_file(entry->child, &dmi_sel_raw_attr);
468a3857a5cSMike Waychison if (ret)
469a3857a5cSMike Waychison goto out_del;
470a3857a5cSMike Waychison
471925a1da7SMike Waychison return 0;
472925a1da7SMike Waychison
473925a1da7SMike Waychison out_del:
474925a1da7SMike Waychison kobject_del(entry->child);
475925a1da7SMike Waychison out_free:
476925a1da7SMike Waychison kfree(entry->child);
477948af1f0SMike Waychison return ret;
478948af1f0SMike Waychison }
479948af1f0SMike Waychison
480948af1f0SMike Waychison /*************************************************
481948af1f0SMike Waychison * Generic DMI entry support.
482948af1f0SMike Waychison *************************************************/
483948af1f0SMike Waychison
dmi_sysfs_entry_length(struct dmi_sysfs_entry * entry,char * buf)484948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_length(struct dmi_sysfs_entry *entry, char *buf)
485948af1f0SMike Waychison {
486948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->dh.length);
487948af1f0SMike Waychison }
488948af1f0SMike Waychison
dmi_sysfs_entry_handle(struct dmi_sysfs_entry * entry,char * buf)489948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_handle(struct dmi_sysfs_entry *entry, char *buf)
490948af1f0SMike Waychison {
491948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->dh.handle);
492948af1f0SMike Waychison }
493948af1f0SMike Waychison
dmi_sysfs_entry_type(struct dmi_sysfs_entry * entry,char * buf)494948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_type(struct dmi_sysfs_entry *entry, char *buf)
495948af1f0SMike Waychison {
496948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->dh.type);
497948af1f0SMike Waychison }
498948af1f0SMike Waychison
dmi_sysfs_entry_instance(struct dmi_sysfs_entry * entry,char * buf)499948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_instance(struct dmi_sysfs_entry *entry,
500948af1f0SMike Waychison char *buf)
501948af1f0SMike Waychison {
502948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->instance);
503948af1f0SMike Waychison }
504948af1f0SMike Waychison
dmi_sysfs_entry_position(struct dmi_sysfs_entry * entry,char * buf)505948af1f0SMike Waychison static ssize_t dmi_sysfs_entry_position(struct dmi_sysfs_entry *entry,
506948af1f0SMike Waychison char *buf)
507948af1f0SMike Waychison {
508948af1f0SMike Waychison return sprintf(buf, "%d\n", entry->position);
509948af1f0SMike Waychison }
510948af1f0SMike Waychison
511948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, length);
512948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, handle);
513948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, type);
514948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, instance);
515948af1f0SMike Waychison static DMI_SYSFS_ATTR(entry, position);
516948af1f0SMike Waychison
517948af1f0SMike Waychison static struct attribute *dmi_sysfs_entry_attrs[] = {
518948af1f0SMike Waychison &dmi_sysfs_attr_entry_length.attr,
519948af1f0SMike Waychison &dmi_sysfs_attr_entry_handle.attr,
520948af1f0SMike Waychison &dmi_sysfs_attr_entry_type.attr,
521ab6d0f57SGreg Kroah-Hartman &dmi_sysfs_attr_entry_instance.attr,
522948af1f0SMike Waychison &dmi_sysfs_attr_entry_position.attr,
523948af1f0SMike Waychison NULL,
524948af1f0SMike Waychison };
525948af1f0SMike Waychison ATTRIBUTE_GROUPS(dmi_sysfs_entry);
526948af1f0SMike Waychison
dmi_entry_raw_read_helper(struct dmi_sysfs_entry * entry,const struct dmi_header * dh,void * _state)527948af1f0SMike Waychison static ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry,
528948af1f0SMike Waychison const struct dmi_header *dh,
529948af1f0SMike Waychison void *_state)
530948af1f0SMike Waychison {
531948af1f0SMike Waychison struct dmi_read_state *state = _state;
532948af1f0SMike Waychison size_t entry_length;
533948af1f0SMike Waychison
534948af1f0SMike Waychison entry_length = dmi_entry_length(dh);
535948af1f0SMike Waychison
536948af1f0SMike Waychison return memory_read_from_buffer(state->buf, state->count,
537948af1f0SMike Waychison &state->pos, dh, entry_length);
538948af1f0SMike Waychison }
539948af1f0SMike Waychison
dmi_entry_raw_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t pos,size_t count)540948af1f0SMike Waychison static ssize_t dmi_entry_raw_read(struct file *filp,
541948af1f0SMike Waychison struct kobject *kobj,
542948af1f0SMike Waychison struct bin_attribute *bin_attr,
543948af1f0SMike Waychison char *buf, loff_t pos, size_t count)
544948af1f0SMike Waychison {
545948af1f0SMike Waychison struct dmi_sysfs_entry *entry = to_entry(kobj);
546948af1f0SMike Waychison struct dmi_read_state state = {
547948af1f0SMike Waychison .buf = buf,
548948af1f0SMike Waychison .pos = pos,
549948af1f0SMike Waychison .count = count,
550948af1f0SMike Waychison };
551948af1f0SMike Waychison
552948af1f0SMike Waychison return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state);
553948af1f0SMike Waychison }
554948af1f0SMike Waychison
555948af1f0SMike Waychison static const struct bin_attribute dmi_entry_raw_attr = {
556948af1f0SMike Waychison .attr = {.name = "raw", .mode = 0400},
557948af1f0SMike Waychison .read = dmi_entry_raw_read,
558948af1f0SMike Waychison };
559a61aca28SBjorn Helgaas
dmi_sysfs_entry_release(struct kobject * kobj)560948af1f0SMike Waychison static void dmi_sysfs_entry_release(struct kobject *kobj)
561948af1f0SMike Waychison {
562948af1f0SMike Waychison struct dmi_sysfs_entry *entry = to_entry(kobj);
563948af1f0SMike Waychison
564948af1f0SMike Waychison spin_lock(&entry_list_lock);
565948af1f0SMike Waychison list_del(&entry->list);
566*81773c98SThomas Weißschuh spin_unlock(&entry_list_lock);
567948af1f0SMike Waychison kfree(entry);
568948af1f0SMike Waychison }
569ab6d0f57SGreg Kroah-Hartman
570948af1f0SMike Waychison static const struct kobj_type dmi_sysfs_entry_ktype = {
571948af1f0SMike Waychison .release = dmi_sysfs_entry_release,
572948af1f0SMike Waychison .sysfs_ops = &dmi_sysfs_attr_ops,
573948af1f0SMike Waychison .default_groups = dmi_sysfs_entry_groups,
574948af1f0SMike Waychison };
575948af1f0SMike Waychison
576948af1f0SMike Waychison static struct kset *dmi_kset;
577948af1f0SMike Waychison
578948af1f0SMike Waychison /* Global count of all instances seen. Only for setup */
579948af1f0SMike Waychison static int __initdata instance_counts[MAX_ENTRY_TYPE + 1];
580948af1f0SMike Waychison
581948af1f0SMike Waychison /* Global positional count of all entries seen. Only for setup */
582948af1f0SMike Waychison static int __initdata position_count;
583948af1f0SMike Waychison
dmi_sysfs_register_handle(const struct dmi_header * dh,void * _ret)584948af1f0SMike Waychison static void __init dmi_sysfs_register_handle(const struct dmi_header *dh,
585948af1f0SMike Waychison void *_ret)
586948af1f0SMike Waychison {
587948af1f0SMike Waychison struct dmi_sysfs_entry *entry;
588948af1f0SMike Waychison int *ret = _ret;
589948af1f0SMike Waychison
590948af1f0SMike Waychison /* If a previous entry saw an error, short circuit */
591948af1f0SMike Waychison if (*ret)
592948af1f0SMike Waychison return;
593948af1f0SMike Waychison
594948af1f0SMike Waychison /* Allocate and register a new entry into the entries set */
595948af1f0SMike Waychison entry = kzalloc(sizeof(*entry), GFP_KERNEL);
596948af1f0SMike Waychison if (!entry) {
597948af1f0SMike Waychison *ret = -ENOMEM;
59866245ad0SMike Waychison return;
599948af1f0SMike Waychison }
600948af1f0SMike Waychison
601948af1f0SMike Waychison /* Set the key */
602948af1f0SMike Waychison memcpy(&entry->dh, dh, sizeof(*dh));
603948af1f0SMike Waychison entry->instance = instance_counts[dh->type]++;
604948af1f0SMike Waychison entry->position = position_count++;
605948af1f0SMike Waychison
606948af1f0SMike Waychison entry->kobj.kset = dmi_kset;
607948af1f0SMike Waychison *ret = kobject_init_and_add(&entry->kobj, &dmi_sysfs_entry_ktype, NULL,
608948af1f0SMike Waychison "%d-%d", dh->type, entry->instance);
609948af1f0SMike Waychison
610948af1f0SMike Waychison /* Thread on the global list for cleanup */
61118e126e9SChen Zhongjin spin_lock(&entry_list_lock);
61218e126e9SChen Zhongjin list_add_tail(&entry->list, &entry_list);
61318e126e9SChen Zhongjin spin_unlock(&entry_list_lock);
61418e126e9SChen Zhongjin
61518e126e9SChen Zhongjin if (*ret) {
616925a1da7SMike Waychison kobject_put(&entry->kobj);
617925a1da7SMike Waychison return;
618925a1da7SMike Waychison }
619925a1da7SMike Waychison
620925a1da7SMike Waychison /* Handle specializations by type */
621925a1da7SMike Waychison switch (dh->type) {
622925a1da7SMike Waychison case DMI_ENTRY_SYSTEM_EVENT_LOG:
623925a1da7SMike Waychison *ret = dmi_system_event_log(entry);
624925a1da7SMike Waychison break;
625925a1da7SMike Waychison default:
626925a1da7SMike Waychison /* No specialization */
627925a1da7SMike Waychison break;
628948af1f0SMike Waychison }
629948af1f0SMike Waychison if (*ret)
630948af1f0SMike Waychison goto out_err;
631948af1f0SMike Waychison
632948af1f0SMike Waychison /* Create the raw binary file to access the entry */
633948af1f0SMike Waychison *ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr);
634948af1f0SMike Waychison if (*ret)
635925a1da7SMike Waychison goto out_err;
636948af1f0SMike Waychison
637948af1f0SMike Waychison return;
638948af1f0SMike Waychison out_err:
639948af1f0SMike Waychison kobject_put(entry->child);
640948af1f0SMike Waychison kobject_put(&entry->kobj);
641948af1f0SMike Waychison return;
642948af1f0SMike Waychison }
643948af1f0SMike Waychison
cleanup_entry_list(void)644948af1f0SMike Waychison static void cleanup_entry_list(void)
645948af1f0SMike Waychison {
646925a1da7SMike Waychison struct dmi_sysfs_entry *entry, *next;
647948af1f0SMike Waychison
648948af1f0SMike Waychison /* No locks, we are on our way out */
649948af1f0SMike Waychison list_for_each_entry_safe(entry, next, &entry_list, list) {
650948af1f0SMike Waychison kobject_put(entry->child);
651948af1f0SMike Waychison kobject_put(&entry->kobj);
652948af1f0SMike Waychison }
653d7f96f97SIvan Khoronzhuk }
654948af1f0SMike Waychison
dmi_sysfs_init(void)655948af1f0SMike Waychison static int __init dmi_sysfs_init(void)
656d7f96f97SIvan Khoronzhuk {
657a81114d0SArd Biesheuvel int error;
658d7f96f97SIvan Khoronzhuk int val;
659948af1f0SMike Waychison
660d7f96f97SIvan Khoronzhuk if (!dmi_kobj) {
661948af1f0SMike Waychison pr_debug("dmi-sysfs: dmi entry is absent.\n");
662948af1f0SMike Waychison error = -ENODATA;
663d7f96f97SIvan Khoronzhuk goto err;
664d7f96f97SIvan Khoronzhuk }
665948af1f0SMike Waychison
666d7f96f97SIvan Khoronzhuk dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj);
667948af1f0SMike Waychison if (!dmi_kset) {
668948af1f0SMike Waychison error = -ENOMEM;
669948af1f0SMike Waychison goto err;
670948af1f0SMike Waychison }
671948af1f0SMike Waychison
672948af1f0SMike Waychison val = 0;
673948af1f0SMike Waychison error = dmi_walk(dmi_sysfs_register_handle, &val);
674948af1f0SMike Waychison if (error)
675948af1f0SMike Waychison goto err;
676948af1f0SMike Waychison if (val) {
677948af1f0SMike Waychison error = val;
678948af1f0SMike Waychison goto err;
679948af1f0SMike Waychison }
680948af1f0SMike Waychison
681948af1f0SMike Waychison pr_debug("dmi-sysfs: loaded.\n");
682948af1f0SMike Waychison
683948af1f0SMike Waychison return 0;
684948af1f0SMike Waychison err:
685948af1f0SMike Waychison cleanup_entry_list();
686948af1f0SMike Waychison kset_unregister(dmi_kset);
687948af1f0SMike Waychison return error;
688948af1f0SMike Waychison }
689948af1f0SMike Waychison
690948af1f0SMike Waychison /* clean up everything. */
dmi_sysfs_exit(void)691948af1f0SMike Waychison static void __exit dmi_sysfs_exit(void)
692948af1f0SMike Waychison {
693948af1f0SMike Waychison pr_debug("dmi-sysfs: unloading.\n");
694948af1f0SMike Waychison cleanup_entry_list();
695948af1f0SMike Waychison kset_unregister(dmi_kset);
696948af1f0SMike Waychison }
697948af1f0SMike Waychison
698948af1f0SMike Waychison module_init(dmi_sysfs_init);
699948af1f0SMike Waychison module_exit(dmi_sysfs_exit);
700
701 MODULE_AUTHOR("Mike Waychison <mikew@google.com>");
702 MODULE_DESCRIPTION("DMI sysfs support");
703 MODULE_LICENSE("GPL");
704