aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/efivars.c191
1 files changed, 189 insertions, 2 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 5f29aafd4462..2bbb22670d2d 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
@@ -161,18 +164,28 @@ utf8_strsize(efi_char16_t *data, unsigned long maxlength)
161} 164}
162 165
163static efi_status_t 166static efi_status_t
164get_var_data(struct efivars *efivars, struct efi_variable *var) 167get_var_data_locked(struct efivars *efivars, struct efi_variable *var)
165{ 168{
166 efi_status_t status; 169 efi_status_t status;
167 170
168 spin_lock(&efivars->lock);
169 var->DataSize = 1024; 171 var->DataSize = 1024;
170 status = efivars->ops->get_variable(var->VariableName, 172 status = efivars->ops->get_variable(var->VariableName,
171 &var->VendorGuid, 173 &var->VendorGuid,
172 &var->Attributes, 174 &var->Attributes,
173 &var->DataSize, 175 &var->DataSize,
174 var->Data); 176 var->Data);
177 return status;
178}
179
180static efi_status_t
181get_var_data(struct efivars *efivars, struct efi_variable *var)
182{
183 efi_status_t status;
184
185 spin_lock(&efivars->lock);
186 status = get_var_data_locked(efivars, var);
175 spin_unlock(&efivars->lock); 187 spin_unlock(&efivars->lock);
188
176 if (status != EFI_SUCCESS) { 189 if (status != EFI_SUCCESS) {
177 printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n", 190 printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
178 status); 191 status);
@@ -387,12 +400,176 @@ static struct kobj_type efivar_ktype = {
387 .default_attrs = def_attrs, 400 .default_attrs = def_attrs,
388}; 401};
389 402
403static struct pstore_info efi_pstore_info;
404
390static inline void 405static inline void
391efivar_unregister(struct efivar_entry *var) 406efivar_unregister(struct efivar_entry *var)
392{ 407{
393 kobject_put(&var->kobj); 408 kobject_put(&var->kobj);
394} 409}
395 410
411#ifdef CONFIG_PSTORE
412
413static int efi_pstore_open(struct pstore_info *psi)
414{
415 struct efivars *efivars = psi->data;
416
417 spin_lock(&efivars->lock);
418 efivars->walk_entry = list_first_entry(&efivars->list,
419 struct efivar_entry, list);
420 return 0;
421}
422
423static int efi_pstore_close(struct pstore_info *psi)
424{
425 struct efivars *efivars = psi->data;
426
427 spin_unlock(&efivars->lock);
428 return 0;
429}
430
431static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
432 struct timespec *timespec, struct pstore_info *psi)
433{
434 efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
435 struct efivars *efivars = psi->data;
436 char name[DUMP_NAME_LEN];
437 int i;
438 unsigned int part, size;
439 unsigned long time;
440
441 while (&efivars->walk_entry->list != &efivars->list) {
442 if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid,
443 vendor)) {
444 for (i = 0; i < DUMP_NAME_LEN; i++) {
445 name[i] = efivars->walk_entry->var.VariableName[i];
446 }
447 if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) {
448 *id = part;
449 timespec->tv_sec = time;
450 timespec->tv_nsec = 0;
451 get_var_data_locked(efivars, &efivars->walk_entry->var);
452 size = efivars->walk_entry->var.DataSize;
453 memcpy(psi->buf, efivars->walk_entry->var.Data, size);
454 efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
455 struct efivar_entry, list);
456 return size;
457 }
458 }
459 efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
460 struct efivar_entry, list);
461 }
462 return 0;
463}
464
465static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
466 size_t size, struct pstore_info *psi)
467{
468 char name[DUMP_NAME_LEN];
469 char stub_name[DUMP_NAME_LEN];
470 efi_char16_t efi_name[DUMP_NAME_LEN];
471 efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
472 struct efivars *efivars = psi->data;
473 struct efivar_entry *entry, *found = NULL;
474 int i;
475
476 sprintf(stub_name, "dump-type%u-%u-", type, part);
477 sprintf(name, "%s%lu", stub_name, get_seconds());
478
479 spin_lock(&efivars->lock);
480
481 for (i = 0; i < DUMP_NAME_LEN; i++)
482 efi_name[i] = stub_name[i];
483
484 /*
485 * Clean up any entries with the same name
486 */
487
488 list_for_each_entry(entry, &efivars->list, list) {
489 get_var_data_locked(efivars, &entry->var);
490
491 for (i = 0; i < DUMP_NAME_LEN; i++) {
492 if (efi_name[i] == 0) {
493 found = entry;
494 efivars->ops->set_variable(entry->var.VariableName,
495 &entry->var.VendorGuid,
496 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
497 0, NULL);
498 break;
499 } else if (efi_name[i] != entry->var.VariableName[i]) {
500 break;
501 }
502 }
503 }
504
505 if (found)
506 list_del(&found->list);
507
508 for (i = 0; i < DUMP_NAME_LEN; i++)
509 efi_name[i] = name[i];
510
511 efivars->ops->set_variable(efi_name, &vendor,
512 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
513 size, psi->buf);
514
515 spin_unlock(&efivars->lock);
516
517 if (found)
518 efivar_unregister(found);
519
520 if (size)
521 efivar_create_sysfs_entry(efivars, utf8_strsize(efi_name, DUMP_NAME_LEN * 2),
522 efi_name, &vendor);
523
524 return part;
525};
526
527static int efi_pstore_erase(enum pstore_type_id type, u64 id,
528 struct pstore_info *psi)
529{
530 efi_pstore_write(type, id, 0, psi);
531
532 return 0;
533}
534#else
535static int efi_pstore_open(struct pstore_info *psi)
536{
537 return 0;
538}
539
540static int efi_pstore_close(struct pstore_info *psi)
541{
542 return 0;
543}
544
545static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
546 struct timespec *time, struct pstore_info *psi)
547{
548 return -1;
549}
550
551static u64 efi_pstore_write(enum pstore_type_id type, int part, size_t size,
552 struct pstore_info *psi)
553{
554 return 0;
555}
556
557static int efi_pstore_erase(enum pstore_type_id type, u64 id,
558 struct pstore_info *psi)
559{
560 return 0;
561}
562#endif
563
564static struct pstore_info efi_pstore_info = {
565 .owner = THIS_MODULE,
566 .name = "efi",
567 .open = efi_pstore_open,
568 .close = efi_pstore_close,
569 .read = efi_pstore_read,
570 .write = efi_pstore_write,
571 .erase = efi_pstore_erase,
572};
396 573
397static ssize_t efivar_create(struct file *filp, struct kobject *kobj, 574static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
398 struct bin_attribute *bin_attr, 575 struct bin_attribute *bin_attr,
@@ -763,6 +940,16 @@ int register_efivars(struct efivars *efivars,
763 if (error) 940 if (error)
764 unregister_efivars(efivars); 941 unregister_efivars(efivars);
765 942
943 efivars->efi_pstore_info = efi_pstore_info;
944
945 efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
946 if (efivars->efi_pstore_info.buf) {
947 efivars->efi_pstore_info.bufsize = 1024;
948 efivars->efi_pstore_info.data = efivars;
949 mutex_init(&efivars->efi_pstore_info.buf_mutex);
950 pstore_register(&efivars->efi_pstore_info);
951 }
952
766out: 953out:
767 kfree(variable_name); 954 kfree(variable_name);
768 955