diff options
author | Mike Waychison <mikew@google.com> | 2011-02-22 20:53:26 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-02-25 15:02:13 -0500 |
commit | 925a1da7477fc4ba5849c6f0243934fa5072493c (patch) | |
tree | 969e9b156cc5a027de3f44c2936d140857eb39a6 /drivers/firmware | |
parent | 948af1f0bbc8526448e8cbe3f8d3bf211bdf5181 (diff) |
firmware: Break out system_event_log in dmi-sysfs
The optional type 15 entry of the DMI table describes a non-volatile
storage-backed system event log.
In preparation for the next commit which exposes the raw bits of the
event log to userland, create a new sub-directory within the dmi entry
called "system_event_log" and expose attribute files that describe the
event log itself.
Currently, only a single child object is permitted within a
dmi_sysfs_entry. We simply point at this child from the dmi_sysfs_entry
if it exists.
Signed-off-by: Mike Waychison <mikew@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/dmi-sysfs.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index 2d8a04a95085..d20961011039 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c | |||
@@ -35,6 +35,7 @@ struct dmi_sysfs_entry { | |||
35 | int instance; | 35 | int instance; |
36 | int position; | 36 | int position; |
37 | struct list_head list; | 37 | struct list_head list; |
38 | struct kobject *child; | ||
38 | }; | 39 | }; |
39 | 40 | ||
40 | /* | 41 | /* |
@@ -77,6 +78,10 @@ struct dmi_sysfs_mapped_attribute dmi_sysfs_attr_##_entry##_##_name = { \ | |||
77 | /************************************************* | 78 | /************************************************* |
78 | * Generic DMI entry support. | 79 | * Generic DMI entry support. |
79 | *************************************************/ | 80 | *************************************************/ |
81 | static void dmi_entry_free(struct kobject *kobj) | ||
82 | { | ||
83 | kfree(kobj); | ||
84 | } | ||
80 | 85 | ||
81 | static struct dmi_sysfs_entry *to_entry(struct kobject *kobj) | 86 | static struct dmi_sysfs_entry *to_entry(struct kobject *kobj) |
82 | { | 87 | { |
@@ -186,6 +191,146 @@ static size_t dmi_entry_length(const struct dmi_header *dh) | |||
186 | } | 191 | } |
187 | 192 | ||
188 | /************************************************* | 193 | /************************************************* |
194 | * Support bits for specialized DMI entry support | ||
195 | *************************************************/ | ||
196 | struct dmi_entry_attr_show_data { | ||
197 | struct attribute *attr; | ||
198 | char *buf; | ||
199 | }; | ||
200 | |||
201 | static ssize_t dmi_entry_attr_show_helper(struct dmi_sysfs_entry *entry, | ||
202 | const struct dmi_header *dh, | ||
203 | void *_data) | ||
204 | { | ||
205 | struct dmi_entry_attr_show_data *data = _data; | ||
206 | struct dmi_sysfs_mapped_attribute *attr; | ||
207 | |||
208 | attr = container_of(data->attr, | ||
209 | struct dmi_sysfs_mapped_attribute, attr); | ||
210 | return attr->show(entry, dh, data->buf); | ||
211 | } | ||
212 | |||
213 | static ssize_t dmi_entry_attr_show(struct kobject *kobj, | ||
214 | struct attribute *attr, | ||
215 | char *buf) | ||
216 | { | ||
217 | struct dmi_entry_attr_show_data data = { | ||
218 | .attr = attr, | ||
219 | .buf = buf, | ||
220 | }; | ||
221 | /* Find the entry according to our parent and call the | ||
222 | * normalized show method hanging off of the attribute */ | ||
223 | return find_dmi_entry(to_entry(kobj->parent), | ||
224 | dmi_entry_attr_show_helper, &data); | ||
225 | } | ||
226 | |||
227 | static const struct sysfs_ops dmi_sysfs_specialize_attr_ops = { | ||
228 | .show = dmi_entry_attr_show, | ||
229 | }; | ||
230 | |||
231 | /************************************************* | ||
232 | * Specialized DMI entry support. | ||
233 | *************************************************/ | ||
234 | |||
235 | /*** Type 15 - System Event Table ***/ | ||
236 | |||
237 | #define DMI_SEL_ACCESS_METHOD_IO8 0x00 | ||
238 | #define DMI_SEL_ACCESS_METHOD_IO2x8 0x01 | ||
239 | #define DMI_SEL_ACCESS_METHOD_IO16 0x02 | ||
240 | #define DMI_SEL_ACCESS_METHOD_PHYS32 0x03 | ||
241 | #define DMI_SEL_ACCESS_METHOD_GPNV 0x04 | ||
242 | |||
243 | struct dmi_system_event_log { | ||
244 | struct dmi_header header; | ||
245 | u16 area_length; | ||
246 | u16 header_start_offset; | ||
247 | u16 data_start_offset; | ||
248 | u8 access_method; | ||
249 | u8 status; | ||
250 | u32 change_token; | ||
251 | union { | ||
252 | struct { | ||
253 | u16 index_addr; | ||
254 | u16 data_addr; | ||
255 | } io; | ||
256 | u32 phys_addr32; | ||
257 | u16 gpnv_handle; | ||
258 | u32 access_method_address; | ||
259 | }; | ||
260 | u8 header_format; | ||
261 | u8 type_descriptors_supported_count; | ||
262 | u8 per_log_type_descriptor_length; | ||
263 | u8 supported_log_type_descriptos[0]; | ||
264 | } __packed; | ||
265 | |||
266 | static const struct dmi_system_event_log *to_sel(const struct dmi_header *dh) | ||
267 | { | ||
268 | return (const struct dmi_system_event_log *)dh; | ||
269 | } | ||
270 | |||
271 | #define DMI_SYSFS_SEL_FIELD(_field) \ | ||
272 | static ssize_t dmi_sysfs_sel_##_field(struct dmi_sysfs_entry *entry, \ | ||
273 | const struct dmi_header *dh, \ | ||
274 | char *buf) \ | ||
275 | { \ | ||
276 | const struct dmi_system_event_log *sel = to_sel(dh); \ | ||
277 | if (sizeof(*sel) > dmi_entry_length(dh)) \ | ||
278 | return -EIO; \ | ||
279 | return sprintf(buf, "%u\n", sel->_field); \ | ||
280 | } \ | ||
281 | static DMI_SYSFS_MAPPED_ATTR(sel, _field) | ||
282 | |||
283 | DMI_SYSFS_SEL_FIELD(area_length); | ||
284 | DMI_SYSFS_SEL_FIELD(header_start_offset); | ||
285 | DMI_SYSFS_SEL_FIELD(data_start_offset); | ||
286 | DMI_SYSFS_SEL_FIELD(access_method); | ||
287 | DMI_SYSFS_SEL_FIELD(status); | ||
288 | DMI_SYSFS_SEL_FIELD(change_token); | ||
289 | DMI_SYSFS_SEL_FIELD(access_method_address); | ||
290 | DMI_SYSFS_SEL_FIELD(header_format); | ||
291 | DMI_SYSFS_SEL_FIELD(type_descriptors_supported_count); | ||
292 | DMI_SYSFS_SEL_FIELD(per_log_type_descriptor_length); | ||
293 | |||
294 | static struct attribute *dmi_sysfs_sel_attrs[] = { | ||
295 | &dmi_sysfs_attr_sel_area_length.attr, | ||
296 | &dmi_sysfs_attr_sel_header_start_offset.attr, | ||
297 | &dmi_sysfs_attr_sel_data_start_offset.attr, | ||
298 | &dmi_sysfs_attr_sel_access_method.attr, | ||
299 | &dmi_sysfs_attr_sel_status.attr, | ||
300 | &dmi_sysfs_attr_sel_change_token.attr, | ||
301 | &dmi_sysfs_attr_sel_access_method_address.attr, | ||
302 | &dmi_sysfs_attr_sel_header_format.attr, | ||
303 | &dmi_sysfs_attr_sel_type_descriptors_supported_count.attr, | ||
304 | &dmi_sysfs_attr_sel_per_log_type_descriptor_length.attr, | ||
305 | NULL, | ||
306 | }; | ||
307 | |||
308 | |||
309 | static struct kobj_type dmi_system_event_log_ktype = { | ||
310 | .release = dmi_entry_free, | ||
311 | .sysfs_ops = &dmi_sysfs_specialize_attr_ops, | ||
312 | .default_attrs = dmi_sysfs_sel_attrs, | ||
313 | }; | ||
314 | |||
315 | static int dmi_system_event_log(struct dmi_sysfs_entry *entry) | ||
316 | { | ||
317 | int ret; | ||
318 | |||
319 | entry->child = kzalloc(sizeof(*entry->child), GFP_KERNEL); | ||
320 | if (!entry->child) | ||
321 | return -ENOMEM; | ||
322 | ret = kobject_init_and_add(entry->child, | ||
323 | &dmi_system_event_log_ktype, | ||
324 | &entry->kobj, | ||
325 | "system_event_log"); | ||
326 | if (ret) | ||
327 | goto out_free; | ||
328 | out_free: | ||
329 | kfree(entry->child); | ||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | /************************************************* | ||
189 | * Generic DMI entry support. | 334 | * Generic DMI entry support. |
190 | *************************************************/ | 335 | *************************************************/ |
191 | 336 | ||
@@ -325,6 +470,18 @@ static void __init dmi_sysfs_register_handle(const struct dmi_header *dh, | |||
325 | list_add_tail(&entry->list, &entry_list); | 470 | list_add_tail(&entry->list, &entry_list); |
326 | spin_unlock(&entry_list_lock); | 471 | spin_unlock(&entry_list_lock); |
327 | 472 | ||
473 | /* Handle specializations by type */ | ||
474 | switch (dh->type) { | ||
475 | case DMI_ENTRY_SYSTEM_EVENT_LOG: | ||
476 | *ret = dmi_system_event_log(entry); | ||
477 | break; | ||
478 | default: | ||
479 | /* No specialization */ | ||
480 | break; | ||
481 | } | ||
482 | if (*ret) | ||
483 | goto out_err; | ||
484 | |||
328 | /* Create the raw binary file to access the entry */ | 485 | /* Create the raw binary file to access the entry */ |
329 | *ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr); | 486 | *ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr); |
330 | if (*ret) | 487 | if (*ret) |
@@ -332,6 +489,7 @@ static void __init dmi_sysfs_register_handle(const struct dmi_header *dh, | |||
332 | 489 | ||
333 | return; | 490 | return; |
334 | out_err: | 491 | out_err: |
492 | kobject_put(entry->child); | ||
335 | kobject_put(&entry->kobj); | 493 | kobject_put(&entry->kobj); |
336 | return; | 494 | return; |
337 | } | 495 | } |
@@ -342,6 +500,7 @@ static void cleanup_entry_list(void) | |||
342 | 500 | ||
343 | /* No locks, we are on our way out */ | 501 | /* No locks, we are on our way out */ |
344 | list_for_each_entry_safe(entry, next, &entry_list, list) { | 502 | list_for_each_entry_safe(entry, next, &entry_list, list) { |
503 | kobject_put(entry->child); | ||
345 | kobject_put(&entry->kobj); | 504 | kobject_put(&entry->kobj); |
346 | } | 505 | } |
347 | } | 506 | } |