aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware/efi
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2014-03-17 11:36:37 -0400
committerMatt Fleming <matt.fleming@intel.com>2014-04-17 08:53:53 -0400
commite33655a386ed3b26ad36fb97a47ebb1c2ca1e928 (patch)
tree95adf8067766c15247e92349ca4a0be94fa0165a /drivers/firmware/efi
parent54d2fbfb0c9d341c891926100ed0e5d4c4b0c987 (diff)
efivars: Add compatibility code for compat tasks
It seems people are using 32-bit efibootmgr on top of 64-bit kernels, which will currently fail horribly when using the efivars interface, which is the traditional efibootmgr backend (the other being efivarfs). Since there is no versioning info in the data structure, figure out when we need to munge the structure data via judicious use of is_compat_task(). Cc: Mike Waychison <mikew@google.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'drivers/firmware/efi')
-rw-r--r--drivers/firmware/efi/efivars.c142
1 files changed, 116 insertions, 26 deletions
diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c
index a44310c6a8ba..463c56545ae8 100644
--- a/drivers/firmware/efi/efivars.c
+++ b/drivers/firmware/efi/efivars.c
@@ -69,6 +69,7 @@
69#include <linux/module.h> 69#include <linux/module.h>
70#include <linux/slab.h> 70#include <linux/slab.h>
71#include <linux/ucs2_string.h> 71#include <linux/ucs2_string.h>
72#include <linux/compat.h>
72 73
73#define EFIVARS_VERSION "0.08" 74#define EFIVARS_VERSION "0.08"
74#define EFIVARS_DATE "2004-May-17" 75#define EFIVARS_DATE "2004-May-17"
@@ -86,6 +87,15 @@ static struct kset *efivars_kset;
86static struct bin_attribute *efivars_new_var; 87static struct bin_attribute *efivars_new_var;
87static struct bin_attribute *efivars_del_var; 88static struct bin_attribute *efivars_del_var;
88 89
90struct compat_efi_variable {
91 efi_char16_t VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)];
92 efi_guid_t VendorGuid;
93 __u32 DataSize;
94 __u8 Data[1024];
95 __u32 Status;
96 __u32 Attributes;
97} __packed;
98
89struct efivar_attribute { 99struct efivar_attribute {
90 struct attribute attr; 100 struct attribute attr;
91 ssize_t (*show) (struct efivar_entry *entry, char *buf); 101 ssize_t (*show) (struct efivar_entry *entry, char *buf);
@@ -218,6 +228,25 @@ sanity_check(struct efi_variable *var, efi_char16_t *name, efi_guid_t vendor,
218 return 0; 228 return 0;
219} 229}
220 230
231static inline bool is_compat(void)
232{
233 if (IS_ENABLED(CONFIG_COMPAT) && is_compat_task())
234 return true;
235
236 return false;
237}
238
239static void
240copy_out_compat(struct efi_variable *dst, struct compat_efi_variable *src)
241{
242 memcpy(dst->VariableName, src->VariableName, EFI_VAR_NAME_LEN);
243 memcpy(dst->Data, src->Data, sizeof(src->Data));
244
245 dst->VendorGuid = src->VendorGuid;
246 dst->DataSize = src->DataSize;
247 dst->Attributes = src->Attributes;
248}
249
221/* 250/*
222 * We allow each variable to be edited via rewriting the 251 * We allow each variable to be edited via rewriting the
223 * entire efi variable structure. 252 * entire efi variable structure.
@@ -233,22 +262,42 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
233 u8 *data; 262 u8 *data;
234 int err; 263 int err;
235 264
236 if (count != sizeof(struct efi_variable)) 265 if (is_compat()) {
237 return -EINVAL; 266 struct compat_efi_variable *compat;
238 267
239 new_var = (struct efi_variable *)buf; 268 if (count != sizeof(*compat))
269 return -EINVAL;
240 270
241 attributes = new_var->Attributes; 271 compat = (struct compat_efi_variable *)buf;
242 vendor = new_var->VendorGuid; 272 attributes = compat->Attributes;
243 name = new_var->VariableName; 273 vendor = compat->VendorGuid;
244 size = new_var->DataSize; 274 name = compat->VariableName;
245 data = new_var->Data; 275 size = compat->DataSize;
276 data = compat->Data;
246 277
247 err = sanity_check(var, name, vendor, size, attributes, data); 278 err = sanity_check(var, name, vendor, size, attributes, data);
248 if (err) 279 if (err)
249 return err; 280 return err;
281
282 copy_out_compat(&entry->var, compat);
283 } else {
284 if (count != sizeof(struct efi_variable))
285 return -EINVAL;
286
287 new_var = (struct efi_variable *)buf;
250 288
251 memcpy(&entry->var, new_var, count); 289 attributes = new_var->Attributes;
290 vendor = new_var->VendorGuid;
291 name = new_var->VariableName;
292 size = new_var->DataSize;
293 data = new_var->Data;
294
295 err = sanity_check(var, name, vendor, size, attributes, data);
296 if (err)
297 return err;
298
299 memcpy(&entry->var, new_var, count);
300 }
252 301
253 err = efivar_entry_set(entry, attributes, size, data, NULL); 302 err = efivar_entry_set(entry, attributes, size, data, NULL);
254 if (err) { 303 if (err) {
@@ -263,6 +312,8 @@ static ssize_t
263efivar_show_raw(struct efivar_entry *entry, char *buf) 312efivar_show_raw(struct efivar_entry *entry, char *buf)
264{ 313{
265 struct efi_variable *var = &entry->var; 314 struct efi_variable *var = &entry->var;
315 struct compat_efi_variable *compat;
316 size_t size;
266 317
267 if (!entry || !buf) 318 if (!entry || !buf)
268 return 0; 319 return 0;
@@ -272,9 +323,23 @@ efivar_show_raw(struct efivar_entry *entry, char *buf)
272 &entry->var.DataSize, entry->var.Data)) 323 &entry->var.DataSize, entry->var.Data))
273 return -EIO; 324 return -EIO;
274 325
275 memcpy(buf, var, sizeof(*var)); 326 if (is_compat()) {
327 compat = (struct compat_efi_variable *)buf;
328
329 size = sizeof(*compat);
330 memcpy(compat->VariableName, var->VariableName,
331 EFI_VAR_NAME_LEN);
332 memcpy(compat->Data, var->Data, sizeof(compat->Data));
333
334 compat->VendorGuid = var->VendorGuid;
335 compat->DataSize = var->DataSize;
336 compat->Attributes = var->Attributes;
337 } else {
338 size = sizeof(*var);
339 memcpy(buf, var, size);
340 }
276 341
277 return sizeof(*var); 342 return size;
278} 343}
279 344
280/* 345/*
@@ -349,8 +414,10 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
349 struct bin_attribute *bin_attr, 414 struct bin_attribute *bin_attr,
350 char *buf, loff_t pos, size_t count) 415 char *buf, loff_t pos, size_t count)
351{ 416{
417 struct compat_efi_variable *compat = (struct compat_efi_variable *)buf;
352 struct efi_variable *new_var = (struct efi_variable *)buf; 418 struct efi_variable *new_var = (struct efi_variable *)buf;
353 struct efivar_entry *new_entry; 419 struct efivar_entry *new_entry;
420 bool need_compat = is_compat();
354 efi_char16_t *name; 421 efi_char16_t *name;
355 unsigned long size; 422 unsigned long size;
356 u32 attributes; 423 u32 attributes;
@@ -360,13 +427,23 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
360 if (!capable(CAP_SYS_ADMIN)) 427 if (!capable(CAP_SYS_ADMIN))
361 return -EACCES; 428 return -EACCES;
362 429
363 if (count != sizeof(*new_var)) 430 if (need_compat) {
364 return -EINVAL; 431 if (count != sizeof(*compat))
365 432 return -EINVAL;
366 attributes = new_var->Attributes; 433
367 name = new_var->VariableName; 434 attributes = compat->Attributes;
368 size = new_var->DataSize; 435 name = compat->VariableName;
369 data = new_var->Data; 436 size = compat->DataSize;
437 data = compat->Data;
438 } else {
439 if (count != sizeof(*new_var))
440 return -EINVAL;
441
442 attributes = new_var->Attributes;
443 name = new_var->VariableName;
444 size = new_var->DataSize;
445 data = new_var->Data;
446 }
370 447
371 if ((attributes & ~EFI_VARIABLE_MASK) != 0 || 448 if ((attributes & ~EFI_VARIABLE_MASK) != 0 ||
372 efivar_validate(name, data, size) == false) { 449 efivar_validate(name, data, size) == false) {
@@ -378,7 +455,10 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
378 if (!new_entry) 455 if (!new_entry)
379 return -ENOMEM; 456 return -ENOMEM;
380 457
381 memcpy(&new_entry->var, new_var, sizeof(*new_var)); 458 if (need_compat)
459 copy_out_compat(&new_entry->var, compat);
460 else
461 memcpy(&new_entry->var, new_var, sizeof(*new_var));
382 462
383 err = efivar_entry_set(new_entry, attributes, size, 463 err = efivar_entry_set(new_entry, attributes, size,
384 data, &efivar_sysfs_list); 464 data, &efivar_sysfs_list);
@@ -404,6 +484,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
404 char *buf, loff_t pos, size_t count) 484 char *buf, loff_t pos, size_t count)
405{ 485{
406 struct efi_variable *del_var = (struct efi_variable *)buf; 486 struct efi_variable *del_var = (struct efi_variable *)buf;
487 struct compat_efi_variable *compat;
407 struct efivar_entry *entry; 488 struct efivar_entry *entry;
408 efi_char16_t *name; 489 efi_char16_t *name;
409 efi_guid_t vendor; 490 efi_guid_t vendor;
@@ -412,11 +493,20 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
412 if (!capable(CAP_SYS_ADMIN)) 493 if (!capable(CAP_SYS_ADMIN))
413 return -EACCES; 494 return -EACCES;
414 495
415 if (count != sizeof(*del_var)) 496 if (is_compat()) {
416 return -EINVAL; 497 if (count != sizeof(*compat))
498 return -EINVAL;
499
500 compat = (struct compat_efi_variable *)buf;
501 name = compat->VariableName;
502 vendor = compat->VendorGuid;
503 } else {
504 if (count != sizeof(*del_var))
505 return -EINVAL;
417 506
418 name = del_var->VariableName; 507 name = del_var->VariableName;
419 vendor = del_var->VendorGuid; 508 vendor = del_var->VendorGuid;
509 }
420 510
421 efivar_entry_iter_begin(); 511 efivar_entry_iter_begin();
422 entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true); 512 entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true);