efivars.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:12k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  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.  *  25 Mar 2002 - Matt Domsch <Matt_Domsch@dell.com>
  33.  *   move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse()
  34.  *
  35.  *  12 Feb 2002 - Matt Domsch <Matt_Domsch@dell.com>
  36.  *   use list_for_each_safe when deleting vars.
  37.  *   remove ifdef CONFIG_SMP around include <linux/smp.h>
  38.  *   v0.04 release to linux-ia64@linuxia64.org
  39.  *
  40.  *  20 April 2001 - Matt Domsch <Matt_Domsch@dell.com>
  41.  *   Moved vars from /proc/efi to /proc/efi/vars, and made
  42.  *   efi.c own the /proc/efi directory.
  43.  *   v0.03 release to linux-ia64@linuxia64.org
  44.  *
  45.  *  26 March 2001 - Matt Domsch <Matt_Domsch@dell.com>
  46.  *   At the request of Stephane, moved ownership of /proc/efi
  47.  *   to efi.c, and now efivars lives under /proc/efi/vars.
  48.  *
  49.  *  12 March 2001 - Matt Domsch <Matt_Domsch@dell.com>
  50.  *   Feedback received from Stephane Eranian incorporated.
  51.  *   efivar_write() checks copy_from_user() return value.
  52.  *   efivar_read/write() returns proper errno.
  53.  *   v0.02 release to linux-ia64@linuxia64.org
  54.  *
  55.  *  26 February 2001 - Matt Domsch <Matt_Domsch@dell.com>
  56.  *   v0.01 release to linux-ia64@linuxia64.org
  57.  */
  58. #include <linux/config.h>
  59. #include <linux/types.h>
  60. #include <linux/errno.h>
  61. #include <linux/init.h>
  62. #include <linux/proc_fs.h>
  63. #include <linux/sched.h> /* for capable() */
  64. #include <linux/mm.h>
  65. #include <linux/module.h>
  66. #include <linux/smp.h>
  67. #include <linux/efi.h>
  68. #include <asm/uaccess.h>
  69. MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>");
  70. MODULE_DESCRIPTION("/proc interface to EFI Variables");
  71. MODULE_LICENSE("GPL");
  72. #define EFIVARS_VERSION "0.05 2002-Mar-26"
  73. static int
  74. efivar_read(char *page, char **start, off_t off,
  75.     int count, int *eof, void *data);
  76. static int
  77. efivar_write(struct file *file, const char *buffer,
  78.      unsigned long count, void *data);
  79. /*
  80.  * The maximum size of VariableName + Data = 1024
  81.  * Therefore, it's reasonable to save that much
  82.  * space in each part of the structure,
  83.  * and we use a page for reading/writing.
  84.  */
  85. typedef struct _efi_variable_t {
  86. efi_char16_t  VariableName[1024/sizeof(efi_char16_t)];
  87. efi_guid_t    VendorGuid;
  88. unsigned long DataSize;
  89. __u8          Data[1024];
  90. efi_status_t  Status;
  91. __u32         Attributes;
  92. } __attribute__((packed)) efi_variable_t;
  93. typedef struct _efivar_entry_t {
  94. efi_variable_t          var;
  95. struct proc_dir_entry   *entry;
  96. struct list_head        list;
  97. } efivar_entry_t;
  98. static spinlock_t efivars_lock = SPIN_LOCK_UNLOCKED;
  99. static LIST_HEAD(efivar_list);
  100. static struct proc_dir_entry *efi_vars_dir = NULL;
  101. #define efivar_entry(n) list_entry(n, efivar_entry_t, list)
  102. /* Return the number of unicode characters in data */
  103. static unsigned long
  104. utf8_strlen(efi_char16_t *data, unsigned long maxlength)
  105. {
  106. unsigned long length = 0;
  107. while (*data++ != 0 && length < maxlength)
  108. length++;
  109. return length;
  110. }
  111. /* Return the number of bytes is the length of this string */
  112. /* Note: this is NOT the same as the number of unicode characters */
  113. static inline unsigned long
  114. utf8_strsize(efi_char16_t *data, unsigned long maxlength)
  115. {
  116. return utf8_strlen(data, maxlength/sizeof(efi_char16_t)) *
  117. sizeof(efi_char16_t);
  118. }
  119. static int
  120. proc_calc_metrics(char *page, char **start, off_t off,
  121.   int count, int *eof, int len)
  122. {
  123. if (len <= off+count) *eof = 1;
  124. *start = page + off;
  125. len -= off;
  126. if (len>count) len = count;
  127. if (len<0) len = 0;
  128. return len;
  129. }
  130. /*
  131.  * efivar_create_proc_entry()
  132.  * Requires:
  133.  *    variable_name_size = number of bytes required to hold
  134.  *                         variable_name (not counting the NULL
  135.  *                         character at the end.
  136.  * Returns 1 on failure, 0 on success
  137.  */
  138. static int
  139. efivar_create_proc_entry(unsigned long variable_name_size,
  140.  efi_char16_t *variable_name,
  141.  efi_guid_t *vendor_guid)
  142. {
  143. int i, short_name_size = variable_name_size /
  144. sizeof(efi_char16_t) + 38;
  145. char *short_name = kmalloc(short_name_size+1,
  146.    GFP_KERNEL);
  147. efivar_entry_t *new_efivar = kmalloc(sizeof(efivar_entry_t),
  148.      GFP_KERNEL);
  149. if (!short_name || !new_efivar)  {
  150. if (short_name)        kfree(short_name);
  151. if (new_efivar)        kfree(new_efivar);
  152. return 1;
  153. }
  154. memset(short_name, 0, short_name_size+1);
  155. memset(new_efivar, 0, sizeof(efivar_entry_t));
  156. memcpy(new_efivar->var.VariableName, variable_name,
  157.        variable_name_size);
  158. memcpy(&(new_efivar->var.VendorGuid), vendor_guid, sizeof(efi_guid_t));
  159. /* Convert Unicode to normal chars (assume top bits are 0),
  160.    ala UTF-8 */
  161. for (i=0; i<variable_name_size / sizeof(efi_char16_t); i++) {
  162. short_name[i] = variable_name[i] & 0xFF;
  163. }
  164. /* This is ugly, but necessary to separate one vendor's
  165.    private variables from another's.         */
  166. *(short_name + strlen(short_name)) = '-';
  167. efi_guid_unparse(vendor_guid, short_name + strlen(short_name));
  168. /* Create the entry in proc */
  169. new_efivar->entry = create_proc_entry(short_name, 0600, efi_vars_dir);
  170. kfree(short_name); short_name = NULL;
  171. if (!new_efivar->entry) return 1;
  172. new_efivar->entry->data = new_efivar;
  173. new_efivar->entry->read_proc = efivar_read;
  174. new_efivar->entry->write_proc = efivar_write;
  175. list_add(&new_efivar->list, &efivar_list);
  176. return 0;
  177. }
  178. /***********************************************************
  179.  * efivar_read()
  180.  * Requires:
  181.  * Modifies: page
  182.  * Returns: number of bytes written, or -EINVAL on failure
  183.  ***********************************************************/
  184. static int
  185. efivar_read(char *page, char **start, off_t off, int count, int *eof, void *data)
  186. {
  187. int len = sizeof(efi_variable_t);
  188. efivar_entry_t *efi_var = data;
  189. efi_variable_t *var_data = (efi_variable_t *)page;
  190. if (!page || !data) return -EINVAL;
  191. spin_lock(&efivars_lock);
  192. MOD_INC_USE_COUNT;
  193. memcpy(var_data, &efi_var->var, len);
  194. var_data->DataSize = 1024;
  195. var_data->Status = efi.get_variable(var_data->VariableName,
  196.     &var_data->VendorGuid,
  197.     &var_data->Attributes,
  198.     &var_data->DataSize,
  199.     var_data->Data);
  200. MOD_DEC_USE_COUNT;
  201. spin_unlock(&efivars_lock);
  202. return proc_calc_metrics(page, start, off, count, eof, len);
  203. }
  204. /***********************************************************
  205.  * efivar_write()
  206.  * Requires: data is an efi_setvariable_t data type,
  207.  *           properly filled in, possibly by a call
  208.  *           first to efivar_read().
  209.  *           Caller must have CAP_SYS_ADMIN
  210.  * Modifies: NVRAM
  211.  * Returns: var_data->DataSize on success, errno on failure
  212.  *
  213.  ***********************************************************/
  214. static int
  215. efivar_write(struct file *file, const char *buffer,
  216.      unsigned long count, void *data)
  217. {
  218. unsigned long strsize1, strsize2;
  219. int found=0;
  220. struct list_head *pos, *n;
  221. unsigned long size = sizeof(efi_variable_t);
  222. efi_status_t status;
  223. efivar_entry_t *efivar = data, *search_efivar = NULL;
  224. efi_variable_t *var_data;
  225. if (!data || count != size) {
  226. printk(KERN_WARNING "efivars: improper struct of size 0x%lx passed.n", count);
  227. return -EINVAL;
  228. }
  229. if (!capable(CAP_SYS_ADMIN))
  230. return -EACCES;
  231. MOD_INC_USE_COUNT;
  232. var_data = kmalloc(size, GFP_KERNEL);
  233. if (!var_data) {
  234. MOD_DEC_USE_COUNT;
  235. return -ENOMEM;
  236. }
  237. if (copy_from_user(var_data, buffer, size)) {
  238. MOD_DEC_USE_COUNT;
  239.                 kfree(var_data);
  240. return -EFAULT;
  241. }
  242. spin_lock(&efivars_lock);
  243. /* Since the data ptr we've currently got is probably for
  244.    a different variable find the right variable.
  245.    This allows any properly formatted data structure to
  246.    be written to any of the files in /proc/efi/vars and it will work.
  247. */
  248. list_for_each_safe(pos, n, &efivar_list) {
  249. search_efivar = efivar_entry(pos);
  250. strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
  251. strsize2 = utf8_strsize(var_data->VariableName, 1024);
  252. if ( strsize1 == strsize2 &&
  253.      !memcmp(&(search_efivar->var.VariableName),
  254.      var_data->VariableName, strsize1) &&
  255.      !efi_guidcmp(search_efivar->var.VendorGuid,
  256.   var_data->VendorGuid)) {
  257. found = 1;
  258. break;
  259. }
  260. }
  261. if (found) efivar = search_efivar;
  262. status = efi.set_variable(var_data->VariableName,
  263.   &var_data->VendorGuid,
  264.   var_data->Attributes,
  265.   var_data->DataSize,
  266.   var_data->Data);
  267. if (status != EFI_SUCCESS) {
  268. printk(KERN_WARNING "set_variable() failed: status=%lxn", status);
  269. kfree(var_data);
  270. MOD_DEC_USE_COUNT;
  271. spin_unlock(&efivars_lock);
  272. return -EIO;
  273. }
  274. if (!var_data->DataSize || !var_data->Attributes) {
  275. /* We just deleted the NVRAM variable */
  276. remove_proc_entry(efivar->entry->name, efi_vars_dir);
  277. list_del(&efivar->list);
  278. kfree(efivar);
  279. }
  280. /* If this is a new variable, set up the proc entry for it. */
  281. if (!found) {
  282. efivar_create_proc_entry(utf8_strsize(var_data->VariableName,
  283.       1024),
  284.  var_data->VariableName,
  285.  &var_data->VendorGuid);
  286. }
  287. kfree(var_data);
  288. MOD_DEC_USE_COUNT;
  289. spin_unlock(&efivars_lock);
  290. return size;
  291. }
  292. static int __init
  293. efivars_init(void)
  294. {
  295. efi_status_t status;
  296. efi_guid_t vendor_guid;
  297. efi_char16_t *variable_name = kmalloc(1024, GFP_KERNEL);
  298. unsigned long variable_name_size = 1024;
  299. spin_lock(&efivars_lock);
  300. printk(KERN_INFO "EFI Variables Facility v%sn", EFIVARS_VERSION);
  301.         /* Since efi.c happens before procfs is available,
  302.            we create the directory here if it doesn't
  303.            already exist.  There's probably a better way
  304.            to do this.
  305.         */
  306.         if (!efi_dir)
  307.                 efi_dir = proc_mkdir("efi", NULL);
  308. efi_vars_dir = proc_mkdir("vars", efi_dir);
  309. /* Per EFI spec, the maximum storage allocated for both
  310.    the variable name and variable data is 1024 bytes.
  311. */
  312. memset(variable_name, 0, 1024);
  313. do {
  314. variable_name_size=1024;
  315. status = efi.get_next_variable(&variable_name_size,
  316.        variable_name,
  317.        &vendor_guid);
  318. switch (status) {
  319. case EFI_SUCCESS:
  320. efivar_create_proc_entry(variable_name_size,
  321.  variable_name,
  322.  &vendor_guid);
  323. break;
  324. case EFI_NOT_FOUND:
  325. break;
  326. default:
  327. printk(KERN_WARNING "get_next_variable: status=%lxn", status);
  328. status = EFI_NOT_FOUND;
  329. break;
  330. }
  331. } while (status != EFI_NOT_FOUND);
  332. kfree(variable_name);
  333. spin_unlock(&efivars_lock);
  334. return 0;
  335. }
  336. static void __exit
  337. efivars_exit(void)
  338. {
  339. struct list_head *pos, *n;
  340. efivar_entry_t *efivar;
  341. spin_lock(&efivars_lock);
  342. list_for_each_safe(pos, n, &efivar_list) {
  343. efivar = efivar_entry(pos);
  344. remove_proc_entry(efivar->entry->name, efi_vars_dir);
  345. list_del(&efivar->list);
  346. kfree(efivar);
  347. }
  348. remove_proc_entry(efi_vars_dir->name, efi_dir);
  349. spin_unlock(&efivars_lock);
  350. }
  351. module_init(efivars_init);
  352. module_exit(efivars_exit);
  353. /*
  354.  * Overrides for Emacs so that we follow Linus's tabbing style.
  355.  * Emacs will notice this stuff at the end of the file and automatically
  356.  * adjust the settings for this buffer only.  This must remain at the end
  357.  * of the file.
  358.  * ---------------------------------------------------------------------------
  359.  * Local variables:
  360.  * c-indent-level: 4
  361.  * c-brace-imaginary-offset: 0
  362.  * c-brace-offset: -4
  363.  * c-argdecl-indent: 4
  364.  * c-label-offset: -4
  365.  * c-continued-statement-offset: 4
  366.  * c-continued-brace-offset: 0
  367.  * indent-tabs-mode: nil
  368.  * tab-width: 8
  369.  * End:
  370.  */