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