diff options
-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 | }; |