aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-08-01 19:40:51 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-08-01 19:40:51 -0400
commita2d773023552f68baa2db2226dfd6d761c0df5da (patch)
treec905702c4f318d0e569222187472289c574bde3c /drivers/firmware
parent72f9adfd20e3be8a33ff3ef96cec787ed97b9ba9 (diff)
parent7644c16c7e7431fa398e834109dbb76dc1b51617 (diff)
Merge branch 'pstore-efi' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6
* 'pstore-efi' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6: efivars: Introduce PSTORE_EFI_ATTRIBUTES efivars: Use string functions in pstore_write efivars: introduce utf16_strncmp efivars: String functions efi: Add support for using efivars as a pstore backend pstore: Allow the user to explicitly choose a backend pstore: Make "part" unsigned pstore: Add extra context for writes and erases pstore: Extend API for more flexibility in new backends
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/efivars.c243
1 files changed, 231 insertions, 12 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 5f29aafd4462..eacb05e6cfb3 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -78,6 +78,7 @@
78#include <linux/kobject.h> 78#include <linux/kobject.h>
79#include <linux/device.h> 79#include <linux/device.h>
80#include <linux/slab.h> 80#include <linux/slab.h>
81#include <linux/pstore.h>
81 82
82#include <asm/uaccess.h> 83#include <asm/uaccess.h>
83 84
@@ -89,6 +90,8 @@ MODULE_DESCRIPTION("sysfs interface to EFI Variables");
89MODULE_LICENSE("GPL"); 90MODULE_LICENSE("GPL");
90MODULE_VERSION(EFIVARS_VERSION); 91MODULE_VERSION(EFIVARS_VERSION);
91 92
93#define DUMP_NAME_LEN 52
94
92/* 95/*
93 * The maximum size of VariableName + Data = 1024 96 * The maximum size of VariableName + Data = 1024
94 * Therefore, it's reasonable to save that much 97 * Therefore, it's reasonable to save that much
@@ -119,6 +122,10 @@ struct efivar_attribute {
119 ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count); 122 ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
120}; 123};
121 124
125#define PSTORE_EFI_ATTRIBUTES \
126 (EFI_VARIABLE_NON_VOLATILE | \
127 EFI_VARIABLE_BOOTSERVICE_ACCESS | \
128 EFI_VARIABLE_RUNTIME_ACCESS)
122 129
123#define EFIVAR_ATTR(_name, _mode, _show, _store) \ 130#define EFIVAR_ATTR(_name, _mode, _show, _store) \
124struct efivar_attribute efivar_attr_##_name = { \ 131struct efivar_attribute efivar_attr_##_name = { \
@@ -141,38 +148,72 @@ efivar_create_sysfs_entry(struct efivars *efivars,
141 148
142/* Return the number of unicode characters in data */ 149/* Return the number of unicode characters in data */
143static unsigned long 150static unsigned long
144utf8_strlen(efi_char16_t *data, unsigned long maxlength) 151utf16_strnlen(efi_char16_t *s, size_t maxlength)
145{ 152{
146 unsigned long length = 0; 153 unsigned long length = 0;
147 154
148 while (*data++ != 0 && length < maxlength) 155 while (*s++ != 0 && length < maxlength)
149 length++; 156 length++;
150 return length; 157 return length;
151} 158}
152 159
160static unsigned long
161utf16_strlen(efi_char16_t *s)
162{
163 return utf16_strnlen(s, ~0UL);
164}
165
153/* 166/*
154 * Return the number of bytes is the length of this string 167 * Return the number of bytes is the length of this string
155 * Note: this is NOT the same as the number of unicode characters 168 * Note: this is NOT the same as the number of unicode characters
156 */ 169 */
157static inline unsigned long 170static inline unsigned long
158utf8_strsize(efi_char16_t *data, unsigned long maxlength) 171utf16_strsize(efi_char16_t *data, unsigned long maxlength)
159{ 172{
160 return utf8_strlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t); 173 return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
174}
175
176static inline int
177utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len)
178{
179 while (1) {
180 if (len == 0)
181 return 0;
182 if (*a < *b)
183 return -1;
184 if (*a > *b)
185 return 1;
186 if (*a == 0) /* implies *b == 0 */
187 return 0;
188 a++;
189 b++;
190 len--;
191 }
161} 192}
162 193
163static efi_status_t 194static efi_status_t
164get_var_data(struct efivars *efivars, struct efi_variable *var) 195get_var_data_locked(struct efivars *efivars, struct efi_variable *var)
165{ 196{
166 efi_status_t status; 197 efi_status_t status;
167 198
168 spin_lock(&efivars->lock);
169 var->DataSize = 1024; 199 var->DataSize = 1024;
170 status = efivars->ops->get_variable(var->VariableName, 200 status = efivars->ops->get_variable(var->VariableName,
171 &var->VendorGuid, 201 &var->VendorGuid,
172 &var->Attributes, 202 &var->Attributes,
173 &var->DataSize, 203 &var->DataSize,
174 var->Data); 204 var->Data);
205 return status;
206}
207
208static efi_status_t
209get_var_data(struct efivars *efivars, struct efi_variable *var)
210{
211 efi_status_t status;
212
213 spin_lock(&efivars->lock);
214 status = get_var_data_locked(efivars, var);
175 spin_unlock(&efivars->lock); 215 spin_unlock(&efivars->lock);
216
176 if (status != EFI_SUCCESS) { 217 if (status != EFI_SUCCESS) {
177 printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n", 218 printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
178 status); 219 status);
@@ -387,12 +428,180 @@ static struct kobj_type efivar_ktype = {
387 .default_attrs = def_attrs, 428 .default_attrs = def_attrs,
388}; 429};
389 430
431static struct pstore_info efi_pstore_info;
432
390static inline void 433static inline void
391efivar_unregister(struct efivar_entry *var) 434efivar_unregister(struct efivar_entry *var)
392{ 435{
393 kobject_put(&var->kobj); 436 kobject_put(&var->kobj);
394} 437}
395 438
439#ifdef CONFIG_PSTORE
440
441static int efi_pstore_open(struct pstore_info *psi)
442{
443 struct efivars *efivars = psi->data;
444
445 spin_lock(&efivars->lock);
446 efivars->walk_entry = list_first_entry(&efivars->list,
447 struct efivar_entry, list);
448 return 0;
449}
450
451static int efi_pstore_close(struct pstore_info *psi)
452{
453 struct efivars *efivars = psi->data;
454
455 spin_unlock(&efivars->lock);
456 return 0;
457}
458
459static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
460 struct timespec *timespec, struct pstore_info *psi)
461{
462 efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
463 struct efivars *efivars = psi->data;
464 char name[DUMP_NAME_LEN];
465 int i;
466 unsigned int part, size;
467 unsigned long time;
468
469 while (&efivars->walk_entry->list != &efivars->list) {
470 if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid,
471 vendor)) {
472 for (i = 0; i < DUMP_NAME_LEN; i++) {
473 name[i] = efivars->walk_entry->var.VariableName[i];
474 }
475 if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) {
476 *id = part;
477 timespec->tv_sec = time;
478 timespec->tv_nsec = 0;
479 get_var_data_locked(efivars, &efivars->walk_entry->var);
480 size = efivars->walk_entry->var.DataSize;
481 memcpy(psi->buf, efivars->walk_entry->var.Data, size);
482 efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
483 struct efivar_entry, list);
484 return size;
485 }
486 }
487 efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
488 struct efivar_entry, list);
489 }
490 return 0;
491}
492
493static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
494 size_t size, struct pstore_info *psi)
495{
496 char name[DUMP_NAME_LEN];
497 char stub_name[DUMP_NAME_LEN];
498 efi_char16_t efi_name[DUMP_NAME_LEN];
499 efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
500 struct efivars *efivars = psi->data;
501 struct efivar_entry *entry, *found = NULL;
502 int i;
503
504 sprintf(stub_name, "dump-type%u-%u-", type, part);
505 sprintf(name, "%s%lu", stub_name, get_seconds());
506
507 spin_lock(&efivars->lock);
508
509 for (i = 0; i < DUMP_NAME_LEN; i++)
510 efi_name[i] = stub_name[i];
511
512 /*
513 * Clean up any entries with the same name
514 */
515
516 list_for_each_entry(entry, &efivars->list, list) {
517 get_var_data_locked(efivars, &entry->var);
518
519 if (efi_guidcmp(entry->var.VendorGuid, vendor))
520 continue;
521 if (utf16_strncmp(entry->var.VariableName, efi_name,
522 utf16_strlen(efi_name)))
523 continue;
524 /* Needs to be a prefix */
525 if (entry->var.VariableName[utf16_strlen(efi_name)] == 0)
526 continue;
527
528 /* found */
529 found = entry;
530 efivars->ops->set_variable(entry->var.VariableName,
531 &entry->var.VendorGuid,
532 PSTORE_EFI_ATTRIBUTES,
533 0, NULL);
534 }
535
536 if (found)
537 list_del(&found->list);
538
539 for (i = 0; i < DUMP_NAME_LEN; i++)
540 efi_name[i] = name[i];
541
542 efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
543 size, psi->buf);
544
545 spin_unlock(&efivars->lock);
546
547 if (found)
548 efivar_unregister(found);
549
550 if (size)
551 efivar_create_sysfs_entry(efivars,
552 utf16_strsize(efi_name,
553 DUMP_NAME_LEN * 2),
554 efi_name, &vendor);
555
556 return part;
557};
558
559static int efi_pstore_erase(enum pstore_type_id type, u64 id,
560 struct pstore_info *psi)
561{
562 efi_pstore_write(type, id, 0, psi);
563
564 return 0;
565}
566#else
567static int efi_pstore_open(struct pstore_info *psi)
568{
569 return 0;
570}
571
572static int efi_pstore_close(struct pstore_info *psi)
573{
574 return 0;
575}
576
577static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
578 struct timespec *time, struct pstore_info *psi)
579{
580 return -1;
581}
582
583static u64 efi_pstore_write(enum pstore_type_id type, int part, size_t size,
584 struct pstore_info *psi)
585{
586 return 0;
587}
588
589static int efi_pstore_erase(enum pstore_type_id type, u64 id,
590 struct pstore_info *psi)
591{
592 return 0;
593}
594#endif
595
596static struct pstore_info efi_pstore_info = {
597 .owner = THIS_MODULE,
598 .name = "efi",
599 .open = efi_pstore_open,
600 .close = efi_pstore_close,
601 .read = efi_pstore_read,
602 .write = efi_pstore_write,
603 .erase = efi_pstore_erase,
604};
396 605
397static ssize_t efivar_create(struct file *filp, struct kobject *kobj, 606static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
398 struct bin_attribute *bin_attr, 607 struct bin_attribute *bin_attr,
@@ -414,8 +623,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
414 * Does this variable already exist? 623 * Does this variable already exist?
415 */ 624 */
416 list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { 625 list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
417 strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024); 626 strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
418 strsize2 = utf8_strsize(new_var->VariableName, 1024); 627 strsize2 = utf16_strsize(new_var->VariableName, 1024);
419 if (strsize1 == strsize2 && 628 if (strsize1 == strsize2 &&
420 !memcmp(&(search_efivar->var.VariableName), 629 !memcmp(&(search_efivar->var.VariableName),
421 new_var->VariableName, strsize1) && 630 new_var->VariableName, strsize1) &&
@@ -447,8 +656,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
447 656
448 /* Create the entry in sysfs. Locking is not required here */ 657 /* Create the entry in sysfs. Locking is not required here */
449 status = efivar_create_sysfs_entry(efivars, 658 status = efivar_create_sysfs_entry(efivars,
450 utf8_strsize(new_var->VariableName, 659 utf16_strsize(new_var->VariableName,
451 1024), 660 1024),
452 new_var->VariableName, 661 new_var->VariableName,
453 &new_var->VendorGuid); 662 &new_var->VendorGuid);
454 if (status) { 663 if (status) {
@@ -477,8 +686,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
477 * Does this variable already exist? 686 * Does this variable already exist?
478 */ 687 */
479 list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { 688 list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
480 strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024); 689 strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
481 strsize2 = utf8_strsize(del_var->VariableName, 1024); 690 strsize2 = utf16_strsize(del_var->VariableName, 1024);
482 if (strsize1 == strsize2 && 691 if (strsize1 == strsize2 &&
483 !memcmp(&(search_efivar->var.VariableName), 692 !memcmp(&(search_efivar->var.VariableName),
484 del_var->VariableName, strsize1) && 693 del_var->VariableName, strsize1) &&
@@ -763,6 +972,16 @@ int register_efivars(struct efivars *efivars,
763 if (error) 972 if (error)
764 unregister_efivars(efivars); 973 unregister_efivars(efivars);
765 974
975 efivars->efi_pstore_info = efi_pstore_info;
976
977 efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
978 if (efivars->efi_pstore_info.buf) {
979 efivars->efi_pstore_info.bufsize = 1024;
980 efivars->efi_pstore_info.data = efivars;
981 mutex_init(&efivars->efi_pstore_info.buf_mutex);
982 pstore_register(&efivars->efi_pstore_info);
983 }
984
766out: 985out:
767 kfree(variable_name); 986 kfree(variable_name);
768 987