aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/platform
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/platform')
-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;