Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a2d77302 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'pstore-efi' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6:
  efivars: Introduce PSTORE_EFI_ATTRIBUTES
  efivars: Use string functions in pstore_write
  efivars: introduce utf16_strncmp
  efivars: String functions
  efi: Add support for using efivars as a pstore backend
  pstore: Allow the user to explicitly choose a backend
  pstore: Make "part" unsigned
  pstore: Add extra context for writes and erases
  pstore: Extend API for more flexibility in new backends
parents 72f9adfd 7644c16c
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -39,3 +39,9 @@ Description: Generic interface to platform dependent persistent storage.
		multiple) files based on the record size of the underlying
		multiple) files based on the record size of the underlying
		persistent storage until at least this amount is reached.
		persistent storage until at least this amount is reached.
		Default is 10 Kbytes.
		Default is 10 Kbytes.

		Pstore only supports one backend at a time. If multiple
		backends are available, the preferred backend may be
		set by passing the pstore.backend= argument to the kernel at
		boot time.
+2 −0
Original line number Original line Diff line number Diff line
@@ -2153,6 +2153,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
			[HW,MOUSE] Controls Logitech smartscroll autorepeat.
			[HW,MOUSE] Controls Logitech smartscroll autorepeat.
			0 = disabled, 1 = enabled (default).
			0 = disabled, 1 = enabled (default).


	pstore.backend=	Specify the name of the pstore backend to use

	pt.		[PARIDE]
	pt.		[PARIDE]
			See Documentation/blockdev/paride.txt.
			See Documentation/blockdev/paride.txt.


+15 −5
Original line number Original line Diff line number Diff line
@@ -932,8 +932,11 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
static int erst_open_pstore(struct pstore_info *psi);
static int erst_open_pstore(struct pstore_info *psi);
static int erst_close_pstore(struct pstore_info *psi);
static int erst_close_pstore(struct pstore_info *psi);
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
		       struct timespec *time);
			   struct timespec *time, struct pstore_info *psi);
static u64 erst_writer(enum pstore_type_id type, size_t size);
static u64 erst_writer(enum pstore_type_id type, unsigned int part,
		       size_t size, struct pstore_info *psi);
static int erst_clearer(enum pstore_type_id type, u64 id,
			struct pstore_info *psi);


static struct pstore_info erst_info = {
static struct pstore_info erst_info = {
	.owner		= THIS_MODULE,
	.owner		= THIS_MODULE,
@@ -942,7 +945,7 @@ static struct pstore_info erst_info = {
	.close		= erst_close_pstore,
	.close		= erst_close_pstore,
	.read		= erst_reader,
	.read		= erst_reader,
	.write		= erst_writer,
	.write		= erst_writer,
	.erase		= erst_clear
	.erase		= erst_clearer
};
};


#define CPER_CREATOR_PSTORE						\
#define CPER_CREATOR_PSTORE						\
@@ -983,7 +986,7 @@ static int erst_close_pstore(struct pstore_info *psi)
}
}


static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
		       struct timespec *time)
			   struct timespec *time, struct pstore_info *psi)
{
{
	int rc;
	int rc;
	ssize_t len = 0;
	ssize_t len = 0;
@@ -1037,7 +1040,8 @@ out:
	return (rc < 0) ? rc : (len - sizeof(*rcd));
	return (rc < 0) ? rc : (len - sizeof(*rcd));
}
}


static u64 erst_writer(enum pstore_type_id type, size_t size)
static u64 erst_writer(enum pstore_type_id type, unsigned int part,
		       size_t size, struct pstore_info *psi)
{
{
	struct cper_pstore_record *rcd = (struct cper_pstore_record *)
	struct cper_pstore_record *rcd = (struct cper_pstore_record *)
					(erst_info.buf - sizeof(*rcd));
					(erst_info.buf - sizeof(*rcd));
@@ -1080,6 +1084,12 @@ static u64 erst_writer(enum pstore_type_id type, size_t size)
	return rcd->hdr.record_id;
	return rcd->hdr.record_id;
}
}


static int erst_clearer(enum pstore_type_id type, u64 id,
			struct pstore_info *psi)
{
	return erst_clear(id);
}

