1d68772b7SMatt Fleming /* 2d68772b7SMatt Fleming * Copyright (C) 2012 Red Hat, Inc. 3d68772b7SMatt Fleming * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com> 4d68772b7SMatt Fleming * 5d68772b7SMatt Fleming * This program is free software; you can redistribute it and/or modify 6d68772b7SMatt Fleming * it under the terms of the GNU General Public License version 2 as 7d68772b7SMatt Fleming * published by the Free Software Foundation. 8d68772b7SMatt Fleming */ 9d68772b7SMatt Fleming 10d68772b7SMatt Fleming #include <linux/ctype.h> 11d68772b7SMatt Fleming #include <linux/efi.h> 12d68772b7SMatt Fleming #include <linux/fs.h> 13d68772b7SMatt Fleming #include <linux/module.h> 14d68772b7SMatt Fleming #include <linux/pagemap.h> 15a614e192SMatt Fleming #include <linux/ucs2_string.h> 1620b4fb48SLinus Torvalds #include <linux/slab.h> 1720b4fb48SLinus Torvalds #include <linux/magic.h> 18d68772b7SMatt Fleming 19d68772b7SMatt Fleming #include "internal.h" 20d68772b7SMatt Fleming 21d68772b7SMatt Fleming LIST_HEAD(efivarfs_list); 22d68772b7SMatt Fleming 23d68772b7SMatt Fleming static void efivarfs_evict_inode(struct inode *inode) 24d68772b7SMatt Fleming { 25d68772b7SMatt Fleming clear_inode(inode); 26d68772b7SMatt Fleming } 27d68772b7SMatt Fleming 28d68772b7SMatt Fleming static const struct super_operations efivarfs_ops = { 29d68772b7SMatt Fleming .statfs = simple_statfs, 30d68772b7SMatt Fleming .drop_inode = generic_delete_inode, 31d68772b7SMatt Fleming .evict_inode = efivarfs_evict_inode, 32d68772b7SMatt Fleming .show_options = generic_show_options, 33d68772b7SMatt Fleming }; 34d68772b7SMatt Fleming 35d68772b7SMatt Fleming static struct super_block *efivarfs_sb; 36d68772b7SMatt Fleming 37d68772b7SMatt Fleming /* 38d68772b7SMatt Fleming * Compare two efivarfs file names. 39d68772b7SMatt Fleming * 40d68772b7SMatt Fleming * An efivarfs filename is composed of two parts, 41d68772b7SMatt Fleming * 42d68772b7SMatt Fleming * 1. A case-sensitive variable name 43d68772b7SMatt Fleming * 2. A case-insensitive GUID 44d68772b7SMatt Fleming * 45d68772b7SMatt Fleming * So we need to perform a case-sensitive match on part 1 and a 46d68772b7SMatt Fleming * case-insensitive match on part 2. 47d68772b7SMatt Fleming */ 48da53be12SLinus Torvalds static int efivarfs_d_compare(const struct dentry *parent, 49da53be12SLinus Torvalds const struct dentry *dentry, 50d68772b7SMatt Fleming unsigned int len, const char *str, 51d68772b7SMatt Fleming const struct qstr *name) 52d68772b7SMatt Fleming { 53d68772b7SMatt Fleming int guid = len - EFI_VARIABLE_GUID_LEN; 54d68772b7SMatt Fleming 55d68772b7SMatt Fleming if (name->len != len) 56d68772b7SMatt Fleming return 1; 57d68772b7SMatt Fleming 58d68772b7SMatt Fleming /* Case-sensitive compare for the variable name */ 59d68772b7SMatt Fleming if (memcmp(str, name->name, guid)) 60d68772b7SMatt Fleming return 1; 61d68772b7SMatt Fleming 62d68772b7SMatt Fleming /* Case-insensitive compare for the GUID */ 63d68772b7SMatt Fleming return strncasecmp(name->name + guid, str + guid, EFI_VARIABLE_GUID_LEN); 64d68772b7SMatt Fleming } 65d68772b7SMatt Fleming 66da53be12SLinus Torvalds static int efivarfs_d_hash(const struct dentry *dentry, struct qstr *qstr) 67d68772b7SMatt Fleming { 68d68772b7SMatt Fleming unsigned long hash = init_name_hash(); 69d68772b7SMatt Fleming const unsigned char *s = qstr->name; 70d68772b7SMatt Fleming unsigned int len = qstr->len; 71d68772b7SMatt Fleming 72d68772b7SMatt Fleming if (!efivarfs_valid_name(s, len)) 73d68772b7SMatt Fleming return -EINVAL; 74d68772b7SMatt Fleming 75d68772b7SMatt Fleming while (len-- > EFI_VARIABLE_GUID_LEN) 76d68772b7SMatt Fleming hash = partial_name_hash(*s++, hash); 77d68772b7SMatt Fleming 78d68772b7SMatt Fleming /* GUID is case-insensitive. */ 79d68772b7SMatt Fleming while (len--) 80d68772b7SMatt Fleming hash = partial_name_hash(tolower(*s++), hash); 81d68772b7SMatt Fleming 82d68772b7SMatt Fleming qstr->hash = end_name_hash(hash); 83d68772b7SMatt Fleming return 0; 84d68772b7SMatt Fleming } 85d68772b7SMatt Fleming 86e37dcbfbSFabian Frederick static const struct dentry_operations efivarfs_d_ops = { 87d68772b7SMatt Fleming .d_compare = efivarfs_d_compare, 88d68772b7SMatt Fleming .d_hash = efivarfs_d_hash, 89b26d4cd3SAl Viro .d_delete = always_delete_dentry, 90d68772b7SMatt Fleming }; 91d68772b7SMatt Fleming 92d68772b7SMatt Fleming static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) 93d68772b7SMatt Fleming { 94d68772b7SMatt Fleming struct dentry *d; 95d68772b7SMatt Fleming struct qstr q; 96d68772b7SMatt Fleming int err; 97d68772b7SMatt Fleming 98d68772b7SMatt Fleming q.name = name; 99d68772b7SMatt Fleming q.len = strlen(name); 100d68772b7SMatt Fleming 101da53be12SLinus Torvalds err = efivarfs_d_hash(NULL, &q); 102d68772b7SMatt Fleming if (err) 103d68772b7SMatt Fleming return ERR_PTR(err); 104d68772b7SMatt Fleming 105d68772b7SMatt Fleming d = d_alloc(parent, &q); 106d68772b7SMatt Fleming if (d) 107d68772b7SMatt Fleming return d; 108d68772b7SMatt Fleming 109d68772b7SMatt Fleming return ERR_PTR(-ENOMEM); 110d68772b7SMatt Fleming } 111d68772b7SMatt Fleming 112d68772b7SMatt Fleming static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, 113d68772b7SMatt Fleming unsigned long name_size, void *data) 114d68772b7SMatt Fleming { 115d68772b7SMatt Fleming struct super_block *sb = (struct super_block *)data; 116d68772b7SMatt Fleming struct efivar_entry *entry; 117d68772b7SMatt Fleming struct inode *inode = NULL; 118d68772b7SMatt Fleming struct dentry *dentry, *root = sb->s_root; 119d68772b7SMatt Fleming unsigned long size = 0; 120d68772b7SMatt Fleming char *name; 121d68772b7SMatt Fleming int len, i; 122d68772b7SMatt Fleming int err = -ENOMEM; 123d68772b7SMatt Fleming 124c57dcb56SRoss Lagerwall entry = kzalloc(sizeof(*entry), GFP_KERNEL); 125d68772b7SMatt Fleming if (!entry) 126d68772b7SMatt Fleming return err; 127d68772b7SMatt Fleming 128d68772b7SMatt Fleming memcpy(entry->var.VariableName, name16, name_size); 129d68772b7SMatt Fleming memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); 130d68772b7SMatt Fleming 131a614e192SMatt Fleming len = ucs2_strlen(entry->var.VariableName); 132d68772b7SMatt Fleming 133d68772b7SMatt Fleming /* name, plus '-', plus GUID, plus NUL*/ 134d68772b7SMatt Fleming name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL); 135d68772b7SMatt Fleming if (!name) 136d68772b7SMatt Fleming goto fail; 137d68772b7SMatt Fleming 138d68772b7SMatt Fleming for (i = 0; i < len; i++) 139d68772b7SMatt Fleming name[i] = entry->var.VariableName[i] & 0xFF; 140d68772b7SMatt Fleming 141d68772b7SMatt Fleming name[len] = '-'; 142d68772b7SMatt Fleming 14326e02272SBorislav Petkov efi_guid_to_str(&entry->var.VendorGuid, name + len + 1); 144d68772b7SMatt Fleming 145d68772b7SMatt Fleming name[len + EFI_VARIABLE_GUID_LEN+1] = '\0'; 146d68772b7SMatt Fleming 1472b0143b5SDavid Howells inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0); 148d68772b7SMatt Fleming if (!inode) 149d68772b7SMatt Fleming goto fail_name; 150d68772b7SMatt Fleming 151d68772b7SMatt Fleming dentry = efivarfs_alloc_dentry(root, name); 152d68772b7SMatt Fleming if (IS_ERR(dentry)) { 153d68772b7SMatt Fleming err = PTR_ERR(dentry); 154d68772b7SMatt Fleming goto fail_inode; 155d68772b7SMatt Fleming } 156d68772b7SMatt Fleming 157d68772b7SMatt Fleming /* copied by the above to local storage in the dentry. */ 158d68772b7SMatt Fleming kfree(name); 159d68772b7SMatt Fleming 160d68772b7SMatt Fleming efivar_entry_size(entry, &size); 161d68772b7SMatt Fleming efivar_entry_add(entry, &efivarfs_list); 162d68772b7SMatt Fleming 163d68772b7SMatt Fleming mutex_lock(&inode->i_mutex); 164d68772b7SMatt Fleming inode->i_private = entry; 165d68772b7SMatt Fleming i_size_write(inode, size + sizeof(entry->var.Attributes)); 166d68772b7SMatt Fleming mutex_unlock(&inode->i_mutex); 167d68772b7SMatt Fleming d_add(dentry, inode); 168d68772b7SMatt Fleming 169d68772b7SMatt Fleming return 0; 170d68772b7SMatt Fleming 171d68772b7SMatt Fleming fail_inode: 172d68772b7SMatt Fleming iput(inode); 173d68772b7SMatt Fleming fail_name: 174d68772b7SMatt Fleming kfree(name); 175d68772b7SMatt Fleming fail: 176d68772b7SMatt Fleming kfree(entry); 177d68772b7SMatt Fleming return err; 178d68772b7SMatt Fleming } 179d68772b7SMatt Fleming 180d68772b7SMatt Fleming static int efivarfs_destroy(struct efivar_entry *entry, void *data) 181d68772b7SMatt Fleming { 182d68772b7SMatt Fleming efivar_entry_remove(entry); 183d68772b7SMatt Fleming kfree(entry); 184d68772b7SMatt Fleming return 0; 185d68772b7SMatt Fleming } 186d68772b7SMatt Fleming 187d68772b7SMatt Fleming static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) 188d68772b7SMatt Fleming { 189d68772b7SMatt Fleming struct inode *inode = NULL; 190d68772b7SMatt Fleming struct dentry *root; 191d68772b7SMatt Fleming int err; 192d68772b7SMatt Fleming 193d68772b7SMatt Fleming efivarfs_sb = sb; 194d68772b7SMatt Fleming 195d68772b7SMatt Fleming sb->s_maxbytes = MAX_LFS_FILESIZE; 196d68772b7SMatt Fleming sb->s_blocksize = PAGE_CACHE_SIZE; 197d68772b7SMatt Fleming sb->s_blocksize_bits = PAGE_CACHE_SHIFT; 198d68772b7SMatt Fleming sb->s_magic = EFIVARFS_MAGIC; 199d68772b7SMatt Fleming sb->s_op = &efivarfs_ops; 200d68772b7SMatt Fleming sb->s_d_op = &efivarfs_d_ops; 201d68772b7SMatt Fleming sb->s_time_gran = 1; 202d68772b7SMatt Fleming 203d68772b7SMatt Fleming inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0); 204d68772b7SMatt Fleming if (!inode) 205d68772b7SMatt Fleming return -ENOMEM; 206d68772b7SMatt Fleming inode->i_op = &efivarfs_dir_inode_operations; 207d68772b7SMatt Fleming 208d68772b7SMatt Fleming root = d_make_root(inode); 209d68772b7SMatt Fleming sb->s_root = root; 210d68772b7SMatt Fleming if (!root) 211d68772b7SMatt Fleming return -ENOMEM; 212d68772b7SMatt Fleming 213d68772b7SMatt Fleming INIT_LIST_HEAD(&efivarfs_list); 214d68772b7SMatt Fleming 215d68772b7SMatt Fleming err = efivar_init(efivarfs_callback, (void *)sb, false, 216d68772b7SMatt Fleming true, &efivarfs_list); 217d68772b7SMatt Fleming if (err) 218d68772b7SMatt Fleming __efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL); 219d68772b7SMatt Fleming 220d68772b7SMatt Fleming return err; 221d68772b7SMatt Fleming } 222d68772b7SMatt Fleming 223d68772b7SMatt Fleming static struct dentry *efivarfs_mount(struct file_system_type *fs_type, 224d68772b7SMatt Fleming int flags, const char *dev_name, void *data) 225d68772b7SMatt Fleming { 226d68772b7SMatt Fleming return mount_single(fs_type, flags, data, efivarfs_fill_super); 227d68772b7SMatt Fleming } 228d68772b7SMatt Fleming 229d68772b7SMatt Fleming static void efivarfs_kill_sb(struct super_block *sb) 230d68772b7SMatt Fleming { 231d68772b7SMatt Fleming kill_litter_super(sb); 232d68772b7SMatt Fleming efivarfs_sb = NULL; 233d68772b7SMatt Fleming 234d68772b7SMatt Fleming /* Remove all entries and destroy */ 235d68772b7SMatt Fleming __efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL); 236d68772b7SMatt Fleming } 237d68772b7SMatt Fleming 238d68772b7SMatt Fleming static struct file_system_type efivarfs_type = { 239af5a29aeSMathias Krause .owner = THIS_MODULE, 240d68772b7SMatt Fleming .name = "efivarfs", 241d68772b7SMatt Fleming .mount = efivarfs_mount, 242d68772b7SMatt Fleming .kill_sb = efivarfs_kill_sb, 243d68772b7SMatt Fleming }; 244d68772b7SMatt Fleming 245d68772b7SMatt Fleming static __init int efivarfs_init(void) 246d68772b7SMatt Fleming { 247d68772b7SMatt Fleming if (!efi_enabled(EFI_RUNTIME_SERVICES)) 248af5a29aeSMathias Krause return -ENODEV; 249d68772b7SMatt Fleming 250d68772b7SMatt Fleming if (!efivars_kobject()) 251af5a29aeSMathias Krause return -ENODEV; 252d68772b7SMatt Fleming 253d68772b7SMatt Fleming return register_filesystem(&efivarfs_type); 254d68772b7SMatt Fleming } 255d68772b7SMatt Fleming 256af5a29aeSMathias Krause static __exit void efivarfs_exit(void) 257af5a29aeSMathias Krause { 258af5a29aeSMathias Krause unregister_filesystem(&efivarfs_type); 259af5a29aeSMathias Krause } 260af5a29aeSMathias Krause 261d68772b7SMatt Fleming MODULE_AUTHOR("Matthew Garrett, Jeremy Kerr"); 262d68772b7SMatt Fleming MODULE_DESCRIPTION("EFI Variable Filesystem"); 263d68772b7SMatt Fleming MODULE_LICENSE("GPL"); 264d68772b7SMatt Fleming MODULE_ALIAS_FS("efivarfs"); 265d68772b7SMatt Fleming 266d68772b7SMatt Fleming module_init(efivarfs_init); 267af5a29aeSMathias Krause module_exit(efivarfs_exit); 268