Commit 54b3a4d311c98ad94b737802a8b5f2c8c6bfd627
Committed by
Linus Torvalds
1 parent
fec6c20b57
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
efivars: Improve variable validation
Ben Hutchings pointed out that the validation in efivars was inadequate - most obviously, an entry with size 0 would server as a DoS against the kernel. Improve this based on his suggestions. Signed-off-by: Matthew Garrett <mjg@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 1 changed file with 30 additions and 16 deletions Inline Diff
drivers/firmware/efivars.c
1 | /* | 1 | /* |
2 | * EFI Variables - efivars.c | 2 | * EFI Variables - efivars.c |
3 | * | 3 | * |
4 | * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com> | 4 | * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com> |
5 | * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com> | 5 | * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com> |
6 | * | 6 | * |
7 | * This code takes all variables accessible from EFI runtime and | 7 | * This code takes all variables accessible from EFI runtime and |
8 | * exports them via sysfs | 8 | * exports them via sysfs |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License as published by | 11 | * it under the terms of the GNU General Public License as published by |
12 | * the Free Software Foundation; either version 2 of the License, or | 12 | * the Free Software Foundation; either version 2 of the License, or |
13 | * (at your option) any later version. | 13 | * (at your option) any later version. |
14 | * | 14 | * |
15 | * This program is distributed in the hope that it will be useful, | 15 | * This program is distributed in the hope that it will be useful, |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | * GNU General Public License for more details. | 18 | * GNU General Public License for more details. |
19 | * | 19 | * |
20 | * You should have received a copy of the GNU General Public License | 20 | * You should have received a copy of the GNU General Public License |
21 | * along with this program; if not, write to the Free Software | 21 | * along with this program; if not, write to the Free Software |
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 | * | 23 | * |
24 | * Changelog: | 24 | * Changelog: |
25 | * | 25 | * |
26 | * 17 May 2004 - Matt Domsch <Matt_Domsch@dell.com> | 26 | * 17 May 2004 - Matt Domsch <Matt_Domsch@dell.com> |
27 | * remove check for efi_enabled in exit | 27 | * remove check for efi_enabled in exit |
28 | * add MODULE_VERSION | 28 | * add MODULE_VERSION |
29 | * | 29 | * |
30 | * 26 Apr 2004 - Matt Domsch <Matt_Domsch@dell.com> | 30 | * 26 Apr 2004 - Matt Domsch <Matt_Domsch@dell.com> |
31 | * minor bug fixes | 31 | * minor bug fixes |
32 | * | 32 | * |
33 | * 21 Apr 2004 - Matt Tolentino <matthew.e.tolentino@intel.com) | 33 | * 21 Apr 2004 - Matt Tolentino <matthew.e.tolentino@intel.com) |
34 | * converted driver to export variable information via sysfs | 34 | * converted driver to export variable information via sysfs |
35 | * and moved to drivers/firmware directory | 35 | * and moved to drivers/firmware directory |
36 | * bumped revision number to v0.07 to reflect conversion & move | 36 | * bumped revision number to v0.07 to reflect conversion & move |
37 | * | 37 | * |
38 | * 10 Dec 2002 - Matt Domsch <Matt_Domsch@dell.com> | 38 | * 10 Dec 2002 - Matt Domsch <Matt_Domsch@dell.com> |
39 | * fix locking per Peter Chubb's findings | 39 | * fix locking per Peter Chubb's findings |
40 | * | 40 | * |
41 | * 25 Mar 2002 - Matt Domsch <Matt_Domsch@dell.com> | 41 | * 25 Mar 2002 - Matt Domsch <Matt_Domsch@dell.com> |
42 | * move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse() | 42 | * move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse() |
43 | * | 43 | * |
44 | * 12 Feb 2002 - Matt Domsch <Matt_Domsch@dell.com> | 44 | * 12 Feb 2002 - Matt Domsch <Matt_Domsch@dell.com> |
45 | * use list_for_each_safe when deleting vars. | 45 | * use list_for_each_safe when deleting vars. |
46 | * remove ifdef CONFIG_SMP around include <linux/smp.h> | 46 | * remove ifdef CONFIG_SMP around include <linux/smp.h> |
47 | * v0.04 release to linux-ia64@linuxia64.org | 47 | * v0.04 release to linux-ia64@linuxia64.org |
48 | * | 48 | * |
49 | * 20 April 2001 - Matt Domsch <Matt_Domsch@dell.com> | 49 | * 20 April 2001 - Matt Domsch <Matt_Domsch@dell.com> |
50 | * Moved vars from /proc/efi to /proc/efi/vars, and made | 50 | * Moved vars from /proc/efi to /proc/efi/vars, and made |
51 | * efi.c own the /proc/efi directory. | 51 | * efi.c own the /proc/efi directory. |
52 | * v0.03 release to linux-ia64@linuxia64.org | 52 | * v0.03 release to linux-ia64@linuxia64.org |
53 | * | 53 | * |
54 | * 26 March 2001 - Matt Domsch <Matt_Domsch@dell.com> | 54 | * 26 March 2001 - Matt Domsch <Matt_Domsch@dell.com> |
55 | * At the request of Stephane, moved ownership of /proc/efi | 55 | * At the request of Stephane, moved ownership of /proc/efi |
56 | * to efi.c, and now efivars lives under /proc/efi/vars. | 56 | * to efi.c, and now efivars lives under /proc/efi/vars. |
57 | * | 57 | * |
58 | * 12 March 2001 - Matt Domsch <Matt_Domsch@dell.com> | 58 | * 12 March 2001 - Matt Domsch <Matt_Domsch@dell.com> |
59 | * Feedback received from Stephane Eranian incorporated. | 59 | * Feedback received from Stephane Eranian incorporated. |
60 | * efivar_write() checks copy_from_user() return value. | 60 | * efivar_write() checks copy_from_user() return value. |
61 | * efivar_read/write() returns proper errno. | 61 | * efivar_read/write() returns proper errno. |
62 | * v0.02 release to linux-ia64@linuxia64.org | 62 | * v0.02 release to linux-ia64@linuxia64.org |
63 | * | 63 | * |
64 | * 26 February 2001 - Matt Domsch <Matt_Domsch@dell.com> | 64 | * 26 February 2001 - Matt Domsch <Matt_Domsch@dell.com> |
65 | * v0.01 release to linux-ia64@linuxia64.org | 65 | * v0.01 release to linux-ia64@linuxia64.org |
66 | */ | 66 | */ |
67 | 67 | ||
68 | #include <linux/capability.h> | 68 | #include <linux/capability.h> |
69 | #include <linux/types.h> | 69 | #include <linux/types.h> |
70 | #include <linux/errno.h> | 70 | #include <linux/errno.h> |
71 | #include <linux/init.h> | 71 | #include <linux/init.h> |
72 | #include <linux/mm.h> | 72 | #include <linux/mm.h> |
73 | #include <linux/module.h> | 73 | #include <linux/module.h> |
74 | #include <linux/string.h> | 74 | #include <linux/string.h> |
75 | #include <linux/smp.h> | 75 | #include <linux/smp.h> |
76 | #include <linux/efi.h> | 76 | #include <linux/efi.h> |
77 | #include <linux/sysfs.h> | 77 | #include <linux/sysfs.h> |
78 | #include <linux/kobject.h> | 78 | #include <linux/kobject.h> |
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 | 82 | ||
83 | #include <asm/uaccess.h> | 83 | #include <asm/uaccess.h> |
84 | 84 | ||
85 | #define EFIVARS_VERSION "0.08" | 85 | #define EFIVARS_VERSION "0.08" |
86 | #define EFIVARS_DATE "2004-May-17" | 86 | #define EFIVARS_DATE "2004-May-17" |
87 | 87 | ||
88 | MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>"); | 88 | MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>"); |
89 | MODULE_DESCRIPTION("sysfs interface to EFI Variables"); | 89 | MODULE_DESCRIPTION("sysfs interface to EFI Variables"); |
90 | MODULE_LICENSE("GPL"); | 90 | MODULE_LICENSE("GPL"); |
91 | MODULE_VERSION(EFIVARS_VERSION); | 91 | MODULE_VERSION(EFIVARS_VERSION); |
92 | 92 | ||
93 | #define DUMP_NAME_LEN 52 | 93 | #define DUMP_NAME_LEN 52 |
94 | 94 | ||
95 | /* | 95 | /* |
96 | * The maximum size of VariableName + Data = 1024 | 96 | * The maximum size of VariableName + Data = 1024 |
97 | * Therefore, it's reasonable to save that much | 97 | * Therefore, it's reasonable to save that much |
98 | * space in each part of the structure, | 98 | * space in each part of the structure, |
99 | * and we use a page for reading/writing. | 99 | * and we use a page for reading/writing. |
100 | */ | 100 | */ |
101 | 101 | ||
102 | struct efi_variable { | 102 | struct efi_variable { |
103 | efi_char16_t VariableName[1024/sizeof(efi_char16_t)]; | 103 | efi_char16_t VariableName[1024/sizeof(efi_char16_t)]; |
104 | efi_guid_t VendorGuid; | 104 | efi_guid_t VendorGuid; |
105 | unsigned long DataSize; | 105 | unsigned long DataSize; |
106 | __u8 Data[1024]; | 106 | __u8 Data[1024]; |
107 | efi_status_t Status; | 107 | efi_status_t Status; |
108 | __u32 Attributes; | 108 | __u32 Attributes; |
109 | } __attribute__((packed)); | 109 | } __attribute__((packed)); |
110 | 110 | ||
111 | 111 | ||
112 | struct efivar_entry { | 112 | struct efivar_entry { |
113 | struct efivars *efivars; | 113 | struct efivars *efivars; |
114 | struct efi_variable var; | 114 | struct efi_variable var; |
115 | struct list_head list; | 115 | struct list_head list; |
116 | struct kobject kobj; | 116 | struct kobject kobj; |
117 | }; | 117 | }; |
118 | 118 | ||
119 | struct efivar_attribute { | 119 | struct efivar_attribute { |
120 | struct attribute attr; | 120 | struct attribute attr; |
121 | ssize_t (*show) (struct efivar_entry *entry, char *buf); | 121 | ssize_t (*show) (struct efivar_entry *entry, char *buf); |
122 | ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count); | 122 | ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count); |
123 | }; | 123 | }; |
124 | 124 | ||
125 | #define PSTORE_EFI_ATTRIBUTES \ | 125 | #define PSTORE_EFI_ATTRIBUTES \ |
126 | (EFI_VARIABLE_NON_VOLATILE | \ | 126 | (EFI_VARIABLE_NON_VOLATILE | \ |
127 | EFI_VARIABLE_BOOTSERVICE_ACCESS | \ | 127 | EFI_VARIABLE_BOOTSERVICE_ACCESS | \ |
128 | EFI_VARIABLE_RUNTIME_ACCESS) | 128 | EFI_VARIABLE_RUNTIME_ACCESS) |
129 | 129 | ||
130 | #define EFIVAR_ATTR(_name, _mode, _show, _store) \ | 130 | #define EFIVAR_ATTR(_name, _mode, _show, _store) \ |
131 | struct efivar_attribute efivar_attr_##_name = { \ | 131 | struct efivar_attribute efivar_attr_##_name = { \ |
132 | .attr = {.name = __stringify(_name), .mode = _mode}, \ | 132 | .attr = {.name = __stringify(_name), .mode = _mode}, \ |
133 | .show = _show, \ | 133 | .show = _show, \ |
134 | .store = _store, \ | 134 | .store = _store, \ |
135 | }; | 135 | }; |
136 | 136 | ||
137 | #define to_efivar_attr(_attr) container_of(_attr, struct efivar_attribute, attr) | 137 | #define to_efivar_attr(_attr) container_of(_attr, struct efivar_attribute, attr) |
138 | #define to_efivar_entry(obj) container_of(obj, struct efivar_entry, kobj) | 138 | #define to_efivar_entry(obj) container_of(obj, struct efivar_entry, kobj) |
139 | 139 | ||
140 | /* | 140 | /* |
141 | * Prototype for sysfs creation function | 141 | * Prototype for sysfs creation function |
142 | */ | 142 | */ |
143 | static int | 143 | static int |
144 | efivar_create_sysfs_entry(struct efivars *efivars, | 144 | efivar_create_sysfs_entry(struct efivars *efivars, |
145 | unsigned long variable_name_size, | 145 | unsigned long variable_name_size, |
146 | efi_char16_t *variable_name, | 146 | efi_char16_t *variable_name, |
147 | efi_guid_t *vendor_guid); | 147 | efi_guid_t *vendor_guid); |
148 | 148 | ||
149 | /* Return the number of unicode characters in data */ | 149 | /* Return the number of unicode characters in data */ |
150 | static unsigned long | 150 | static unsigned long |
151 | utf16_strnlen(efi_char16_t *s, size_t maxlength) | 151 | utf16_strnlen(efi_char16_t *s, size_t maxlength) |
152 | { | 152 | { |
153 | unsigned long length = 0; | 153 | unsigned long length = 0; |
154 | 154 | ||
155 | while (*s++ != 0 && length < maxlength) | 155 | while (*s++ != 0 && length < maxlength) |
156 | length++; | 156 | length++; |
157 | return length; | 157 | return length; |
158 | } | 158 | } |
159 | 159 | ||
160 | static inline unsigned long | 160 | static inline unsigned long |
161 | utf16_strlen(efi_char16_t *s) | 161 | utf16_strlen(efi_char16_t *s) |
162 | { | 162 | { |
163 | return utf16_strnlen(s, ~0UL); | 163 | return utf16_strnlen(s, ~0UL); |
164 | } | 164 | } |
165 | 165 | ||
166 | /* | 166 | /* |
167 | * Return the number of bytes is the length of this string | 167 | * Return the number of bytes is the length of this string |
168 | * Note: this is NOT the same as the number of unicode characters | 168 | * Note: this is NOT the same as the number of unicode characters |
169 | */ | 169 | */ |
170 | static inline unsigned long | 170 | static inline unsigned long |
171 | utf16_strsize(efi_char16_t *data, unsigned long maxlength) | 171 | utf16_strsize(efi_char16_t *data, unsigned long maxlength) |
172 | { | 172 | { |
173 | return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t); | 173 | return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t); |
174 | } | 174 | } |
175 | 175 | ||
176 | static inline int | 176 | static inline int |
177 | utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len) | 177 | utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len) |
178 | { | 178 | { |
179 | while (1) { | 179 | while (1) { |
180 | if (len == 0) | 180 | if (len == 0) |
181 | return 0; | 181 | return 0; |
182 | if (*a < *b) | 182 | if (*a < *b) |
183 | return -1; | 183 | return -1; |
184 | if (*a > *b) | 184 | if (*a > *b) |
185 | return 1; | 185 | return 1; |
186 | if (*a == 0) /* implies *b == 0 */ | 186 | if (*a == 0) /* implies *b == 0 */ |
187 | return 0; | 187 | return 0; |
188 | a++; | 188 | a++; |
189 | b++; | 189 | b++; |
190 | len--; | 190 | len--; |
191 | } | 191 | } |
192 | } | 192 | } |
193 | 193 | ||
194 | static bool | 194 | static bool |
195 | validate_device_path(struct efi_variable *var, int match, u8 *buffer, int len) | 195 | validate_device_path(struct efi_variable *var, int match, u8 *buffer, |
196 | unsigned long len) | ||
196 | { | 197 | { |
197 | struct efi_generic_dev_path *node; | 198 | struct efi_generic_dev_path *node; |
198 | int offset = 0; | 199 | int offset = 0; |
199 | 200 | ||
200 | node = (struct efi_generic_dev_path *)buffer; | 201 | node = (struct efi_generic_dev_path *)buffer; |
201 | 202 | ||
202 | while (offset < len) { | 203 | if (len < sizeof(*node)) |
204 | return false; | ||
205 | |||
206 | while (offset <= len - sizeof(*node) && | ||
207 | node->length >= sizeof(*node) && | ||
208 | node->length <= len - offset) { | ||
203 | offset += node->length; | 209 | offset += node->length; |
204 | 210 | ||
205 | if (offset > len) | ||
206 | return false; | ||
207 | |||
208 | if ((node->type == EFI_DEV_END_PATH || | 211 | if ((node->type == EFI_DEV_END_PATH || |
209 | node->type == EFI_DEV_END_PATH2) && | 212 | node->type == EFI_DEV_END_PATH2) && |
210 | node->sub_type == EFI_DEV_END_ENTIRE) | 213 | node->sub_type == EFI_DEV_END_ENTIRE) |
211 | return true; | 214 | return true; |
212 | 215 | ||
213 | node = (struct efi_generic_dev_path *)(buffer + offset); | 216 | node = (struct efi_generic_dev_path *)(buffer + offset); |
214 | } | 217 | } |
215 | 218 | ||
216 | /* | 219 | /* |
217 | * If we're here then either node->length pointed past the end | 220 | * If we're here then either node->length pointed past the end |
218 | * of the buffer or we reached the end of the buffer without | 221 | * of the buffer or we reached the end of the buffer without |
219 | * finding a device path end node. | 222 | * finding a device path end node. |
220 | */ | 223 | */ |
221 | return false; | 224 | return false; |
222 | } | 225 | } |
223 | 226 | ||
224 | static bool | 227 | static bool |
225 | validate_boot_order(struct efi_variable *var, int match, u8 *buffer, int len) | 228 | validate_boot_order(struct efi_variable *var, int match, u8 *buffer, |
229 | unsigned long len) | ||
226 | { | 230 | { |
227 | /* An array of 16-bit integers */ | 231 | /* An array of 16-bit integers */ |
228 | if ((len % 2) != 0) | 232 | if ((len % 2) != 0) |
229 | return false; | 233 | return false; |
230 | 234 | ||
231 | return true; | 235 | return true; |
232 | } | 236 | } |
233 | 237 | ||
234 | static bool | 238 | static bool |
235 | validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len) | 239 | validate_load_option(struct efi_variable *var, int match, u8 *buffer, |
240 | unsigned long len) | ||
236 | { | 241 | { |
237 | u16 filepathlength; | 242 | u16 filepathlength; |
238 | int i, desclength = 0; | 243 | int i, desclength = 0, namelen; |
239 | 244 | ||
245 | namelen = utf16_strnlen(var->VariableName, sizeof(var->VariableName)); | ||
246 | |||
240 | /* Either "Boot" or "Driver" followed by four digits of hex */ | 247 | /* Either "Boot" or "Driver" followed by four digits of hex */ |
241 | for (i = match; i < match+4; i++) { | 248 | for (i = match; i < match+4; i++) { |
242 | if (hex_to_bin(var->VariableName[i] & 0xff) < 0) | 249 | if (var->VariableName[i] > 127 || |
250 | hex_to_bin(var->VariableName[i] & 0xff) < 0) | ||
243 | return true; | 251 | return true; |
244 | } | 252 | } |
245 | 253 | ||
246 | /* A valid entry must be at least 6 bytes */ | 254 | /* Reject it if there's 4 digits of hex and then further content */ |
247 | if (len < 6) | 255 | if (namelen > match + 4) |
248 | return false; | 256 | return false; |
249 | 257 | ||
258 | /* A valid entry must be at least 8 bytes */ | ||
259 | if (len < 8) | ||
260 | return false; | ||
261 | |||
250 | filepathlength = buffer[4] | buffer[5] << 8; | 262 | filepathlength = buffer[4] | buffer[5] << 8; |
251 | 263 | ||
252 | /* | 264 | /* |
253 | * There's no stored length for the description, so it has to be | 265 | * There's no stored length for the description, so it has to be |
254 | * found by hand | 266 | * found by hand |
255 | */ | 267 | */ |
256 | desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len) + 2; | 268 | desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2; |
257 | 269 | ||
258 | /* Each boot entry must have a descriptor */ | 270 | /* Each boot entry must have a descriptor */ |
259 | if (!desclength) | 271 | if (!desclength) |
260 | return false; | 272 | return false; |
261 | 273 | ||
262 | /* | 274 | /* |
263 | * If the sum of the length of the description, the claimed filepath | 275 | * If the sum of the length of the description, the claimed filepath |
264 | * length and the original header are greater than the length of the | 276 | * length and the original header are greater than the length of the |
265 | * variable, it's malformed | 277 | * variable, it's malformed |
266 | */ | 278 | */ |
267 | if ((desclength + filepathlength + 6) > len) | 279 | if ((desclength + filepathlength + 6) > len) |
268 | return false; | 280 | return false; |
269 | 281 | ||
270 | /* | 282 | /* |
271 | * And, finally, check the filepath | 283 | * And, finally, check the filepath |
272 | */ | 284 | */ |
273 | return validate_device_path(var, match, buffer + desclength + 6, | 285 | return validate_device_path(var, match, buffer + desclength + 6, |
274 | filepathlength); | 286 | filepathlength); |
275 | } | 287 | } |
276 | 288 | ||
277 | static bool | 289 | static bool |
278 | validate_uint16(struct efi_variable *var, int match, u8 *buffer, int len) | 290 | validate_uint16(struct efi_variable *var, int match, u8 *buffer, |
291 | unsigned long len) | ||
279 | { | 292 | { |
280 | /* A single 16-bit integer */ | 293 | /* A single 16-bit integer */ |
281 | if (len != 2) | 294 | if (len != 2) |
282 | return false; | 295 | return false; |
283 | 296 | ||
284 | return true; | 297 | return true; |
285 | } | 298 | } |
286 | 299 | ||
287 | static bool | 300 | static bool |
288 | validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, int len) | 301 | validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, |
302 | unsigned long len) | ||
289 | { | 303 | { |
290 | int i; | 304 | int i; |
291 | 305 | ||
292 | for (i = 0; i < len; i++) { | 306 | for (i = 0; i < len; i++) { |
293 | if (buffer[i] > 127) | 307 | if (buffer[i] > 127) |
294 | return false; | 308 | return false; |
295 | 309 | ||
296 | if (buffer[i] == 0) | 310 | if (buffer[i] == 0) |
297 | return true; | 311 | return true; |
298 | } | 312 | } |
299 | 313 | ||
300 | return false; | 314 | return false; |
301 | } | 315 | } |
302 | 316 | ||
303 | struct variable_validate { | 317 | struct variable_validate { |
304 | char *name; | 318 | char *name; |
305 | bool (*validate)(struct efi_variable *var, int match, u8 *data, | 319 | bool (*validate)(struct efi_variable *var, int match, u8 *data, |
306 | int len); | 320 | unsigned long len); |
307 | }; | 321 | }; |
308 | 322 | ||
309 | static const struct variable_validate variable_validate[] = { | 323 | static const struct variable_validate variable_validate[] = { |
310 | { "BootNext", validate_uint16 }, | 324 | { "BootNext", validate_uint16 }, |
311 | { "BootOrder", validate_boot_order }, | 325 | { "BootOrder", validate_boot_order }, |
312 | { "DriverOrder", validate_boot_order }, | 326 | { "DriverOrder", validate_boot_order }, |
313 | { "Boot*", validate_load_option }, | 327 | { "Boot*", validate_load_option }, |
314 | { "Driver*", validate_load_option }, | 328 | { "Driver*", validate_load_option }, |
315 | { "ConIn", validate_device_path }, | 329 | { "ConIn", validate_device_path }, |
316 | { "ConInDev", validate_device_path }, | 330 | { "ConInDev", validate_device_path }, |
317 | { "ConOut", validate_device_path }, | 331 | { "ConOut", validate_device_path }, |
318 | { "ConOutDev", validate_device_path }, | 332 | { "ConOutDev", validate_device_path }, |
319 | { "ErrOut", validate_device_path }, | 333 | { "ErrOut", validate_device_path }, |
320 | { "ErrOutDev", validate_device_path }, | 334 | { "ErrOutDev", validate_device_path }, |
321 | { "Timeout", validate_uint16 }, | 335 | { "Timeout", validate_uint16 }, |
322 | { "Lang", validate_ascii_string }, | 336 | { "Lang", validate_ascii_string }, |
323 | { "PlatformLang", validate_ascii_string }, | 337 | { "PlatformLang", validate_ascii_string }, |
324 | { "", NULL }, | 338 | { "", NULL }, |
325 | }; | 339 | }; |
326 | 340 | ||
327 | static bool | 341 | static bool |
328 | validate_var(struct efi_variable *var, u8 *data, int len) | 342 | validate_var(struct efi_variable *var, u8 *data, unsigned long len) |
329 | { | 343 | { |
330 | int i; | 344 | int i; |
331 | u16 *unicode_name = var->VariableName; | 345 | u16 *unicode_name = var->VariableName; |
332 | 346 | ||
333 | for (i = 0; variable_validate[i].validate != NULL; i++) { | 347 | for (i = 0; variable_validate[i].validate != NULL; i++) { |
334 | const char *name = variable_validate[i].name; | 348 | const char *name = variable_validate[i].name; |
335 | int match; | 349 | int match; |
336 | 350 | ||
337 | for (match = 0; ; match++) { | 351 | for (match = 0; ; match++) { |
338 | char c = name[match]; | 352 | char c = name[match]; |
339 | u16 u = unicode_name[match]; | 353 | u16 u = unicode_name[match]; |
340 | 354 | ||
341 | /* All special variables are plain ascii */ | 355 | /* All special variables are plain ascii */ |
342 | if (u > 127) | 356 | if (u > 127) |
343 | return true; | 357 | return true; |
344 | 358 | ||
345 | /* Wildcard in the matching name means we've matched */ | 359 | /* Wildcard in the matching name means we've matched */ |
346 | if (c == '*') | 360 | if (c == '*') |
347 | return variable_validate[i].validate(var, | 361 | return variable_validate[i].validate(var, |
348 | match, data, len); | 362 | match, data, len); |
349 | 363 | ||
350 | /* Case sensitive match */ | 364 | /* Case sensitive match */ |
351 | if (c != u) | 365 | if (c != u) |
352 | break; | 366 | break; |
353 | 367 | ||
354 | /* Reached the end of the string while matching */ | 368 | /* Reached the end of the string while matching */ |
355 | if (!c) | 369 | if (!c) |
356 | return variable_validate[i].validate(var, | 370 | return variable_validate[i].validate(var, |
357 | match, data, len); | 371 | match, data, len); |
358 | } | 372 | } |
359 | } | 373 | } |
360 | 374 | ||
361 | return true; | 375 | return true; |
362 | } | 376 | } |
363 | 377 | ||
364 | static efi_status_t | 378 | static efi_status_t |
365 | get_var_data_locked(struct efivars *efivars, struct efi_variable *var) | 379 | get_var_data_locked(struct efivars *efivars, struct efi_variable *var) |
366 | { | 380 | { |
367 | efi_status_t status; | 381 | efi_status_t status; |
368 | 382 | ||
369 | var->DataSize = 1024; | 383 | var->DataSize = 1024; |
370 | status = efivars->ops->get_variable(var->VariableName, | 384 | status = efivars->ops->get_variable(var->VariableName, |
371 | &var->VendorGuid, | 385 | &var->VendorGuid, |
372 | &var->Attributes, | 386 | &var->Attributes, |
373 | &var->DataSize, | 387 | &var->DataSize, |
374 | var->Data); | 388 | var->Data); |
375 | return status; | 389 | return status; |
376 | } | 390 | } |
377 | 391 | ||
378 | static efi_status_t | 392 | static efi_status_t |
379 | get_var_data(struct efivars *efivars, struct efi_variable *var) | 393 | get_var_data(struct efivars *efivars, struct efi_variable *var) |
380 | { | 394 | { |
381 | efi_status_t status; | 395 | efi_status_t status; |
382 | 396 | ||
383 | spin_lock(&efivars->lock); | 397 | spin_lock(&efivars->lock); |
384 | status = get_var_data_locked(efivars, var); | 398 | status = get_var_data_locked(efivars, var); |
385 | spin_unlock(&efivars->lock); | 399 | spin_unlock(&efivars->lock); |
386 | 400 | ||
387 | if (status != EFI_SUCCESS) { | 401 | if (status != EFI_SUCCESS) { |
388 | printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n", | 402 | printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n", |
389 | status); | 403 | status); |
390 | } | 404 | } |
391 | return status; | 405 | return status; |
392 | } | 406 | } |
393 | 407 | ||
394 | static ssize_t | 408 | static ssize_t |
395 | efivar_guid_read(struct efivar_entry *entry, char *buf) | 409 | efivar_guid_read(struct efivar_entry *entry, char *buf) |
396 | { | 410 | { |
397 | struct efi_variable *var = &entry->var; | 411 | struct efi_variable *var = &entry->var; |
398 | char *str = buf; | 412 | char *str = buf; |
399 | 413 | ||
400 | if (!entry || !buf) | 414 | if (!entry || !buf) |
401 | return 0; | 415 | return 0; |
402 | 416 | ||
403 | efi_guid_unparse(&var->VendorGuid, str); | 417 | efi_guid_unparse(&var->VendorGuid, str); |
404 | str += strlen(str); | 418 | str += strlen(str); |
405 | str += sprintf(str, "\n"); | 419 | str += sprintf(str, "\n"); |
406 | 420 | ||
407 | return str - buf; | 421 | return str - buf; |
408 | } | 422 | } |
409 | 423 | ||
410 | static ssize_t | 424 | static ssize_t |
411 | efivar_attr_read(struct efivar_entry *entry, char *buf) | 425 | efivar_attr_read(struct efivar_entry *entry, char *buf) |
412 | { | 426 | { |
413 | struct efi_variable *var = &entry->var; | 427 | struct efi_variable *var = &entry->var; |
414 | char *str = buf; | 428 | char *str = buf; |
415 | efi_status_t status; | 429 | efi_status_t status; |
416 | 430 | ||
417 | if (!entry || !buf) | 431 | if (!entry || !buf) |
418 | return -EINVAL; | 432 | return -EINVAL; |
419 | 433 | ||
420 | status = get_var_data(entry->efivars, var); | 434 | status = get_var_data(entry->efivars, var); |
421 | if (status != EFI_SUCCESS) | 435 | if (status != EFI_SUCCESS) |
422 | return -EIO; | 436 | return -EIO; |
423 | 437 | ||
424 | if (var->Attributes & 0x1) | 438 | if (var->Attributes & 0x1) |
425 | str += sprintf(str, "EFI_VARIABLE_NON_VOLATILE\n"); | 439 | str += sprintf(str, "EFI_VARIABLE_NON_VOLATILE\n"); |
426 | if (var->Attributes & 0x2) | 440 | if (var->Attributes & 0x2) |
427 | str += sprintf(str, "EFI_VARIABLE_BOOTSERVICE_ACCESS\n"); | 441 | str += sprintf(str, "EFI_VARIABLE_BOOTSERVICE_ACCESS\n"); |
428 | if (var->Attributes & 0x4) | 442 | if (var->Attributes & 0x4) |
429 | str += sprintf(str, "EFI_VARIABLE_RUNTIME_ACCESS\n"); | 443 | str += sprintf(str, "EFI_VARIABLE_RUNTIME_ACCESS\n"); |
430 | return str - buf; | 444 | return str - buf; |
431 | } | 445 | } |
432 | 446 | ||
433 | static ssize_t | 447 | static ssize_t |
434 | efivar_size_read(struct efivar_entry *entry, char *buf) | 448 | efivar_size_read(struct efivar_entry *entry, char *buf) |
435 | { | 449 | { |
436 | struct efi_variable *var = &entry->var; | 450 | struct efi_variable *var = &entry->var; |
437 | char *str = buf; | 451 | char *str = buf; |
438 | efi_status_t status; | 452 | efi_status_t status; |
439 | 453 | ||
440 | if (!entry || !buf) | 454 | if (!entry || !buf) |
441 | return -EINVAL; | 455 | return -EINVAL; |
442 | 456 | ||
443 | status = get_var_data(entry->efivars, var); | 457 | status = get_var_data(entry->efivars, var); |
444 | if (status != EFI_SUCCESS) | 458 | if (status != EFI_SUCCESS) |
445 | return -EIO; | 459 | return -EIO; |
446 | 460 | ||
447 | str += sprintf(str, "0x%lx\n", var->DataSize); | 461 | str += sprintf(str, "0x%lx\n", var->DataSize); |
448 | return str - buf; | 462 | return str - buf; |
449 | } | 463 | } |
450 | 464 | ||
451 | static ssize_t | 465 | static ssize_t |
452 | efivar_data_read(struct efivar_entry *entry, char *buf) | 466 | efivar_data_read(struct efivar_entry *entry, char *buf) |
453 | { | 467 | { |
454 | struct efi_variable *var = &entry->var; | 468 | struct efi_variable *var = &entry->var; |
455 | efi_status_t status; | 469 | efi_status_t status; |
456 | 470 | ||
457 | if (!entry || !buf) | 471 | if (!entry || !buf) |
458 | return -EINVAL; | 472 | return -EINVAL; |
459 | 473 | ||
460 | status = get_var_data(entry->efivars, var); | 474 | status = get_var_data(entry->efivars, var); |
461 | if (status != EFI_SUCCESS) | 475 | if (status != EFI_SUCCESS) |
462 | return -EIO; | 476 | return -EIO; |
463 | 477 | ||
464 | memcpy(buf, var->Data, var->DataSize); | 478 | memcpy(buf, var->Data, var->DataSize); |
465 | return var->DataSize; | 479 | return var->DataSize; |
466 | } | 480 | } |
467 | /* | 481 | /* |
468 | * We allow each variable to be edited via rewriting the | 482 | * We allow each variable to be edited via rewriting the |
469 | * entire efi variable structure. | 483 | * entire efi variable structure. |
470 | */ | 484 | */ |
471 | static ssize_t | 485 | static ssize_t |
472 | efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) | 486 | efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) |
473 | { | 487 | { |
474 | struct efi_variable *new_var, *var = &entry->var; | 488 | struct efi_variable *new_var, *var = &entry->var; |
475 | struct efivars *efivars = entry->efivars; | 489 | struct efivars *efivars = entry->efivars; |
476 | efi_status_t status = EFI_NOT_FOUND; | 490 | efi_status_t status = EFI_NOT_FOUND; |
477 | 491 | ||
478 | if (count != sizeof(struct efi_variable)) | 492 | if (count != sizeof(struct efi_variable)) |
479 | return -EINVAL; | 493 | return -EINVAL; |
480 | 494 | ||
481 | new_var = (struct efi_variable *)buf; | 495 | new_var = (struct efi_variable *)buf; |
482 | /* | 496 | /* |
483 | * If only updating the variable data, then the name | 497 | * If only updating the variable data, then the name |
484 | * and guid should remain the same | 498 | * and guid should remain the same |
485 | */ | 499 | */ |
486 | if (memcmp(new_var->VariableName, var->VariableName, sizeof(var->VariableName)) || | 500 | if (memcmp(new_var->VariableName, var->VariableName, sizeof(var->VariableName)) || |
487 | efi_guidcmp(new_var->VendorGuid, var->VendorGuid)) { | 501 | efi_guidcmp(new_var->VendorGuid, var->VendorGuid)) { |
488 | printk(KERN_ERR "efivars: Cannot edit the wrong variable!\n"); | 502 | printk(KERN_ERR "efivars: Cannot edit the wrong variable!\n"); |
489 | return -EINVAL; | 503 | return -EINVAL; |
490 | } | 504 | } |
491 | 505 | ||
492 | if ((new_var->DataSize <= 0) || (new_var->Attributes == 0)){ | 506 | if ((new_var->DataSize <= 0) || (new_var->Attributes == 0)){ |
493 | printk(KERN_ERR "efivars: DataSize & Attributes must be valid!\n"); | 507 | printk(KERN_ERR "efivars: DataSize & Attributes must be valid!\n"); |
494 | return -EINVAL; | 508 | return -EINVAL; |
495 | } | 509 | } |
496 | 510 | ||
497 | if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 || | 511 | if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 || |
498 | validate_var(new_var, new_var->Data, new_var->DataSize) == false) { | 512 | validate_var(new_var, new_var->Data, new_var->DataSize) == false) { |
499 | printk(KERN_ERR "efivars: Malformed variable content\n"); | 513 | printk(KERN_ERR "efivars: Malformed variable content\n"); |
500 | return -EINVAL; | 514 | return -EINVAL; |
501 | } | 515 | } |
502 | 516 | ||
503 | spin_lock(&efivars->lock); | 517 | spin_lock(&efivars->lock); |
504 | status = efivars->ops->set_variable(new_var->VariableName, | 518 | status = efivars->ops->set_variable(new_var->VariableName, |
505 | &new_var->VendorGuid, | 519 | &new_var->VendorGuid, |
506 | new_var->Attributes, | 520 | new_var->Attributes, |
507 | new_var->DataSize, | 521 | new_var->DataSize, |
508 | new_var->Data); | 522 | new_var->Data); |
509 | 523 | ||
510 | spin_unlock(&efivars->lock); | 524 | spin_unlock(&efivars->lock); |
511 | 525 | ||
512 | if (status != EFI_SUCCESS) { | 526 | if (status != EFI_SUCCESS) { |
513 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", | 527 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", |
514 | status); | 528 | status); |
515 | return -EIO; | 529 | return -EIO; |
516 | } | 530 | } |
517 | 531 | ||
518 | memcpy(&entry->var, new_var, count); | 532 | memcpy(&entry->var, new_var, count); |
519 | return count; | 533 | return count; |
520 | } | 534 | } |
521 | 535 | ||
522 | static ssize_t | 536 | static ssize_t |
523 | efivar_show_raw(struct efivar_entry *entry, char *buf) | 537 | efivar_show_raw(struct efivar_entry *entry, char *buf) |
524 | { | 538 | { |
525 | struct efi_variable *var = &entry->var; | 539 | struct efi_variable *var = &entry->var; |
526 | efi_status_t status; | 540 | efi_status_t status; |
527 | 541 | ||
528 | if (!entry || !buf) | 542 | if (!entry || !buf) |
529 | return 0; | 543 | return 0; |
530 | 544 | ||
531 | status = get_var_data(entry->efivars, var); | 545 | status = get_var_data(entry->efivars, var); |
532 | if (status != EFI_SUCCESS) | 546 | if (status != EFI_SUCCESS) |
533 | return -EIO; | 547 | return -EIO; |
534 | 548 | ||
535 | memcpy(buf, var, sizeof(*var)); | 549 | memcpy(buf, var, sizeof(*var)); |
536 | return sizeof(*var); | 550 | return sizeof(*var); |
537 | } | 551 | } |
538 | 552 | ||
539 | /* | 553 | /* |
540 | * Generic read/write functions that call the specific functions of | 554 | * Generic read/write functions that call the specific functions of |
541 | * the attributes... | 555 | * the attributes... |
542 | */ | 556 | */ |
543 | static ssize_t efivar_attr_show(struct kobject *kobj, struct attribute *attr, | 557 | static ssize_t efivar_attr_show(struct kobject *kobj, struct attribute *attr, |
544 | char *buf) | 558 | char *buf) |
545 | { | 559 | { |
546 | struct efivar_entry *var = to_efivar_entry(kobj); | 560 | struct efivar_entry *var = to_efivar_entry(kobj); |
547 | struct efivar_attribute *efivar_attr = to_efivar_attr(attr); | 561 | struct efivar_attribute *efivar_attr = to_efivar_attr(attr); |
548 | ssize_t ret = -EIO; | 562 | ssize_t ret = -EIO; |
549 | 563 | ||
550 | if (!capable(CAP_SYS_ADMIN)) | 564 | if (!capable(CAP_SYS_ADMIN)) |
551 | return -EACCES; | 565 | return -EACCES; |
552 | 566 | ||
553 | if (efivar_attr->show) { | 567 | if (efivar_attr->show) { |
554 | ret = efivar_attr->show(var, buf); | 568 | ret = efivar_attr->show(var, buf); |
555 | } | 569 | } |
556 | return ret; | 570 | return ret; |
557 | } | 571 | } |
558 | 572 | ||
559 | static ssize_t efivar_attr_store(struct kobject *kobj, struct attribute *attr, | 573 | static ssize_t efivar_attr_store(struct kobject *kobj, struct attribute *attr, |
560 | const char *buf, size_t count) | 574 | const char *buf, size_t count) |
561 | { | 575 | { |
562 | struct efivar_entry *var = to_efivar_entry(kobj); | 576 | struct efivar_entry *var = to_efivar_entry(kobj); |
563 | struct efivar_attribute *efivar_attr = to_efivar_attr(attr); | 577 | struct efivar_attribute *efivar_attr = to_efivar_attr(attr); |
564 | ssize_t ret = -EIO; | 578 | ssize_t ret = -EIO; |
565 | 579 | ||
566 | if (!capable(CAP_SYS_ADMIN)) | 580 | if (!capable(CAP_SYS_ADMIN)) |
567 | return -EACCES; | 581 | return -EACCES; |
568 | 582 | ||
569 | if (efivar_attr->store) | 583 | if (efivar_attr->store) |
570 | ret = efivar_attr->store(var, buf, count); | 584 | ret = efivar_attr->store(var, buf, count); |
571 | 585 | ||
572 | return ret; | 586 | return ret; |
573 | } | 587 | } |
574 | 588 | ||
575 | static const struct sysfs_ops efivar_attr_ops = { | 589 | static const struct sysfs_ops efivar_attr_ops = { |
576 | .show = efivar_attr_show, | 590 | .show = efivar_attr_show, |
577 | .store = efivar_attr_store, | 591 | .store = efivar_attr_store, |
578 | }; | 592 | }; |
579 | 593 | ||
580 | static void efivar_release(struct kobject *kobj) | 594 | static void efivar_release(struct kobject *kobj) |
581 | { | 595 | { |
582 | struct efivar_entry *var = container_of(kobj, struct efivar_entry, kobj); | 596 | struct efivar_entry *var = container_of(kobj, struct efivar_entry, kobj); |
583 | kfree(var); | 597 | kfree(var); |
584 | } | 598 | } |
585 | 599 | ||
586 | static EFIVAR_ATTR(guid, 0400, efivar_guid_read, NULL); | 600 | static EFIVAR_ATTR(guid, 0400, efivar_guid_read, NULL); |
587 | static EFIVAR_ATTR(attributes, 0400, efivar_attr_read, NULL); | 601 | static EFIVAR_ATTR(attributes, 0400, efivar_attr_read, NULL); |
588 | static EFIVAR_ATTR(size, 0400, efivar_size_read, NULL); | 602 | static EFIVAR_ATTR(size, 0400, efivar_size_read, NULL); |
589 | static EFIVAR_ATTR(data, 0400, efivar_data_read, NULL); | 603 | static EFIVAR_ATTR(data, 0400, efivar_data_read, NULL); |
590 | static EFIVAR_ATTR(raw_var, 0600, efivar_show_raw, efivar_store_raw); | 604 | static EFIVAR_ATTR(raw_var, 0600, efivar_show_raw, efivar_store_raw); |
591 | 605 | ||
592 | static struct attribute *def_attrs[] = { | 606 | static struct attribute *def_attrs[] = { |
593 | &efivar_attr_guid.attr, | 607 | &efivar_attr_guid.attr, |
594 | &efivar_attr_size.attr, | 608 | &efivar_attr_size.attr, |
595 | &efivar_attr_attributes.attr, | 609 | &efivar_attr_attributes.attr, |
596 | &efivar_attr_data.attr, | 610 | &efivar_attr_data.attr, |
597 | &efivar_attr_raw_var.attr, | 611 | &efivar_attr_raw_var.attr, |
598 | NULL, | 612 | NULL, |
599 | }; | 613 | }; |
600 | 614 | ||
601 | static struct kobj_type efivar_ktype = { | 615 | static struct kobj_type efivar_ktype = { |
602 | .release = efivar_release, | 616 | .release = efivar_release, |
603 | .sysfs_ops = &efivar_attr_ops, | 617 | .sysfs_ops = &efivar_attr_ops, |
604 | .default_attrs = def_attrs, | 618 | .default_attrs = def_attrs, |
605 | }; | 619 | }; |
606 | 620 | ||
607 | static struct pstore_info efi_pstore_info; | 621 | static struct pstore_info efi_pstore_info; |
608 | 622 | ||
609 | static inline void | 623 | static inline void |
610 | efivar_unregister(struct efivar_entry *var) | 624 | efivar_unregister(struct efivar_entry *var) |
611 | { | 625 | { |
612 | kobject_put(&var->kobj); | 626 | kobject_put(&var->kobj); |
613 | } | 627 | } |
614 | 628 | ||
615 | #ifdef CONFIG_PSTORE | 629 | #ifdef CONFIG_PSTORE |
616 | 630 | ||
617 | static int efi_pstore_open(struct pstore_info *psi) | 631 | static int efi_pstore_open(struct pstore_info *psi) |
618 | { | 632 | { |
619 | struct efivars *efivars = psi->data; | 633 | struct efivars *efivars = psi->data; |
620 | 634 | ||
621 | spin_lock(&efivars->lock); | 635 | spin_lock(&efivars->lock); |
622 | efivars->walk_entry = list_first_entry(&efivars->list, | 636 | efivars->walk_entry = list_first_entry(&efivars->list, |
623 | struct efivar_entry, list); | 637 | struct efivar_entry, list); |
624 | return 0; | 638 | return 0; |
625 | } | 639 | } |
626 | 640 | ||
627 | static int efi_pstore_close(struct pstore_info *psi) | 641 | static int efi_pstore_close(struct pstore_info *psi) |
628 | { | 642 | { |
629 | struct efivars *efivars = psi->data; | 643 | struct efivars *efivars = psi->data; |
630 | 644 | ||
631 | spin_unlock(&efivars->lock); | 645 | spin_unlock(&efivars->lock); |
632 | return 0; | 646 | return 0; |
633 | } | 647 | } |
634 | 648 | ||
635 | static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, | 649 | static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, |
636 | struct timespec *timespec, | 650 | struct timespec *timespec, |
637 | char **buf, struct pstore_info *psi) | 651 | char **buf, struct pstore_info *psi) |
638 | { | 652 | { |
639 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; | 653 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; |
640 | struct efivars *efivars = psi->data; | 654 | struct efivars *efivars = psi->data; |
641 | char name[DUMP_NAME_LEN]; | 655 | char name[DUMP_NAME_LEN]; |
642 | int i; | 656 | int i; |
643 | unsigned int part, size; | 657 | unsigned int part, size; |
644 | unsigned long time; | 658 | unsigned long time; |
645 | 659 | ||
646 | while (&efivars->walk_entry->list != &efivars->list) { | 660 | while (&efivars->walk_entry->list != &efivars->list) { |
647 | if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid, | 661 | if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid, |
648 | vendor)) { | 662 | vendor)) { |
649 | for (i = 0; i < DUMP_NAME_LEN; i++) { | 663 | for (i = 0; i < DUMP_NAME_LEN; i++) { |
650 | name[i] = efivars->walk_entry->var.VariableName[i]; | 664 | name[i] = efivars->walk_entry->var.VariableName[i]; |
651 | } | 665 | } |
652 | if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) { | 666 | if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) { |
653 | *id = part; | 667 | *id = part; |
654 | timespec->tv_sec = time; | 668 | timespec->tv_sec = time; |
655 | timespec->tv_nsec = 0; | 669 | timespec->tv_nsec = 0; |
656 | get_var_data_locked(efivars, &efivars->walk_entry->var); | 670 | get_var_data_locked(efivars, &efivars->walk_entry->var); |
657 | size = efivars->walk_entry->var.DataSize; | 671 | size = efivars->walk_entry->var.DataSize; |
658 | *buf = kmalloc(size, GFP_KERNEL); | 672 | *buf = kmalloc(size, GFP_KERNEL); |
659 | if (*buf == NULL) | 673 | if (*buf == NULL) |
660 | return -ENOMEM; | 674 | return -ENOMEM; |
661 | memcpy(*buf, efivars->walk_entry->var.Data, | 675 | memcpy(*buf, efivars->walk_entry->var.Data, |
662 | size); | 676 | size); |
663 | efivars->walk_entry = list_entry(efivars->walk_entry->list.next, | 677 | efivars->walk_entry = list_entry(efivars->walk_entry->list.next, |
664 | struct efivar_entry, list); | 678 | struct efivar_entry, list); |
665 | return size; | 679 | return size; |
666 | } | 680 | } |
667 | } | 681 | } |
668 | efivars->walk_entry = list_entry(efivars->walk_entry->list.next, | 682 | efivars->walk_entry = list_entry(efivars->walk_entry->list.next, |
669 | struct efivar_entry, list); | 683 | struct efivar_entry, list); |
670 | } | 684 | } |
671 | return 0; | 685 | return 0; |
672 | } | 686 | } |
673 | 687 | ||
674 | static int efi_pstore_write(enum pstore_type_id type, | 688 | static int efi_pstore_write(enum pstore_type_id type, |
675 | enum kmsg_dump_reason reason, u64 *id, | 689 | enum kmsg_dump_reason reason, u64 *id, |
676 | unsigned int part, size_t size, struct pstore_info *psi) | 690 | unsigned int part, size_t size, struct pstore_info *psi) |
677 | { | 691 | { |
678 | char name[DUMP_NAME_LEN]; | 692 | char name[DUMP_NAME_LEN]; |
679 | char stub_name[DUMP_NAME_LEN]; | 693 | char stub_name[DUMP_NAME_LEN]; |
680 | efi_char16_t efi_name[DUMP_NAME_LEN]; | 694 | efi_char16_t efi_name[DUMP_NAME_LEN]; |
681 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; | 695 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; |
682 | struct efivars *efivars = psi->data; | 696 | struct efivars *efivars = psi->data; |
683 | struct efivar_entry *entry, *found = NULL; | 697 | struct efivar_entry *entry, *found = NULL; |
684 | int i, ret = 0; | 698 | int i, ret = 0; |
685 | 699 | ||
686 | sprintf(stub_name, "dump-type%u-%u-", type, part); | 700 | sprintf(stub_name, "dump-type%u-%u-", type, part); |
687 | sprintf(name, "%s%lu", stub_name, get_seconds()); | 701 | sprintf(name, "%s%lu", stub_name, get_seconds()); |
688 | 702 | ||
689 | spin_lock(&efivars->lock); | 703 | spin_lock(&efivars->lock); |
690 | 704 | ||
691 | for (i = 0; i < DUMP_NAME_LEN; i++) | 705 | for (i = 0; i < DUMP_NAME_LEN; i++) |
692 | efi_name[i] = stub_name[i]; | 706 | efi_name[i] = stub_name[i]; |
693 | 707 | ||
694 | /* | 708 | /* |
695 | * Clean up any entries with the same name | 709 | * Clean up any entries with the same name |
696 | */ | 710 | */ |
697 | 711 | ||
698 | list_for_each_entry(entry, &efivars->list, list) { | 712 | list_for_each_entry(entry, &efivars->list, list) { |
699 | get_var_data_locked(efivars, &entry->var); | 713 | get_var_data_locked(efivars, &entry->var); |
700 | 714 | ||
701 | if (efi_guidcmp(entry->var.VendorGuid, vendor)) | 715 | if (efi_guidcmp(entry->var.VendorGuid, vendor)) |
702 | continue; | 716 | continue; |
703 | if (utf16_strncmp(entry->var.VariableName, efi_name, | 717 | if (utf16_strncmp(entry->var.VariableName, efi_name, |
704 | utf16_strlen(efi_name))) | 718 | utf16_strlen(efi_name))) |
705 | continue; | 719 | continue; |
706 | /* Needs to be a prefix */ | 720 | /* Needs to be a prefix */ |
707 | if (entry->var.VariableName[utf16_strlen(efi_name)] == 0) | 721 | if (entry->var.VariableName[utf16_strlen(efi_name)] == 0) |
708 | continue; | 722 | continue; |
709 | 723 | ||
710 | /* found */ | 724 | /* found */ |
711 | found = entry; | 725 | found = entry; |
712 | efivars->ops->set_variable(entry->var.VariableName, | 726 | efivars->ops->set_variable(entry->var.VariableName, |
713 | &entry->var.VendorGuid, | 727 | &entry->var.VendorGuid, |
714 | PSTORE_EFI_ATTRIBUTES, | 728 | PSTORE_EFI_ATTRIBUTES, |
715 | 0, NULL); | 729 | 0, NULL); |
716 | } | 730 | } |
717 | 731 | ||
718 | if (found) | 732 | if (found) |
719 | list_del(&found->list); | 733 | list_del(&found->list); |
720 | 734 | ||
721 | for (i = 0; i < DUMP_NAME_LEN; i++) | 735 | for (i = 0; i < DUMP_NAME_LEN; i++) |
722 | efi_name[i] = name[i]; | 736 | efi_name[i] = name[i]; |
723 | 737 | ||
724 | efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES, | 738 | efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES, |
725 | size, psi->buf); | 739 | size, psi->buf); |
726 | 740 | ||
727 | spin_unlock(&efivars->lock); | 741 | spin_unlock(&efivars->lock); |
728 | 742 | ||
729 | if (found) | 743 | if (found) |
730 | efivar_unregister(found); | 744 | efivar_unregister(found); |
731 | 745 | ||
732 | if (size) | 746 | if (size) |
733 | ret = efivar_create_sysfs_entry(efivars, | 747 | ret = efivar_create_sysfs_entry(efivars, |
734 | utf16_strsize(efi_name, | 748 | utf16_strsize(efi_name, |
735 | DUMP_NAME_LEN * 2), | 749 | DUMP_NAME_LEN * 2), |
736 | efi_name, &vendor); | 750 | efi_name, &vendor); |
737 | 751 | ||
738 | *id = part; | 752 | *id = part; |
739 | return ret; | 753 | return ret; |
740 | }; | 754 | }; |
741 | 755 | ||
742 | static int efi_pstore_erase(enum pstore_type_id type, u64 id, | 756 | static int efi_pstore_erase(enum pstore_type_id type, u64 id, |
743 | struct pstore_info *psi) | 757 | struct pstore_info *psi) |
744 | { | 758 | { |
745 | efi_pstore_write(type, 0, &id, (unsigned int)id, 0, psi); | 759 | efi_pstore_write(type, 0, &id, (unsigned int)id, 0, psi); |
746 | 760 | ||
747 | return 0; | 761 | return 0; |
748 | } | 762 | } |
749 | #else | 763 | #else |
750 | static int efi_pstore_open(struct pstore_info *psi) | 764 | static int efi_pstore_open(struct pstore_info *psi) |
751 | { | 765 | { |
752 | return 0; | 766 | return 0; |
753 | } | 767 | } |
754 | 768 | ||
755 | static int efi_pstore_close(struct pstore_info *psi) | 769 | static int efi_pstore_close(struct pstore_info *psi) |
756 | { | 770 | { |
757 | return 0; | 771 | return 0; |
758 | } | 772 | } |
759 | 773 | ||
760 | static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, | 774 | static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, |
761 | struct timespec *timespec, | 775 | struct timespec *timespec, |
762 | char **buf, struct pstore_info *psi) | 776 | char **buf, struct pstore_info *psi) |
763 | { | 777 | { |
764 | return -1; | 778 | return -1; |
765 | } | 779 | } |
766 | 780 | ||
767 | static int efi_pstore_write(enum pstore_type_id type, | 781 | static int efi_pstore_write(enum pstore_type_id type, |
768 | enum kmsg_dump_reason reason, u64 *id, | 782 | enum kmsg_dump_reason reason, u64 *id, |
769 | unsigned int part, size_t size, struct pstore_info *psi) | 783 | unsigned int part, size_t size, struct pstore_info *psi) |
770 | { | 784 | { |
771 | return 0; | 785 | return 0; |
772 | } | 786 | } |
773 | 787 | ||
774 | static int efi_pstore_erase(enum pstore_type_id type, u64 id, | 788 | static int efi_pstore_erase(enum pstore_type_id type, u64 id, |
775 | struct pstore_info *psi) | 789 | struct pstore_info *psi) |
776 | { | 790 | { |
777 | return 0; | 791 | return 0; |
778 | } | 792 | } |
779 | #endif | 793 | #endif |
780 | 794 | ||
781 | static struct pstore_info efi_pstore_info = { | 795 | static struct pstore_info efi_pstore_info = { |
782 | .owner = THIS_MODULE, | 796 | .owner = THIS_MODULE, |
783 | .name = "efi", | 797 | .name = "efi", |
784 | .open = efi_pstore_open, | 798 | .open = efi_pstore_open, |
785 | .close = efi_pstore_close, | 799 | .close = efi_pstore_close, |
786 | .read = efi_pstore_read, | 800 | .read = efi_pstore_read, |
787 | .write = efi_pstore_write, | 801 | .write = efi_pstore_write, |
788 | .erase = efi_pstore_erase, | 802 | .erase = efi_pstore_erase, |
789 | }; | 803 | }; |
790 | 804 | ||
791 | static ssize_t efivar_create(struct file *filp, struct kobject *kobj, | 805 | static ssize_t efivar_create(struct file *filp, struct kobject *kobj, |
792 | struct bin_attribute *bin_attr, | 806 | struct bin_attribute *bin_attr, |
793 | char *buf, loff_t pos, size_t count) | 807 | char *buf, loff_t pos, size_t count) |
794 | { | 808 | { |
795 | struct efi_variable *new_var = (struct efi_variable *)buf; | 809 | struct efi_variable *new_var = (struct efi_variable *)buf; |
796 | struct efivars *efivars = bin_attr->private; | 810 | struct efivars *efivars = bin_attr->private; |
797 | struct efivar_entry *search_efivar, *n; | 811 | struct efivar_entry *search_efivar, *n; |
798 | unsigned long strsize1, strsize2; | 812 | unsigned long strsize1, strsize2; |
799 | efi_status_t status = EFI_NOT_FOUND; | 813 | efi_status_t status = EFI_NOT_FOUND; |
800 | int found = 0; | 814 | int found = 0; |
801 | 815 | ||
802 | if (!capable(CAP_SYS_ADMIN)) | 816 | if (!capable(CAP_SYS_ADMIN)) |
803 | return -EACCES; | 817 | return -EACCES; |
804 | 818 | ||
805 | if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 || | 819 | if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 || |
806 | validate_var(new_var, new_var->Data, new_var->DataSize) == false) { | 820 | validate_var(new_var, new_var->Data, new_var->DataSize) == false) { |
807 | printk(KERN_ERR "efivars: Malformed variable content\n"); | 821 | printk(KERN_ERR "efivars: Malformed variable content\n"); |
808 | return -EINVAL; | 822 | return -EINVAL; |
809 | } | 823 | } |
810 | 824 | ||
811 | spin_lock(&efivars->lock); | 825 | spin_lock(&efivars->lock); |
812 | 826 | ||
813 | /* | 827 | /* |
814 | * Does this variable already exist? | 828 | * Does this variable already exist? |
815 | */ | 829 | */ |
816 | list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { | 830 | list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { |
817 | strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024); | 831 | strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024); |
818 | strsize2 = utf16_strsize(new_var->VariableName, 1024); | 832 | strsize2 = utf16_strsize(new_var->VariableName, 1024); |
819 | if (strsize1 == strsize2 && | 833 | if (strsize1 == strsize2 && |
820 | !memcmp(&(search_efivar->var.VariableName), | 834 | !memcmp(&(search_efivar->var.VariableName), |
821 | new_var->VariableName, strsize1) && | 835 | new_var->VariableName, strsize1) && |
822 | !efi_guidcmp(search_efivar->var.VendorGuid, | 836 | !efi_guidcmp(search_efivar->var.VendorGuid, |
823 | new_var->VendorGuid)) { | 837 | new_var->VendorGuid)) { |
824 | found = 1; | 838 | found = 1; |
825 | break; | 839 | break; |
826 | } | 840 | } |
827 | } | 841 | } |
828 | if (found) { | 842 | if (found) { |
829 | spin_unlock(&efivars->lock); | 843 | spin_unlock(&efivars->lock); |
830 | return -EINVAL; | 844 | return -EINVAL; |
831 | } | 845 | } |
832 | 846 | ||
833 | /* now *really* create the variable via EFI */ | 847 | /* now *really* create the variable via EFI */ |
834 | status = efivars->ops->set_variable(new_var->VariableName, | 848 | status = efivars->ops->set_variable(new_var->VariableName, |
835 | &new_var->VendorGuid, | 849 | &new_var->VendorGuid, |
836 | new_var->Attributes, | 850 | new_var->Attributes, |
837 | new_var->DataSize, | 851 | new_var->DataSize, |
838 | new_var->Data); | 852 | new_var->Data); |
839 | 853 | ||
840 | if (status != EFI_SUCCESS) { | 854 | if (status != EFI_SUCCESS) { |
841 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", | 855 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", |
842 | status); | 856 | status); |
843 | spin_unlock(&efivars->lock); | 857 | spin_unlock(&efivars->lock); |
844 | return -EIO; | 858 | return -EIO; |
845 | } | 859 | } |
846 | spin_unlock(&efivars->lock); | 860 | spin_unlock(&efivars->lock); |
847 | 861 | ||
848 | /* Create the entry in sysfs. Locking is not required here */ | 862 | /* Create the entry in sysfs. Locking is not required here */ |
849 | status = efivar_create_sysfs_entry(efivars, | 863 | status = efivar_create_sysfs_entry(efivars, |
850 | utf16_strsize(new_var->VariableName, | 864 | utf16_strsize(new_var->VariableName, |
851 | 1024), | 865 | 1024), |
852 | new_var->VariableName, | 866 | new_var->VariableName, |
853 | &new_var->VendorGuid); | 867 | &new_var->VendorGuid); |
854 | if (status) { | 868 | if (status) { |
855 | printk(KERN_WARNING "efivars: variable created, but sysfs entry wasn't.\n"); | 869 | printk(KERN_WARNING "efivars: variable created, but sysfs entry wasn't.\n"); |
856 | } | 870 | } |
857 | return count; | 871 | return count; |
858 | } | 872 | } |
859 | 873 | ||
860 | static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, | 874 | static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, |
861 | struct bin_attribute *bin_attr, | 875 | struct bin_attribute *bin_attr, |
862 | char *buf, loff_t pos, size_t count) | 876 | char *buf, loff_t pos, size_t count) |
863 | { | 877 | { |
864 | struct efi_variable *del_var = (struct efi_variable *)buf; | 878 | struct efi_variable *del_var = (struct efi_variable *)buf; |
865 | struct efivars *efivars = bin_attr->private; | 879 | struct efivars *efivars = bin_attr->private; |
866 | struct efivar_entry *search_efivar, *n; | 880 | struct efivar_entry *search_efivar, *n; |
867 | unsigned long strsize1, strsize2; | 881 | unsigned long strsize1, strsize2; |
868 | efi_status_t status = EFI_NOT_FOUND; | 882 | efi_status_t status = EFI_NOT_FOUND; |
869 | int found = 0; | 883 | int found = 0; |
870 | 884 | ||
871 | if (!capable(CAP_SYS_ADMIN)) | 885 | if (!capable(CAP_SYS_ADMIN)) |
872 | return -EACCES; | 886 | return -EACCES; |
873 | 887 | ||
874 | spin_lock(&efivars->lock); | 888 | spin_lock(&efivars->lock); |
875 | 889 | ||
876 | /* | 890 | /* |
877 | * Does this variable already exist? | 891 | * Does this variable already exist? |
878 | */ | 892 | */ |
879 | list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { | 893 | list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { |
880 | strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024); | 894 | strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024); |
881 | strsize2 = utf16_strsize(del_var->VariableName, 1024); | 895 | strsize2 = utf16_strsize(del_var->VariableName, 1024); |
882 | if (strsize1 == strsize2 && | 896 | if (strsize1 == strsize2 && |
883 | !memcmp(&(search_efivar->var.VariableName), | 897 | !memcmp(&(search_efivar->var.VariableName), |
884 | del_var->VariableName, strsize1) && | 898 | del_var->VariableName, strsize1) && |
885 | !efi_guidcmp(search_efivar->var.VendorGuid, | 899 | !efi_guidcmp(search_efivar->var.VendorGuid, |
886 | del_var->VendorGuid)) { | 900 | del_var->VendorGuid)) { |
887 | found = 1; | 901 | found = 1; |
888 | break; | 902 | break; |
889 | } | 903 | } |
890 | } | 904 | } |
891 | if (!found) { | 905 | if (!found) { |
892 | spin_unlock(&efivars->lock); | 906 | spin_unlock(&efivars->lock); |
893 | return -EINVAL; | 907 | return -EINVAL; |
894 | } | 908 | } |
895 | /* force the Attributes/DataSize to 0 to ensure deletion */ | 909 | /* force the Attributes/DataSize to 0 to ensure deletion */ |
896 | del_var->Attributes = 0; | 910 | del_var->Attributes = 0; |
897 | del_var->DataSize = 0; | 911 | del_var->DataSize = 0; |
898 | 912 | ||
899 | status = efivars->ops->set_variable(del_var->VariableName, | 913 | status = efivars->ops->set_variable(del_var->VariableName, |
900 | &del_var->VendorGuid, | 914 | &del_var->VendorGuid, |
901 | del_var->Attributes, | 915 | del_var->Attributes, |
902 | del_var->DataSize, | 916 | del_var->DataSize, |
903 | del_var->Data); | 917 | del_var->Data); |
904 | 918 | ||
905 | if (status != EFI_SUCCESS) { | 919 | if (status != EFI_SUCCESS) { |
906 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", | 920 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", |
907 | status); | 921 | status); |
908 | spin_unlock(&efivars->lock); | 922 | spin_unlock(&efivars->lock); |
909 | return -EIO; | 923 | return -EIO; |
910 | } | 924 | } |
911 | list_del(&search_efivar->list); | 925 | list_del(&search_efivar->list); |
912 | /* We need to release this lock before unregistering. */ | 926 | /* We need to release this lock before unregistering. */ |
913 | spin_unlock(&efivars->lock); | 927 | spin_unlock(&efivars->lock); |
914 | efivar_unregister(search_efivar); | 928 | efivar_unregister(search_efivar); |
915 | 929 | ||
916 | /* It's dead Jim.... */ | 930 | /* It's dead Jim.... */ |
917 | return count; | 931 | return count; |
918 | } | 932 | } |
919 | 933 | ||
920 | /* | 934 | /* |
921 | * Let's not leave out systab information that snuck into | 935 | * Let's not leave out systab information that snuck into |
922 | * the efivars driver | 936 | * the efivars driver |
923 | */ | 937 | */ |
924 | static ssize_t systab_show(struct kobject *kobj, | 938 | static ssize_t systab_show(struct kobject *kobj, |
925 | struct kobj_attribute *attr, char *buf) | 939 | struct kobj_attribute *attr, char *buf) |
926 | { | 940 | { |
927 | char *str = buf; | 941 | char *str = buf; |
928 | 942 | ||
929 | if (!kobj || !buf) | 943 | if (!kobj || !buf) |
930 | return -EINVAL; | 944 | return -EINVAL; |
931 | 945 | ||
932 | if (efi.mps != EFI_INVALID_TABLE_ADDR) | 946 | if (efi.mps != EFI_INVALID_TABLE_ADDR) |
933 | str += sprintf(str, "MPS=0x%lx\n", efi.mps); | 947 | str += sprintf(str, "MPS=0x%lx\n", efi.mps); |
934 | if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) | 948 | if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) |
935 | str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); | 949 | str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); |
936 | if (efi.acpi != EFI_INVALID_TABLE_ADDR) | 950 | if (efi.acpi != EFI_INVALID_TABLE_ADDR) |
937 | str += sprintf(str, "ACPI=0x%lx\n", efi.acpi); | 951 | str += sprintf(str, "ACPI=0x%lx\n", efi.acpi); |
938 | if (efi.smbios != EFI_INVALID_TABLE_ADDR) | 952 | if (efi.smbios != EFI_INVALID_TABLE_ADDR) |
939 | str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); | 953 | str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); |
940 | if (efi.hcdp != EFI_INVALID_TABLE_ADDR) | 954 | if (efi.hcdp != EFI_INVALID_TABLE_ADDR) |
941 | str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); | 955 | str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); |
942 | if (efi.boot_info != EFI_INVALID_TABLE_ADDR) | 956 | if (efi.boot_info != EFI_INVALID_TABLE_ADDR) |
943 | str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info); | 957 | str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info); |
944 | if (efi.uga != EFI_INVALID_TABLE_ADDR) | 958 | if (efi.uga != EFI_INVALID_TABLE_ADDR) |
945 | str += sprintf(str, "UGA=0x%lx\n", efi.uga); | 959 | str += sprintf(str, "UGA=0x%lx\n", efi.uga); |
946 | 960 | ||
947 | return str - buf; | 961 | return str - buf; |
948 | } | 962 | } |
949 | 963 | ||
950 | static struct kobj_attribute efi_attr_systab = | 964 | static struct kobj_attribute efi_attr_systab = |
951 | __ATTR(systab, 0400, systab_show, NULL); | 965 | __ATTR(systab, 0400, systab_show, NULL); |
952 | 966 | ||
953 | static struct attribute *efi_subsys_attrs[] = { | 967 | static struct attribute *efi_subsys_attrs[] = { |
954 | &efi_attr_systab.attr, | 968 | &efi_attr_systab.attr, |
955 | NULL, /* maybe more in the future? */ | 969 | NULL, /* maybe more in the future? */ |
956 | }; | 970 | }; |
957 | 971 | ||
958 | static struct attribute_group efi_subsys_attr_group = { | 972 | static struct attribute_group efi_subsys_attr_group = { |
959 | .attrs = efi_subsys_attrs, | 973 | .attrs = efi_subsys_attrs, |
960 | }; | 974 | }; |
961 | 975 | ||
962 | static struct kobject *efi_kobj; | 976 | static struct kobject *efi_kobj; |
963 | 977 | ||
964 | /* | 978 | /* |
965 | * efivar_create_sysfs_entry() | 979 | * efivar_create_sysfs_entry() |
966 | * Requires: | 980 | * Requires: |
967 | * variable_name_size = number of bytes required to hold | 981 | * variable_name_size = number of bytes required to hold |
968 | * variable_name (not counting the NULL | 982 | * variable_name (not counting the NULL |
969 | * character at the end. | 983 | * character at the end. |
970 | * efivars->lock is not held on entry or exit. | 984 | * efivars->lock is not held on entry or exit. |
971 | * Returns 1 on failure, 0 on success | 985 | * Returns 1 on failure, 0 on success |
972 | */ | 986 | */ |
973 | static int | 987 | static int |
974 | efivar_create_sysfs_entry(struct efivars *efivars, | 988 | efivar_create_sysfs_entry(struct efivars *efivars, |
975 | unsigned long variable_name_size, | 989 | unsigned long variable_name_size, |
976 | efi_char16_t *variable_name, | 990 | efi_char16_t *variable_name, |
977 | efi_guid_t *vendor_guid) | 991 | efi_guid_t *vendor_guid) |
978 | { | 992 | { |
979 | int i, short_name_size = variable_name_size / sizeof(efi_char16_t) + 38; | 993 | int i, short_name_size = variable_name_size / sizeof(efi_char16_t) + 38; |
980 | char *short_name; | 994 | char *short_name; |
981 | struct efivar_entry *new_efivar; | 995 | struct efivar_entry *new_efivar; |
982 | 996 | ||
983 | short_name = kzalloc(short_name_size + 1, GFP_KERNEL); | 997 | short_name = kzalloc(short_name_size + 1, GFP_KERNEL); |
984 | new_efivar = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL); | 998 | new_efivar = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL); |
985 | 999 | ||
986 | if (!short_name || !new_efivar) { | 1000 | if (!short_name || !new_efivar) { |
987 | kfree(short_name); | 1001 | kfree(short_name); |
988 | kfree(new_efivar); | 1002 | kfree(new_efivar); |
989 | return 1; | 1003 | return 1; |
990 | } | 1004 | } |
991 | 1005 | ||
992 | new_efivar->efivars = efivars; | 1006 | new_efivar->efivars = efivars; |
993 | memcpy(new_efivar->var.VariableName, variable_name, | 1007 | memcpy(new_efivar->var.VariableName, variable_name, |
994 | variable_name_size); | 1008 | variable_name_size); |
995 | memcpy(&(new_efivar->var.VendorGuid), vendor_guid, sizeof(efi_guid_t)); | 1009 | memcpy(&(new_efivar->var.VendorGuid), vendor_guid, sizeof(efi_guid_t)); |
996 | 1010 | ||
997 | /* Convert Unicode to normal chars (assume top bits are 0), | 1011 | /* Convert Unicode to normal chars (assume top bits are 0), |
998 | ala UTF-8 */ | 1012 | ala UTF-8 */ |
999 | for (i=0; i < (int)(variable_name_size / sizeof(efi_char16_t)); i++) { | 1013 | for (i=0; i < (int)(variable_name_size / sizeof(efi_char16_t)); i++) { |
1000 | short_name[i] = variable_name[i] & 0xFF; | 1014 | short_name[i] = variable_name[i] & 0xFF; |
1001 | } | 1015 | } |
1002 | /* This is ugly, but necessary to separate one vendor's | 1016 | /* This is ugly, but necessary to separate one vendor's |
1003 | private variables from another's. */ | 1017 | private variables from another's. */ |
1004 | 1018 | ||
1005 | *(short_name + strlen(short_name)) = '-'; | 1019 | *(short_name + strlen(short_name)) = '-'; |
1006 | efi_guid_unparse(vendor_guid, short_name + strlen(short_name)); | 1020 | efi_guid_unparse(vendor_guid, short_name + strlen(short_name)); |
1007 | 1021 | ||
1008 | new_efivar->kobj.kset = efivars->kset; | 1022 | new_efivar->kobj.kset = efivars->kset; |
1009 | i = kobject_init_and_add(&new_efivar->kobj, &efivar_ktype, NULL, | 1023 | i = kobject_init_and_add(&new_efivar->kobj, &efivar_ktype, NULL, |
1010 | "%s", short_name); | 1024 | "%s", short_name); |
1011 | if (i) { | 1025 | if (i) { |
1012 | kfree(short_name); | 1026 | kfree(short_name); |
1013 | kfree(new_efivar); | 1027 | kfree(new_efivar); |
1014 | return 1; | 1028 | return 1; |
1015 | } | 1029 | } |
1016 | 1030 | ||
1017 | kobject_uevent(&new_efivar->kobj, KOBJ_ADD); | 1031 | kobject_uevent(&new_efivar->kobj, KOBJ_ADD); |
1018 | kfree(short_name); | 1032 | kfree(short_name); |
1019 | short_name = NULL; | 1033 | short_name = NULL; |
1020 | 1034 | ||
1021 | spin_lock(&efivars->lock); | 1035 | spin_lock(&efivars->lock); |
1022 | list_add(&new_efivar->list, &efivars->list); | 1036 | list_add(&new_efivar->list, &efivars->list); |
1023 | spin_unlock(&efivars->lock); | 1037 | spin_unlock(&efivars->lock); |
1024 | 1038 | ||
1025 | return 0; | 1039 | return 0; |
1026 | } | 1040 | } |
1027 | 1041 | ||
1028 | static int | 1042 | static int |
1029 | create_efivars_bin_attributes(struct efivars *efivars) | 1043 | create_efivars_bin_attributes(struct efivars *efivars) |
1030 | { | 1044 | { |
1031 | struct bin_attribute *attr; | 1045 | struct bin_attribute *attr; |
1032 | int error; | 1046 | int error; |
1033 | 1047 | ||
1034 | /* new_var */ | 1048 | /* new_var */ |
1035 | attr = kzalloc(sizeof(*attr), GFP_KERNEL); | 1049 | attr = kzalloc(sizeof(*attr), GFP_KERNEL); |
1036 | if (!attr) | 1050 | if (!attr) |
1037 | return -ENOMEM; | 1051 | return -ENOMEM; |
1038 | 1052 | ||
1039 | attr->attr.name = "new_var"; | 1053 | attr->attr.name = "new_var"; |
1040 | attr->attr.mode = 0200; | 1054 | attr->attr.mode = 0200; |
1041 | attr->write = efivar_create; | 1055 | attr->write = efivar_create; |
1042 | attr->private = efivars; | 1056 | attr->private = efivars; |
1043 | efivars->new_var = attr; | 1057 | efivars->new_var = attr; |
1044 | 1058 | ||
1045 | /* del_var */ | 1059 | /* del_var */ |
1046 | attr = kzalloc(sizeof(*attr), GFP_KERNEL); | 1060 | attr = kzalloc(sizeof(*attr), GFP_KERNEL); |
1047 | if (!attr) { | 1061 | if (!attr) { |
1048 | error = -ENOMEM; | 1062 | error = -ENOMEM; |
1049 | goto out_free; | 1063 | goto out_free; |
1050 | } | 1064 | } |
1051 | attr->attr.name = "del_var"; | 1065 | attr->attr.name = "del_var"; |
1052 | attr->attr.mode = 0200; | 1066 | attr->attr.mode = 0200; |
1053 | attr->write = efivar_delete; | 1067 | attr->write = efivar_delete; |
1054 | attr->private = efivars; | 1068 | attr->private = efivars; |
1055 | efivars->del_var = attr; | 1069 | efivars->del_var = attr; |
1056 | 1070 | ||
1057 | sysfs_bin_attr_init(efivars->new_var); | 1071 | sysfs_bin_attr_init(efivars->new_var); |
1058 | sysfs_bin_attr_init(efivars->del_var); | 1072 | sysfs_bin_attr_init(efivars->del_var); |
1059 | 1073 | ||
1060 | /* Register */ | 1074 | /* Register */ |
1061 | error = sysfs_create_bin_file(&efivars->kset->kobj, | 1075 | error = sysfs_create_bin_file(&efivars->kset->kobj, |
1062 | efivars->new_var); | 1076 | efivars->new_var); |
1063 | if (error) { | 1077 | if (error) { |
1064 | printk(KERN_ERR "efivars: unable to create new_var sysfs file" | 1078 | printk(KERN_ERR "efivars: unable to create new_var sysfs file" |
1065 | " due to error %d\n", error); | 1079 | " due to error %d\n", error); |
1066 | goto out_free; | 1080 | goto out_free; |
1067 | } | 1081 | } |
1068 | error = sysfs_create_bin_file(&efivars->kset->kobj, | 1082 | error = sysfs_create_bin_file(&efivars->kset->kobj, |
1069 | efivars->del_var); | 1083 | efivars->del_var); |
1070 | if (error) { | 1084 | if (error) { |
1071 | printk(KERN_ERR "efivars: unable to create del_var sysfs file" | 1085 | printk(KERN_ERR "efivars: unable to create del_var sysfs file" |
1072 | " due to error %d\n", error); | 1086 | " due to error %d\n", error); |
1073 | sysfs_remove_bin_file(&efivars->kset->kobj, | 1087 | sysfs_remove_bin_file(&efivars->kset->kobj, |
1074 | efivars->new_var); | 1088 | efivars->new_var); |
1075 | goto out_free; | 1089 | goto out_free; |
1076 | } | 1090 | } |
1077 | 1091 | ||
1078 | return 0; | 1092 | return 0; |
1079 | out_free: | 1093 | out_free: |
1080 | kfree(efivars->del_var); | 1094 | kfree(efivars->del_var); |
1081 | efivars->del_var = NULL; | 1095 | efivars->del_var = NULL; |
1082 | kfree(efivars->new_var); | 1096 | kfree(efivars->new_var); |
1083 | efivars->new_var = NULL; | 1097 | efivars->new_var = NULL; |
1084 | return error; | 1098 | return error; |
1085 | } | 1099 | } |
1086 | 1100 | ||
1087 | void unregister_efivars(struct efivars *efivars) | 1101 | void unregister_efivars(struct efivars *efivars) |
1088 | { | 1102 | { |
1089 | struct efivar_entry *entry, *n; | 1103 | struct efivar_entry *entry, *n; |
1090 | 1104 | ||
1091 | list_for_each_entry_safe(entry, n, &efivars->list, list) { | 1105 | list_for_each_entry_safe(entry, n, &efivars->list, list) { |
1092 | spin_lock(&efivars->lock); | 1106 | spin_lock(&efivars->lock); |
1093 | list_del(&entry->list); | 1107 | list_del(&entry->list); |
1094 | spin_unlock(&efivars->lock); | 1108 | spin_unlock(&efivars->lock); |
1095 | efivar_unregister(entry); | 1109 | efivar_unregister(entry); |
1096 | } | 1110 | } |
1097 | if (efivars->new_var) | 1111 | if (efivars->new_var) |
1098 | sysfs_remove_bin_file(&efivars->kset->kobj, efivars->new_var); | 1112 | sysfs_remove_bin_file(&efivars->kset->kobj, efivars->new_var); |
1099 | if (efivars->del_var) | 1113 | if (efivars->del_var) |
1100 | sysfs_remove_bin_file(&efivars->kset->kobj, efivars->del_var); | 1114 | sysfs_remove_bin_file(&efivars->kset->kobj, efivars->del_var); |
1101 | kfree(efivars->new_var); | 1115 | kfree(efivars->new_var); |
1102 | kfree(efivars->del_var); | 1116 | kfree(efivars->del_var); |
1103 | kset_unregister(efivars->kset); | 1117 | kset_unregister(efivars->kset); |
1104 | } | 1118 | } |
1105 | EXPORT_SYMBOL_GPL(unregister_efivars); | 1119 | EXPORT_SYMBOL_GPL(unregister_efivars); |
1106 | 1120 | ||
1107 | int register_efivars(struct efivars *efivars, | 1121 | int register_efivars(struct efivars *efivars, |
1108 | const struct efivar_operations *ops, | 1122 | const struct efivar_operations *ops, |
1109 | struct kobject *parent_kobj) | 1123 | struct kobject *parent_kobj) |
1110 | { | 1124 | { |
1111 | efi_status_t status = EFI_NOT_FOUND; | 1125 | efi_status_t status = EFI_NOT_FOUND; |
1112 | efi_guid_t vendor_guid; | 1126 | efi_guid_t vendor_guid; |
1113 | efi_char16_t *variable_name; | 1127 | efi_char16_t *variable_name; |
1114 | unsigned long variable_name_size = 1024; | 1128 | unsigned long variable_name_size = 1024; |
1115 | int error = 0; | 1129 | int error = 0; |
1116 | 1130 | ||
1117 | variable_name = kzalloc(variable_name_size, GFP_KERNEL); | 1131 | variable_name = kzalloc(variable_name_size, GFP_KERNEL); |
1118 | if (!variable_name) { | 1132 | if (!variable_name) { |
1119 | printk(KERN_ERR "efivars: Memory allocation failed.\n"); | 1133 | printk(KERN_ERR "efivars: Memory allocation failed.\n"); |
1120 | return -ENOMEM; | 1134 | return -ENOMEM; |
1121 | } | 1135 | } |
1122 | 1136 | ||
1123 | spin_lock_init(&efivars->lock); | 1137 | spin_lock_init(&efivars->lock); |
1124 | INIT_LIST_HEAD(&efivars->list); | 1138 | INIT_LIST_HEAD(&efivars->list); |
1125 | efivars->ops = ops; | 1139 | efivars->ops = ops; |
1126 | 1140 | ||
1127 | efivars->kset = kset_create_and_add("vars", NULL, parent_kobj); | 1141 | efivars->kset = kset_create_and_add("vars", NULL, parent_kobj); |
1128 | if (!efivars->kset) { | 1142 | if (!efivars->kset) { |
1129 | printk(KERN_ERR "efivars: Subsystem registration failed.\n"); | 1143 | printk(KERN_ERR "efivars: Subsystem registration failed.\n"); |
1130 | error = -ENOMEM; | 1144 | error = -ENOMEM; |
1131 | goto out; | 1145 | goto out; |
1132 | } | 1146 | } |
1133 | 1147 | ||
1134 | /* | 1148 | /* |
1135 | * Per EFI spec, the maximum storage allocated for both | 1149 | * Per EFI spec, the maximum storage allocated for both |
1136 | * the variable name and variable data is 1024 bytes. | 1150 | * the variable name and variable data is 1024 bytes. |
1137 | */ | 1151 | */ |
1138 | 1152 | ||
1139 | do { | 1153 | do { |
1140 | variable_name_size = 1024; | 1154 | variable_name_size = 1024; |
1141 | 1155 | ||
1142 | status = ops->get_next_variable(&variable_name_size, | 1156 | status = ops->get_next_variable(&variable_name_size, |
1143 | variable_name, | 1157 | variable_name, |
1144 | &vendor_guid); | 1158 | &vendor_guid); |
1145 | switch (status) { | 1159 | switch (status) { |
1146 | case EFI_SUCCESS: | 1160 | case EFI_SUCCESS: |
1147 | efivar_create_sysfs_entry(efivars, | 1161 | efivar_create_sysfs_entry(efivars, |
1148 | variable_name_size, | 1162 | variable_name_size, |
1149 | variable_name, | 1163 | variable_name, |
1150 | &vendor_guid); | 1164 | &vendor_guid); |
1151 | break; | 1165 | break; |
1152 | case EFI_NOT_FOUND: | 1166 | case EFI_NOT_FOUND: |
1153 | break; | 1167 | break; |
1154 | default: | 1168 | default: |
1155 | printk(KERN_WARNING "efivars: get_next_variable: status=%lx\n", | 1169 | printk(KERN_WARNING "efivars: get_next_variable: status=%lx\n", |
1156 | status); | 1170 | status); |
1157 | status = EFI_NOT_FOUND; | 1171 | status = EFI_NOT_FOUND; |
1158 | break; | 1172 | break; |
1159 | } | 1173 | } |
1160 | } while (status != EFI_NOT_FOUND); | 1174 | } while (status != EFI_NOT_FOUND); |
1161 | 1175 | ||
1162 | error = create_efivars_bin_attributes(efivars); | 1176 | error = create_efivars_bin_attributes(efivars); |
1163 | if (error) | 1177 | if (error) |
1164 | unregister_efivars(efivars); | 1178 | unregister_efivars(efivars); |
1165 | 1179 | ||
1166 | efivars->efi_pstore_info = efi_pstore_info; | 1180 | efivars->efi_pstore_info = efi_pstore_info; |
1167 | 1181 | ||
1168 | efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); | 1182 | efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); |
1169 | if (efivars->efi_pstore_info.buf) { | 1183 | if (efivars->efi_pstore_info.buf) { |
1170 | efivars->efi_pstore_info.bufsize = 1024; | 1184 | efivars->efi_pstore_info.bufsize = 1024; |
1171 | efivars->efi_pstore_info.data = efivars; | 1185 | efivars->efi_pstore_info.data = efivars; |
1172 | spin_lock_init(&efivars->efi_pstore_info.buf_lock); | 1186 | spin_lock_init(&efivars->efi_pstore_info.buf_lock); |
1173 | pstore_register(&efivars->efi_pstore_info); | 1187 | pstore_register(&efivars->efi_pstore_info); |
1174 | } | 1188 | } |
1175 | 1189 | ||
1176 | out: | 1190 | out: |
1177 | kfree(variable_name); | 1191 | kfree(variable_name); |
1178 | 1192 | ||
1179 | return error; | 1193 | return error; |
1180 | } | 1194 | } |
1181 | EXPORT_SYMBOL_GPL(register_efivars); | 1195 | EXPORT_SYMBOL_GPL(register_efivars); |
1182 | 1196 | ||
1183 | static struct efivars __efivars; | 1197 | static struct efivars __efivars; |
1184 | static struct efivar_operations ops; | 1198 | static struct efivar_operations ops; |
1185 | 1199 | ||
1186 | /* | 1200 | /* |
1187 | * For now we register the efi subsystem with the firmware subsystem | 1201 | * For now we register the efi subsystem with the firmware subsystem |
1188 | * and the vars subsystem with the efi subsystem. In the future, it | 1202 | * and the vars subsystem with the efi subsystem. In the future, it |
1189 | * might make sense to split off the efi subsystem into its own | 1203 | * might make sense to split off the efi subsystem into its own |
1190 | * driver, but for now only efivars will register with it, so just | 1204 | * driver, but for now only efivars will register with it, so just |
1191 | * include it here. | 1205 | * include it here. |
1192 | */ | 1206 | */ |
1193 | 1207 | ||
1194 | static int __init | 1208 | static int __init |
1195 | efivars_init(void) | 1209 | efivars_init(void) |
1196 | { | 1210 | { |
1197 | int error = 0; | 1211 | int error = 0; |
1198 | 1212 | ||
1199 | printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION, | 1213 | printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION, |
1200 | EFIVARS_DATE); | 1214 | EFIVARS_DATE); |
1201 | 1215 | ||
1202 | if (!efi_enabled) | 1216 | if (!efi_enabled) |
1203 | return 0; | 1217 | return 0; |
1204 | 1218 | ||
1205 | /* For now we'll register the efi directory at /sys/firmware/efi */ | 1219 | /* For now we'll register the efi directory at /sys/firmware/efi */ |
1206 | efi_kobj = kobject_create_and_add("efi", firmware_kobj); | 1220 | efi_kobj = kobject_create_and_add("efi", firmware_kobj); |
1207 | if (!efi_kobj) { | 1221 | if (!efi_kobj) { |
1208 | printk(KERN_ERR "efivars: Firmware registration failed.\n"); | 1222 | printk(KERN_ERR "efivars: Firmware registration failed.\n"); |
1209 | return -ENOMEM; | 1223 | return -ENOMEM; |
1210 | } | 1224 | } |
1211 | 1225 | ||
1212 | ops.get_variable = efi.get_variable; | 1226 | ops.get_variable = efi.get_variable; |
1213 | ops.set_variable = efi.set_variable; | 1227 | ops.set_variable = efi.set_variable; |
1214 | ops.get_next_variable = efi.get_next_variable; | 1228 | ops.get_next_variable = efi.get_next_variable; |
1215 | error = register_efivars(&__efivars, &ops, efi_kobj); | 1229 | error = register_efivars(&__efivars, &ops, efi_kobj); |
1216 | if (error) | 1230 | if (error) |
1217 | goto err_put; | 1231 | goto err_put; |
1218 | 1232 | ||
1219 | /* Don't forget the systab entry */ | 1233 | /* Don't forget the systab entry */ |
1220 | error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); | 1234 | error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); |
1221 | if (error) { | 1235 | if (error) { |
1222 | printk(KERN_ERR | 1236 | printk(KERN_ERR |
1223 | "efivars: Sysfs attribute export failed with error %d.\n", | 1237 | "efivars: Sysfs attribute export failed with error %d.\n", |
1224 | error); | 1238 | error); |
1225 | goto err_unregister; | 1239 | goto err_unregister; |
1226 | } | 1240 | } |
1227 | 1241 | ||
1228 | return 0; | 1242 | return 0; |
1229 | 1243 | ||
1230 | err_unregister: | 1244 | err_unregister: |
1231 | unregister_efivars(&__efivars); | 1245 | unregister_efivars(&__efivars); |
1232 | err_put: | 1246 | err_put: |
1233 | kobject_put(efi_kobj); | 1247 | kobject_put(efi_kobj); |
1234 | return error; | 1248 | return error; |
1235 | } | 1249 | } |
1236 | 1250 | ||
1237 | static void __exit | 1251 | static void __exit |
1238 | efivars_exit(void) | 1252 | efivars_exit(void) |
1239 | { | 1253 | { |
1240 | if (efi_enabled) { | 1254 | if (efi_enabled) { |
1241 | unregister_efivars(&__efivars); | 1255 | unregister_efivars(&__efivars); |
1242 | kobject_put(efi_kobj); | 1256 | kobject_put(efi_kobj); |
1243 | } | 1257 | } |
1244 | } | 1258 | } |
1245 | 1259 | ||
1246 | module_init(efivars_init); | 1260 | module_init(efivars_init); |