static int __init erst_init(void)
static int __init erst_init(void)
{
{
	int rc = 0;
	int rc = 0;
+231 −12
Original line number Original line Diff line number Diff line
@@ -78,6 +78,7 @@
#include <linux/kobject.h>
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/pstore.h>


#include <asm/uaccess.h>
#include <asm/uaccess.h>


@@ -89,6 +90,8 @@ MODULE_DESCRIPTION("sysfs interface to EFI Variables");
MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL");
MODULE_VERSION(EFIVARS_VERSION);
MODULE_VERSION(EFIVARS_VERSION);


#define DUMP_NAME_LEN 52

/*
/*
 * The maximum size of VariableName + Data = 1024
 * The maximum size of VariableName + Data = 1024
 * Therefore, it's reasonable to save that much
 * Therefore, it's reasonable to save that much
@@ -119,6 +122,10 @@ struct efivar_attribute {
	ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
	ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
};
};


#define PSTORE_EFI_ATTRIBUTES \
	(EFI_VARIABLE_NON_VOLATILE | \
	 EFI_VARIABLE_BOOTSERVICE_ACCESS | \
	 EFI_VARIABLE_RUNTIME_ACCESS)


#define EFIVAR_ATTR(_name, _mode, _show, _store) \
#define EFIVAR_ATTR(_name, _mode, _show, _store) \
struct efivar_attribute efivar_attr_##_name = { \
struct efivar_attribute efivar_attr_##_name = { \
@@ -141,38 +148,72 @@ efivar_create_sysfs_entry(struct efivars *efivars,


/* Return the number of unicode characters in data */
/* Return the number of unicode characters in data */
static unsigned long
static unsigned long
utf8_strlen(efi_char16_t *data, unsigned long maxlength)
utf16_strnlen(efi_char16_t *s, size_t maxlength)
{
{
	unsigned long length = 0;
	unsigned long length = 0;


	while (*data++ != 0 && length < maxlength)
	while (*s++ != 0 && length < maxlength)
		length++;
		length++;
	return length;
	return length;
}
}


static unsigned long
utf16_strlen(efi_char16_t *s)
{
	return utf16_strnlen(s, ~0UL);
}

/*
/*
 * Return the number of bytes is the length of this string
 * Return the number of bytes is the length of this string
 * Note: this is NOT the same as the number of unicode characters
 * Note: this is NOT the same as the number of unicode characters
 */
 */
static inline unsigned long
static inline unsigned long
utf8_strsize(efi_char16_t *data, unsigned long maxlength)
utf16_strsize(efi_char16_t *data, unsigned long maxlength)
{
	return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
}

static inline int
utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len)
{
{
	return utf8_strlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
	while (1) {
		if (len == 0)
			return 0;
		if (*a < *b)
			return -1;
		if (*a > *b)
			return 1;
		if (*a == 0) /* implies *b == 0 */
			return 0;
		a++;
		b++;
		len--;
	}
}
}


static efi_status_t
static efi_status_t
get_var_data(struct efivars *efivars, struct efi_variable *var)
get_var_data_locked(struct efivars *efivars, struct efi_variable *var)
{
{
	efi_status_t status;
	efi_status_t status;


	spin_lock(&efivars->lock);
	var->DataSize = 1024;
	var->DataSize = 1024;
	status = efivars->ops->get_variable(var->VariableName,
	status = efivars->ops->get_variable(var->VariableName,
					    &var->VendorGuid,
					    &var->VendorGuid,
					    &var->Attributes,
					    &var->Attributes,
					    &var->DataSize,
					    &var->DataSize,
					    var->Data);
					    var->Data);
	return status;
}

static efi_status_t
get_var_data(struct efivars *efivars, struct efi_variable *var)
{
	efi_status_t status;

	spin_lock(&efivars->lock);
	status = get_var_data_locked(efivars, var);
	spin_unlock(&efivars->lock);
	spin_unlock(&efivars->lock);

	if (status != EFI_SUCCESS) {
	if (status != EFI_SUCCESS) {
		printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
		printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
			status);
			status);
@@ -387,12 +428,180 @@ static struct kobj_type efivar_ktype = {
	.default_attrs = def_attrs,
	.default_attrs = def_attrs,
};
};


static struct pstore_info efi_pstore_info;

static inline void
static inline void
efivar_unregister(struct efivar_entry *var)
efivar_unregister(struct efivar_entry *var)
{
{
	kobject_put(&var->kobj);
	kobject_put(&var->kobj);
}
}


#ifdef CONFIG_PSTORE

static int efi_pstore_open(struct pstore_info *psi)
{
	struct efivars *efivars = psi->data;

	spin_lock(&efivars->lock);
	efivars->walk_entry = list_first_entry(&efivars->list,
					       struct efivar_entry, list);
	return 0;
}

static int efi_pstore_close(struct pstore_info *psi)
{
	struct efivars *efivars = psi->data;

	spin_unlock(&efivars->lock);
	return 0;
}

static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
			       struct timespec *timespec, struct pstore_info *psi)
{
	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
	struct efivars *efivars = psi->data;
	char name[DUMP_NAME_LEN];
	int i;
	unsigned int part, size;
	unsigned long time;

	while (&efivars->walk_entry->list != &efivars->list) {
		if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid,
				 vendor)) {
			for (i = 0; i < DUMP_NAME_LEN; i++) {
				name[i] = efivars->walk_entry->var.VariableName[i];
			}
			if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) {
				*id = part;
				timespec->tv_sec = time;
				timespec->tv_nsec = 0;
				get_var_data_locked(efivars, &efivars->walk_entry->var);
				size = efivars->walk_entry->var.DataSize;
				memcpy(psi->buf, efivars->walk_entry->var.Data, size);
				efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
					           struct efivar_entry, list);
				return size;
			}
		}
		efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
						 struct efivar_entry, list);
	}
	return 0;
}

