diff options
| -rw-r--r-- | drivers/firmware/efivars.c | 85 | ||||
| -rw-r--r-- | include/linux/efi.h | 3 |
2 files changed, 82 insertions, 6 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index a64fb7bda365..69225115304d 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
| @@ -158,6 +158,13 @@ efivar_create_sysfs_entry(struct efivars *efivars, | |||
| 158 | efi_char16_t *variable_name, | 158 | efi_char16_t *variable_name, |
| 159 | efi_guid_t *vendor_guid); | 159 | efi_guid_t *vendor_guid); |
| 160 | 160 | ||
| 161 | /* | ||
| 162 | * Prototype for workqueue functions updating sysfs entry | ||
| 163 | */ | ||
| 164 | |||
| 165 | static void efivar_update_sysfs_entries(struct work_struct *); | ||
| 166 | static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries); | ||
| 167 | |||
| 161 | /* Return the number of unicode characters in data */ | 168 | /* Return the number of unicode characters in data */ |
| 162 | static unsigned long | 169 | static unsigned long |
| 163 | utf16_strnlen(efi_char16_t *s, size_t maxlength) | 170 | utf16_strnlen(efi_char16_t *s, size_t maxlength) |
| @@ -1248,11 +1255,8 @@ static int efi_pstore_write(enum pstore_type_id type, | |||
| 1248 | 1255 | ||
| 1249 | spin_unlock_irqrestore(&efivars->lock, flags); | 1256 | spin_unlock_irqrestore(&efivars->lock, flags); |
| 1250 | 1257 | ||
| 1251 | if (size) | 1258 | if (reason == KMSG_DUMP_OOPS) |
| 1252 | ret = efivar_create_sysfs_entry(efivars, | 1259 | schedule_work(&efivar_work); |
| 1253 | utf16_strsize(efi_name, | ||
| 1254 | DUMP_NAME_LEN * 2), | ||
| 1255 | efi_name, &vendor); | ||
| 1256 | 1260 | ||
| 1257 | *id = part; | 1261 | *id = part; |
| 1258 | return ret; | 1262 | return ret; |
| @@ -1496,6 +1500,75 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, | |||
| 1496 | return count; | 1500 | return count; |
| 1497 | } | 1501 | } |
| 1498 | 1502 | ||
| 1503 | static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor) | ||
| 1504 | { | ||
| 1505 | struct efivar_entry *entry, *n; | ||
| 1506 | struct efivars *efivars = &__efivars; | ||
| 1507 | unsigned long strsize1, strsize2; | ||
| 1508 | bool found = false; | ||
| 1509 | |||
| 1510 | strsize1 = utf16_strsize(variable_name, 1024); | ||
| 1511 | list_for_each_entry_safe(entry, n, &efivars->list, list) { | ||
| 1512 | strsize2 = utf16_strsize(entry->var.VariableName, 1024); | ||
| 1513 | if (strsize1 == strsize2 && | ||
| 1514 | !memcmp(variable_name, &(entry->var.VariableName), | ||
| 1515 | strsize2) && | ||
| 1516 | !efi_guidcmp(entry->var.VendorGuid, | ||
| 1517 | *vendor)) { | ||
| 1518 | found = true; | ||
| 1519 | break; | ||
| 1520 | } | ||
| 1521 | } | ||
| 1522 | return found; | ||
| 1523 | } | ||
| 1524 | |||
| 1525 | static void efivar_update_sysfs_entries(struct work_struct *work) | ||
| 1526 | { | ||
| 1527 | struct efivars *efivars = &__efivars; | ||
| 1528 | efi_guid_t vendor; | ||
| 1529 | efi_char16_t *variable_name; | ||
| 1530 | unsigned long variable_name_size = 1024; | ||
| 1531 | efi_status_t status = EFI_NOT_FOUND; | ||
| 1532 | bool found; | ||
| 1533 | |||
| 1534 | /* Add new sysfs entries */ | ||
| 1535 | while (1) { | ||
| 1536 | variable_name = kzalloc(variable_name_size, GFP_KERNEL); | ||
| 1537 | if (!variable_name) { | ||
| 1538 | pr_err("efivars: Memory allocation failed.\n"); | ||
| 1539 | return; | ||
| 1540 | } | ||
| 1541 | |||
| 1542 | spin_lock_irq(&efivars->lock); | ||
| 1543 | found = false; | ||
| 1544 | while (1) { | ||
| 1545 | variable_name_size = 1024; | ||
| 1546 | status = efivars->ops->get_next_variable( | ||
| 1547 | &variable_name_size, | ||
| 1548 | variable_name, | ||
| 1549 | &vendor); | ||
| 1550 | if (status != EFI_SUCCESS) { | ||
| 1551 | break; | ||
| 1552 | } else { | ||
| 1553 | if (!variable_is_present(variable_name, | ||
| 1554 | &vendor)) { | ||
| 1555 | found = true; | ||
| 1556 | break; | ||
| 1557 | } | ||
| 1558 | } | ||
| 1559 | } | ||
| 1560 | spin_unlock_irq(&efivars->lock); | ||
| 1561 | |||
| 1562 | if (!found) { | ||
| 1563 | kfree(variable_name); | ||
| 1564 | break; | ||
| 1565 | } else | ||
| 1566 | efivar_create_sysfs_entry(efivars, | ||
| 1567 | variable_name_size, | ||
| 1568 | variable_name, &vendor); | ||
| 1569 | } | ||
| 1570 | } | ||
| 1571 | |||
| 1499 | /* | 1572 | /* |
| 1500 | * Let's not leave out systab information that snuck into | 1573 | * Let's not leave out systab information that snuck into |
| 1501 | * the efivars driver | 1574 | * the efivars driver |
| @@ -1833,6 +1906,8 @@ err_put: | |||
| 1833 | static void __exit | 1906 | static void __exit |
| 1834 | efivars_exit(void) | 1907 | efivars_exit(void) |
| 1835 | { | 1908 | { |
| 1909 | cancel_work_sync(&efivar_work); | ||
| 1910 | |||
| 1836 | if (efi_enabled) { | 1911 | if (efi_enabled) { |
| 1837 | unregister_efivars(&__efivars); | 1912 | unregister_efivars(&__efivars); |
| 1838 | kobject_put(efi_kobj); | 1913 | kobject_put(efi_kobj); |
diff --git a/include/linux/efi.h b/include/linux/efi.h index 8b84916dc671..d0c68aee0925 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h | |||
| @@ -728,7 +728,8 @@ struct efivars { | |||
| 728 | * 1) ->list - adds, removals, reads, writes | 728 | * 1) ->list - adds, removals, reads, writes |
| 729 | * 2) ops.[gs]et_variable() calls. | 729 | * 2) ops.[gs]et_variable() calls. |
| 730 | * It must not be held when creating sysfs entries or calling kmalloc. | 730 | * It must not be held when creating sysfs entries or calling kmalloc. |
| 731 | * ops.get_next_variable() is only called from register_efivars(), | 731 | * ops.get_next_variable() is only called from register_efivars() |
| 732 | * or efivar_update_sysfs_entries(), | ||
| 732 | * which is protected by the BKL, so that path is safe. | 733 | * which is protected by the BKL, so that path is safe. |
| 733 | */ | 734 | */ |
| 734 | spinlock_t lock; | 735 | spinlock_t lock; |
