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

Commit e3c4877d 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:

 - Improve the initrd handling in the EFI boot stub by allowing forward
   slashes in the pathname - from Chun-Yi Lee.

 - Cleanup code duplication in the EFI mixed kernel/firmware code - from
   Satoru Takeuchi.

 - efivarfs bug fixes for more strict filename validation, with lots of
   input from Al Viro.

* 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86, efi: remove duplicate code in setup_arch() by using, efi_is_native()
  efivarfs: guid part of filenames are case-insensitive
  efivarfs: Validate filenames much more aggressively
  efivarfs: Use sizeof() instead of magic number
  x86, efi: Allow slash in file path of initrd
parents 18a44a7f 6b59e366
Loading
Loading
Loading
Loading
+19 −7
Original line number Diff line number Diff line
@@ -19,23 +19,28 @@

static efi_system_table_t *sys_table;

static void efi_char16_printk(efi_char16_t *str)
{
	struct efi_simple_text_output_protocol *out;

	out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
	efi_call_phys2(out->output_string, out, str);
}

static void efi_printk(char *str)
{
	char *s8;

	for (s8 = str; *s8; s8++) {
		struct efi_simple_text_output_protocol *out;
		efi_char16_t ch[2] = { 0 };

		ch[0] = *s8;
		out = (struct efi_simple_text_output_protocol *)sys_table->con_out;

		if (*s8 == '\n') {
			efi_char16_t nl[2] = { '\r', 0 };
			efi_call_phys2(out->output_string, out, nl);
			efi_char16_printk(nl);
		}

		efi_call_phys2(out->output_string, out, ch);
		efi_char16_printk(ch);
	}
}

@@ -709,8 +714,13 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
			if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16))
				break;

			if (*str == '/') {
				*p++ = '\\';
				*str++;
			} else {
				*p++ = *str++;
			}
		}

		*p = '\0';

@@ -737,7 +747,9 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
		status = efi_call_phys5(fh->open, fh, &h, filename_16,
					EFI_FILE_MODE_READ, (u64)0);
		if (status != EFI_SUCCESS) {
			efi_printk("Failed to open initrd file\n");
			efi_printk("Failed to open initrd file: ");
			efi_char16_printk(filename_16);
			efi_printk("\n");
			goto close_handles;
		}

+8 −1
Original line number Diff line number Diff line
@@ -102,7 +102,14 @@ extern void efi_call_phys_epilog(void);
extern void efi_unmap_memmap(void);
extern void efi_memory_uc(u64 addr, unsigned long size);

#ifndef CONFIG_EFI
#ifdef CONFIG_EFI

static inline bool efi_is_native(void)
{
	return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT);
}

#else
/*
 * IF EFI is not configured, have the EFI calls return -ENOSYS.
 */
+1 −2
Original line number Diff line number Diff line
@@ -1196,8 +1196,7 @@ void __init setup_arch(char **cmdline_p)
	 * mismatched firmware/kernel archtectures since there is no
	 * support for runtime services.
	 */
	if (efi_enabled(EFI_BOOT) &&
	    IS_ENABLED(CONFIG_X86_64) != efi_enabled(EFI_64BIT)) {
	if (efi_enabled(EFI_BOOT) && !efi_is_native()) {
		pr_info("efi: Setup done, disabling due to 32/64-bit mismatch\n");
		efi_unmap_memmap();
	}
+0 −5
Original line number Diff line number Diff line
@@ -69,11 +69,6 @@ struct efi_memory_map memmap;
static struct efi efi_phys __initdata;
static efi_system_table_t efi_systab __initdata;

static inline bool efi_is_native(void)
{
	return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT);
}

unsigned long x86_efi_facility;

/*
+138 −8
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/pstore.h>
#include <linux/ctype.h>

#include <linux/fs.h>
#include <linux/ramfs.h>
@@ -908,6 +909,48 @@ static struct inode *efivarfs_get_inode(struct super_block *sb,
	return inode;
}

/*
 * Return true if 'str' is a valid efivarfs filename of the form,
 *
 *	VariableName-12345678-1234-1234-1234-1234567891bc
 */
static bool efivarfs_valid_name(const char *str, int len)
{
	static const char dashes[GUID_LEN] = {
		[8] = 1, [13] = 1, [18] = 1, [23] = 1
	};
	const char *s = str + len - GUID_LEN;
	int i;

	/*
	 * We need a GUID, plus at least one letter for the variable name,
	 * plus the '-' separator
	 */
	if (len < GUID_LEN + 2)
		return false;

	/* GUID should be right after the first '-' */
	if (s - 1 != strchr(str, '-'))
		return false;

	/*
	 * Validate that 's' is of the correct format, e.g.
	 *
	 *	12345678-1234-1234-1234-123456789abc
	 */
	for (i = 0; i < GUID_LEN; i++) {
		if (dashes[i]) {
			if (*s++ != '-')
				return false;
		} else {
			if (!isxdigit(*s++))
				return false;
		}
	}

	return true;
}

