Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 362697
b: refs/heads/master
c: 31ff2f2
h: refs/heads/master
i:
  362695: 61d1bd6
v: v3
  • Loading branch information
Matthew Garrett authored and Matt Fleming committed Apr 15, 2013
1 parent b06ee1d commit 42fa8bf
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 7 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: cc5a080c5d40c36089bb08a8a16fa3fc7047fe0f
refs/heads/master: 31ff2f20d9003e74991d135f56e503fe776c127c
106 changes: 100 additions & 6 deletions trunk/arch/x86/platform/efi/efi.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <linux/io.h>
#include <linux/reboot.h>
#include <linux/bcd.h>
#include <linux/ucs2_string.h>

#include <asm/setup.h>
#include <asm/efi.h>
Expand All @@ -51,6 +52,13 @@

#define EFI_DEBUG 1

/*
* There's some additional metadata associated with each
* variable. Intel's reference implementation is 60 bytes - bump that
* to account for potential alignment constraints
*/
#define VAR_METADATA_SIZE 64

struct efi __read_mostly efi = {
.mps = EFI_INVALID_TABLE_ADDR,
.acpi = EFI_INVALID_TABLE_ADDR,
Expand All @@ -72,6 +80,9 @@ static efi_system_table_t efi_systab __initdata;
static u64 efi_var_store_size;
static u64 efi_var_remaining_size;
static u64 efi_var_max_var_size;
static u64 boot_used_size;
static u64 boot_var_size;
static u64 active_size;

unsigned long x86_efi_facility;

Expand Down Expand Up @@ -166,8 +177,53 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
efi_char16_t *name,
efi_guid_t *vendor)
{
return efi_call_virt3(get_next_variable,
name_size, name, vendor);
efi_status_t status;
static bool finished = false;
static u64 var_size;

status = efi_call_virt3(get_next_variable,
name_size, name, vendor);

if (status == EFI_NOT_FOUND) {
finished = true;
if (var_size < boot_used_size) {
boot_var_size = boot_used_size - var_size;
active_size += boot_var_size;
} else {
printk(KERN_WARNING FW_BUG "efi: Inconsistent initial sizes\n");
}
}

if (boot_used_size && !finished) {
unsigned long size;
u32 attr;
efi_status_t s;
void *tmp;

s = virt_efi_get_variable(name, vendor, &attr, &size, NULL);

if (s != EFI_BUFFER_TOO_SMALL || !size)
return status;

tmp = kmalloc(size, GFP_ATOMIC);

if (!tmp)
return status;

s = virt_efi_get_variable(name, vendor, &attr, &size, tmp);

if (s == EFI_SUCCESS && (attr & EFI_VARIABLE_NON_VOLATILE)) {
var_size += size;
var_size += ucs2_strsize(name, 1024);
active_size += size;
active_size += VAR_METADATA_SIZE;
active_size += ucs2_strsize(name, 1024);
}

kfree(tmp);
}

return status;
}

static efi_status_t virt_efi_set_variable(efi_char16_t *name,
Expand All @@ -176,9 +232,34 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name,
unsigned long data_size,
void *data)
{
return efi_call_virt5(set_variable,
name, vendor, attr,
data_size, data);
efi_status_t status;
u32 orig_attr = 0;
unsigned long orig_size = 0;

status = virt_efi_get_variable(name, vendor, &orig_attr, &orig_size,
NULL);

if (status != EFI_BUFFER_TOO_SMALL)
orig_size = 0;

status = efi_call_virt5(set_variable,
name, vendor, attr,
data_size, data);

if (status == EFI_SUCCESS) {
if (orig_size) {
active_size -= orig_size;
active_size -= ucs2_strsize(name, 1024);
active_size -= VAR_METADATA_SIZE;
}
if (data_size) {
active_size += data_size;
active_size += ucs2_strsize(name, 1024);
active_size += VAR_METADATA_SIZE;
}
}

return status;
}

static efi_status_t virt_efi_query_variable_info(u32 attr,
Expand Down Expand Up @@ -720,6 +801,8 @@ void __init efi_init(void)
early_iounmap(data, sizeof(*efi_var_data));
}

boot_used_size = efi_var_store_size - efi_var_remaining_size;

set_bit(EFI_SYSTEM_TABLES, &x86_efi_facility);

/*
Expand Down Expand Up @@ -1042,10 +1125,21 @@ efi_status_t efi_query_variable_store(u32 attributes, unsigned long size)
if (!max_size && remaining_size > size)
printk_once(KERN_ERR FW_BUG "Broken EFI implementation"
" is returning MaxVariableSize=0\n");
/*
* Some firmware implementations refuse to boot if there's insufficient
* space in the variable store. We account for that by refusing the
* write if permitting it would reduce the available space to under
* 50%. However, some firmware won't reclaim variable space until
* after the used (not merely the actively used) space drops below
* a threshold. We can approximate that case with the value calculated
* above. If both the firmware and our calculations indicate that the
* available space would drop below 50%, refuse the write.
*/

if (!storage_size || size > remaining_size ||
(max_size && size > max_size) ||
(remaining_size - size) < (storage_size / 2))
((active_size + size + VAR_METADATA_SIZE > storage_size / 2) &&
(remaining_size - size < storage_size / 2)))
return EFI_OUT_OF_RESOURCES;

return EFI_SUCCESS;
Expand Down

0 comments on commit 42fa8bf

Please sign in to comment.