2009-10-16 Robert Millan <rmh.grub@aybabtu.com>
[grub2/phcoder/solaris.git] / efiemu / pnvram.c
blob04ad6e28433775a83e14ee6271ae7b20e02d9f8a
1 /* Export pnvram and some variables for runtime */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2009 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/file.h>
21 #include <grub/err.h>
22 #include <grub/normal.h>
23 #include <grub/mm.h>
24 #include <grub/misc.h>
25 #include <grub/efiemu/efiemu.h>
26 #include <grub/efiemu/runtime.h>
27 #include <grub/extcmd.h>
29 /* Place for final location of variables */
30 static int nvram_handle = 0;
31 static int nvramsize_handle = 0;
32 static int high_monotonic_count_handle = 0;
33 static int timezone_handle = 0;
34 static int accuracy_handle = 0;
35 static int daylight_handle = 0;
37 /* Temporary place */
38 static grub_uint8_t *nvram;
39 static grub_size_t nvramsize;
40 static grub_uint32_t high_monotonic_count;
41 static grub_int16_t timezone;
42 static grub_uint8_t daylight;
43 static grub_uint32_t accuracy;
45 static const struct grub_arg_option options[] = {
46 {"size", 's', 0, "number of bytes to reserve for pseudo NVRAM", 0,
47 ARG_TYPE_INT},
48 {"high-monotonic-count", 'm', 0,
49 "Initial value of high monotonic count", 0, ARG_TYPE_INT},
50 {"timezone", 't', 0,
51 "Timezone, offset in minutes from GMT", 0, ARG_TYPE_INT},
52 {"accuracy", 'a', 0,
53 "Accuracy of clock, in 1e-12 units", 0, ARG_TYPE_INT},
54 {"daylight", 'd', 0,
55 "Daylight value, as per EFI specifications", 0, ARG_TYPE_INT},
56 {0, 0, 0, 0, 0, 0}
59 /* Parse signed value */
60 static int
61 grub_strtosl (char *arg, char **end, int base)
63 if (arg[0] == '-')
64 return -grub_strtoul (arg + 1, end, base);
65 return grub_strtoul (arg, end, base);
68 /* Export stuff for efiemu */
69 static grub_err_t
70 nvram_set (void * data __attribute__ ((unused)))
72 /* Take definitive pointers */
73 grub_uint8_t *nvram_def = grub_efiemu_mm_obtain_request (nvram_handle);
74 grub_uint32_t *nvramsize_def
75 = grub_efiemu_mm_obtain_request (nvramsize_handle);
76 grub_uint32_t *high_monotonic_count_def
77 = grub_efiemu_mm_obtain_request (high_monotonic_count_handle);
78 grub_int16_t *timezone_def
79 = grub_efiemu_mm_obtain_request (timezone_handle);
80 grub_uint8_t *daylight_def
81 = grub_efiemu_mm_obtain_request (daylight_handle);
82 grub_uint32_t *accuracy_def
83 = grub_efiemu_mm_obtain_request (accuracy_handle);
85 /* Copy to definitive loaction */
86 grub_dprintf ("efiemu", "preparing pnvram\n");
87 grub_memcpy (nvram_def, nvram, nvramsize);
88 *nvramsize_def = nvramsize;
89 *high_monotonic_count_def = high_monotonic_count;
90 *timezone_def = timezone;
91 *daylight_def = daylight;
92 *accuracy_def = accuracy;
94 /* Register symbols */
95 grub_efiemu_register_symbol ("efiemu_variables", nvram_handle, 0);
96 grub_efiemu_register_symbol ("efiemu_varsize", nvramsize_handle, 0);
97 grub_efiemu_register_symbol ("efiemu_high_monotonic_count",
98 high_monotonic_count_handle, 0);
99 grub_efiemu_register_symbol ("efiemu_time_zone", timezone_handle, 0);
100 grub_efiemu_register_symbol ("efiemu_time_daylight", daylight_handle, 0);
101 grub_efiemu_register_symbol ("efiemu_time_accuracy",
102 accuracy_handle, 0);
104 return GRUB_ERR_NONE;
107 static void
108 nvram_unload (void * data __attribute__ ((unused)))
110 grub_efiemu_mm_return_request (nvram_handle);
111 grub_efiemu_mm_return_request (nvramsize_handle);
112 grub_efiemu_mm_return_request (high_monotonic_count_handle);
113 grub_efiemu_mm_return_request (timezone_handle);
114 grub_efiemu_mm_return_request (accuracy_handle);
115 grub_efiemu_mm_return_request (daylight_handle);
117 grub_free (nvram);
118 nvram = 0;
121 /* Load the variables file It's in format
122 guid1:attr1:name1:data1;
123 guid2:attr2:name2:data2;
125 Where all fields are in hex
127 static grub_err_t
128 read_pnvram (char *filename)
130 char *buf, *ptr, *ptr2;
131 grub_file_t file;
132 grub_size_t size;
133 grub_uint8_t *nvramptr = nvram;
134 struct efi_variable *efivar;
135 grub_size_t guidlen, datalen;
136 unsigned i, j;
138 file = grub_file_open (filename);
139 if (!file)
140 return grub_error (GRUB_ERR_BAD_OS, "couldn't read pnvram");
141 size = grub_file_size (file);
142 buf = grub_malloc (size + 1);
143 if (!buf)
144 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't read pnvram");
145 if (grub_file_read (file, buf, size) != (grub_ssize_t) size)
146 return grub_error (GRUB_ERR_BAD_OS, "couldn't read pnvram");
147 buf[size] = 0;
148 grub_file_close (file);
150 for (ptr = buf; *ptr; )
152 if (grub_isspace (*ptr))
154 ptr++;
155 continue;
158 efivar = (struct efi_variable *) nvramptr;
159 if (nvramptr - nvram + sizeof (struct efi_variable) > nvramsize)
160 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
161 "file is too large for reserved variable space");
163 nvramptr += sizeof (struct efi_variable);
165 /* look ahow long guid field is*/
166 guidlen = 0;
167 for (ptr2 = ptr; (grub_isspace (*ptr2)
168 || (*ptr2 >= '0' && *ptr2 <= '9')
169 || (*ptr2 >= 'a' && *ptr2 <= 'f')
170 || (*ptr2 >= 'A' && *ptr2 <= 'F'));
171 ptr2++)
172 if (!grub_isspace (*ptr2))
173 guidlen++;
174 guidlen /= 2;
176 /* Read guid */
177 if (guidlen != sizeof (efivar->guid))
179 grub_free (buf);
180 return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
182 for (i = 0; i < 2 * sizeof (efivar->guid); i++)
184 int hex = 0;
185 while (grub_isspace (*ptr))
186 ptr++;
187 if (*ptr >= '0' && *ptr <= '9')
188 hex = *ptr - '0';
189 if (*ptr >= 'a' && *ptr <= 'f')
190 hex = *ptr - 'a' + 10;
191 if (*ptr >= 'A' && *ptr <= 'F')
192 hex = *ptr - 'A' + 10;
194 if (i%2 == 0)
195 ((grub_uint8_t *)&(efivar->guid))[i/2] = hex << 4;
196 else
197 ((grub_uint8_t *)&(efivar->guid))[i/2] |= hex;
198 ptr++;
201 while (grub_isspace (*ptr))
202 ptr++;
203 if (*ptr != ':')
205 grub_dprintf ("efiemu", "Not colon\n");
206 grub_free (buf);
207 return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
209 ptr++;
210 while (grub_isspace (*ptr))
211 ptr++;
213 /* Attributes can be just parsed by existing functions */
214 efivar->attributes = grub_strtoul (ptr, &ptr, 16);
216 while (grub_isspace (*ptr))
217 ptr++;
218 if (*ptr != ':')
220 grub_dprintf ("efiemu", "Not colon\n");
221 grub_free (buf);
222 return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
224 ptr++;
225 while (grub_isspace (*ptr))
226 ptr++;
228 /* Read name and value */
229 for (j = 0; j < 2; j++)
231 /* Look the length */
232 datalen = 0;
233 for (ptr2 = ptr; *ptr2 && (grub_isspace (*ptr2)
234 || (*ptr2 >= '0' && *ptr2 <= '9')
235 || (*ptr2 >= 'a' && *ptr2 <= 'f')
236 || (*ptr2 >= 'A' && *ptr2 <= 'F'));
237 ptr2++)
238 if (!grub_isspace (*ptr2))
239 datalen++;
240 datalen /= 2;
242 if (nvramptr - nvram + datalen > nvramsize)
244 grub_free (buf);
245 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
246 "file is too large for reserved "
247 " variable space");
250 for (i = 0; i < 2 * datalen; i++)
252 int hex = 0;
253 while (grub_isspace (*ptr))
254 ptr++;
255 if (*ptr >= '0' && *ptr <= '9')
256 hex = *ptr - '0';
257 if (*ptr >= 'a' && *ptr <= 'f')
258 hex = *ptr - 'a' + 10;
259 if (*ptr >= 'A' && *ptr <= 'F')
260 hex = *ptr - 'A' + 10;
262 if (i%2 == 0)
263 nvramptr[i/2] = hex << 4;
264 else
265 nvramptr[i/2] |= hex;
266 ptr++;
268 nvramptr += datalen;
269 while (grub_isspace (*ptr))
270 ptr++;
271 if (*ptr != (j ? ';' : ':'))
273 grub_free (buf);
274 grub_dprintf ("efiemu", j?"Not semicolon\n":"Not colon\n");
275 return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
277 if (j)
278 efivar->size = datalen;
279 else
280 efivar->namelen = datalen;
282 ptr++;
285 grub_free (buf);
286 return GRUB_ERR_NONE;
289 static grub_err_t
290 grub_efiemu_make_nvram (void)
292 grub_err_t err;
294 err = grub_efiemu_autocore ();
295 if (err)
297 grub_free (nvram);
298 return err;
301 err = grub_efiemu_register_prepare_hook (nvram_set, nvram_unload, 0);
302 if (err)
304 grub_free (nvram);
305 return err;
307 nvram_handle
308 = grub_efiemu_request_memalign (1, nvramsize,
309 GRUB_EFI_RUNTIME_SERVICES_DATA);
310 nvramsize_handle
311 = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t),
312 GRUB_EFI_RUNTIME_SERVICES_DATA);
313 high_monotonic_count_handle
314 = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t),
315 GRUB_EFI_RUNTIME_SERVICES_DATA);
316 timezone_handle
317 = grub_efiemu_request_memalign (1, sizeof (grub_uint16_t),
318 GRUB_EFI_RUNTIME_SERVICES_DATA);
319 daylight_handle
320 = grub_efiemu_request_memalign (1, sizeof (grub_uint8_t),
321 GRUB_EFI_RUNTIME_SERVICES_DATA);
322 accuracy_handle
323 = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t),
324 GRUB_EFI_RUNTIME_SERVICES_DATA);
326 grub_efiemu_request_symbols (6);
327 return GRUB_ERR_NONE;
330 grub_err_t
331 grub_efiemu_pnvram (void)
333 if (nvram)
334 return GRUB_ERR_NONE;
336 nvramsize = 2048;
337 high_monotonic_count = 1;
338 timezone = GRUB_EFI_UNSPECIFIED_TIMEZONE;
339 accuracy = 50000000;
340 daylight = 0;
342 nvram = grub_zalloc (nvramsize);
343 if (!nvram)
344 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
345 "Couldn't allocate space for temporary pnvram storage");
347 return grub_efiemu_make_nvram ();
350 static grub_err_t
351 grub_cmd_efiemu_pnvram (struct grub_extcmd *cmd,
352 int argc, char **args)
354 struct grub_arg_list *state = cmd->state;
355 grub_err_t err;
357 if (argc > 1)
358 return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one argument expected");
360 nvramsize = state[0].set ? grub_strtoul (state[0].arg, 0, 0) : 2048;
361 high_monotonic_count = state[1].set ? grub_strtoul (state[1].arg, 0, 0) : 1;
362 timezone = state[2].set ? grub_strtosl (state[2].arg, 0, 0)
363 : GRUB_EFI_UNSPECIFIED_TIMEZONE;
364 accuracy = state[3].set ? grub_strtoul (state[3].arg, 0, 0) : 50000000;
365 daylight = state[4].set ? grub_strtoul (state[4].arg, 0, 0) : 0;
367 nvram = grub_zalloc (nvramsize);
368 if (!nvram)
369 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
370 "Couldn't allocate space for temporary pnvram storage");
372 if (argc == 1 && (err = read_pnvram (args[0])))
374 grub_free (nvram);
375 return err;
377 return grub_efiemu_make_nvram ();
380 static grub_extcmd_t cmd;
382 void grub_efiemu_pnvram_cmd_register (void);
383 void grub_efiemu_pnvram_cmd_unregister (void);
385 void
386 grub_efiemu_pnvram_cmd_register (void)
388 cmd = grub_register_extcmd ("efiemu_pnvram", grub_cmd_efiemu_pnvram,
389 GRUB_COMMAND_FLAG_BOTH,
390 "efiemu_pnvram [FILENAME]",
391 "Initialise pseudo-NVRAM and load variables "
392 "from FILE",
393 options);
396 void
397 grub_efiemu_pnvram_cmd_unregister (void)
399 grub_unregister_extcmd (cmd);