diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/firmware/efi/runtime-wrappers.c | 154 |
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 | */ | ||
62 | static 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 | ||
84 | static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, | 171 | static 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 | ||
91 | static efi_status_t virt_efi_set_variable(efi_char16_t *name, | 184 | static 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 | ||
100 | static efi_status_t virt_efi_query_variable_info(u32 attr, | 203 | static 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 | ||
112 | static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) | 224 | static 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 | ||
117 | static void virt_efi_reset_system(int reset_type, | 235 | static 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 | ||
125 | static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, | 247 | static 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 | ||
135 | static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, | 263 | static 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 | ||
147 | void efi_native_runtime_setup(void) | 281 | void efi_native_runtime_setup(void) |