static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
			    size_t size, struct pstore_info *psi)
{
	char name[DUMP_NAME_LEN];
	char stub_name[DUMP_NAME_LEN];
	efi_char16_t efi_name[DUMP_NAME_LEN];
	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
	struct efivars *efivars = psi->data;
	struct efivar_entry *entry, *found = NULL;
	int i;

	sprintf(stub_name, "dump-type%u-%u-", type, part);
	sprintf(name, "%s%lu", stub_name, get_seconds());

	spin_lock(&efivars->lock);

	for (i = 0; i < DUMP_NAME_LEN; i++)
		efi_name[i] = stub_name[i];

	/*
	 * Clean up any entries with the same name
	 */

	list_for_each_entry(entry, &efivars->list, list) {
		get_var_data_locked(efivars, &entry->var);

		if (efi_guidcmp(entry->var.VendorGuid, vendor))
			continue;
		if (utf16_strncmp(entry->var.VariableName, efi_name,
				  utf16_strlen(efi_name)))
			continue;
		/* Needs to be a prefix */
		if (entry->var.VariableName[utf16_strlen(efi_name)] == 0)
			continue;

		/* found */
		found = entry;
		efivars->ops->set_variable(entry->var.VariableName,
					   &entry->var.VendorGuid,
					   PSTORE_EFI_ATTRIBUTES,
					   0, NULL);
	}

	if (found)
		list_del(&found->list);

	for (i = 0; i < DUMP_NAME_LEN; i++)
		efi_name[i] = name[i];

	efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
				   size, psi->buf);

	spin_unlock(&efivars->lock);

	if (found)
		efivar_unregister(found);

	if (size)
		efivar_create_sysfs_entry(efivars,
					  utf16_strsize(efi_name,
							DUMP_NAME_LEN * 2),
					  efi_name, &vendor);

	return part;
};

static int efi_pstore_erase(enum pstore_type_id type, u64 id,
			    struct pstore_info *psi)
{
	efi_pstore_write(type, id, 0, psi);

	return 0;
}
#else
static int efi_pstore_open(struct pstore_info *psi)
{
	return 0;
}

static int efi_pstore_close(struct pstore_info *psi)
{
	return 0;
}

static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
			       struct timespec *time, struct pstore_info *psi)
{
	return -1;
}

static u64 efi_pstore_write(enum pstore_type_id type, int part, size_t size,
			    struct pstore_info *psi)
{
	return 0;
}

