efivars.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:12k
源码类别:

嵌入式Linux

开发平台:

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