Blame view

drivers/firmware/efivars.c 26.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  /*
   * EFI Variables - efivars.c
   *
   * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com>
   * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com>
   *
   * This code takes all variables accessible from EFI runtime and
   *  exports them via sysfs
   *
   *  This program is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation; either version 2 of the License, or
   *  (at your option) any later version.
   *
   *  This program is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *  along with this program; if not, write to the Free Software
   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   *
   * Changelog:
   *
   *  17 May 2004 - Matt Domsch <Matt_Domsch@dell.com>
   *   remove check for efi_enabled in exit
   *   add MODULE_VERSION
   *
   *  26 Apr 2004 - Matt Domsch <Matt_Domsch@dell.com>
   *   minor bug fixes
   *
   *  21 Apr 2004 - Matt Tolentino <matthew.e.tolentino@intel.com)
   *   converted driver to export variable information via sysfs
   *   and moved to drivers/firmware directory
   *   bumped revision number to v0.07 to reflect conversion & move
   *
   *  10 Dec 2002 - Matt Domsch <Matt_Domsch@dell.com>
   *   fix locking per Peter Chubb's findings
   *
   *  25 Mar 2002 - Matt Domsch <Matt_Domsch@dell.com>
   *   move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse()
   *
   *  12 Feb 2002 - Matt Domsch <Matt_Domsch@dell.com>
   *   use list_for_each_safe when deleting vars.
   *   remove ifdef CONFIG_SMP around include <linux/smp.h>
   *   v0.04 release to linux-ia64@linuxia64.org
   *
   *  20 April 2001 - Matt Domsch <Matt_Domsch@dell.com>
   *   Moved vars from /proc/efi to /proc/efi/vars, and made
   *   efi.c own the /proc/efi directory.
   *   v0.03 release to linux-ia64@linuxia64.org
   *
   *  26 March 2001 - Matt Domsch <Matt_Domsch@dell.com>
   *   At the request of Stephane, moved ownership of /proc/efi
   *   to efi.c, and now efivars lives under /proc/efi/vars.
   *
   *  12 March 2001 - Matt Domsch <Matt_Domsch@dell.com>
   *   Feedback received from Stephane Eranian incorporated.
   *   efivar_write() checks copy_from_user() return value.
   *   efivar_read/write() returns proper errno.
   *   v0.02 release to linux-ia64@linuxia64.org
   *
   *  26 February 2001 - Matt Domsch <Matt_Domsch@dell.com>
   *   v0.01 release to linux-ia64@linuxia64.org
   */
c59ede7b7   Randy.Dunlap   [PATCH] move capa...
67
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
  #include <linux/types.h>
  #include <linux/errno.h>
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
75
76
77
78
  #include <linux/mm.h>
  #include <linux/module.h>
  #include <linux/string.h>
  #include <linux/smp.h>
  #include <linux/efi.h>
  #include <linux/sysfs.h>
  #include <linux/kobject.h>
  #include <linux/device.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
79
  #include <linux/slab.h>
5ee9c198a   Matthew Garrett   efi: Add support ...
80
  #include <linux/pstore.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
85
86
87
88
89
90
  
  #include <asm/uaccess.h>
  
  #define EFIVARS_VERSION "0.08"
  #define EFIVARS_DATE "2004-May-17"
  
  MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>");
  MODULE_DESCRIPTION("sysfs interface to EFI Variables");
  MODULE_LICENSE("GPL");
  MODULE_VERSION(EFIVARS_VERSION);
