diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-27 19:17:42 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-27 19:17:42 -0500 |
| commit | e3c4877de8b9d93bd47b6ee88eb594b1c1e10da5 (patch) | |
| tree | 17ade3a3a13b1e7768abe3561b6808dc4980a5f6 | |
| parent | 18a44a7ff1075ce5157ac07cde573aca6b5e9973 (diff) | |
| parent | 6b59e366e074d3962e04f01efb8acc10a33c0e1e (diff) | |
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
| -rw-r--r-- | arch/x86/boot/compressed/eboot.c | 26 | ||||
| -rw-r--r-- | arch/x86/include/asm/efi.h | 9 | ||||
| -rw-r--r-- | arch/x86/kernel/setup.c | 3 | ||||
| -rw-r--r-- | arch/x86/platform/efi/efi.c | 5 | ||||
| -rw-r--r-- | drivers/firmware/efivars.c | 146 |
5 files changed, 166 insertions, 23 deletions
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index f8fa41190c35..c205035a6b96 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c | |||
| @@ -19,23 +19,28 @@ | |||
| 19 | 19 | ||
| 20 | static efi_system_table_t *sys_table; | 20 | static efi_system_table_t *sys_table; |
| 21 | 21 | ||
| 22 | static void efi_char16_printk(efi_char16_t *str) | ||
| 23 | { | ||
| 24 | struct efi_simple_text_output_protocol *out; | ||
| 25 | |||
| 26 | out = (struct efi_simple_text_output_protocol *)sys_table->con_out; | ||
| 27 | efi_call_phys2(out->output_string, out, str); | ||
| 28 | } | ||
| 29 | |||
| 22 | static void efi_printk(char *str) | 30 | static void efi_printk(char *str) |
| 23 | { | 31 | { |
| 24 | char *s8; | 32 | char *s8; |
| 25 | 33 | ||
| 26 | for (s8 = str; *s8; s8++) { | 34 | for (s8 = str; *s8; s8++) { |
| 27 | struct efi_simple_text_output_protocol *out; | ||
| 28 | efi_char16_t ch[2] = { 0 }; | 35 | efi_char16_t ch[2] = { 0 }; |
| 29 | 36 | ||
| 30 | ch[0] = *s8; | 37 | ch[0] = *s8; |
| 31 | out = (struct efi_simple_text_output_protocol *)sys_table->con_out; | ||
| 32 | |||
| 33 | if (*s8 == '\n') { | 38 | if (*s8 == '\n') { |
| 34 | efi_char16_t nl[2] = { '\r', 0 }; | 39 | efi_char16_t nl[2] = { '\r', 0 }; |
| 35 | efi_call_phys2(out->output_string, out, nl); | 40 | efi_char16_printk(nl); |
| 36 | } | 41 | } |
| 37 | 42 | ||
| 38 | efi_call_phys2(out->output_string, out, ch); | 43 | efi_char16_printk(ch); |
| 39 | } | 44 | } |
| 40 | } | 45 | } |
| 41 | 46 | ||
| @@ -709,7 +714,12 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image, | |||
| 709 | if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) | 714 | if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) |
| 710 | break; | 715 | break; |
| 711 | 716 | ||
| 712 | *p++ = *str++; | 717 | if (*str == '/') { |
| 718 | *p++ = '\\'; | ||
| 719 | *str++; | ||
| 720 | } else { | ||
| 721 | *p++ = *str++; | ||
| 722 | } | ||
| 713 | } | 723 | } |
| 714 | 724 | ||
| 715 | *p = '\0'; | 725 | *p = '\0'; |
| @@ -737,7 +747,9 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image, | |||
| 737 | status = efi_call_phys5(fh->open, fh, &h, filename_16, | 747 | status = efi_call_phys5(fh->open, fh, &h, filename_16, |
| 738 | EFI_FILE_MODE_READ, (u64)0); | 748 | EFI_FILE_MODE_READ, (u64)0); |
| 739 | if (status != EFI_SUCCESS) { | 749 | if (status != EFI_SUCCESS) { |
| 740 | efi_printk("Failed to open initrd file\n"); | 750 | efi_printk("Failed to open initrd file: "); |
| 751 | efi_char16_printk(filename_16); | ||
| 752 | efi_printk("\n"); | ||
| 741 | goto close_handles; | 753 | goto close_handles; |
| 742 | } | 754 | } |
| 743 | 755 | ||
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 28677c55113f..60c89f30c727 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h | |||
| @@ -102,7 +102,14 @@ extern void efi_call_phys_epilog(void); | |||
| 102 | extern void efi_unmap_memmap(void); | 102 | extern void efi_unmap_memmap(void); |
| 103 | extern void efi_memory_uc(u64 addr, unsigned long size); | 103 | extern void efi_memory_uc(u64 addr, unsigned long size); |
| 104 | 104 | ||
| 105 | #ifndef CONFIG_EFI | 105 | #ifdef CONFIG_EFI |
| 106 | |||
| 107 | static inline bool efi_is_native(void) | ||
| 108 | { | ||
| 109 | return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT); | ||
| 110 | } | ||
| 111 | |||
| 112 | #else | ||
| 106 | /* | 113 | /* |
| 107 | * IF EFI is not configured, have the EFI calls return -ENOSYS. | 114 | * IF EFI is not configured, have the EFI calls return -ENOSYS. |
| 108 | */ | 115 | */ |
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 9c857f05cef0..e89acdf6b77b 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c | |||
| @@ -1196,8 +1196,7 @@ void __init setup_arch(char **cmdline_p) | |||
| 1196 | * mismatched firmware/kernel archtectures since there is no | 1196 | * mismatched firmware/kernel archtectures since there is no |
| 1197 | * support for runtime services. | 1197 | * support for runtime services. |
| 1198 | */ | 1198 | */ |
| 1199 | if (efi_enabled(EFI_BOOT) && | 1199 | if (efi_enabled(EFI_BOOT) && !efi_is_native()) { |
| 1200 | IS_ENABLED(CONFIG_X86_64) != efi_enabled(EFI_64BIT)) { | ||
| 1201 | pr_info("efi: Setup done, disabling due to 32/64-bit mismatch\n"); | 1200 | pr_info("efi: Setup done, disabling due to 32/64-bit mismatch\n"); |
| 1202 | efi_unmap_memmap(); | 1201 | efi_unmap_memmap(); |
| 1203 | } | 1202 | } |
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 5ae99f9160d9..5f2ecaf3f9d8 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c | |||
| @@ -69,11 +69,6 @@ struct efi_memory_map memmap; | |||
| 69 | static struct efi efi_phys __initdata; | 69 | static struct efi efi_phys __initdata; |
| 70 | static efi_system_table_t efi_systab __initdata; | 70 | static efi_system_table_t efi_systab __initdata; |
| 71 | 71 | ||
| 72 | static inline bool efi_is_native(void) | ||
| 73 | { | ||
| 74 | return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT); | ||
| 75 | } | ||
| 76 | |||
| 77 | unsigned long x86_efi_facility; | 72 | unsigned long x86_efi_facility; |
| 78 | 73 | ||
| 79 | /* | 74 | /* |
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index fed08b661711..7320bf891706 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
| @@ -79,6 +79,7 @@ | |||
| 79 | #include <linux/device.h> | 79 | #include <linux/device.h> |
| 80 | #include <linux/slab.h> | 80 | #include <linux/slab.h> |
| 81 | #include <linux/pstore.h> | 81 | #include <linux/pstore.h> |
| 82 | #include <linux/ctype.h> | ||
| 82 | 83 | ||
| 83 | #include <linux/fs.h> | 84 | #include <linux/fs.h> |
| 84 | #include <linux/ramfs.h> | 85 | #include <linux/ramfs.h> |
| @@ -908,6 +909,48 @@ static struct inode *efivarfs_get_inode(struct super_block *sb, | |||
| 908 | return inode; | 909 | return inode; |
| 909 | } | 910 | } |
| 910 | 911 | ||
| 912 | /* | ||
| 913 | * Return true if 'str' is a valid efivarfs filename of the form, | ||
| 914 | * | ||
| 915 | * VariableName-12345678-1234-1234-1234-1234567891bc | ||
| 916 | */ | ||
| 917 | static bool efivarfs_valid_name(const char *str, int len) | ||
| 918 | { | ||
| 919 | static const char dashes[GUID_LEN] = { | ||
| 920 | [8] = 1, [13] = 1, [18] = 1, [23] = 1 | ||
| 921 | }; | ||
| 922 | const char *s = str + len - GUID_LEN; | ||
| 923 | int i; | ||
| 924 | |||
| 925 | /* | ||
| 926 | * We need a GUID, plus at least one letter for the variable name, | ||
| 927 | * plus the '-' separator | ||
| 928 | */ | ||
| 929 | if (len < GUID_LEN + 2) | ||
| 930 | return false; | ||
| 931 | |||
| 932 | /* GUID should be right after the first '-' */ | ||
| 933 | if (s - 1 != strchr(str, '-')) | ||
| 934 | return false; | ||
| 935 | |||
| 936 | /* | ||
| 937 | * Validate that 's' is of the correct format, e.g. | ||
| 938 | * | ||
| 939 | * 12345678-1234-1234-1234-123456789abc | ||
| 940 | */ | ||
| 941 | for (i = 0; i < GUID_LEN; i++) { | ||
| 942 | if (dashes[i]) { | ||
| 943 | if (*s++ != '-') | ||
| 944 | return false; | ||
| 945 | } else { | ||
| 946 | if (!isxdigit(*s++)) | ||
| 947 | return false; | ||
| 948 | } | ||
| 949 | } | ||
| 950 | |||
| 951 | return true; | ||
| 952 | } | ||
| 953 | |||
| 911 | static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid) | 954 | static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid) |
| 912 | { | 955 | { |
| 913 | guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]); | 956 | 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, | |||
| 936 | struct efivar_entry *var; | 979 | struct efivar_entry *var; |
| 937 | int namelen, i = 0, err = 0; | 980 | int namelen, i = 0, err = 0; |
| 938 | 981 | ||
| 939 | /* | 982 | if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len)) |
| 940 | * We need a GUID, plus at least one letter for the variable name, | ||
| 941 | * plus the '-' separator | ||
| 942 | */ | ||
| 943 | if (dentry->d_name.len < GUID_LEN + 2) | ||
| 944 | return -EINVAL; | 983 | return -EINVAL; |
| 945 | 984 | ||
| 946 | inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0); | 985 | inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0); |
| @@ -1012,6 +1051,84 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) | |||
| 1012 | return -EINVAL; | 1051 | return -EINVAL; |
| 1013 | }; | 1052 | }; |
| 1014 | 1053 | ||
| 1054 | /* | ||
| 1055 | * Compare two efivarfs file names. | ||
| 1056 | * | ||
| 1057 | * An efivarfs filename is composed of two parts, | ||
| 1058 | * | ||
| 1059 | * 1. A case-sensitive variable name | ||
| 1060 | * 2. A case-insensitive GUID | ||
| 1061 | * | ||
| 1062 | * So we need to perform a case-sensitive match on part 1 and a | ||
| 1063 | * case-insensitive match on part 2. | ||
| 1064 | */ | ||
| 1065 | static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode, | ||
| 1066 | const struct dentry *dentry, const struct inode *inode, | ||
| 1067 | unsigned int len, const char *str, | ||
| 1068 | const struct qstr *name) | ||
| 1069 | { | ||
| 1070 | int guid = len - GUID_LEN; | ||
| 1071 | |||
| 1072 | if (name->len != len) | ||
| 1073 | return 1; | ||
| 1074 | |||
| 1075 | /* Case-sensitive compare for the variable name */ | ||
| 1076 | if (memcmp(str, name->name, guid)) | ||
| 1077 | return 1; | ||
| 1078 | |||
| 1079 | /* Case-insensitive compare for the GUID */ | ||
| 1080 | return strncasecmp(name->name + guid, str + guid, GUID_LEN); | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | static int efivarfs_d_hash(const struct dentry *dentry, | ||
| 1084 | const struct inode *inode, struct qstr *qstr) | ||
| 1085 | { | ||
| 1086 | unsigned long hash = init_name_hash(); | ||
| 1087 | const unsigned char *s = qstr->name; | ||
| 1088 | unsigned int len = qstr->len; | ||
| 1089 | |||
| 1090 | if (!efivarfs_valid_name(s, len)) | ||
| 1091 | return -EINVAL; | ||
| 1092 | |||
| 1093 | while (len-- > GUID_LEN) | ||
| 1094 | hash = partial_name_hash(*s++, hash); | ||
| 1095 | |||
| 1096 | /* GUID is case-insensitive. */ | ||
| 1097 | while (len--) | ||
| 1098 | hash = partial_name_hash(tolower(*s++), hash); | ||
| 1099 | |||
| 1100 | qstr->hash = end_name_hash(hash); | ||
| 1101 | return 0; | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | /* | ||
| 1105 | * Retaining negative dentries for an in-memory filesystem just wastes | ||
| 1106 | * memory and lookup time: arrange for them to be deleted immediately. | ||
| 1107 | */ | ||
| 1108 | static int efivarfs_delete_dentry(const struct dentry *dentry) | ||
| 1109 | { | ||
| 1110 | return 1; | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | static struct dentry_operations efivarfs_d_ops = { | ||
| 1114 | .d_compare = efivarfs_d_compare, | ||
| 1115 | .d_hash = efivarfs_d_hash, | ||
| 1116 | .d_delete = efivarfs_delete_dentry, | ||
| 1117 | }; | ||
| 1118 | |||
| 1119 | static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) | ||
| 1120 | { | ||
| 1121 | struct qstr q; | ||
| 1122 | |||
| 1123 | q.name = name; | ||
| 1124 | q.len = strlen(name); | ||
| 1125 | |||
| 1126 | if (efivarfs_d_hash(NULL, NULL, &q)) | ||
| 1127 | return NULL; | ||
| 1128 | |||
| 1129 | return d_alloc(parent, &q); | ||
| 1130 | } | ||
| 1131 | |||
| 1015 | static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | 1132 | static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) |
| 1016 | { | 1133 | { |
| 1017 | struct inode *inode = NULL; | 1134 | struct inode *inode = NULL; |
| @@ -1027,6 +1144,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
| 1027 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | 1144 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; |
| 1028 | sb->s_magic = EFIVARFS_MAGIC; | 1145 | sb->s_magic = EFIVARFS_MAGIC; |
| 1029 | sb->s_op = &efivarfs_ops; | 1146 | sb->s_op = &efivarfs_ops; |
| 1147 | sb->s_d_op = &efivarfs_d_ops; | ||
| 1030 | sb->s_time_gran = 1; | 1148 | sb->s_time_gran = 1; |
| 1031 | 1149 | ||
| 1032 | inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0); | 1150 | 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) | |||
| 1067 | if (!inode) | 1185 | if (!inode) |
| 1068 | goto fail_name; | 1186 | goto fail_name; |
| 1069 | 1187 | ||
| 1070 | dentry = d_alloc_name(root, name); | 1188 | dentry = efivarfs_alloc_dentry(root, name); |
| 1071 | if (!dentry) | 1189 | if (!dentry) |
| 1072 | goto fail_inode; | 1190 | goto fail_inode; |
| 1073 | 1191 | ||
| @@ -1084,7 +1202,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
| 1084 | 1202 | ||
| 1085 | mutex_lock(&inode->i_mutex); | 1203 | mutex_lock(&inode->i_mutex); |
| 1086 | inode->i_private = entry; | 1204 | inode->i_private = entry; |
| 1087 | i_size_write(inode, size+4); | 1205 | i_size_write(inode, size + sizeof(entry->var.Attributes)); |
| 1088 | mutex_unlock(&inode->i_mutex); | 1206 | mutex_unlock(&inode->i_mutex); |
| 1089 | d_add(dentry, inode); | 1207 | d_add(dentry, inode); |
| 1090 | } | 1208 | } |
| @@ -1117,8 +1235,20 @@ static struct file_system_type efivarfs_type = { | |||
| 1117 | .kill_sb = efivarfs_kill_sb, | 1235 | .kill_sb = efivarfs_kill_sb, |
| 1118 | }; | 1236 | }; |
| 1119 | 1237 | ||
| 1238 | /* | ||
| 1239 | * Handle negative dentry. | ||
| 1240 | */ | ||
| 1241 | static struct dentry *efivarfs_lookup(struct inode *dir, struct dentry *dentry, | ||
| 1242 | unsigned int flags) | ||
| 1243 | { | ||
| 1244 | if (dentry->d_name.len > NAME_MAX) | ||
| 1245 | return ERR_PTR(-ENAMETOOLONG); | ||
| 1246 | d_add(dentry, NULL); | ||
| 1247 | return NULL; | ||
| 1248 | } | ||
| 1249 | |||
| 1120 | static const struct inode_operations efivarfs_dir_inode_operations = { | 1250 | static const struct inode_operations efivarfs_dir_inode_operations = { |
| 1121 | .lookup = simple_lookup, | 1251 | .lookup = efivarfs_lookup, |
| 1122 | .unlink = efivarfs_unlink, | 1252 | .unlink = efivarfs_unlink, |
| 1123 | .create = efivarfs_create, | 1253 | .create = efivarfs_create, |
| 1124 | }; | 1254 | }; |
