aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2014-08-04 12:16:00 -0400
committerMatt Fleming <matt.fleming@intel.com>2014-10-03 13:40:57 -0400
commit161485e8273001e56b2f20755ad9b6217b601fb3 (patch)
tree3e99e9f4a6515c0382c7ccb3e7a3888382f4c3b3 /drivers/firmware
parent7d1311b93e58ed55f3a31cc8f94c4b8fe988a2b9 (diff)
efi: Implement mandatory locking for UEFI Runtime Services
According to section 7.1 of the UEFI spec, Runtime Services are not fully reentrant, and there are particular combinations of calls that need to be serialized. Use a spinlock to serialize all Runtime Services with respect to all others, even if this is more than strictly needed. We've managed to get away without requiring a runtime services lock until now because most of the interactions with EFI involve EFI variables, and those operations are already serialised with __efivars->lock. Some of the assumptions underlying the decision whether locks are needed or not (e.g., SetVariable() against ResetSystem()) may not apply universally to all [new] architectures that implement UEFI. Rather than try to reason our way out of this, let's just implement at least what the spec requires in terms of locking. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/efi/runtime-wrappers.c154
1 files changed, 144 insertions, 10 deletions
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index 10daa4bbb258..9694cba665c4 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -14,11 +14,83 @@
14 * This file is released under the GPLv2. 14 * This file is released under the GPLv2.
15 */ 15 */
16 16
17#include <linux/bug.h>
17#include <linux/efi.h> 18#include <linux/efi.h>
18#include <linux/spinlock.h> /* spinlock_t */ 19#include <linux/mutex.h>
20#include <linux/spinlock.h>
19#include <asm/efi.h> 21#include <asm/efi.h>
20 22
21/* 23/*
24 * According to section 7.1 of the UEFI spec, Runtime Services are not fully
25 * reentrant, and there are particular combinations of calls that need to be
26 * serialized. (source: UEFI Specification v2.4A)
27 *
28 * Table 31. Rules for Reentry Into Runtime Services
29 * +------------------------------------+-------------------------------+
30 * | If previous call is busy in | Forbidden to call |
31 * +------------------------------------+-------------------------------+
32 * | Any | SetVirtualAddressMap() |
33 * +------------------------------------+-------------------------------+
34 * | ConvertPointer() | ConvertPointer() |
35 * +------------------------------------+-------------------------------+
36 * | SetVariable() | ResetSystem() |
37 * | UpdateCapsule() | |
38 * | SetTime() | |
39 * | SetWakeupTime() | |
40 * | GetNextHighMonotonicCount() | |
41 * +------------------------------------+-------------------------------+
42 * | GetVariable() | GetVariable() |
43 * | GetNextVariableName() | GetNextVariableName() |
44 * | SetVariable() | SetVariable() |
45 * | QueryVariableInfo() | QueryVariableInfo() |
46 * | UpdateCapsule() | UpdateCapsule() |
47 * | QueryCapsuleCapabilities() | QueryCapsuleCapabilities() |
48 * | GetNextHighMonotonicCount() | GetNextHighMonotonicCount() |
49 * +------------------------------------+-------------------------------+
50 * | GetTime() | GetTime() |
51 * | SetTime() | SetTime() |
52 * | GetWakeupTime() | GetWakeupTime() |
53 * | SetWakeupTime() | SetWakeupTime() |
54 * +------------------------------------+-------------------------------+
55 *
56 * Due to the fact that the EFI pstore may write to the variable store in
57 * interrupt context, we need to use a spinlock for at least the groups that
58 * contain SetVariable() and QueryVariableInfo(). That leaves little else, as
59 * none of the remaining functions are actually ever called at runtime.
60 * So let's just use a single spinlock to serialize all Runtime Services calls.
61 */
62static DEFINE_SPINLOCK(efi_runtime_lock);
63
64/*
65 * Some runtime services calls can be reentrant under NMI, even if the table
66 * above says they are not. (source: UEFI Specification v2.4A)
67 *
68 * Table 32. Functions that may be called after Machine Check, INIT and NMI
69 * +----------------------------+------------------------------------------+
70 * | Function | Called after Machine Check, INIT and NMI |
71 * +----------------------------+------------------------------------------+
72 * | GetTime() | Yes, even if previously busy. |
73 * | GetVariable() | Yes, even if previously busy |
74 * | GetNextVariableName() | Yes, even if previously busy |
75 * | QueryVariableInfo() | Yes, even if previously busy |
76 * | SetVariable() | Yes, even if previously busy |
77 * | UpdateCapsule() | Yes, even if previously busy |
78 * | QueryCapsuleCapabilities() | Yes, even if previously busy |
79 * | ResetSystem() | Yes, even if previously busy |
80 * +----------------------------+------------------------------------------+
81 *
82 * In order to prevent deadlocks under NMI, the wrappers for these functions
83 * may only grab the efi_runtime_lock or rtc_lock spinlocks if !efi_in_nmi().
84 * However, not all of the services listed are reachable through NMI code paths,
85 * so the the special handling as suggested by the UEFI spec is only implemented
86 * for QueryVariableInfo() and SetVariable(), as these can be reached in NMI
87 * context through efi_pstore_write().
88 */
89#ifndef efi_in_nmi
90#define efi_in_nmi() (0)
91#endif
92
93/*
22 * As per commit ef68c8f87ed1 ("x86: Serialize EFI time accesses on rtc_lock"), 94 * As per commit ef68c8f87ed1 ("x86: Serialize EFI time accesses on rtc_lock"),
23 * the EFI specification requires that callers of the time related runtime 95 * the EFI specification requires that callers of the time related runtime
24 * functions serialize with other CMOS accesses in the kernel, as the EFI time 96 * functions serialize with other CMOS accesses in the kernel, as the EFI time
@@ -32,7 +104,9 @@ static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
32 efi_status_t status; 104 efi_status_t status;
33 105
34 spin_lock_irqsave(&rtc_lock, flags); 106 spin_lock_irqsave(&rtc_lock, flags);
107 spin_lock(&efi_runtime_lock);
35 status = efi_call_virt(get_time, tm, tc); 108 status = efi_call_virt(get_time, tm, tc);
109 spin_unlock(&efi_runtime_lock);
36 spin_unlock_irqrestore(&rtc_lock, flags); 110 spin_unlock_irqrestore(&rtc_lock, flags);
37 return status; 111 return status;
38} 112}
@@ -43,7 +117,9 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm)
43 efi_status_t status; 117 efi_status_t status;
44 118
45 spin_lock_irqsave(&rtc_lock, flags); 119 spin_lock_irqsave(&rtc_lock, flags);
120 spin_lock(&efi_runtime_lock);
46 status = efi_call_virt(set_time, tm); 121 status = efi_call_virt(set_time, tm);
122 spin_unlock(&efi_runtime_lock);
47 spin_unlock_irqrestore(&rtc_lock, flags); 123 spin_unlock_irqrestore(&rtc_lock, flags);
48 return status; 124 return status;
49} 125}
@@ -56,7 +132,9 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
56 efi_status_t status; 132 efi_status_t status;
57 133
58 spin_lock_irqsave(&rtc_lock, flags); 134 spin_lock_irqsave(&rtc_lock, flags);
135 spin_lock(&efi_runtime_lock);
59 status = efi_call_virt(get_wakeup_time, enabled, pending, tm); 136 status = efi_call_virt(get_wakeup_time, enabled, pending, tm);
137 spin_unlock(&efi_runtime_lock);
60 spin_unlock_irqrestore(&rtc_lock, flags); 138 spin_unlock_irqrestore(&rtc_lock, flags);
61 return status; 139 return status;
62} 140}
@@ -67,7 +145,9 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
67 efi_status_t status; 145 efi_status_t status;
68 146
69 spin_lock_irqsave(&rtc_lock, flags); 147 spin_lock_irqsave(&rtc_lock, flags);
148 spin_lock(&efi_runtime_lock);
70 status = efi_call_virt(set_wakeup_time, enabled, tm); 149 status = efi_call_virt(set_wakeup_time, enabled, tm);
150 spin_unlock(&efi_runtime_lock);
71 spin_unlock_irqrestore(&rtc_lock, flags); 151 spin_unlock_irqrestore(&rtc_lock, flags);
72 return status; 152 return status;
73} 153}
@@ -78,14 +158,27 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name,
78 unsigned long *data_size, 158 unsigned long *data_size,
79 void *data) 159 void *data)
80{ 160{
81 return efi_call_virt(get_variable, name, vendor, attr, data_size, data); 161 unsigned long flags;
162 efi_status_t status;
163
164 spin_lock_irqsave(&efi_runtime_lock, flags);
165 status = efi_call_virt(get_variable, name, vendor, attr, data_size,
166 data);
167 spin_unlock_irqrestore(&efi_runtime_lock, flags);
168 return status;
82} 169}
83 170
84static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, 171static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
85 efi_char16_t *name, 172 efi_char16_t *name,
86 efi_guid_t *vendor) 173 efi_guid_t *vendor)
87{ 174{
88 return efi_call_virt(get_next_variable, name_size, name, vendor); 175 unsigned long flags;
176 efi_status_t status;
177
178 spin_lock_irqsave(&efi_runtime_lock, flags);
179 status = efi_call_virt(get_next_variable, name_size, name, vendor);
180 spin_unlock_irqrestore(&efi_runtime_lock, flags);
181 return status;
89} 182}
90 183
91static efi_status_t virt_efi_set_variable(efi_char16_t *name, 184static efi_status_t virt_efi_set_variable(efi_char16_t *name,
@@ -94,7 +187,17 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name,
94 unsigned long data_size, 187 unsigned long data_size,
95 void *data) 188 void *data)
96{ 189{
97 return efi_call_virt(set_variable, name, vendor, attr, data_size, data); 190 unsigned long flags;
191 efi_status_t status;
192 bool __in_nmi = efi_in_nmi();
193
194 if (!__in_nmi)
195 spin_lock_irqsave(&efi_runtime_lock, flags);
196 status = efi_call_virt(set_variable, name, vendor, attr, data_size,
197 data);
198 if (!__in_nmi)
199 spin_unlock_irqrestore(&efi_runtime_lock, flags);
200 return status;
98} 201}
99 202
100static efi_status_t virt_efi_query_variable_info(u32 attr, 203static efi_status_t virt_efi_query_variable_info(u32 attr,
@@ -102,16 +205,31 @@ static efi_status_t virt_efi_query_variable_info(u32 attr,
102 u64 *remaining_space, 205 u64 *remaining_space,
103 u64 *max_variable_size) 206 u64 *max_variable_size)
104{ 207{
208 unsigned long flags;
209 efi_status_t status;
210 bool __in_nmi = efi_in_nmi();
211
105 if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) 212 if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
106 return EFI_UNSUPPORTED; 213 return EFI_UNSUPPORTED;
107 214
108 return efi_call_virt(query_variable_info, attr, storage_space, 215 if (!__in_nmi)
109 remaining_space, max_variable_size); 216 spin_lock_irqsave(&efi_runtime_lock, flags);
217 status = efi_call_virt(query_variable_info, attr, storage_space,
218 remaining_space, max_variable_size);
219 if (!__in_nmi)
220 spin_unlock_irqrestore(&efi_runtime_lock, flags);
221 return status;
110} 222}
111 223
112static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) 224static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
113{ 225{
114 return efi_call_virt(get_next_high_mono_count, count); 226 unsigned long flags;
227 efi_status_t status;
228
229 spin_lock_irqsave(&efi_runtime_lock, flags);
230 status = efi_call_virt(get_next_high_mono_count, count);
231 spin_unlock_irqrestore(&efi_runtime_lock, flags);
232 return status;
115} 233}
116 234
117static void virt_efi_reset_system(int reset_type, 235static void virt_efi_reset_system(int reset_type,
@@ -119,17 +237,27 @@ static void virt_efi_reset_system(int reset_type,
119 unsigned long data_size, 237 unsigned long data_size,
120 efi_char16_t *data) 238 efi_char16_t *data)
121{ 239{
240 unsigned long flags;
241
242 spin_lock_irqsave(&efi_runtime_lock, flags);
122 __efi_call_virt(reset_system, reset_type, status, data_size, data); 243 __efi_call_virt(reset_system, reset_type, status, data_size, data);
244 spin_unlock_irqrestore(&efi_runtime_lock, flags);
123} 245}
124 246
125static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, 247static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
126 unsigned long count, 248 unsigned long count,
127 unsigned long sg_list) 249 unsigned long sg_list)
128{ 250{
251 unsigned long flags;
252 efi_status_t status;
253
129 if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) 254 if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
130 return EFI_UNSUPPORTED; 255 return EFI_UNSUPPORTED;
131 256
132 return efi_call_virt(update_capsule, capsules, count, sg_list); 257 spin_lock_irqsave(&efi_runtime_lock, flags);
258 status = efi_call_virt(update_capsule, capsules, count, sg_list);
259 spin_unlock_irqrestore(&efi_runtime_lock, flags);
260 return status;
133} 261}
134 262
135static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, 263static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
@@ -137,11 +265,17 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
137 u64 *max_size, 265 u64 *max_size,
138 int *reset_type) 266 int *reset_type)
139{ 267{
268 unsigned long flags;
269 efi_status_t status;
270
140 if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) 271 if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
141 return EFI_UNSUPPORTED; 272 return EFI_UNSUPPORTED;
142 273
143 return efi_call_virt(query_capsule_caps, capsules, count, max_size, 274 spin_lock_irqsave(&efi_runtime_lock, flags);
144 reset_type); 275 status = efi_call_virt(query_capsule_caps, capsules, count, max_size,
276 reset_type);
277 spin_unlock_irqrestore(&efi_runtime_lock, flags);
278 return status;
145} 279}
146 280
147void efi_native_runtime_setup(void) 281void efi_native_runtime_setup(void)