static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid)
{
	guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]);
@@ -936,11 +979,7 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
	struct efivar_entry *var;
	int namelen, i = 0, err = 0;

	/*
	 * We need a GUID, plus at least one letter for the variable name,
	 * plus the '-' separator
	 */
	if (dentry->d_name.len < GUID_LEN + 2)
	if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len))
		return -EINVAL;

	inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0);
@@ -1012,6 +1051,84 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
	return -EINVAL;
};

/*
 * Compare two efivarfs file names.
 *
 * An efivarfs filename is composed of two parts,
 *
 *	1. A case-sensitive variable name
 *	2. A case-insensitive GUID
 *
 * So we need to perform a case-sensitive match on part 1 and a
 * case-insensitive match on part 2.
 */
static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode,
			      const struct dentry *dentry, const struct inode *inode,
			      unsigned int len, const char *str,
			      const struct qstr *name)
{
	int guid = len - GUID_LEN;

	if (name->len != len)
		return 1;

	/* Case-sensitive compare for the variable name */
	if (memcmp(str, name->name, guid))
		return 1;

	/* Case-insensitive compare for the GUID */
	return strncasecmp(name->name + guid, str + guid, GUID_LEN);
}

static int efivarfs_d_hash(const struct dentry *dentry,
			   const struct inode *inode, struct qstr *qstr)
{
	unsigned long hash = init_name_hash();
	const unsigned char *s = qstr->name;
	unsigned int len = qstr->len;

	if (!efivarfs_valid_name(s, len))
		return -EINVAL;

	while (len-- > GUID_LEN)
		hash = partial_name_hash(*s++, hash);

	/* GUID is case-insensitive. */
	while (len--)
		hash = partial_name_hash(tolower(*s++), hash);

	qstr->hash = end_name_hash(hash);
	return 0;
}

/*
 * Retaining negative dentries for an in-memory filesystem just wastes
 * memory and lookup time: arrange for them to be deleted immediately.
 */
static int efivarfs_delete_dentry(const struct dentry *dentry)
{
	return 1;
}

static struct dentry_operations efivarfs_d_ops = {
	.d_compare = efivarfs_d_compare,
	.d_hash = efivarfs_d_hash,
	.d_delete = efivarfs_delete_dentry,
};

static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
{
	struct qstr q;

	q.name = name;
	q.len = strlen(name);

	if (efivarfs_d_hash(NULL, NULL, &q))
		return NULL;

	return d_alloc(parent, &q);
}

static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
{
	struct inode *inode = NULL;
@@ -1027,6 +1144,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
	sb->s_blocksize_bits    = PAGE_CACHE_SHIFT;
	sb->s_magic             = EFIVARFS_MAGIC;
	sb->s_op                = &efivarfs_ops;
	sb->s_d_op		= &efivarfs_d_ops;
	sb->s_time_gran         = 1;

	inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
@@ -1067,7 +1185,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
		if (!inode)
			goto fail_name;

		dentry = d_alloc_name(root, name);
		dentry = efivarfs_alloc_dentry(root, name);
		if (!dentry)
			goto fail_inode;

@@ -1084,7 +1202,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)

		mutex_lock(&inode->i_mutex);
		inode->i_private = entry;
		i_size_write(inode, size+4);
		i_size_write(inode, size + sizeof(entry->var.Attributes));
		mutex_unlock(&inode->i_mutex);
		d_add(dentry, inode);
	}
@@ -1117,8 +1235,20 @@ static struct file_system_type efivarfs_type = {
	.kill_sb = efivarfs_kill_sb,
};

/*
 * Handle negative dentry.
 */
static struct dentry *efivarfs_lookup(struct inode *dir, struct dentry *dentry,
				      unsigned int flags)
{
	if (dentry->d_name.len > NAME_MAX)
		return ERR_PTR(-ENAMETOOLONG);
	d_add(dentry, NULL);
	return NULL;
}

static const struct inode_operations efivarfs_dir_inode_operations = {
	.lookup = simple_lookup,
	.lookup = efivarfs_lookup,
	.unlink = efivarfs_unlink,
	.create = efivarfs_create,
};