/*
* Originally from efivars.c
*
* Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com>
* Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/capability.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/smp.h>
#include <linux/efi.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/ucs2_string.h>
/* Private pointer to registered efivars */
static struct efivars *__efivars;
static bool efivar_wq_enabled = true;
DECLARE_WORK(efivar_work, NULL);
EXPORT_SYMBOL_GPL(efivar_work);
static bool
validate_device_path(efi_char16_t *var_name, int match, u8 *buffer,
unsigned long len)
{
struct efi_generic_dev_path *node;
int offset = 0;
node = (struct efi_generic_dev_path *)buffer;
if (len < sizeof(*node))
return false;
while (offset <= len - sizeof(*node) &&
node->length >= sizeof(*node) &&
node->length <= len - offset) {
offset += node->length;
if ((node->type == EFI_DEV_END_PATH ||
node->type == EFI_DEV_END_PATH2) &&
node->sub_type == EFI_DEV_END_ENTIRE)
return true;
node = (struct efi_generic_dev_path *)(buffer + offset);
}
/*
* If we're here then either node->length pointed past the end
* of the buffer or we reached the end of the buffer without
* finding a device path end node.
*/
return false;
}
static bool
validate_boot_order(efi_char16_t *var_name, int match, u8 *buffer,
unsigned long len)
{
/* An array of 16-bit integers */
if ((len % 2) != 0)
return false;
return true;
}
static bool
validate_load_option(efi_char16_t *var_name, int match, u8 *buffer,
unsigned long len)
{
u16 filepathlength;
int i, desclength = 0, namelen;
namelen = ucs2_strnlen(var_name, EFI_VAR_NAME_LEN);
/* Either "Boot" or "Driver" followed by four digits of hex */
for (i = match; i < match+4; i++) {
if (var_name[i] > 127 ||
hex_to_bin(var_name[i] & 0xff) < 0)
return true;
}
/* Reject it if there's 4 digits of hex and then further content */
if (namelen > match + 4)
return false;
/* A valid entry must be at least 8 bytes */
if (len < 8)
return false;
filepathlength = buffer[4] | buffer[5] << 8;
/*
* There's no stored length for the description, so it has to be
* found by hand
*/
desclength = ucs2_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2;
/* Each boot entry must have a descriptor */
if (!desclength)
return false;
/*
* If the sum of the length of the description, the claimed filepath
* length and the original header are greater than the length of the
* variable, it's malformed
*/
if ((desclength + filepathlength + 6) > len)
return false;
/*
* And, finally, check the filepath
*/
return validate_device_path(var_name, match, buffer + desclength + 6,
filepathlength);
}
static bool
validate_uint16(efi_char16_t *var_name, int match, u8 *buffer,
unsigned long len)
{
/* A single 16-bit integer */
if (len != 2)
return false;
return true;
}
static bool
validate_ascii_string(efi_char16_t *var_name, int match, u8 *buffer,
unsigned long len)
{
int i;
for (i = 0; i < len; i++) {
if (buffer[i] > 127)