diff options
Diffstat (limited to 'arch/x86/platform/efi/efi.c')
-rw-r--r-- | arch/x86/platform/efi/efi.c | 106 |
1 files changed, 100 insertions, 6 deletions
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 977d1ce7e173..4959e3f89d74 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <linux/io.h> | 41 | #include <linux/io.h> |
42 | #include <linux/reboot.h> | 42 | #include <linux/reboot.h> |
43 | #include <linux/bcd.h> | 43 | #include <linux/bcd.h> |
44 | #include <linux/ucs2_string.h> | ||
44 | 45 | ||
45 | #include <asm/setup.h> | 46 | #include <asm/setup.h> |
46 | #include <asm/efi.h> | 47 | #include <asm/efi.h> |
@@ -51,6 +52,13 @@ | |||
51 | 52 | ||
52 | #define EFI_DEBUG 1 | 53 | #define EFI_DEBUG 1 |
53 | 54 | ||
55 | /* | ||
56 | * There's some additional metadata associated with each | ||
57 | * variable. Intel's reference implementation is 60 bytes - bump that | ||
58 | * to account for potential alignment constraints | ||
59 | */ | ||
60 | #define VAR_METADATA_SIZE 64 | ||
61 | |||
54 | struct efi __read_mostly efi = { | 62 | struct efi __read_mostly efi = { |
55 | .mps = EFI_INVALID_TABLE_ADDR, | 63 | .mps = EFI_INVALID_TABLE_ADDR, |
56 | .acpi = EFI_INVALID_TABLE_ADDR, | 64 | .acpi = EFI_INVALID_TABLE_ADDR, |
@@ -72,6 +80,9 @@ static efi_system_table_t efi_systab __initdata; | |||
72 | static u64 efi_var_store_size; | 80 | static u64 efi_var_store_size; |
73 | static u64 efi_var_remaining_size; | 81 | static u64 efi_var_remaining_size; |
74 | static u64 efi_var_max_var_size; | 82 | static u64 efi_var_max_var_size; |
83 | static u64 boot_used_size; | ||
84 | static u64 boot_var_size; | ||
85 | static u64 active_size; | ||
75 | 86 | ||
76 | unsigned long x86_efi_facility; | 87 | unsigned long x86_efi_facility; |
77 | 88 | ||
@@ -166,8 +177,53 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, | |||
166 | efi_char16_t *name, | 177 | efi_char16_t *name, |
167 | efi_guid_t *vendor) | 178 | efi_guid_t *vendor) |
168 | { | 179 | { |
169 | return efi_call_virt3(get_next_variable, | 180 | efi_status_t status; |
170 | name_size, name, vendor); | 181 | static bool finished = false; |
182 | static u64 var_size; | ||
183 | |||
184 | status = efi_call_virt3(get_next_variable, | ||
185 | name_size, name, vendor); | ||
186 | |||
187 | if (status == EFI_NOT_FOUND) { | ||
188 | finished = true; | ||
189 | if (var_size < boot_used_size) { | ||
190 | boot_var_size = boot_used_size - var_size; | ||
191 | active_size += boot_var_size; | ||
192 | } else { | ||
193 | printk(KERN_WARNING FW_BUG "efi: Inconsistent initial sizes\n"); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | if (boot_used_size && !finished) { | ||
198 | unsigned long size; | ||
199 | u32 attr; | ||
200 | efi_status_t s; | ||
201 | void *tmp; | ||
202 | |||
203 | s = virt_efi_get_variable(name, vendor, &attr, &size, NULL); | ||
204 | |||
205 | if (s != EFI_BUFFER_TOO_SMALL || !size) | ||
206 | return status; | ||
207 | |||
208 | tmp = kmalloc(size, GFP_ATOMIC); | ||
209 | |||
210 | if (!tmp) | ||
211 | return status; | ||
212 | |||
213 | s = virt_efi_get_variable(name, vendor, &attr, &size, tmp); | ||
214 | |||
215 | if (s == EFI_SUCCESS && (attr & EFI_VARIABLE_NON_VOLATILE)) { | ||
216 | var_size += size; | ||
217 | var_size += ucs2_strsize(name, 1024); | ||
218 | active_size += size; | ||
219 | active_size += VAR_METADATA_SIZE; | ||
220 | active_size += ucs2_strsize(name, 1024); | ||
221 | } | ||
222 | |||
223 | kfree(tmp); | ||
224 | } | ||
225 | |||
226 | return status; | ||
171 | } | 227 | } |
172 | 228 | ||
173 | static efi_status_t virt_efi_set_variable(efi_char16_t *name, | 229 | static efi_status_t virt_efi_set_variable(efi_char16_t *name, |
@@ -176,9 +232,34 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, | |||
176 | unsigned long data_size, | 232 | unsigned long data_size, |
177 | void *data) | 233 | void *data) |
178 | { | 234 | { |
179 | return efi_call_virt5(set_variable, | 235 | efi_status_t status; |
180 | name, vendor, attr, | 236 | u32 orig_attr = 0; |
181 | data_size, data); | 237 | unsigned long orig_size = 0; |
238 | |||
239 | status = virt_efi_get_variable(name, vendor, &orig_attr, &orig_size, | ||
240 | NULL); | ||
241 | |||
242 | if (status != EFI_BUFFER_TOO_SMALL) | ||
243 | orig_size = 0; | ||
244 | |||
245 | status = efi_call_virt5(set_variable, | ||
246 | name, vendor, attr, | ||
247 | data_size, data); | ||
248 | |||
249 | if (status == EFI_SUCCESS) { | ||
250 | if (orig_size) { | ||
251 | active_size -= orig_size; | ||
252 | active_size -= ucs2_strsize(name, 1024); | ||
253 | active_size -= VAR_METADATA_SIZE; | ||
254 | } | ||
255 | if (data_size) { | ||
256 | active_size += data_size; | ||
257 | active_size += ucs2_strsize(name, 1024); | ||
258 | active_size += VAR_METADATA_SIZE; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | return status; | ||
182 | } | 263 | } |
183 | 264 | ||
184 | static efi_status_t virt_efi_query_variable_info(u32 attr, | 265 | static efi_status_t virt_efi_query_variable_info(u32 attr, |
@@ -720,6 +801,8 @@ void __init efi_init(void) | |||
720 | early_iounmap(data, sizeof(*efi_var_data)); | 801 | early_iounmap(data, sizeof(*efi_var_data)); |
721 | } | 802 | } |
722 | 803 | ||
804 | boot_used_size = efi_var_store_size - efi_var_remaining_size; | ||
805 | |||
723 | set_bit(EFI_SYSTEM_TABLES, &x86_efi_facility); | 806 | set_bit(EFI_SYSTEM_TABLES, &x86_efi_facility); |
724 | 807 | ||
725 | /* | 808 | /* |
@@ -1042,10 +1125,21 @@ efi_status_t efi_query_variable_store(u32 attributes, unsigned long size) | |||
1042 | if (!max_size && remaining_size > size) | 1125 | if (!max_size && remaining_size > size) |
1043 | printk_once(KERN_ERR FW_BUG "Broken EFI implementation" | 1126 | printk_once(KERN_ERR FW_BUG "Broken EFI implementation" |
1044 | " is returning MaxVariableSize=0\n"); | 1127 | " is returning MaxVariableSize=0\n"); |
1128 | /* | ||
1129 | * Some firmware implementations refuse to boot if there's insufficient | ||
1130 | * space in the variable store. We account for that by refusing the | ||
1131 | * write if permitting it would reduce the available space to under | ||
1132 | * 50%. However, some firmware won't reclaim variable space until | ||
1133 | * after the used (not merely the actively used) space drops below | ||
1134 | * a threshold. We can approximate that case with the value calculated | ||
1135 | * above. If both the firmware and our calculations indicate that the | ||
1136 | * available space would drop below 50%, refuse the write. | ||
1137 | */ | ||
1045 | 1138 | ||
1046 | if (!storage_size || size > remaining_size || | 1139 | if (!storage_size || size > remaining_size || |
1047 | (max_size && size > max_size) || | 1140 | (max_size && size > max_size) || |
1048 | (remaining_size - size) < (storage_size / 2)) | 1141 | ((active_size + size + VAR_METADATA_SIZE > storage_size / 2) && |
1142 | (remaining_size - size < storage_size / 2))) | ||
1049 | return EFI_OUT_OF_RESOURCES; | 1143 | return EFI_OUT_OF_RESOURCES; |
1050 | 1144 | ||
1051 | return EFI_SUCCESS; | 1145 | return EFI_SUCCESS; |