diff options
| -rw-r--r-- | drivers/firmware/efivars.c | 191 | ||||
| -rw-r--r-- | include/linux/efi.h | 6 |
2 files changed, 195 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"); | |||
| 89 | MODULE_LICENSE("GPL"); | 90 | MODULE_LICENSE("GPL"); |
| 90 | MODULE_VERSION(EFIVARS_VERSION); | 91 | MODULE_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 | ||
| 163 | static efi_status_t | 166 | static efi_status_t |
| 164 | get_var_data(struct efivars *efivars, struct efi_variable *var) | 167 | get_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 | |||
| 180 | static efi_status_t | ||
| 181 | get_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 | ||
| 403 | static struct pstore_info efi_pstore_info; | ||
| 404 | |||
| 390 | static inline void | 405 | static inline void |
| 391 | efivar_unregister(struct efivar_entry *var) | 406 | efivar_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 | |||
| 413 | static 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 | |||
| 423 | static 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 | |||
| 431 | static 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 | |||
| 465 | static 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 | |||
| 527 | static 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; | ||