5ee9c198a   Matthew Garrett   efi: Add support ...
91
  #define DUMP_NAME_LEN 52
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  /*
   * The maximum size of VariableName + Data = 1024
   * Therefore, it's reasonable to save that much
   * space in each part of the structure,
   * and we use a page for reading/writing.
   */
  
  struct efi_variable {
  	efi_char16_t  VariableName[1024/sizeof(efi_char16_t)];
  	efi_guid_t    VendorGuid;
  	unsigned long DataSize;
  	__u8          Data[1024];
  	efi_status_t  Status;
  	__u32         Attributes;
  } __attribute__((packed));
  
  
  struct efivar_entry {
4142ef146   Mike Waychison   efivars: paramete...
110
  	struct efivars *efivars;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
114
  	struct efi_variable var;
  	struct list_head list;
  	struct kobject kobj;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
  struct efivar_attribute {
  	struct attribute attr;
  	ssize_t (*show) (struct efivar_entry *entry, char *buf);
  	ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
  };
7644c16c7   Mike Waychison   efivars: Introduc...
120
121
122
123
  #define PSTORE_EFI_ATTRIBUTES \
  	(EFI_VARIABLE_NON_VOLATILE | \
  	 EFI_VARIABLE_BOOTSERVICE_ACCESS | \
  	 EFI_VARIABLE_RUNTIME_ACCESS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
  #define EFIVAR_ATTR(_name, _mode, _show, _store) \
  struct efivar_attribute efivar_attr_##_name = { \
7b595756e   Tejun Heo   sysfs: kill unnec...
127
  	.attr = {.name = __stringify(_name), .mode = _mode}, \
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
  	.show = _show, \
  	.store = _store, \
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
132
133
134
135
136
137
  #define to_efivar_attr(_attr) container_of(_attr, struct efivar_attribute, attr)
  #define to_efivar_entry(obj)  container_of(obj, struct efivar_entry, kobj)
  
  /*
   * Prototype for sysfs creation function
   */
  static int
4142ef146   Mike Waychison   efivars: paramete...
138
139
140
141
  efivar_create_sysfs_entry(struct efivars *efivars,
  			  unsigned long variable_name_size,
  			  efi_char16_t *variable_name,
  			  efi_guid_t *vendor_guid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
  
  /* Return the number of unicode characters in data */
  static unsigned long
a29409083   Mike Waychison   efivars: String f...
145
  utf16_strnlen(efi_char16_t *s, size_t maxlength)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
  {
  	unsigned long length = 0;
a29409083   Mike Waychison   efivars: String f...
148
  	while (*s++ != 0 && length < maxlength)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
  		length++;
  	return length;
  }
b728a5c80   Tony Luck   efivars: fix warn...
152
  static inline unsigned long
a29409083   Mike Waychison   efivars: String f...
153
154
155
156
  utf16_strlen(efi_char16_t *s)
  {
  	return utf16_strnlen(s, ~0UL);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
  /*
   * Return the number of bytes is the length of this string
   * Note: this is NOT the same as the number of unicode characters
   */
  static inline unsigned long
a29409083   Mike Waychison   efivars: String f...
162
  utf16_strsize(efi_char16_t *data, unsigned long maxlength)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  {
a29409083   Mike Waychison   efivars: String f...
164
  	return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  }
828aa1f00   Mike Waychison   efivars: introduc...
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
  static inline int
  utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len)
  {
  	while (1) {
  		if (len == 0)
  			return 0;
  		if (*a < *b)
  			return -1;
  		if (*a > *b)
  			return 1;
  		if (*a == 0) /* implies *b == 0 */
  			return 0;
  		a++;
  		b++;
  		len--;
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  static efi_status_t
5ee9c198a   Matthew Garrett   efi: Add support ...
184
  get_var_data_locked(struct efivars *efivars, struct efi_variable *var)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
  {
  	efi_status_t status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
  	var->DataSize = 1024;
3295814d8   Mike Waychison   efivars: Paramete...
188
189
190
191
192
  	status = efivars->ops->get_variable(var->VariableName,
  					    &var->VendorGuid,
  					    &var->Attributes,
  					    &var->DataSize,
  					    var->Data);
5ee9c198a   Matthew Garrett   efi: Add support ...
193
194
195
196
197
198
199
200
201
202
  	return status;
  }
  
  static efi_status_t
  get_var_data(struct efivars *efivars, struct efi_variable *var)
  {
  	efi_status_t status;
  
  	spin_lock(&efivars->lock);
  	status = get_var_data_locked(efivars, var);
29422693c   Mike Waychison   efivars: move efi...
203
  	spin_unlock(&efivars->lock);
5ee9c198a   Matthew Garrett   efi: Add support ...
204

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  	if (status != EFI_SUCCESS) {
  		printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!
  ",
  			status);
  	}
  	return status;
  }
  
  static ssize_t
  efivar_guid_read(struct efivar_entry *entry, char *buf)
  {
  	struct efi_variable *var = &entry->var;
  	char *str = buf;
  
  	if (!entry || !buf)
  		return 0;
  
  	efi_guid_unparse(&var->VendorGuid, str);
  	str += strlen(str);
  	str += sprintf(str, "
  ");
  
  	return str - buf;
  }
  
  static ssize_t
  efivar_attr_read(struct efivar_entry *entry, char *buf)
  {
  	struct efi_variable *var = &entry->var;
  	char *str = buf;
  	efi_status_t status;
  
  	if (!entry || !buf)
  		return -EINVAL;
4142ef146   Mike Waychison   efivars: paramete...
239
  	status = get_var_data(entry->efivars, var);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  	if (status != EFI_SUCCESS)
  		return -EIO;
  
  	if (var->Attributes & 0x1)
  		str += sprintf(str, "EFI_VARIABLE_NON_VOLATILE
  ");
  	if (var->Attributes & 0x2)
  		str += sprintf(str, "EFI_VARIABLE_BOOTSERVICE_ACCESS
  ");
  	if (var->Attributes & 0x4)
  		str += sprintf(str, "EFI_VARIABLE_RUNTIME_ACCESS
  ");
  	return str - buf;
  }
  
  static ssize_t
  efivar_size_read(struct efivar_entry *entry, char *buf)
  {
  	struct efi_variable *var = &entry->var;
  	char *str = buf;
  	efi_status_t status;
  
  	if (!entry || !buf)
  		return -EINVAL;
4142ef146   Mike Waychison   efivars: paramete...
264
  	status = get_var_data(entry->efivars, var);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
  	if (status != EFI_SUCCESS)
  		return -EIO;
  
  	str += sprintf(str, "0x%lx
  ", var->DataSize);
  	return str - buf;
  }
  
  static ssize_t
  efivar_data_read(struct efivar_entry *entry, char *buf)
  {
  	struct efi_variable *var = &entry->var;
  	efi_status_t status;
  
  	if (!entry || !buf)
  		return -EINVAL;
4142ef146   Mike Waychison   efivars: paramete...
281
  	status = get_var_data(entry->efivars, var);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  	if (status != EFI_SUCCESS)
  		return -EIO;
  
  	memcpy(buf, var->Data, var->DataSize);
  	return var->DataSize;
  }
  /*
   * We allow each variable to be edited via rewriting the
   * entire efi variable structure.
   */
  static ssize_t
  efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
  {
  	struct efi_variable *new_var, *var = &entry->var;
4142ef146   Mike Waychison   efivars: paramete...
296
  	struct efivars *efivars = entry->efivars;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
  	efi_status_t status = EFI_NOT_FOUND;
  
  	if (count != sizeof(struct efi_variable))
  		return -EINVAL;
  
  	new_var = (struct efi_variable *)buf;
  	/*
  	 * If only updating the variable data, then the name
  	 * and guid should remain the same
  	 */
  	if (memcmp(new_var->VariableName, var->VariableName, sizeof(var->VariableName)) ||
  		efi_guidcmp(new_var->VendorGuid, var->VendorGuid)) {
  		printk(KERN_ERR "efivars: Cannot edit the wrong variable!
  ");
  		return -EINVAL;
  	}
  
  	if ((new_var->DataSize <= 0) || (new_var->Attributes == 0)){
  		printk(KERN_ERR "efivars: DataSize & Attributes must be valid!
  ");
  		return -EINVAL;
  	}
29422693c   Mike Waychison   efivars: move efi...
319
  	spin_lock(&efivars->lock);
3295814d8   Mike Waychison   efivars: Paramete...
320
321
322
323
324
  	status = efivars->ops->set_variable(new_var->VariableName,
  					    &new_var->VendorGuid,
  					    new_var->Attributes,
  					    new_var->DataSize,
  					    new_var->Data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325

29422693c   Mike Waychison   efivars: move efi...
326
  	spin_unlock(&efivars->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
  
  	if (status != EFI_SUCCESS) {
  		printk(KERN_WARNING "efivars: set_variable() failed: status=%lx
  ",
  			status);
  		return -EIO;
  	}
  
  	memcpy(&entry->var, new_var, count);
  	return count;
  }
  
  static ssize_t
  efivar_show_raw(struct efivar_entry *entry, char *buf)
  {
  	struct efi_variable *var = &entry->var;
  	efi_status_t status;
  
  	if (!entry || !buf)
  		return 0;
4142ef146   Mike Waychison   efivars: paramete...
347
  	status = get_var_data(entry->efivars, var);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
349
350
351
352
353
354
355
356
  	if (status != EFI_SUCCESS)
  		return -EIO;
  
  	memcpy(buf, var, sizeof(*var));
  	return sizeof(*var);
  }
  
  /*
   * Generic read/write functions that call the specific functions of
70f23fd66   Justin P. Mattock   treewide: fix a f...
357
   * the attributes...
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
361
362
363
   */
  static ssize_t efivar_attr_show(struct kobject *kobj, struct attribute *attr,
  				char *buf)
  {
  	struct efivar_entry *var = to_efivar_entry(kobj);
  	struct efivar_attribute *efivar_attr = to_efivar_attr(attr);
70f2817a4   Dmitry Torokhov   [PATCH] sysfs: (r...
364
  	ssize_t ret = -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
  
  	if (efivar_attr->show) {
  		ret = efivar_attr->show(var, buf);
  	}
  	return ret;
  }
  
  static ssize_t efivar_attr_store(struct kobject *kobj, struct attribute *attr,
  				const char *buf, size_t count)
  {
  	struct efivar_entry *var = to_efivar_entry(kobj);
  	struct efivar_attribute *efivar_attr = to_efivar_attr(attr);
70f2817a4   Dmitry Torokhov   [PATCH] sysfs: (r...
380
  	ssize_t ret = -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
383
384
385
386
387
388
389
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
  
  	if (efivar_attr->store)
  		ret = efivar_attr->store(var, buf, count);
  
  	return ret;
  }
52cf25d0a   Emese Revfy   Driver core: Cons...
390
  static const struct sysfs_ops efivar_attr_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
393
394
395
396
397
  	.show = efivar_attr_show,
  	.store = efivar_attr_store,
  };
  
  static void efivar_release(struct kobject *kobj)
  {
  	struct efivar_entry *var = container_of(kobj, struct efivar_entry, kobj);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
  	kfree(var);
  }
  
  static EFIVAR_ATTR(guid, 0400, efivar_guid_read, NULL);
  static EFIVAR_ATTR(attributes, 0400, efivar_attr_read, NULL);
  static EFIVAR_ATTR(size, 0400, efivar_size_read, NULL);
  static EFIVAR_ATTR(data, 0400, efivar_data_read, NULL);
  static EFIVAR_ATTR(raw_var, 0600, efivar_show_raw, efivar_store_raw);
  
  static struct attribute *def_attrs[] = {
  	&efivar_attr_guid.attr,
  	&efivar_attr_size.attr,
  	&efivar_attr_attributes.attr,
  	&efivar_attr_data.attr,
  	&efivar_attr_raw_var.attr,
  	NULL,
  };
e89a4116e   Greg Kroah-Hartman   Driver core: rena...
415
  static struct kobj_type efivar_ktype = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
418
419
  	.release = efivar_release,
  	.sysfs_ops = &efivar_attr_ops,
  	.default_attrs = def_attrs,
  };
5ee9c198a   Matthew Garrett   efi: Add support ...
420
  static struct pstore_info efi_pstore_info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
  static inline void
  efivar_unregister(struct efivar_entry *var)
  {
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
424
  	kobject_put(&var->kobj);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
  }
5ee9c198a   Matthew Garrett   efi: Add support ...
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
  #ifdef CONFIG_PSTORE
  
  static int efi_pstore_open(struct pstore_info *psi)
  {
  	struct efivars *efivars = psi->data;
  
  	spin_lock(&efivars->lock);
  	efivars->walk_entry = list_first_entry(&efivars->list,
  					       struct efivar_entry, list);
  	return 0;
  }
  
  static int efi_pstore_close(struct pstore_info *psi)
  {
  	struct efivars *efivars = psi->data;
  
  	spin_unlock(&efivars->lock);
  	return 0;
  }
  
  static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
  			       struct timespec *timespec, struct pstore_info *psi)
  {
  	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
  	struct efivars *efivars = psi->data;
  	char name[DUMP_NAME_LEN];
  	int i;
  	unsigned int part, size;
  	unsigned long time;
  
  	while (&efivars->walk_entry->list != &efivars->list) {
  		if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid,
  				 vendor)) {
  			for (i = 0; i < DUMP_NAME_LEN; i++) {
  				name[i] = efivars->walk_entry->var.VariableName[i];
  			}
  			if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) {
  				*id = part;
  				timespec->tv_sec = time;
  				timespec->tv_nsec = 0;
  				get_var_data_locked(efivars, &efivars->walk_entry->var);
  				size = efivars->walk_entry->var.DataSize;
  				memcpy(psi->buf, efivars->walk_entry->var.Data, size);
  				efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
  					           struct efivar_entry, list);
  				return size;
  			}
  		}
  		efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
  						 struct efivar_entry, list);
  	}
  	return 0;
  }
  
  static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
  			    size_t size, struct pstore_info *psi)
  {
  	char name[DUMP_NAME_LEN];
  	char stub_name[DUMP_NAME_LEN];
  	efi_char16_t efi_name[DUMP_NAME_LEN];
  	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
  	struct efivars *efivars = psi->data;
  	struct efivar_entry *entry, *found = NULL;
  	int i;
  
  	sprintf(stub_name, "dump-type%u-%u-", type, part);
  	sprintf(name, "%s%lu", stub_name, get_seconds());
  
  	spin_lock(&efivars->lock);
  
  	for (i = 0; i < DUMP_NAME_LEN; i++)
  		efi_name[i] = stub_name[i];
  
  	/*
  	 * Clean up any entries with the same name
  	 */
  
  	list_for_each_entry(entry, &efivars->list, list) {
  		get_var_data_locked(efivars, &entry->var);
c475594d8   Mike Waychison   efivars: Use stri...
505
506
507
508
509
510
511
512
513
514
515
  		if (efi_guidcmp(entry->var.VendorGuid, vendor))
  			continue;
  		if (utf16_strncmp(entry->var.VariableName, efi_name,
  				  utf16_strlen(efi_name)))
  			continue;
  		/* Needs to be a prefix */
  		if (entry->var.VariableName[utf16_strlen(efi_name)] == 0)
  			continue;
  
  		/* found */
  		found = entry;
7644c16c7   Mike Waychison   efivars: Introduc...
516
517
518
  		efivars->ops->set_variable(entry->var.VariableName,
  					   &entry->var.VendorGuid,
  					   PSTORE_EFI_ATTRIBUTES,
c475594d8   Mike Waychison   efivars: Use stri...
519
  					   0, NULL);
5ee9c198a   Matthew Garrett   efi: Add support ...
520
521
522
523
524
525
526
  	}
  
  	if (found)
  		list_del(&found->list);
  
  	for (i = 0; i < DUMP_NAME_LEN; i++)
  		efi_name[i] = name[i];
7644c16c7   Mike Waychison   efivars: Introduc...
527
  	efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
5ee9c198a   Matthew Garrett   efi: Add support ...
528
529
530
531
532
533
534
535
  				   size, psi->buf);
  
  	spin_unlock(&efivars->lock);
  
  	if (found)
  		efivar_unregister(found);
  
  	if (size)
a29409083   Mike Waychison   efivars: String f...
536
537
538
  		efivar_create_sysfs_entry(efivars,
  					  utf16_strsize(efi_name,
  							DUMP_NAME_LEN * 2),
5ee9c198a   Matthew Garrett   efi: Add support ...
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
  					  efi_name, &vendor);
  
  	return part;
  };
  
  static int efi_pstore_erase(enum pstore_type_id type, u64 id,
  			    struct pstore_info *psi)
  {
  	efi_pstore_write(type, id, 0, psi);
  
  	return 0;
  }
  #else
  static int efi_pstore_open(struct pstore_info *psi)
  {
  	return 0;
  }
  
  static int efi_pstore_close(struct pstore_info *psi)
  {
  	return 0;
  }
  
  static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
  			       struct timespec *time, struct pstore_info *psi)
  {
  	return -1;
  }
b728a5c80   Tony Luck   efivars: fix warn...
567
568
  static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
  			    size_t size, struct pstore_info *psi)
5ee9c198a   Matthew Garrett   efi: Add support ...
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
  {
  	return 0;
  }
  
  static int efi_pstore_erase(enum pstore_type_id type, u64 id,
  			    struct pstore_info *psi)
  {
  	return 0;
  }
  #endif
  
  static struct pstore_info efi_pstore_info = {
  	.owner		= THIS_MODULE,
  	.name		= "efi",
  	.open		= efi_pstore_open,
  	.close		= efi_pstore_close,
  	.read		= efi_pstore_read,
  	.write		= efi_pstore_write,
  	.erase		= efi_pstore_erase,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589

2c3c8bea6   Chris Wright   sysfs: add struct...
590
  static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
97fa5bb74   Greg Kroah-Hartman   efivars: make new...
591
592
  			     struct bin_attribute *bin_attr,
  			     char *buf, loff_t pos, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
594
  {
  	struct efi_variable *new_var = (struct efi_variable *)buf;
4142ef146   Mike Waychison   efivars: paramete...
595
  	struct efivars *efivars = bin_attr->private;
496a0fc8c   Matt Domsch   [PATCH] Fix race ...
596
  	struct efivar_entry *search_efivar, *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
  	unsigned long strsize1, strsize2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
600
601
602
  	efi_status_t status = EFI_NOT_FOUND;
  	int found = 0;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
29422693c   Mike Waychison   efivars: move efi...
603
  	spin_lock(&efivars->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
606
607
  
  	/*
  	 * Does this variable already exist?
  	 */
29422693c   Mike Waychison   efivars: move efi...
608
  	list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
a29409083   Mike Waychison   efivars: String f...
609
610
  		strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
  		strsize2 = utf16_strsize(new_var->VariableName, 1024);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
613
614
615
616
617
618
619
620
  		if (strsize1 == strsize2 &&
  			!memcmp(&(search_efivar->var.VariableName),
  				new_var->VariableName, strsize1) &&
  			!efi_guidcmp(search_efivar->var.VendorGuid,
  				new_var->VendorGuid)) {
  			found = 1;
  			break;
  		}
  	}
  	if (found) {
29422693c   Mike Waychison   efivars: move efi...
621
  		spin_unlock(&efivars->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
623
624
625
  		return -EINVAL;
  	}
  
  	/* now *really* create the variable via EFI */
3295814d8   Mike Waychison   efivars: Paramete...
626
627
628
629
630
  	status = efivars->ops->set_variable(new_var->VariableName,
  					    &new_var->VendorGuid,
  					    new_var->Attributes,
  					    new_var->DataSize,
  					    new_var->Data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
633
634
635
  
  	if (status != EFI_SUCCESS) {
  		printk(KERN_WARNING "efivars: set_variable() failed: status=%lx
  ",
  			status);
29422693c   Mike Waychison   efivars: move efi...
636
  		spin_unlock(&efivars->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
638
  		return -EIO;
  	}
29422693c   Mike Waychison   efivars: move efi...
639
  	spin_unlock(&efivars->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
  
  	/* Create the entry in sysfs.  Locking is not required here */
4142ef146   Mike Waychison   efivars: paramete...
642
  	status = efivar_create_sysfs_entry(efivars,
a29409083   Mike Waychison   efivars: String f...
643
644
  					   utf16_strsize(new_var->VariableName,
  							 1024),
4142ef146   Mike Waychison   efivars: paramete...
645
646
  					   new_var->VariableName,
  					   &new_var->VendorGuid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
648
649
650
651
652
  	if (status) {
  		printk(KERN_WARNING "efivars: variable created, but sysfs entry wasn't.
  ");
  	}
  	return count;
  }
2c3c8bea6   Chris Wright   sysfs: add struct...
653
  static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
97fa5bb74   Greg Kroah-Hartman   efivars: make new...
654
655
  			     struct bin_attribute *bin_attr,
  			     char *buf, loff_t pos, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
  {
  	struct efi_variable *del_var = (struct efi_variable *)buf;
4142ef146   Mike Waychison   efivars: paramete...
658
  	struct efivars *efivars = bin_attr->private;
496a0fc8c   Matt Domsch   [PATCH] Fix race ...
659
  	struct efivar_entry *search_efivar, *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
  	unsigned long strsize1, strsize2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
661
662
663
664
665
  	efi_status_t status = EFI_NOT_FOUND;
  	int found = 0;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
29422693c   Mike Waychison   efivars: move efi...
666
  	spin_lock(&efivars->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
668
669
670
  
  	/*
  	 * Does this variable already exist?
  	 */
29422693c   Mike Waychison   efivars: move efi...
671
  	list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
a29409083   Mike Waychison   efivars: String f...
672
673
  		strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
  		strsize2 = utf16_strsize(del_var->VariableName, 1024);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
676
677
678
679
680
681
682
683
  		if (strsize1 == strsize2 &&
  			!memcmp(&(search_efivar->var.VariableName),
  				del_var->VariableName, strsize1) &&
  			!efi_guidcmp(search_efivar->var.VendorGuid,
  				del_var->VendorGuid)) {
  			found = 1;
  			break;
  		}
  	}
  	if (!found) {
29422693c   Mike Waychison   efivars: move efi...
684
  		spin_unlock(&efivars->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
686
687
688
689
  		return -EINVAL;
  	}
  	/* force the Attributes/DataSize to 0 to ensure deletion */
  	del_var->Attributes = 0;
  	del_var->DataSize = 0;
3295814d8   Mike Waychison   efivars: Paramete...
690
691
692
693
694
  	status = efivars->ops->set_variable(del_var->VariableName,
  					    &del_var->VendorGuid,
  					    del_var->Attributes,
  					    del_var->DataSize,
  					    del_var->Data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
695
696
697
698
699
  
  	if (status != EFI_SUCCESS) {
  		printk(KERN_WARNING "efivars: set_variable() failed: status=%lx
  ",
  			status);
29422693c   Mike Waychison   efivars: move efi...
700
  		spin_unlock(&efivars->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
  		return -EIO;
  	}
496a0fc8c   Matt Domsch   [PATCH] Fix race ...
703
  	list_del(&search_efivar->list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
  	/* We need to release this lock before unregistering. */
29422693c   Mike Waychison   efivars: move efi...
705
  	spin_unlock(&efivars->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706
707
708
709
710
  	efivar_unregister(search_efivar);
  
  	/* It's dead Jim.... */
  	return count;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
712
713
714
  /*
   * Let's not leave out systab information that snuck into
   * the efivars driver
   */
334c63075   Greg Kroah-Hartman   kobject: convert ...
715
716
  static ssize_t systab_show(struct kobject *kobj,
  			   struct kobj_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
718
  {
  	char *str = buf;
334c63075   Greg Kroah-Hartman   kobject: convert ...
719
  	if (!kobj || !buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720
  		return -EINVAL;
b2c99e3c7   Bjorn Helgaas   [PATCH] EFI: keep...
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
  	if (efi.mps != EFI_INVALID_TABLE_ADDR)
  		str += sprintf(str, "MPS=0x%lx
  ", efi.mps);
  	if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
  		str += sprintf(str, "ACPI20=0x%lx
  ", efi.acpi20);
  	if (efi.acpi != EFI_INVALID_TABLE_ADDR)
  		str += sprintf(str, "ACPI=0x%lx
  ", efi.acpi);
  	if (efi.smbios != EFI_INVALID_TABLE_ADDR)
  		str += sprintf(str, "SMBIOS=0x%lx
  ", efi.smbios);
  	if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
  		str += sprintf(str, "HCDP=0x%lx
  ", efi.hcdp);
  	if (efi.boot_info != EFI_INVALID_TABLE_ADDR)
  		str += sprintf(str, "BOOTINFO=0x%lx
  ", efi.boot_info);
  	if (efi.uga != EFI_INVALID_TABLE_ADDR)
  		str += sprintf(str, "UGA=0x%lx
  ", efi.uga);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
743
744
  
  	return str - buf;
  }
334c63075   Greg Kroah-Hartman   kobject: convert ...
745
746
  static struct kobj_attribute efi_attr_systab =
  			__ATTR(systab, 0400, systab_show, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747

334c63075   Greg Kroah-Hartman   kobject: convert ...
748
749
  static struct attribute *efi_subsys_attrs[] = {
  	&efi_attr_systab.attr,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
751
  	NULL,	/* maybe more in the future? */
  };
334c63075   Greg Kroah-Hartman   kobject: convert ...
752
753
754
  static struct attribute_group efi_subsys_attr_group = {
  	.attrs = efi_subsys_attrs,
  };
bc87d2fe7   Greg Kroah-Hartman   kobject: convert ...
755
  static struct kobject *efi_kobj;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
757
758
759
760
761
762
  
  /*
   * efivar_create_sysfs_entry()
   * Requires:
   *    variable_name_size = number of bytes required to hold
   *                         variable_name (not counting the NULL
   *                         character at the end.
29422693c   Mike Waychison   efivars: move efi...
763
   *    efivars->lock is not held on entry or exit.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
764
765
766
   * Returns 1 on failure, 0 on success
   */
  static int
4142ef146   Mike Waychison   efivars: paramete...
767
768
769
770
  efivar_create_sysfs_entry(struct efivars *efivars,
  			  unsigned long variable_name_size,
  			  efi_char16_t *variable_name,
  			  efi_guid_t *vendor_guid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
771
772
773
774
  {
  	int i, short_name_size = variable_name_size / sizeof(efi_char16_t) + 38;
  	char *short_name;
  	struct efivar_entry *new_efivar;
9c2153844   Deepak Saxena   [PATCH] drivers/f...
775
776
  	short_name = kzalloc(short_name_size + 1, GFP_KERNEL);
  	new_efivar = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
777
778
  
  	if (!short_name || !new_efivar)  {
0933ad9c2   Jesper Juhl   [PATCH] kfree cle...
779
780
  		kfree(short_name);
  		kfree(new_efivar);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
782
  		return 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783

4142ef146   Mike Waychison   efivars: paramete...
784
  	new_efivar->efivars = efivars;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785
786
787
788
789
790
791
792
793
794
795
796
797
798
  	memcpy(new_efivar->var.VariableName, variable_name,
  		variable_name_size);
  	memcpy(&(new_efivar->var.VendorGuid), vendor_guid, sizeof(efi_guid_t));
  
  	/* Convert Unicode to normal chars (assume top bits are 0),
  	   ala UTF-8 */
  	for (i=0; i < (int)(variable_name_size / sizeof(efi_char16_t)); i++) {
  		short_name[i] = variable_name[i] & 0xFF;
  	}
  	/* This is ugly, but necessary to separate one vendor's
  	   private variables from another's.         */
  
  	*(short_name + strlen(short_name)) = '-';
  	efi_guid_unparse(vendor_guid, short_name + strlen(short_name));
29422693c   Mike Waychison   efivars: move efi...
799
  	new_efivar->kobj.kset = efivars->kset;
d6d292c45   Greg Kroah-Hartman   Kobject: change d...
800
801
  	i = kobject_init_and_add(&new_efivar->kobj, &efivar_ktype, NULL,
  				 "%s", short_name);
69b2186c5   Jeff Garzik   [PATCH] firmware/...
802
803
804
805
806
  	if (i) {
  		kfree(short_name);
  		kfree(new_efivar);
  		return 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807

d6d292c45   Greg Kroah-Hartman   Kobject: change d...
808
  	kobject_uevent(&new_efivar->kobj, KOBJ_ADD);
0933ad9c2   Jesper Juhl   [PATCH] kfree cle...
809
810
  	kfree(short_name);
  	short_name = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811

29422693c   Mike Waychison   efivars: move efi...
812
813
814
  	spin_lock(&efivars->lock);
  	list_add(&new_efivar->list, &efivars->list);
  	spin_unlock(&efivars->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
815
816
817
  
  	return 0;
  }
d502fbb0d   Mike Waychison   efivars: Make efi...
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
  
  static int
  create_efivars_bin_attributes(struct efivars *efivars)
  {
  	struct bin_attribute *attr;
  	int error;
  
  	/* new_var */
  	attr = kzalloc(sizeof(*attr), GFP_KERNEL);
  	if (!attr)
  		return -ENOMEM;
  
  	attr->attr.name = "new_var";
  	attr->attr.mode = 0200;
  	attr->write = efivar_create;
  	attr->private = efivars;
  	efivars->new_var = attr;
  
  	/* del_var */
  	attr = kzalloc(sizeof(*attr), GFP_KERNEL);
  	if (!attr) {
  		error = -ENOMEM;
  		goto out_free;
  	}
  	attr->attr.name = "del_var";
  	attr->attr.mode = 0200;
  	attr->write = efivar_delete;
  	attr->private = efivars;
  	efivars->del_var = attr;
  
  	sysfs_bin_attr_init(efivars->new_var);
  	sysfs_bin_attr_init(efivars->del_var);
  
  	/* Register */
  	error = sysfs_create_bin_file(&efivars->kset->kobj,
  				      efivars->new_var);
  	if (error) {
  		printk(KERN_ERR "efivars: unable to create new_var sysfs file"
  			" due to error %d
  ", error);
  		goto out_free;
  	}
  	error = sysfs_create_bin_file(&efivars->kset->kobj,
  				      efivars->del_var);
  	if (error) {
  		printk(KERN_ERR "efivars: unable to create del_var sysfs file"
  			" due to error %d
  ", error);
  		sysfs_remove_bin_file(&efivars->kset->kobj,
  				      efivars->new_var);
  		goto out_free;
  	}
  
  	return 0;
  out_free:
051d51bc6   Dan Carpenter   efivars: memory l...
873
874
  	kfree(efivars->del_var);
  	efivars->del_var = NULL;
d502fbb0d   Mike Waychison   efivars: Make efi...
875
876
877
878
  	kfree(efivars->new_var);
  	efivars->new_var = NULL;
  	return error;
  }
4fc756bd9   Mike Waychison   efivars: Expose e...
879
  void unregister_efivars(struct efivars *efivars)
76b53f7c8   Mike Waychison   efivars: Split ou...
880
881
  {
  	struct efivar_entry *entry, *n;
4142ef146   Mike Waychison   efivars: paramete...
882

76b53f7c8   Mike Waychison   efivars: Split ou...
883
884
885
886
887
888
889
890
891
892
893
894
895
896
  	list_for_each_entry_safe(entry, n, &efivars->list, list) {
  		spin_lock(&efivars->lock);
  		list_del(&entry->list);
  		spin_unlock(&efivars->lock);
  		efivar_unregister(entry);
  	}
  	if (efivars->new_var)
  		sysfs_remove_bin_file(&efivars->kset->kobj, efivars->new_var);
  	if (efivars->del_var)
  		sysfs_remove_bin_file(&efivars->kset->kobj, efivars->del_var);
  	kfree(efivars->new_var);
  	kfree(efivars->del_var);
  	kset_unregister(efivars->kset);
  }
4fc756bd9   Mike Waychison   efivars: Expose e...
897
  EXPORT_SYMBOL_GPL(unregister_efivars);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
898

4fc756bd9   Mike Waychison   efivars: Expose e...
899
900
901
  int register_efivars(struct efivars *efivars,
  		     const struct efivar_operations *ops,
  		     struct kobject *parent_kobj)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902
903
904
905
  {
  	efi_status_t status = EFI_NOT_FOUND;
  	efi_guid_t vendor_guid;
  	efi_char16_t *variable_name;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
906
  	unsigned long variable_name_size = 1024;
334c63075   Greg Kroah-Hartman   kobject: convert ...
907
  	int error = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908

9c2153844   Deepak Saxena   [PATCH] drivers/f...
909
  	variable_name = kzalloc(variable_name_size, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
910
911
912
913
914
  	if (!variable_name) {
  		printk(KERN_ERR "efivars: Memory allocation failed.
  ");
  		return -ENOMEM;
  	}
29422693c   Mike Waychison   efivars: move efi...
915
916
  	spin_lock_init(&efivars->lock);
  	INIT_LIST_HEAD(&efivars->list);
3295814d8   Mike Waychison   efivars: Paramete...
917
  	efivars->ops = ops;
29422693c   Mike Waychison   efivars: move efi...
918

76b53f7c8   Mike Waychison   efivars: Split ou...
919
  	efivars->kset = kset_create_and_add("vars", NULL, parent_kobj);
29422693c   Mike Waychison   efivars: move efi...
920
  	if (!efivars->kset) {
66ac831e0   Greg Kroah-Hartman   kset: convert efi...
921
922
923
  		printk(KERN_ERR "efivars: Subsystem registration failed.
  ");
  		error = -ENOMEM;
76b53f7c8   Mike Waychison   efivars: Split ou...
924
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925
926
927
928
929
930
931
932
933
  	}
  
  	/*
  	 * Per EFI spec, the maximum storage allocated for both
  	 * the variable name and variable data is 1024 bytes.
  	 */
  
  	do {
  		variable_name_size = 1024;
3295814d8   Mike Waychison   efivars: Paramete...
934
  		status = ops->get_next_variable(&variable_name_size,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
935
936
937
938
  						variable_name,
  						&vendor_guid);
  		switch (status) {
  		case EFI_SUCCESS:
4142ef146   Mike Waychison   efivars: paramete...
939
940
941
942
  			efivar_create_sysfs_entry(efivars,
  						  variable_name_size,
  						  variable_name,
  						  &vendor_guid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
943
944
945
946
947
948
949
950
951
952
953
  			break;
  		case EFI_NOT_FOUND:
  			break;
  		default:
  			printk(KERN_WARNING "efivars: get_next_variable: status=%lx
  ",
  				status);
  			status = EFI_NOT_FOUND;
  			break;
  		}
  	} while (status != EFI_NOT_FOUND);
d502fbb0d   Mike Waychison   efivars: Make efi...
954
  	error = create_efivars_bin_attributes(efivars);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
  	if (error)
76b53f7c8   Mike Waychison   efivars: Split ou...
956
  		unregister_efivars(efivars);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
957

5ee9c198a   Matthew Garrett   efi: Add support ...
958
959
960
961
962
963
964
965
966
  	efivars->efi_pstore_info = efi_pstore_info;
  
  	efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
  	if (efivars->efi_pstore_info.buf) {
  		efivars->efi_pstore_info.bufsize = 1024;
  		efivars->efi_pstore_info.data = efivars;
  		mutex_init(&efivars->efi_pstore_info.buf_mutex);
  		pstore_register(&efivars->efi_pstore_info);
  	}
76b53f7c8   Mike Waychison   efivars: Split ou...
967
968
  out:
  	kfree(variable_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969

76b53f7c8   Mike Waychison   efivars: Split ou...
970
971
  	return error;
  }
4fc756bd9   Mike Waychison   efivars: Expose e...
972
  EXPORT_SYMBOL_GPL(register_efivars);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
973

76b53f7c8   Mike Waychison   efivars: Split ou...
974
  static struct efivars __efivars;
3295814d8   Mike Waychison   efivars: Paramete...
975
  static struct efivar_operations ops;
76b53f7c8   Mike Waychison   efivars: Split ou...
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
  
  /*
   * For now we register the efi subsystem with the firmware subsystem
   * and the vars subsystem with the efi subsystem.  In the future, it
   * might make sense to split off the efi subsystem into its own
   * driver, but for now only efivars will register with it, so just
   * include it here.
   */
  
  static int __init
  efivars_init(void)
  {
  	int error = 0;
  
  	printk(KERN_INFO "EFI Variables Facility v%s %s
  ", EFIVARS_VERSION,
  	       EFIVARS_DATE);
  
  	if (!efi_enabled)
4fc756bd9   Mike Waychison   efivars: Expose e...
995
  		return 0;
76b53f7c8   Mike Waychison   efivars: Split ou...
996
997
998
999
1000
1001
1002
1003
  
  	/* For now we'll register the efi directory at /sys/firmware/efi */
  	efi_kobj = kobject_create_and_add("efi", firmware_kobj);
  	if (!efi_kobj) {
  		printk(KERN_ERR "efivars: Firmware registration failed.
  ");
  		return -ENOMEM;
  	}
3295814d8   Mike Waychison   efivars: Paramete...
1004
1005
1006
1007
  	ops.get_variable = efi.get_variable;
  	ops.set_variable = efi.set_variable;
  	ops.get_next_variable = efi.get_next_variable;
  	error = register_efivars(&__efivars, &ops, efi_kobj);
3116aabc8   Dan Carpenter   efivars: handle e...
1008
1009
  	if (error)
  		goto err_put;
76b53f7c8   Mike Waychison   efivars: Split ou...
1010
1011
1012
1013
1014
1015
1016
1017
  
  	/* Don't forget the systab entry */
  	error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
  	if (error) {
  		printk(KERN_ERR
  		       "efivars: Sysfs attribute export failed with error %d.
  ",
  		       error);
3116aabc8   Dan Carpenter   efivars: handle e...
1018
  		goto err_unregister;
76b53f7c8   Mike Waychison   efivars: Split ou...
1019
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1020

3116aabc8   Dan Carpenter   efivars: handle e...
1021
1022
1023
1024
1025
1026
  	return 0;
  
  err_unregister:
  	unregister_efivars(&__efivars);
  err_put:
  	kobject_put(efi_kobj);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1027
1028
1029
1030
1031
1032
  	return error;
  }
  
  static void __exit
  efivars_exit(void)
  {
aabb6e153   Randy Dunlap   efivars: prevent ...
1033
1034
1035
1036
  	if (efi_enabled) {
  		unregister_efivars(&__efivars);
  		kobject_put(efi_kobj);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1037
1038
1039
1040
  }
  
  module_init(efivars_init);
  module_exit(efivars_exit);