pnvram.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /* Export pnvram and some variables for runtime */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2009 Free Software Foundation, Inc.
  5. *
  6. * GRUB is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/file.h>
  20. #include <grub/err.h>
  21. #include <grub/normal.h>
  22. #include <grub/mm.h>
  23. #include <grub/misc.h>
  24. #include <grub/charset.h>
  25. #include <grub/efiemu/efiemu.h>
  26. #include <grub/efiemu/runtime.h>
  27. #include <grub/extcmd.h>
  28. /* Place for final location of variables */
  29. static int nvram_handle = 0;
  30. static int nvramsize_handle = 0;
  31. static int high_monotonic_count_handle = 0;
  32. static int timezone_handle = 0;
  33. static int accuracy_handle = 0;
  34. static int daylight_handle = 0;
  35. static grub_size_t nvramsize;
  36. /* Parse signed value */
  37. static int
  38. grub_strtosl (const char *arg, const char ** const end, int base)
  39. {
  40. if (arg[0] == '-')
  41. return -grub_strtoul (arg + 1, end, base);
  42. return grub_strtoul (arg, end, base);
  43. }
  44. static inline int
  45. hextoval (char c)
  46. {
  47. if (c >= '0' && c <= '9')
  48. return c - '0';
  49. if (c >= 'a' && c <= 'z')
  50. return c - 'a' + 10;
  51. if (c >= 'A' && c <= 'Z')
  52. return c - 'A' + 10;
  53. return 0;
  54. }
  55. static inline grub_err_t
  56. unescape (char *in, char *out, char *outmax, int *len)
  57. {
  58. char *ptr, *dptr;
  59. dptr = out;
  60. for (ptr = in; *ptr && dptr < outmax; )
  61. if (*ptr == '%' && ptr[1] && ptr[2])
  62. {
  63. *dptr = (hextoval (ptr[1]) << 4) | (hextoval (ptr[2]));
  64. ptr += 3;
  65. dptr++;
  66. }
  67. else
  68. {
  69. *dptr = *ptr;
  70. ptr++;
  71. dptr++;
  72. }
  73. if (dptr == outmax)
  74. return grub_error (GRUB_ERR_OUT_OF_MEMORY,
  75. "too many NVRAM variables for reserved variable space."
  76. " Try increasing EfiEmu.pnvram.size");
  77. *len = dptr - out;
  78. return 0;
  79. }
  80. /* Export stuff for efiemu */
  81. static grub_err_t
  82. nvram_set (void * data __attribute__ ((unused)))
  83. {
  84. const char *env;
  85. /* Take definitive pointers */
  86. char *nvram = grub_efiemu_mm_obtain_request (nvram_handle);
  87. grub_uint32_t *nvramsize_def
  88. = grub_efiemu_mm_obtain_request (nvramsize_handle);
  89. grub_uint32_t *high_monotonic_count
  90. = grub_efiemu_mm_obtain_request (high_monotonic_count_handle);
  91. grub_int16_t *timezone
  92. = grub_efiemu_mm_obtain_request (timezone_handle);
  93. grub_uint8_t *daylight
  94. = grub_efiemu_mm_obtain_request (daylight_handle);
  95. grub_uint32_t *accuracy
  96. = grub_efiemu_mm_obtain_request (accuracy_handle);
  97. char *nvramptr;
  98. struct grub_env_var *var;
  99. /* Copy to definitive loaction */
  100. grub_dprintf ("efiemu", "preparing pnvram\n");
  101. env = grub_env_get ("EfiEmu.pnvram.high_monotonic_count");
  102. *high_monotonic_count = env ? grub_strtoul (env, 0, 0) : 1;
  103. env = grub_env_get ("EfiEmu.pnvram.timezone");
  104. *timezone = env ? grub_strtosl (env, 0, 0) : GRUB_EFI_UNSPECIFIED_TIMEZONE;
  105. env = grub_env_get ("EfiEmu.pnvram.accuracy");
  106. *accuracy = env ? grub_strtoul (env, 0, 0) : 50000000;
  107. env = grub_env_get ("EfiEmu.pnvram.daylight");
  108. *daylight = env ? grub_strtoul (env, 0, 0) : 0;
  109. nvramptr = nvram;
  110. grub_memset (nvram, 0, nvramsize);
  111. FOR_SORTED_ENV (var)
  112. {
  113. const char *guid;
  114. char *attr, *name, *varname;
  115. struct efi_variable *efivar;
  116. int len = 0;
  117. int i;
  118. grub_uint64_t guidcomp;
  119. if (grub_memcmp (var->name, "EfiEmu.pnvram.",
  120. sizeof ("EfiEmu.pnvram.") - 1) != 0)
  121. continue;
  122. guid = var->name + sizeof ("EfiEmu.pnvram.") - 1;
  123. attr = grub_strchr (guid, '.');
  124. if (!attr)
  125. continue;
  126. attr++;
  127. name = grub_strchr (attr, '.');
  128. if (!name)
  129. continue;
  130. name++;
  131. efivar = (struct efi_variable *) nvramptr;
  132. if (nvramptr - nvram + sizeof (struct efi_variable) > nvramsize)
  133. return grub_error (GRUB_ERR_OUT_OF_MEMORY,
  134. "too many NVRAM variables for reserved variable space."
  135. " Try increasing EfiEmu.pnvram.size");
  136. nvramptr += sizeof (struct efi_variable);
  137. efivar->guid.data1 = grub_cpu_to_le32 (grub_strtoul (guid, &guid, 16));
  138. if (*guid != '-')
  139. continue;
  140. guid++;
  141. efivar->guid.data2 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16));
  142. if (*guid != '-')
  143. continue;
  144. guid++;
  145. efivar->guid.data3 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16));
  146. if (*guid != '-')
  147. continue;
  148. guid++;
  149. guidcomp = grub_strtoull (guid, 0, 16);
  150. for (i = 0; i < 8; i++)
  151. efivar->guid.data4[i] = (guidcomp >> (56 - 8 * i)) & 0xff;
  152. efivar->attributes = grub_strtoull (attr, 0, 16);
  153. varname = grub_malloc (grub_strlen (name) + 1);
  154. if (! varname)
  155. return grub_errno;
  156. if (unescape (name, varname, varname + grub_strlen (name) + 1, &len))
  157. break;
  158. len = grub_utf8_to_utf16 ((grub_uint16_t *) nvramptr,
  159. (nvramsize - (nvramptr - nvram)) / 2,
  160. (grub_uint8_t *) varname, len, NULL);
  161. nvramptr += 2 * len;
  162. *((grub_uint16_t *) nvramptr) = 0;
  163. nvramptr += 2;
  164. efivar->namelen = 2 * len + 2;
  165. if (unescape (var->value, nvramptr, nvram + nvramsize, &len))
  166. {
  167. efivar->namelen = 0;
  168. break;
  169. }
  170. nvramptr += len;
  171. efivar->size = len;
  172. }
  173. if (grub_errno)
  174. return grub_errno;
  175. *nvramsize_def = nvramsize;
  176. /* Register symbols */
  177. grub_efiemu_register_symbol ("efiemu_variables", nvram_handle, 0);
  178. grub_efiemu_register_symbol ("efiemu_varsize", nvramsize_handle, 0);
  179. grub_efiemu_register_symbol ("efiemu_high_monotonic_count",
  180. high_monotonic_count_handle, 0);
  181. grub_efiemu_register_symbol ("efiemu_time_zone", timezone_handle, 0);
  182. grub_efiemu_register_symbol ("efiemu_time_daylight", daylight_handle, 0);
  183. grub_efiemu_register_symbol ("efiemu_time_accuracy",
  184. accuracy_handle, 0);
  185. return GRUB_ERR_NONE;
  186. }
  187. static void
  188. nvram_unload (void * data __attribute__ ((unused)))
  189. {
  190. grub_efiemu_mm_return_request (nvram_handle);
  191. grub_efiemu_mm_return_request (nvramsize_handle);
  192. grub_efiemu_mm_return_request (high_monotonic_count_handle);
  193. grub_efiemu_mm_return_request (timezone_handle);
  194. grub_efiemu_mm_return_request (accuracy_handle);
  195. grub_efiemu_mm_return_request (daylight_handle);
  196. }
  197. grub_err_t
  198. grub_efiemu_pnvram (void)
  199. {
  200. const char *size;
  201. grub_err_t err;
  202. nvramsize = 0;
  203. size = grub_env_get ("EfiEmu.pnvram.size");
  204. if (size)
  205. nvramsize = grub_strtoul (size, 0, 0);
  206. if (!nvramsize)
  207. nvramsize = 2048;
  208. err = grub_efiemu_register_prepare_hook (nvram_set, nvram_unload, 0);
  209. if (err)
  210. return err;
  211. nvram_handle
  212. = grub_efiemu_request_memalign (1, nvramsize,
  213. GRUB_EFI_RUNTIME_SERVICES_DATA);
  214. nvramsize_handle
  215. = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t),
  216. GRUB_EFI_RUNTIME_SERVICES_DATA);
  217. high_monotonic_count_handle
  218. = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t),
  219. GRUB_EFI_RUNTIME_SERVICES_DATA);
  220. timezone_handle
  221. = grub_efiemu_request_memalign (1, sizeof (grub_uint16_t),
  222. GRUB_EFI_RUNTIME_SERVICES_DATA);
  223. daylight_handle
  224. = grub_efiemu_request_memalign (1, sizeof (grub_uint8_t),
  225. GRUB_EFI_RUNTIME_SERVICES_DATA);
  226. accuracy_handle
  227. = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t),
  228. GRUB_EFI_RUNTIME_SERVICES_DATA);
  229. return GRUB_ERR_NONE;
  230. }