aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Garrett <matthew.garrett@nebula.com>2013-04-15 16:09:47 -0400
committerMatt Fleming <matt.fleming@intel.com>2013-04-15 16:33:05 -0400
commit31ff2f20d9003e74991d135f56e503fe776c127c (patch)
tree00a4270a8b9ff7ff561fa75de80dda14dc235583
parentcc5a080c5d40c36089bb08a8a16fa3fc7047fe0f (diff)
efi: Distinguish between "remaining space" and actually used space
EFI implementations distinguish between space that is actively used by a variable and space that merely hasn't been garbage collected yet. Space that hasn't yet been garbage collected isn't available for use and so isn't counted in the remaining_space field returned by QueryVariableInfo(). Combined with commit 68d9298 this can cause problems. Some implementations don't garbage collect until the remaining space is smaller than the maximum variable size, and as a result check_var_size() will always fail once more than 50% of the variable store has been used even if most of that space is marked as available for garbage collection. The user is unable to create new variables, and deleting variables doesn't increase the remaining space. The problem that 68d9298 was attempting to avoid was one where certain platforms fail if the actively used space is greater than 50% of the available storage space. We should be able to calculate that by simply summing the size of each available variable and subtracting that from the total storage space. With luck this will fix the problem described in https://bugzilla.kernel.org/show_bug.cgi?id=55471 without permitting damage to occur to the machines 68d9298 was attempting to fix. Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
-rw-r--r--arch/x86/platform/efi/efi.c106
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
54struct efi __read_mostly efi = { 62struct 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;
72static u64 efi_var_store_size; 80static u64 efi_var_store_size;
73static u64 efi_var_remaining_size; 81static u64 efi_var_remaining_size;
74static u64 efi_var_max_var_size; 82static u64 efi_var_max_var_size;
83static u64 boot_used_size;
84static u64 boot_var_size;
85static u64 active_size;
75 86
76unsigned long x86_efi_facility; 87unsigned 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
173static efi_status_t virt_efi_set_variable(efi_char16_t *name, 229static 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
184static efi_status_t virt_efi_query_variable_info(u32 attr, 265static 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;