static int efi_pstore_erase(enum pstore_type_id type, u64 id,
			    struct pstore_info *psi)
{
	return 0;
}
#endif

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 ssize_t efivar_create(struct file *filp, struct kobject *kobj,
static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
			     struct bin_attribute *bin_attr,
			     struct bin_attribute *bin_attr,
@@ -414,8 +623,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
	 * Does this variable already exist?
	 * Does this variable already exist?
	 */
	 */
	list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
	list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
		strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
		strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
		strsize2 = utf8_strsize(new_var->VariableName, 1024);
		strsize2 = utf16_strsize(new_var->VariableName, 1024);
		if (strsize1 == strsize2 &&
		if (strsize1 == strsize2 &&
			!memcmp(&(search_efivar->var.VariableName),
			!memcmp(&(search_efivar->var.VariableName),
				new_var->VariableName, strsize1) &&
				new_var->VariableName, strsize1) &&
@@ -447,7 +656,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,


	/* Create the entry in sysfs.  Locking is not required here */
	/* Create the entry in sysfs.  Locking is not required here */
	status = efivar_create_sysfs_entry(efivars,
	status = efivar_create_sysfs_entry(efivars,
					   utf8_strsize(new_var->VariableName,
					   utf16_strsize(new_var->VariableName,
							 1024),
							 1024),
					   new_var->VariableName,
					   new_var->VariableName,
					   &new_var->VendorGuid);
					   &new_var->VendorGuid);
@@ -477,8 +686,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
	 * Does this variable already exist?
	 * Does this variable already exist?
	 */
	 */
	list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
	list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
		strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
		strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
		strsize2 = utf8_strsize(del_var->VariableName, 1024);
		strsize2 = utf16_strsize(del_var->VariableName, 1024);
		if (strsize1 == strsize2 &&
		if (strsize1 == strsize2 &&
			!memcmp(&(search_efivar->var.VariableName),
			!memcmp(&(search_efivar->var.VariableName),
				del_var->VariableName, strsize1) &&
				del_var->VariableName, strsize1) &&
@@ -763,6 +972,16 @@ int register_efivars(struct efivars *efivars,
	if (error)
	if (error)
		unregister_efivars(efivars);
		unregister_efivars(efivars);


	efivars->efi_pstore_info = efi_pstore_info;

	efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
	if (efivars->efi_pstore_info.buf) {
		efivars->efi_pstore_info.bufsize = 1024;
		efivars->efi_pstore_info.data = efivars;
		mutex_init(&efivars->efi_pstore_info.buf_mutex);
		pstore_register(&efivars->efi_pstore_info);
	}

out:
out:
	kfree(variable_name);
	kfree(variable_name);


+7 −5
Original line number Original line Diff line number Diff line
@@ -39,8 +39,9 @@
#define	PSTORE_NAMELEN	64
#define	PSTORE_NAMELEN	64


struct pstore_private {
struct pstore_private {
	struct pstore_info *psi;
	enum pstore_type_id type;
	u64	id;
	u64	id;
	int	(*erase)(u64);
	ssize_t	size;
	ssize_t	size;
	char	data[];
	char	data[];
};
};
@@ -73,7 +74,7 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
{
{
	struct pstore_private *p = dentry->d_inode->i_private;
	struct pstore_private *p = dentry->d_inode->i_private;


	p->erase(p->id);
	p->psi->erase(p->type, p->id, p->psi);


	return simple_unlink(dir, dentry);
	return simple_unlink(dir, dentry);
}
}
@@ -175,8 +176,8 @@ int pstore_is_mounted(void)
 * Set the mtime & ctime to the date that this record was originally stored.
 * Set the mtime & ctime to the date that this record was originally stored.
 */
 */
int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
			      char *data, size_t size,
		  char *data, size_t size, struct timespec time,
			      struct timespec time, int (*erase)(u64))
		  struct pstore_info *psi)
{
{
	struct dentry		*root = pstore_sb->s_root;
	struct dentry		*root = pstore_sb->s_root;
	struct dentry		*dentry;
	struct dentry		*dentry;
@@ -192,8 +193,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
	private = kmalloc(sizeof *private + size, GFP_KERNEL);
	private = kmalloc(sizeof *private + size, GFP_KERNEL);
	if (!private)
	if (!private)
		goto fail_alloc;
		goto fail_alloc;
	private->type = type;
	private->id = id;
	private->id = id;
	private->erase = erase;
	private->psi = psi;


	switch (type) {
	switch (type) {
	case PSTORE_TYPE_DMESG:
	case PSTORE_TYPE_DMESG:
Loading