1 /* 2 * dmi-sysfs.c 3 * 4 * This module exports the DMI tables read-only to userspace through the 5 * sysfs file system. 6 * 7 * Data is currently found below 8 * /sys/firmware/dmi/... 9 * 10 * DMI attributes are presented in attribute files with names 11 * formatted using %d-%d, so that the first integer indicates the 12 * structure type (0-255), and the second field is the instance of that 13 * entry. 14 * 15 * Copyright 2011 Google, Inc. 16 */ 17 18 #include <linux/kernel.h> 19 #include <linux/init.h> 20 #include <linux/module.h> 21 #include <linux/types.h> 22 #include <linux/kobject.h> 23 #include <linux/dmi.h> 24 #include <linux/capability.h> 25 #include <linux/slab.h> 26 #include <linux/list.h> 27 #include <linux/io.h> 28 29 #define MAX_ENTRY_TYPE 255 /* Most of these aren't used, but we consider 30 the top entry type is only 8 bits */ 31 32 struct dmi_sysfs_entry { 33 struct dmi_header dh; 34 struct kobject kobj; 35 int instance; 36 int position; 37 struct list_head list; 38 }; 39 40 /* 41 * Global list of dmi_sysfs_entry. Even though this should only be 42 * manipulated at setup and teardown, the lazy nature of the kobject 43 * system means we get lazy removes. 44 */ 45 static LIST_HEAD(entry_list); 46 static DEFINE_SPINLOCK(entry_list_lock); 47 48 /* dmi_sysfs_attribute - Top level attribute. used by all entries. */ 49 struct dmi_sysfs_attribute { 50 struct attribute attr; 51 ssize_t (*show)(struct dmi_sysfs_entry *entry, char *buf); 52 }; 53 54 #define DMI_SYSFS_ATTR(_entry, _name) \ 55 struct dmi_sysfs_attribute dmi_sysfs_attr_##_entry##_##_name = { \ 56 .attr = {.name = __stringify(_name), .mode = 0400}, \ 57 .show = dmi_sysfs_##_entry##_##_name, \ 58 } 59 60 /* 61 * dmi_sysfs_mapped_attribute - Attribute where we require the entry be 62 * mapped in. Use in conjunction with dmi_sysfs_specialize_attr_ops. 63 */ 64 struct dmi_sysfs_mapped_attribute { 65 struct attribute attr; 66 ssize_t (*show)(struct dmi_sysfs_entry *entry, 67 const struct dmi_header *dh, 68 char *buf); 69 }; 70 71 #define DMI_SYSFS_MAPPED_ATTR(_entry, _name) \ 72 struct dmi_sysfs_mapped_attribute dmi_sysfs_attr_##_entry##_##_name = { \ 73 .attr = {.name = __stringify(_name), .mode = 0400}, \ 74 .show = dmi_sysfs_##_entry##_##_name, \ 75 } 76 77 /************************************************* 78 * Generic DMI entry support. 79 *************************************************/ 80 81 static struct dmi_sysfs_entry *to_entry(struct kobject *kobj) 82 { 83 return container_of(kobj, struct dmi_sysfs_entry, kobj); 84 } 85 86 static struct dmi_sysfs_attribute *to_attr(struct attribute *attr) 87 { 88 return container_of(attr, struct dmi_sysfs_attribute, attr); 89 } 90 91 static ssize_t dmi_sysfs_attr_show(struct kobject *kobj, 92 struct attribute *_attr, char *buf) 93 { 94 struct dmi_sysfs_entry *entry = to_entry(kobj); 95 struct dmi_sysfs_attribute *attr = to_attr(_attr); 96 97 /* DMI stuff is only ever admin visible */ 98 if (!capable(CAP_SYS_ADMIN)) 99 return -EACCES; 100 101 return attr->show(entry, buf); 102 } 103 104 static const struct sysfs_ops dmi_sysfs_attr_ops = { 105 .show = dmi_sysfs_attr_show, 106 }; 107 108 typedef ssize_t (*dmi_callback)(struct dmi_sysfs_entry *, 109 const struct dmi_header *dh, void *); 110 111 struct find_dmi_data { 112 struct dmi_sysfs_entry *entry; 113 dmi_callback callback; 114 void *private; 115 int instance_countdown; 116 ssize_t ret; 117 }; 118 119 static void find_dmi_entry_helper(const struct dmi_header *dh, 120 void *_data) 121 { 122 struct find_dmi_data *data = _data; 123 struct dmi_sysfs_entry *entry = data->entry; 124 125 /* Is this the entry we want? */ 126 if (dh->type != entry->dh.type) 127 return; 128 129 if (data->instance_countdown != 0) { 130 /* try the next instance? */ 131 data->instance_countdown--; 132 return; 133 } 134 135 /* 136 * Don't ever revisit the instance. Short circuit later 137 * instances by letting the instance_countdown run negative 138 */ 139 data->instance_countdown--; 140 141 /* Found the entry */ 142 data->ret = data->callback(entry, dh, data->private); 143 } 144 145 /* State for passing the read parameters through dmi_find_entry() */ 146 struct dmi_read_state { 147 char *buf; 148 loff_t pos; 149 size_t count; 150 }; 151 152 static ssize_t find_dmi_entry(struct dmi_sysfs_entry *entry, 153 dmi_callback callback, void *private) 154 { 155 struct find_dmi_data data = { 156 .entry = entry, 157 .callback = callback, 158 .private = private, 159 .instance_countdown = entry->instance, 160 .ret = -EIO, /* To signal the entry disappeared */ 161 }; 162 int ret; 163 164 ret = dmi_walk(find_dmi_entry_helper, &data); 165 /* This shouldn't happen, but just in case. */ 166 if (ret) 167 return -EINVAL; 168 return data.ret; 169 } 170 171 /* 172 * Calculate and return the byte length of the dmi entry identified by 173 * dh. This includes both the formatted portion as well as the 174 * unformatted string space, including the two trailing nul characters. 175 */ 176 static size_t dmi_entry_length(const struct dmi_header *dh) 177 { 178 const char *p = (const char *)dh; 179 180 p += dh->length; 181 182 while (p[0] || p[1]) 183 p++; 184 185 return 2 + p - (const char *)dh; 186 } 187 188 /************************************************* 189 * Generic DMI entry support. 190 *************************************************/ 191 192 static ssize_t dmi_sysfs_entry_length(struct dmi_sysfs_entry *entry, char *buf) 193 { 194 return sprintf(buf, "%d\n", entry->dh.length); 195 } 196 197 static ssize_t dmi_sysfs_entry_handle(struct dmi_sysfs_entry *entry, char *buf) 198 { 199 return sprintf(buf, "%d\n", entry->dh.handle); 200 } 201 202 static ssize_t dmi_sysfs_entry_type(struct dmi_sysfs_entry *entry, char *buf) 203 { 204 return sprintf(buf, "%d\n", entry->dh.type); 205 } 206 207 static ssize_t dmi_sysfs_entry_instance(struct dmi_sysfs_entry *entry, 208 char *buf) 209 { 210 return sprintf(buf, "%d\n", entry->instance); 211 } 212 213 static ssize_t dmi_sysfs_entry_position(struct dmi_sysfs_entry *entry, 214 char *buf) 215 { 216 return sprintf(buf, "%d\n", entry->position); 217 } 218 219 static DMI_SYSFS_ATTR(entry, length); 220 static DMI_SYSFS_ATTR(entry, handle); 221 static DMI_SYSFS_ATTR(entry, type); 222 static DMI_SYSFS_ATTR(entry, instance); 223 static DMI_SYSFS_ATTR(entry, position); 224 225 static struct attribute *dmi_sysfs_entry_attrs[] = { 226 &dmi_sysfs_attr_entry_length.attr, 227 &dmi_sysfs_attr_entry_handle.attr, 228 &dmi_sysfs_attr_entry_type.attr, 229 &dmi_sysfs_attr_entry_instance.attr, 230 &dmi_sysfs_attr_entry_position.attr, 231 NULL, 232 }; 233 234 static ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry, 235 const struct dmi_header *dh, 236 void *_state) 237 { 238 struct dmi_read_state *state = _state; 239 size_t entry_length; 240 241 entry_length = dmi_entry_length(dh); 242 243 return memory_read_from_buffer(state->buf, state->count, 244 &state->pos, dh, entry_length); 245 } 246 247 static ssize_t dmi_entry_raw_read(struct file *filp, 248 struct kobject *kobj, 249 struct bin_attribute *bin_attr, 250 char *buf, loff_t pos, size_t count) 251 { 252 struct dmi_sysfs_entry *entry = to_entry(kobj); 253 struct dmi_read_state state = { 254 .buf = buf, 255 .pos = pos, 256 .count = count, 257 }; 258 259 return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state); 260 } 261 262 static const struct bin_attribute dmi_entry_raw_attr = { 263 .attr = {.name = "raw", .mode = 0400}, 264 .read = dmi_entry_raw_read, 265 }; 266 267 static void dmi_sysfs_entry_release(struct kobject *kobj) 268 { 269 struct dmi_sysfs_entry *entry = to_entry(kobj); 270 sysfs_remove_bin_file(&entry->kobj, &dmi_entry_raw_attr); 271 spin_lock(&entry_list_lock); 272 list_del(&entry->list); 273 spin_unlock(&entry_list_lock); 274 kfree(entry); 275 } 276 277 static struct kobj_type dmi_sysfs_entry_ktype = { 278 .release = dmi_sysfs_entry_release, 279 .sysfs_ops = &dmi_sysfs_attr_ops, 280 .default_attrs = dmi_sysfs_entry_attrs, 281 }; 282 283 static struct kobject *dmi_kobj; 284 static struct kset *dmi_kset; 285 286 /* Global count of all instances seen. Only for setup */ 287 static int __initdata instance_counts[MAX_ENTRY_TYPE + 1]; 288 289 /* Global positional count of all entries seen. Only for setup */ 290 static int __initdata position_count; 291 292 static void __init dmi_sysfs_register_handle(const struct dmi_header *dh, 293 void *_ret) 294 { 295 struct dmi_sysfs_entry *entry; 296 int *ret = _ret; 297 298 /* If a previous entry saw an error, short circuit */ 299 if (*ret) 300 return; 301 302 /* Allocate and register a new entry into the entries set */ 303 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 304 if (!entry) { 305 *ret = -ENOMEM; 306 return; 307 } 308 309 /* Set the key */ 310 entry->dh = *dh; 311 entry->instance = instance_counts[dh->type]++; 312 entry->position = position_count++; 313 314 entry->kobj.kset = dmi_kset; 315 *ret = kobject_init_and_add(&entry->kobj, &dmi_sysfs_entry_ktype, NULL, 316 "%d-%d", dh->type, entry->instance); 317 318 if (*ret) { 319 kfree(entry); 320 return; 321 } 322 323 /* Thread on the global list for cleanup */ 324 spin_lock(&entry_list_lock); 325 list_add_tail(&entry->list, &entry_list); 326 spin_unlock(&entry_list_lock); 327 328 /* Create the raw binary file to access the entry */ 329 *ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr); 330 if (*ret) 331 goto out_err; 332 333 return; 334 out_err: 335 kobject_put(&entry->kobj); 336 return; 337 } 338 339 static void cleanup_entry_list(void) 340 { 341 struct dmi_sysfs_entry *entry, *next; 342 343 /* No locks, we are on our way out */ 344 list_for_each_entry_safe(entry, next, &entry_list, list) { 345 kobject_put(&entry->kobj); 346 } 347 } 348 349 static int __init dmi_sysfs_init(void) 350 { 351 int error = -ENOMEM; 352 int val; 353 354 /* Set up our directory */ 355 dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); 356 if (!dmi_kobj) 357 goto err; 358 359 dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); 360 if (!dmi_kset) 361 goto err; 362 363 val = 0; 364 error = dmi_walk(dmi_sysfs_register_handle, &val); 365 if (error) 366 goto err; 367 if (val) { 368 error = val; 369 goto err; 370 } 371 372 pr_debug("dmi-sysfs: loaded.\n"); 373 374 return 0; 375 err: 376 cleanup_entry_list(); 377 kset_unregister(dmi_kset); 378 kobject_put(dmi_kobj); 379 return error; 380 } 381 382 /* clean up everything. */ 383 static void __exit dmi_sysfs_exit(void) 384 { 385 pr_debug("dmi-sysfs: unloading.\n"); 386 cleanup_entry_list(); 387 kset_unregister(dmi_kset); 388 kobject_put(dmi_kobj); 389 } 390 391 module_init(dmi_sysfs_init); 392 module_exit(dmi_sysfs_exit); 393 394 MODULE_AUTHOR("Mike Waychison <[email protected]>"); 395 MODULE_DESCRIPTION("DMI sysfs support"); 396 MODULE_LICENSE("GPL"); 397