Commit b9394d8a authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86/efi changes from Peter Anvin:
 "The bulk of these changes are cleaning up the efivars handling and
  breaking it up into a tree of files.  There are a number of fixes as
  well.

  The entire changeset is pretty big, but most of it is code movement.

  Several of these commits are quite new; the history got very messed up
  due to a mismerge with the urgent changes for rc8 which completely
  broke IA64, and so Ingo requested that we rebase it to straighten it
  out."

* 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  efi: remove "kfree(NULL)"
  efi: locking fix in efivar_entry_set_safe()
  efi, pstore: Read data from variable store before memcpy()
  efi, pstore: Remove entry from list when erasing
  efi, pstore: Initialise 'entry' before iterating
  efi: split efisubsystem from efivars
  efivarfs: Move to fs/efivarfs
  efivars: Move pstore code into the new EFI directory
  efivars: efivar_entry API
  efivars: Keep a private global pointer to efivars
  efi: move utf16 string functions to efi.h
  x86, efi: Make efi_memblock_x86_reserve_range more readable
  efivarfs: convert to use simple_open()
parents 126de6b2 7b2dd6d2
......@@ -3025,9 +3025,18 @@ F: arch/ia64/kernel/efi.c
F: arch/x86/boot/compressed/eboot.[ch]
F: arch/x86/include/asm/efi.h
F: arch/x86/platform/efi/*
F: drivers/firmware/efivars.c
F: drivers/firmware/efi/*
F: include/linux/efi*.h
EFI VARIABLE FILESYSTEM
M: Matthew Garrett <matthew.garrett@nebula.com>
M: Jeremy Kerr <jk@ozlabs.org>
M: Matt Fleming <matt.fleming@intel.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi.git
L: linux-efi@vger.kernel.org
S: Maintained
F: fs/efivarfs/
EFIFB FRAMEBUFFER DRIVER
L: linux-fbdev@vger.kernel.org
M: Peter Jones <pjones@redhat.com>
......@@ -6391,7 +6400,7 @@ S: Maintained
T: git git://git.infradead.org/users/cbou/linux-pstore.git
F: fs/pstore/
F: include/linux/pstore*
F: drivers/firmware/efivars.c
F: drivers/firmware/efi/efi-pstore.c
F: drivers/acpi/apei/erst.c
PTP HARDWARE CLOCK SUPPORT
......
......@@ -110,6 +110,7 @@ config DMI
config EFI
bool
select UCS2_STRING
default y
config SCHED_OMIT_FRAME_POINTER
......
......@@ -453,24 +453,25 @@ static void __init do_add_efi_memmap(void)
int __init efi_memblock_x86_reserve_range(void)
{
struct efi_info *e = &boot_params.efi_info;
unsigned long pmap;
#ifdef CONFIG_X86_32
/* Can't handle data above 4GB at this time */
if (boot_params.efi_info.efi_memmap_hi) {
if (e->efi_memmap_hi) {
pr_err("Memory map is above 4GB, disabling EFI.\n");
return -EINVAL;
}
pmap = boot_params.efi_info.efi_memmap;
pmap = e->efi_memmap;
#else
pmap = (boot_params.efi_info.efi_memmap |
((__u64)boot_params.efi_info.efi_memmap_hi<<32));
pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32));
#endif
memmap.phys_map = (void *)pmap;
memmap.nr_map = boot_params.efi_info.efi_memmap_size /
boot_params.efi_info.efi_memdesc_size;
memmap.desc_version = boot_params.efi_info.efi_memdesc_version;
memmap.desc_size = boot_params.efi_info.efi_memdesc_size;
memmap.phys_map = (void *)pmap;
memmap.nr_map = e->efi_memmap_size /
e->efi_memdesc_size;
memmap.desc_size = e->efi_memdesc_size;
memmap.desc_version = e->efi_memdesc_version;
memblock_reserve(pmap, memmap.nr_map * memmap.desc_size);
return 0;
......
......@@ -36,42 +36,6 @@ config FIRMWARE_MEMMAP
See also Documentation/ABI/testing/sysfs-firmware-memmap.
config EFI_VARS
tristate "EFI Variable Support via sysfs"
depends on EFI
select UCS2_STRING
default n
help
If you say Y here, you are able to get EFI (Extensible Firmware
Interface) variable information via sysfs. You may read,
write, create, and destroy EFI variables through this interface.
Note that using this driver in concert with efibootmgr requires
at least test release version 0.5.0-test3 or later, which is
available from Matt Domsch's website located at:
<http://linux.dell.com/efibootmgr/testing/efibootmgr-0.5.0-test3.tar.gz>
Subsequent efibootmgr releases may be found at:
<http://linux.dell.com/efibootmgr>
config EFI_VARS_PSTORE
bool "Register efivars backend for pstore"
depends on EFI_VARS && PSTORE
default y
help
Say Y here to enable use efivars as a backend to pstore. This
will allow writing console messages, crash dumps, or anything
else supported by pstore to EFI variables.
config EFI_VARS_PSTORE_DEFAULT_DISABLE
bool "Disable using efivars as a pstore backend by default"
depends on EFI_VARS_PSTORE
default n
help
Saying Y here will disable the use of efivars as a storage
backend for pstore by default. This setting can be overridden
using the efivars module's pstore_disable parameter.
config EFI_PCDP
bool "Console device selection via EFI PCDP or HCDP table"
depends on ACPI && EFI && IA64
......@@ -165,5 +129,6 @@ config ISCSI_IBFT
Otherwise, say N.
source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig"
endmenu
......@@ -4,7 +4,6 @@
obj-$(CONFIG_DMI) += dmi_scan.o
obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o
obj-$(CONFIG_EDD) += edd.o
obj-$(CONFIG_EFI_VARS) += efivars.o
obj-$(CONFIG_EFI_PCDP) += pcdp.o
obj-$(CONFIG_DELL_RBU) += dell_rbu.o
obj-$(CONFIG_DCDBAS) += dcdbas.o
......@@ -14,3 +13,4 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
obj-$(CONFIG_EFI) += efi/
menu "EFI (Extensible Firmware Interface) Support"
depends on EFI
config EFI_VARS
tristate "EFI Variable Support via sysfs"
depends on EFI
default n
help
If you say Y here, you are able to get EFI (Extensible Firmware
Interface) variable information via sysfs. You may read,
write, create, and destroy EFI variables through this interface.
Note that using this driver in concert with efibootmgr requires
at least test release version 0.5.0-test3 or later, which is
available from Matt Domsch's website located at:
<http://linux.dell.com/efibootmgr/testing/efibootmgr-0.5.0-test3.tar.gz>
Subsequent efibootmgr releases may be found at:
<http://linux.dell.com/efibootmgr>
config EFI_VARS_PSTORE
tristate "Register efivars backend for pstore"
depends on EFI_VARS && PSTORE
default y
help
Say Y here to enable use efivars as a backend to pstore. This
will allow writing console messages, crash dumps, or anything
else supported by pstore to EFI variables.
config EFI_VARS_PSTORE_DEFAULT_DISABLE
bool "Disable using efivars as a pstore backend by default"
depends on EFI_VARS_PSTORE
default n
help
Saying Y here will disable the use of efivars as a storage
backend for pstore by default. This setting can be overridden
using the efivars module's pstore_disable parameter.
endmenu
#
# Makefile for linux kernel
#
obj-y += efi.o vars.o
obj-$(CONFIG_EFI_VARS) += efivars.o
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
#include <linux/efi.h>
#include <linux/module.h>
#include <linux/pstore.h>
#include <linux/ucs2_string.h>
#define DUMP_NAME_LEN 52
static bool efivars_pstore_disable =
IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE);
module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
#define PSTORE_EFI_ATTRIBUTES \
(EFI_VARIABLE_NON_VOLATILE | \
EFI_VARIABLE_BOOTSERVICE_ACCESS | \
EFI_VARIABLE_RUNTIME_ACCESS)
static int efi_pstore_open(struct pstore_info *psi)
{
efivar_entry_iter_begin();
psi->data = NULL;
return 0;
}
static int efi_pstore_close(struct pstore_info *psi)
{
efivar_entry_iter_end();
psi->data = NULL;
return 0;
}
struct pstore_read_data {
u64 *id;
enum pstore_type_id *type;
int *count;
struct timespec *timespec;
char **buf;
};
static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
{
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
struct pstore_read_data *cb_data = data;
char name[DUMP_NAME_LEN];
int i;
int cnt;
unsigned int part;
unsigned long time, size;
if (efi_guidcmp(entry->var.VendorGuid, vendor))
return 0;
for (i = 0; i < DUMP_NAME_LEN; i++)
name[i] = entry->var.VariableName[i];
if (sscanf(name, "dump-type%u-%u-%d-%lu",
cb_data->type, &part, &cnt, &time) == 4) {
*cb_data->id = part;
*cb_data->count = cnt;
cb_data->timespec->tv_sec = time;
cb_data->timespec->tv_nsec = 0;
} else if (sscanf(name, "dump-type%u-%u-%lu",
cb_data->type, &part, &time) == 3) {
/*
* Check if an old format,
* which doesn't support holding
* multiple logs, remains.
*/
*cb_data->id = part;
*cb_data->count = 0;
cb_data->timespec->tv_sec = time;
cb_data->timespec->tv_nsec = 0;
} else
return 0;
entry->var.DataSize = 1024;
__efivar_entry_get(entry, &entry->var.Attributes,
&entry->var.DataSize, entry->var.Data);
size = entry->var.DataSize;
*cb_data->buf = kmalloc(size, GFP_KERNEL);
if (*cb_data->buf == NULL)
return -ENOMEM;
memcpy(*cb_data->buf, entry->var.Data, size);
return size;
}
static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
int *count, struct timespec *timespec,
char **buf, struct pstore_info *psi)
{
struct pstore_read_data data;
data.id = id;
data.type = type;
data.count = count;
data.timespec = timespec;
data.buf = buf;
return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data,
(struct efivar_entry **)&psi->data);
}
static int efi_pstore_write(enum pstore_type_id type,
enum kmsg_dump_reason reason, u64 *id,
unsigned int part, int count, size_t size,
struct pstore_info *psi)
{
char name[DUMP_NAME_LEN];
efi_char16_t efi_name[DUMP_NAME_LEN];
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
int i, ret = 0;
sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count,
get_seconds());
for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = name[i];
efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES,
!pstore_cannot_block_path(reason),
size, psi->buf);
if (reason == KMSG_DUMP_OOPS)
efivar_run_worker();
*id = part;
return ret;
};
struct pstore_erase_data {
u64 id;
enum pstore_type_id type;
int count;
struct timespec time;
efi_char16_t *name;
};
/*
* Clean up an entry with the same name
*/
static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
{
struct pstore_erase_data *ed = data;
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
efi_char16_t efi_name_old[DUMP_NAME_LEN];
efi_char16_t *efi_name = ed->name;
unsigned long ucs2_len = ucs2_strlen(ed->name);
char name_old[DUMP_NAME_LEN];
int i;
if (efi_guidcmp(entry->var.VendorGuid, vendor))
return 0;
if (ucs2_strncmp(entry->var.VariableName,
efi_name, (size_t)ucs2_len)) {
/*
* Check if an old format, which doesn't support
* holding multiple logs, remains.
*/
sprintf(name_old, "dump-type%u-%u-%lu", ed->type,
(unsigned int)ed->id, ed->time.tv_sec);
for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name_old[i] = name_old[i];
if (ucs2_strncmp(entry->var.VariableName, efi_name_old,
ucs2_strlen(efi_name_old)))
return 0;
}
/* found */
__efivar_entry_delete(entry);
list_del(&entry->list);
return 1;
}
static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
struct timespec time, struct pstore_info *psi)
{
struct pstore_erase_data edata;
struct efivar_entry *entry = NULL;
char name[DUMP_NAME_LEN];
efi_char16_t efi_name[DUMP_NAME_LEN];
int found, i;
sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count,
time.tv_sec);
for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = name[i];
edata.id = id;
edata.type = type;
edata.count = count;
edata.time = time;
edata.name = efi_name;
efivar_entry_iter_begin();
found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
efivar_entry_iter_end();
if (found)
efivar_unregister(entry);
return 0;
}
static struct pstore_info efi_pstore_info = {
.owner = THIS_MODULE,
.name = "efi",
.open = efi_pstore_open,
.close = efi_pstore_close,
.read = efi_pstore_read,
.write = efi_pstore_write,
.erase = efi_pstore_erase,
};
static __init int efivars_pstore_init(void)
{
if (!efi_enabled(EFI_RUNTIME_SERVICES))
return 0;
if (!efivars_kobject())
return 0;
if (efivars_pstore_disable)
return 0;
efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
if (!efi_pstore_info.buf)
return -ENOMEM;
efi_pstore_info.bufsize = 1024;
spin_lock_init(&efi_pstore_info.buf_lock);
pstore_register(&efi_pstore_info);
return 0;
}
static __exit void efivars_pstore_exit(void)
{
}
module_init(efivars_pstore_init);
module_exit(efivars_pstore_exit);
MODULE_DESCRIPTION("EFI variable backend for pstore");
MODULE_LICENSE("GPL");
/*
* efi.c - EFI subsystem
*
* Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com>
* Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com>
* Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
*
* This code registers /sys/firmware/efi{,/efivars} when EFI is supported,
* allowing the efivarfs to be mounted or the efivars module to be loaded.
* The existance of /sys/firmware/efi may also be used by userspace to
* determine that the system supports EFI.
*
* This file is released under the GPLv2.
*/
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/efi.h>
static struct kobject *efi_kobj;
static struct kobject *efivars_kobj;
/*
* Let's not leave out systab information that snuck into
* the efivars driver
*/
static ssize_t systab_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
char *str = buf;
if (!kobj || !buf)
return -EINVAL;
if (efi.mps != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "MPS=0x%lx\n", efi.mps);
if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20);
if (efi.acpi != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "ACPI=0x%lx\n", efi.acpi);
if (efi.smbios != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp);
if (efi.boot_info != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
if (efi.uga != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "UGA=0x%lx\n", efi.uga);
return str - buf;
}
static struct kobj_attribute efi_attr_systab =
__ATTR(systab, 0400, systab_show, NULL);
static struct attribute *efi_subsys_attrs[] = {
&efi_attr_systab.attr,
NULL, /* maybe more in the future? */
};
static struct attribute_group efi_subsys_attr_group = {
.attrs = efi_subsys_attrs,
};
static struct efivars generic_efivars;
static struct efivar_operations generic_ops;
static int generic_ops_register(void)
{
generic_ops.get_variable = efi.get_variable;
generic_ops.set_variable = efi.set_variable;
generic_ops.get_next_variable = efi.get_next_variable;
generic_ops.query_variable_store = efi_query_variable_store;
return efivars_register(&generic_efivars, &generic_ops, efi_kobj);
}
static void generic_ops_unregister(void)
{
efivars_unregister(&generic_efivars);
}
/*
* We register the efi subsystem with the firmware subsystem and the
* efivars subsystem with the efi subsystem, if the system was booted with
* EFI.
*/
static int __init efisubsys_init(void)
{
int error;
if (!efi_enabled(EFI_BOOT))
return 0;
/* We register the efi directory at /sys/firmware/efi */
efi_kobj = kobject_create_and_add("efi", firmware_kobj);
if (!efi_kobj) {
pr_err("efi: Firmware registration failed.\n");
return -ENOMEM;
}
error = generic_ops_register();
if (error)
goto err_put;
error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
if (error) {
pr_err("efi: Sysfs attribute export failed with error %d.\n",
error);
goto err_unregister;
}
/* and the standard mountpoint for efivarfs */
efivars_kobj = kobject_create_and_add("efivars", efi_kobj);
if (!efivars_kobj) {
pr_err("efivars: Subsystem registration failed.\n");
error = -ENOMEM;
goto err_remove_group;
}
return 0;
err_remove_group:
sysfs_remove_group(efi_kobj, &efi_subsys_attr_group);
err_unregister:
generic_ops_unregister();
err_put:
kobject_put(efi_kobj);
return error;
}
subsys_initcall(efisubsys_init);
/*
* 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 code takes all variables accessible from EFI runtime and
* exports them via sysfs
*
* 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
*
* Changelog:
*
* 17 May 2004 - Matt Domsch <Matt_Domsch@dell.com>
* remove check for efi_enabled in exit
* add MODULE_VERSION
*
* 26 Apr 2004 - Matt Domsch <Matt_Domsch@dell.com>
* minor bug fixes
*
* 21 Apr 2004 - Matt Tolentino <matthew.e.tolentino@intel.com)
* converted driver to export variable information via sysfs
* and moved to drivers/firmware directory
* bumped revision number to v0.07 to reflect conversion & move
*
* 10 Dec 2002 - Matt Domsch <Matt_Domsch@dell.com>
* fix locking per Peter Chubb's findings
*
* 25 Mar 2002 - Matt Domsch <Matt_Domsch@dell.com>
* move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse()
*
* 12 Feb 2002 - Matt Domsch <Matt_Domsch@dell.com>
* use list_for_each_safe when deleting vars.
* remove ifdef CONFIG_SMP around include <linux/smp.h>
* v0.04 release to linux-ia64@linuxia64.org
*
* 20 April 2001 - Matt Domsch <Matt_Domsch@dell.com>
* Moved vars from /proc/efi to /proc/efi/vars, and made