OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [arch/] [ia64/] [kernel/] [efivars.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 * EFI Variables - efivars.c
3
 *
4
 * Copyright (C) 2001 Dell Computer Corporation <Matt_Domsch@dell.com>
5
 *
6
 * This code takes all variables accessible from EFI runtime and
7
 *  exports them via /proc
8
 *
9
 * Reads to /proc/efi/vars/varname return an efi_variable_t structure.
10
 * Writes to /proc/efi/vars/varname must be an efi_variable_t structure.
11
 * Writes with DataSize = 0 or Attributes = 0 deletes the variable.
12
 * Writes with a new value in VariableName+VendorGuid creates
13
 * a new variable.
14
 *
15
 *
16
 *  This program is free software; you can redistribute it and/or modify
17
 *  it under the terms of the GNU General Public License as published by
18
 *  the Free Software Foundation; either version 2 of the License, or
19
 *  (at your option) any later version.
20
 *
21
 *  This program is distributed in the hope that it will be useful,
22
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 *  GNU General Public License for more details.
25
 *
26
 *  You should have received a copy of the GNU General Public License
27
 *  along with this program; if not, write to the Free Software
28
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29
 *
30
 * Changelog:
31
 *
32
 *  10 Dec 2002 - Matt Domsch <Matt_Domsch@dell.com>
33
 *   fix locking per Peter Chubb's findings
34
 *
35
 *  25 Mar 2002 - Matt Domsch <Matt_Domsch@dell.com>
36
 *   move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse()
37
 *
38
 *  12 Feb 2002 - Matt Domsch <Matt_Domsch@dell.com>
39
 *   use list_for_each_safe when deleting vars.
40
 *   remove ifdef CONFIG_SMP around include <linux/smp.h>
41
 *   v0.04 release to linux-ia64@linuxia64.org
42
 *
43
 *  20 April 2001 - Matt Domsch <Matt_Domsch@dell.com>
44
 *   Moved vars from /proc/efi to /proc/efi/vars, and made
45
 *   efi.c own the /proc/efi directory.
46
 *   v0.03 release to linux-ia64@linuxia64.org
47
 *
48
 *  26 March 2001 - Matt Domsch <Matt_Domsch@dell.com>
49
 *   At the request of Stephane, moved ownership of /proc/efi
50
 *   to efi.c, and now efivars lives under /proc/efi/vars.
51
 *
52
 *  12 March 2001 - Matt Domsch <Matt_Domsch@dell.com>
53
 *   Feedback received from Stephane Eranian incorporated.
54
 *   efivar_write() checks copy_from_user() return value.
55
 *   efivar_read/write() returns proper errno.
56
 *   v0.02 release to linux-ia64@linuxia64.org
57
 *
58
 *  26 February 2001 - Matt Domsch <Matt_Domsch@dell.com>
59
 *   v0.01 release to linux-ia64@linuxia64.org
60
 */
61
 
62
#include <linux/config.h>
63
#include <linux/types.h>
64
#include <linux/errno.h>
65
#include <linux/init.h>
66
#include <linux/proc_fs.h>
67
#include <linux/sched.h>                /* for capable() */
68
#include <linux/mm.h>
69
#include <linux/module.h>
70
#include <linux/smp.h>
71
#include <linux/efi.h>
72
 
73
#include <asm/uaccess.h>
74
 
75
MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>");
76
MODULE_DESCRIPTION("/proc interface to EFI Variables");
77
MODULE_LICENSE("GPL");
78
 
79
#define EFIVARS_VERSION "0.06 2002-Dec-10"
80
 
81
static int
82
efivar_read(char *page, char **start, off_t off,
83
            int count, int *eof, void *data);
84
static int
85
efivar_write(struct file *file, const char *buffer,
86
             unsigned long count, void *data);
87
 
88
 
89
/*
90
 * The maximum size of VariableName + Data = 1024
91
 * Therefore, it's reasonable to save that much
92
 * space in each part of the structure,
93
 * and we use a page for reading/writing.
94
 */
95
 
96
typedef struct _efi_variable_t {
97
        efi_char16_t  VariableName[1024/sizeof(efi_char16_t)];
98
        efi_guid_t    VendorGuid;
99
        unsigned long DataSize;
100
        __u8          Data[1024];
101
        efi_status_t  Status;
102
        __u32         Attributes;
103
} __attribute__((packed)) efi_variable_t;
104
 
105
 
106
typedef struct _efivar_entry_t {
107
        efi_variable_t          var;
108
        struct proc_dir_entry   *entry;
109
        struct list_head        list;
110
} efivar_entry_t;
111
 
112
/*
113
  efivars_lock protects two things:
114
  1) efivar_list - adds, removals, reads, writes
115
  2) efi.[gs]et_variable() calls.
116
  It must not be held when creating proc entries or calling kmalloc.
117
  efi.get_next_variable() is only called from efivars_init(),
118
  which is protected by the BKL, so that path is safe.
119
*/
120
static spinlock_t efivars_lock = SPIN_LOCK_UNLOCKED;
121
static LIST_HEAD(efivar_list);
122
static struct proc_dir_entry *efi_vars_dir;
123
 
124
#define efivar_entry(n) list_entry(n, efivar_entry_t, list)
125
 
126
/* Return the number of unicode characters in data */
127
static unsigned long
128
utf8_strlen(efi_char16_t *data, unsigned long maxlength)
129
{
130
        unsigned long length = 0;
131
        while (*data++ != 0 && length < maxlength)
132
                length++;
133
        return length;
134
}
135
 
136
/* Return the number of bytes is the length of this string */
137
/* Note: this is NOT the same as the number of unicode characters */
138
static inline unsigned long
139
utf8_strsize(efi_char16_t *data, unsigned long maxlength)
140
{
141
        return utf8_strlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
142
}
143
 
144
 
145
static int
146
proc_calc_metrics(char *page, char **start, off_t off,
147
                  int count, int *eof, int len)
148
{
149
        if (len <= off+count) *eof = 1;
150
        *start = page + off;
151
        len -= off;
152
        if (len>count) len = count;
153
        if (len<0) len = 0;
154
        return len;
155
}
156
 
157
/*
158
 * efivar_create_proc_entry()
159
 * Requires:
160
 *    variable_name_size = number of bytes required to hold
161
 *                         variable_name (not counting the NULL
162
 *                         character at the end.
163
 *    efivars_lock is not held on entry or exit.
164
 * Returns 1 on failure, 0 on success
165
 */
166
static int
167
efivar_create_proc_entry(unsigned long variable_name_size,
168
                         efi_char16_t *variable_name,
169
                         efi_guid_t *vendor_guid)
170
{
171
        int i, short_name_size = variable_name_size / sizeof(efi_char16_t) + 38;
172
        char *short_name;
173
        efivar_entry_t *new_efivar;
174
 
175
        short_name = kmalloc(short_name_size+1, GFP_KERNEL);
176
        new_efivar = kmalloc(sizeof(efivar_entry_t), GFP_KERNEL);
177
 
178
        if (!short_name || !new_efivar)  {
179
                if (short_name)        kfree(short_name);
180
                if (new_efivar)        kfree(new_efivar);
181
                return 1;
182
        }
183
        memset(short_name, 0, short_name_size+1);
184
        memset(new_efivar, 0, sizeof(efivar_entry_t));
185
 
186
        memcpy(new_efivar->var.VariableName, variable_name,
187
               variable_name_size);
188
        memcpy(&(new_efivar->var.VendorGuid), vendor_guid, sizeof(efi_guid_t));
189
 
190
        /* Convert Unicode to normal chars (assume top bits are 0),
191
           ala UTF-8 */
192
        for (i=0; i< (int) (variable_name_size / sizeof(efi_char16_t)); i++) {
193
                short_name[i] = variable_name[i] & 0xFF;
194
        }
195
 
196
        /* This is ugly, but necessary to separate one vendor's
197
           private variables from another's.         */
198
 
199
        *(short_name + strlen(short_name)) = '-';
200
        efi_guid_unparse(vendor_guid, short_name + strlen(short_name));
201
 
202
        /* Create the entry in proc */
203
        new_efivar->entry = create_proc_entry(short_name, 0600, efi_vars_dir);
204
        kfree(short_name); short_name = NULL;
205
        if (!new_efivar->entry) return 1;
206
 
207
        new_efivar->entry->data = new_efivar;
208
        new_efivar->entry->read_proc = efivar_read;
209
        new_efivar->entry->write_proc = efivar_write;
210
 
211
        spin_lock(&efivars_lock);
212
        list_add(&new_efivar->list, &efivar_list);
213
        spin_unlock(&efivars_lock);
214
 
215
        return 0;
216
}
217
 
218
 
219
 
220
/***********************************************************
221
 * efivar_read()
222
 * Requires:
223
 * Modifies: page
224
 * Returns: number of bytes written, or -EINVAL on failure
225
 ***********************************************************/
226
 
227
static int
228
efivar_read(char *page, char **start, off_t off, int count, int *eof, void *data)
229
{
230
        int len = sizeof(efi_variable_t);
231
        efivar_entry_t *efi_var = data;
232
        efi_variable_t *var_data = (efi_variable_t *)page;
233
 
234
        if (!page || !data) return -EINVAL;
235
 
236
        spin_lock(&efivars_lock);
237
        MOD_INC_USE_COUNT;
238
 
239
        memcpy(var_data, &efi_var->var, len);
240
 
241
        var_data->DataSize = 1024;
242
        var_data->Status = efi.get_variable(var_data->VariableName,
243
                                            &var_data->VendorGuid,
244
                                            &var_data->Attributes,
245
                                            &var_data->DataSize,
246
                                            var_data->Data);
247
 
248
        MOD_DEC_USE_COUNT;
249
        spin_unlock(&efivars_lock);
250
 
251
        return proc_calc_metrics(page, start, off, count, eof, len);
252
}
253
 
254
/***********************************************************
255
 * efivar_write()
256
 * Requires: data is an efi_setvariable_t data type,
257
 *           properly filled in, possibly by a call
258
 *           first to efivar_read().
259
 *           Caller must have CAP_SYS_ADMIN
260
 * Modifies: NVRAM
261
 * Returns: var_data->DataSize on success, errno on failure
262
 *
263
 ***********************************************************/
264
static int
265
efivar_write(struct file *file, const char *buffer,
266
             unsigned long count, void *data)
267
{
268
        unsigned long strsize1, strsize2;
269
        int found=0;
270
        struct list_head *pos, *n;
271
        unsigned long size = sizeof(efi_variable_t);
272
        efi_status_t status;
273
        efivar_entry_t *efivar = data, *search_efivar = NULL;
274
        efi_variable_t *var_data;
275
        if (!data || count != size) {
276
                printk(KERN_WARNING "efivars: improper struct of size 0x%lx passed.\n", count);
277
                return -EINVAL;
278
        }
279
        if (!capable(CAP_SYS_ADMIN))
280
                return -EACCES;
281
 
282
        MOD_INC_USE_COUNT;
283
 
284
        var_data = kmalloc(size, GFP_KERNEL);
285
        if (!var_data) {
286
                MOD_DEC_USE_COUNT;
287
                return -ENOMEM;
288
        }
289
        if (copy_from_user(var_data, buffer, size)) {
290
                MOD_DEC_USE_COUNT;
291
                kfree(var_data);
292
                return -EFAULT;
293
        }
294
 
295
        spin_lock(&efivars_lock);
296
 
297
        /* Since the data ptr we've currently got is probably for
298
           a different variable find the right variable.
299
           This allows any properly formatted data structure to
300
           be written to any of the files in /proc/efi/vars and it will work.
301
        */
302
        list_for_each_safe(pos, n, &efivar_list) {
303
                search_efivar = efivar_entry(pos);
304
                strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
305
                strsize2 = utf8_strsize(var_data->VariableName, 1024);
306
                if ( strsize1 == strsize2 &&
307
                     !memcmp(&(search_efivar->var.VariableName),
308
                             var_data->VariableName, strsize1) &&
309
                     !efi_guidcmp(search_efivar->var.VendorGuid,
310
                                  var_data->VendorGuid)) {
311
                        found = 1;
312
                        break;
313
                }
314
        }
315
        if (found) efivar = search_efivar;
316
 
317
        status = efi.set_variable(var_data->VariableName,
318
                                  &var_data->VendorGuid,
319
                                  var_data->Attributes,
320
                                  var_data->DataSize,
321
                                  var_data->Data);
322
 
323
        if (status != EFI_SUCCESS) {
324
                printk(KERN_WARNING "set_variable() failed: status=%lx\n", status);
325
                kfree(var_data);
326
                MOD_DEC_USE_COUNT;
327
                spin_unlock(&efivars_lock);
328
                return -EIO;
329
        }
330
 
331
 
332
        if (!var_data->DataSize || !var_data->Attributes) {
333
                /* We just deleted the NVRAM variable */
334
                remove_proc_entry(efivar->entry->name, efi_vars_dir);
335
                list_del(&efivar->list);
336
                kfree(efivar);
337
        }
338
 
339
        spin_unlock(&efivars_lock);
340
 
341
        /* If this is a new variable, set up the proc entry for it. */
342
        if (!found) {
343
                efivar_create_proc_entry(utf8_strsize(var_data->VariableName,
344
                                                      1024),
345
                                         var_data->VariableName,
346
                                         &var_data->VendorGuid);
347
        }
348
 
349
        kfree(var_data);
350
        MOD_DEC_USE_COUNT;
351
        return size;
352
}
353
 
354
/*
355
 * The EFI system table contains pointers to the SAL system table,
356
 * HCDP, ACPI, SMBIOS, etc, that may be useful to applications.
357
 */
358
static ssize_t
359
efi_systab_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
360
{
361
        void *data;
362
        u8 *proc_buffer;
363
        ssize_t size, length;
364
        int ret;
365
        const int max_nr_entries = 7;   /* num ptrs to tables we could expose */
366
        const int max_line_len = 80;
367
 
368
        if (!efi.systab)
369
                return 0;
370
 
371
        proc_buffer = kmalloc(max_nr_entries * max_line_len, GFP_KERNEL);
372
        if (!proc_buffer)
373
                return -ENOMEM;
374
 
375
        length = 0;
376
        if (efi.mps)
377
                length += sprintf(proc_buffer + length, "MPS=0x%lx\n", __pa(efi.mps));
378
        if (efi.acpi20)
379
                length += sprintf(proc_buffer + length, "ACPI20=0x%lx\n", __pa(efi.acpi20));
380
        if (efi.acpi)
381
                length += sprintf(proc_buffer + length, "ACPI=0x%lx\n", __pa(efi.acpi));
382
        if (efi.smbios)
383
                length += sprintf(proc_buffer + length, "SMBIOS=0x%lx\n", __pa(efi.smbios));
384
        if (efi.sal_systab)
385
                length += sprintf(proc_buffer + length, "SAL=0x%lx\n", __pa(efi.sal_systab));
386
        if (efi.hcdp)
387
                length += sprintf(proc_buffer + length, "HCDP=0x%lx\n", __pa(efi.hcdp));
388
        if (efi.boot_info)
389
                length += sprintf(proc_buffer + length, "BOOTINFO=0x%lx\n", __pa(efi.boot_info));
390
 
391
        if (*ppos >= length) {
392
                ret = 0;
393
                goto out;
394
        }
395
 
396
        data = proc_buffer + file->f_pos;
397
        size = length - file->f_pos;
398
        if (size > count)
399
                size = count;
400
        if (copy_to_user(buffer, data, size)) {
401
                ret = -EFAULT;
402
                goto out;
403
        }
404
 
405
        *ppos += size;
406
        ret = size;
407
 
408
out:
409
        kfree(proc_buffer);
410
        return ret;
411
}
412
 
413
static struct proc_dir_entry *efi_systab_entry;
414
static struct file_operations efi_systab_fops = {
415
        .read = efi_systab_read,
416
};
417
 
418
static int __init
419
efivars_init(void)
420
{
421
        efi_status_t status;
422
        efi_guid_t vendor_guid;
423
        efi_char16_t *variable_name = kmalloc(1024, GFP_KERNEL);
424
        unsigned long variable_name_size = 1024;
425
 
426
        printk(KERN_INFO "EFI Variables Facility v%s\n", EFIVARS_VERSION);
427
 
428
        /* Since efi.c happens before procfs is available,
429
           we create the directory here if it doesn't
430
           already exist.  There's probably a better way
431
           to do this.
432
        */
433
        if (!efi_dir)
434
                efi_dir = proc_mkdir("efi", NULL);
435
 
436
        efi_systab_entry = create_proc_entry("systab", S_IRUSR | S_IRGRP, efi_dir);
437
        if (efi_systab_entry)
438
                efi_systab_entry->proc_fops = &efi_systab_fops;
439
 
440
        efi_vars_dir = proc_mkdir("vars", efi_dir);
441
 
442
        /* Per EFI spec, the maximum storage allocated for both
443
           the variable name and variable data is 1024 bytes.
444
        */
445
 
446
        memset(variable_name, 0, 1024);
447
 
448
        do {
449
                variable_name_size=1024;
450
 
451
                status = efi.get_next_variable(&variable_name_size,
452
                                               variable_name,
453
                                               &vendor_guid);
454
 
455
 
456
                switch (status) {
457
                case EFI_SUCCESS:
458
                        efivar_create_proc_entry(variable_name_size,
459
                                                 variable_name,
460
                                                 &vendor_guid);
461
                        break;
462
                case EFI_NOT_FOUND:
463
                        break;
464
                default:
465
                        printk(KERN_WARNING "get_next_variable: status=%lx\n", status);
466
                        status = EFI_NOT_FOUND;
467
                        break;
468
                }
469
 
470
        } while (status != EFI_NOT_FOUND);
471
 
472
        kfree(variable_name);
473
        return 0;
474
}
475
 
476
static void __exit
477
efivars_exit(void)
478
{
479
        struct list_head *pos, *n;
480
        efivar_entry_t *efivar;
481
 
482
        spin_lock(&efivars_lock);
483
        if (efi_systab_entry)
484
                remove_proc_entry(efi_systab_entry->name, efi_dir);
485
        list_for_each_safe(pos, n, &efivar_list) {
486
                efivar = efivar_entry(pos);
487
                remove_proc_entry(efivar->entry->name, efi_vars_dir);
488
                list_del(&efivar->list);
489
                kfree(efivar);
490
        }
491
        spin_unlock(&efivars_lock);
492
 
493
        remove_proc_entry(efi_vars_dir->name, efi_dir);
494
}
495
 
496
module_init(efivars_init);
497
module_exit(efivars_exit);
498
 
499
/*
500
 * Overrides for Emacs so that we follow Linus's tabbing style.
501
 * Emacs will notice this stuff at the end of the file and automatically
502
 * adjust the settings for this buffer only.  This must remain at the end
503
 * of the file.
504
 * ---------------------------------------------------------------------------
505
 * Local variables:
506
 * c-indent-level: 4
507
 * c-brace-imaginary-offset: 0
508
 * c-brace-offset: -4
509
 * c-argdecl-indent: 4
510
 * c-label-offset: -4
511
 * c-continued-statement-offset: 4
512
 * c-continued-brace-offset: 0
513
 * indent-tabs-mode: nil
514
 * tab-width: 8
515
 * End:
516
 */

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.