diff options
author | Jeremy Kerr <jeremy.kerr@canonical.com> | 2012-10-11 09:19:11 -0400 |
---|---|---|
committer | Matt Fleming <matt.fleming@intel.com> | 2012-10-30 06:39:24 -0400 |
commit | f5f6a60ad52fc786c517303590f1efaea614c69b (patch) | |
tree | bb8d8ed9304fea80c240273406385a4e04c2182f /drivers/firmware/efivars.c | |
parent | 5ba6e2919b9e18a051e5bdd6c21f52ee4976513f (diff) |
efivarfs: Implement exclusive access for {get,set}_variable
Currently, efivarfs does not enforce exclusion over the get_variable and
set_variable operations. Section 7.1 of UEFI requires us to only allow a
single processor to enter {get,set}_variable services at once.
This change acquires the efivars->lock over calls to these operations
from the efivarfs paths.
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'drivers/firmware/efivars.c')
-rw-r--r-- | drivers/firmware/efivars.c | 68 |
1 files changed, 43 insertions, 25 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 216086d88380..d478c568ce4f 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
@@ -690,35 +690,45 @@ static ssize_t efivarfs_file_write(struct file *file, | |||
690 | goto out; | 690 | goto out; |
691 | } | 691 | } |
692 | 692 | ||
693 | /* | ||
694 | * The lock here protects the get_variable call, the conditional | ||
695 | * set_variable call, and removal of the variable from the efivars | ||
696 | * list (in the case of an authenticated delete). | ||
697 | */ | ||
698 | spin_lock(&efivars->lock); | ||
699 | |||
693 | status = efivars->ops->set_variable(var->var.VariableName, | 700 | status = efivars->ops->set_variable(var->var.VariableName, |
694 | &var->var.VendorGuid, | 701 | &var->var.VendorGuid, |
695 | attributes, datasize, | 702 | attributes, datasize, |
696 | data); | 703 | data); |
697 | 704 | ||
698 | switch (status) { | 705 | if (status != EFI_SUCCESS) { |
699 | case EFI_SUCCESS: | 706 | spin_unlock(&efivars->lock); |
700 | break; | 707 | kfree(data); |
701 | case EFI_INVALID_PARAMETER: | 708 | |
702 | count = -EINVAL; | 709 | switch (status) { |
703 | goto out; | 710 | case EFI_INVALID_PARAMETER: |
704 | case EFI_OUT_OF_RESOURCES: | 711 | count = -EINVAL; |
705 | count = -ENOSPC; | 712 | break; |
706 | goto out; | 713 | case EFI_OUT_OF_RESOURCES: |
707 | case EFI_DEVICE_ERROR: | 714 | count = -ENOSPC; |
708 | count = -EIO; | 715 | break; |
709 | goto out; | 716 | case EFI_DEVICE_ERROR: |
710 | case EFI_WRITE_PROTECTED: | 717 | count = -EIO; |
711 | count = -EROFS; | 718 | break; |
712 | goto out; | 719 | case EFI_WRITE_PROTECTED: |
713 | case EFI_SECURITY_VIOLATION: | 720 | count = -EROFS; |
714 | count = -EACCES; | 721 | break; |
715 | goto out; | 722 | case EFI_SECURITY_VIOLATION: |
716 | case EFI_NOT_FOUND: | 723 | count = -EACCES; |
717 | count = -ENOENT; | 724 | break; |
718 | goto out; | 725 | case EFI_NOT_FOUND: |
719 | default: | 726 | count = -ENOENT; |
720 | count = -EINVAL; | 727 | break; |
721 | goto out; | 728 | default: |
729 | count = -EINVAL; | ||
730 | } | ||
731 | return count; | ||
722 | } | 732 | } |
723 | 733 | ||
724 | /* | 734 | /* |
@@ -734,12 +744,12 @@ static ssize_t efivarfs_file_write(struct file *file, | |||
734 | NULL); | 744 | NULL); |
735 | 745 | ||
736 | if (status == EFI_BUFFER_TOO_SMALL) { | 746 | if (status == EFI_BUFFER_TOO_SMALL) { |
747 | spin_unlock(&efivars->lock); | ||
737 | mutex_lock(&inode->i_mutex); | 748 | mutex_lock(&inode->i_mutex); |
738 | i_size_write(inode, newdatasize + sizeof(attributes)); | 749 | i_size_write(inode, newdatasize + sizeof(attributes)); |
739 | mutex_unlock(&inode->i_mutex); | 750 | mutex_unlock(&inode->i_mutex); |
740 | 751 | ||
741 | } else if (status == EFI_NOT_FOUND) { | 752 | } else if (status == EFI_NOT_FOUND) { |
742 | spin_lock(&efivars->lock); | ||
743 | list_del(&var->list); | 753 | list_del(&var->list); |
744 | spin_unlock(&efivars->lock); | 754 | spin_unlock(&efivars->lock); |
745 | efivar_unregister(var); | 755 | efivar_unregister(var); |
@@ -747,6 +757,7 @@ static ssize_t efivarfs_file_write(struct file *file, | |||
747 | dput(file->f_dentry); | 757 | dput(file->f_dentry); |
748 | 758 | ||
749 | } else { | 759 | } else { |
760 | spin_unlock(&efivars->lock); | ||
750 | pr_warn("efivarfs: inconsistent EFI variable implementation? " | 761 | pr_warn("efivarfs: inconsistent EFI variable implementation? " |
751 | "status = %lx\n", status); | 762 | "status = %lx\n", status); |
752 | } | 763 | } |
@@ -768,9 +779,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, | |||
768 | void *data; | 779 | void *data; |
769 | ssize_t size = 0; | 780 | ssize_t size = 0; |
770 | 781 | ||
782 | spin_lock(&efivars->lock); | ||
771 | status = efivars->ops->get_variable(var->var.VariableName, | 783 | status = efivars->ops->get_variable(var->var.VariableName, |
772 | &var->var.VendorGuid, | 784 | &var->var.VendorGuid, |
773 | &attributes, &datasize, NULL); | 785 | &attributes, &datasize, NULL); |
786 | spin_unlock(&efivars->lock); | ||
774 | 787 | ||
775 | if (status != EFI_BUFFER_TOO_SMALL) | 788 | if (status != EFI_BUFFER_TOO_SMALL) |
776 | return 0; | 789 | return 0; |
@@ -780,10 +793,13 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, | |||
780 | if (!data) | 793 | if (!data) |
781 | return 0; | 794 | return 0; |
782 | 795 | ||
796 | spin_lock(&efivars->lock); | ||
783 | status = efivars->ops->get_variable(var->var.VariableName, | 797 | status = efivars->ops->get_variable(var->var.VariableName, |
784 | &var->var.VendorGuid, | 798 | &var->var.VendorGuid, |
785 | &attributes, &datasize, | 799 | &attributes, &datasize, |
786 | (data + 4)); | 800 | (data + 4)); |
801 | spin_unlock(&efivars->lock); | ||
802 | |||
787 | if (status != EFI_SUCCESS) | 803 | if (status != EFI_SUCCESS) |
788 | goto out_free; | 804 | goto out_free; |
789 | 805 | ||
@@ -1004,11 +1020,13 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
1004 | /* copied by the above to local storage in the dentry. */ | 1020 | /* copied by the above to local storage in the dentry. */ |
1005 | kfree(name); | 1021 | kfree(name); |
1006 | 1022 | ||
1023 | spin_lock(&efivars->lock); | ||
1007 | efivars->ops->get_variable(entry->var.VariableName, | 1024 | efivars->ops->get_variable(entry->var.VariableName, |
1008 | &entry->var.VendorGuid, | 1025 | &entry->var.VendorGuid, |
1009 | &entry->var.Attributes, | 1026 | &entry->var.Attributes, |
1010 | &size, | 1027 | &size, |
1011 | NULL); | 1028 | NULL); |
1029 | spin_unlock(&efivars->lock); | ||
1012 | 1030 | ||
1013 | mutex_lock(&inode->i_mutex); | 1031 | mutex_lock(&inode->i_mutex); |
1014 | inode->i_private = entry; | 1032 | inode->i_private = entry; |