1 /* Export pnvram and some variables for runtime */
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>
22 #include <grub/normal.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;
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,
48 {"high-monotonic-count", 'm', 0,
49 "Initial value of high monotonic count", 0, ARG_TYPE_INT
},
51 "Timezone, offset in minutes from GMT", 0, ARG_TYPE_INT
},
53 "Accuracy of clock, in 1e-12 units", 0, ARG_TYPE_INT
},
55 "Daylight value, as per EFI specifications", 0, ARG_TYPE_INT
},
59 /* Parse signed value */
61 grub_strtosl (char *arg
, char **end
, int base
)
64 return -grub_strtoul (arg
+ 1, end
, base
);
65 return grub_strtoul (arg
, end
, base
);
68 /* Export stuff for efiemu */
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",
104 return GRUB_ERR_NONE
;
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
);
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
128 read_pnvram (char *filename
)
130 char *buf
, *ptr
, *ptr2
;
133 grub_uint8_t
*nvramptr
= nvram
;
134 struct efi_variable
*efivar
;
135 grub_size_t guidlen
, datalen
;
138 file
= grub_file_open (filename
);
140 return grub_error (GRUB_ERR_BAD_OS
, "couldn't read pnvram");
141 size
= grub_file_size (file
);
142 buf
= grub_malloc (size
+ 1);
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");
148 grub_file_close (file
);
150 for (ptr
= buf
; *ptr
; )
152 if (grub_isspace (*ptr
))
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*/
167 for (ptr2
= ptr
; (grub_isspace (*ptr2
)
168 || (*ptr2
>= '0' && *ptr2
<= '9')
169 || (*ptr2
>= 'a' && *ptr2
<= 'f')
170 || (*ptr2
>= 'A' && *ptr2
<= 'F'));
172 if (!grub_isspace (*ptr2
))
177 if (guidlen
!= sizeof (efivar
->guid
))
180 return grub_error (GRUB_ERR_BAD_OS
, "can't parse %s", filename
);
182 for (i
= 0; i
< 2 * sizeof (efivar
->guid
); i
++)
185 while (grub_isspace (*ptr
))
187 if (*ptr
>= '0' && *ptr
<= '9')
189 if (*ptr
>= 'a' && *ptr
<= 'f')
190 hex
= *ptr
- 'a' + 10;
191 if (*ptr
>= 'A' && *ptr
<= 'F')
192 hex
= *ptr
- 'A' + 10;
195 ((grub_uint8_t
*)&(efivar
->guid
))[i
/2] = hex
<< 4;
197 ((grub_uint8_t
*)&(efivar
->guid
))[i
/2] |= hex
;
201 while (grub_isspace (*ptr
))
205 grub_dprintf ("efiemu", "Not colon\n");
207 return grub_error (GRUB_ERR_BAD_OS
, "can't parse %s", filename
);
210 while (grub_isspace (*ptr
))
213 /* Attributes can be just parsed by existing functions */
214 efivar
->attributes
= grub_strtoul (ptr
, &ptr
, 16);
216 while (grub_isspace (*ptr
))
220 grub_dprintf ("efiemu", "Not colon\n");
222 return grub_error (GRUB_ERR_BAD_OS
, "can't parse %s", filename
);
225 while (grub_isspace (*ptr
))
228 /* Read name and value */
229 for (j
= 0; j
< 2; j
++)
231 /* Look the length */
233 for (ptr2
= ptr
; *ptr2
&& (grub_isspace (*ptr2
)
234 || (*ptr2
>= '0' && *ptr2
<= '9')
235 || (*ptr2
>= 'a' && *ptr2
<= 'f')
236 || (*ptr2
>= 'A' && *ptr2
<= 'F'));
238 if (!grub_isspace (*ptr2
))
242 if (nvramptr
- nvram
+ datalen
> nvramsize
)
245 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
246 "file is too large for reserved "
250 for (i
= 0; i
< 2 * datalen
; i
++)
253 while (grub_isspace (*ptr
))
255 if (*ptr
>= '0' && *ptr
<= '9')
257 if (*ptr
>= 'a' && *ptr
<= 'f')
258 hex
= *ptr
- 'a' + 10;
259 if (*ptr
>= 'A' && *ptr
<= 'F')
260 hex
= *ptr
- 'A' + 10;
263 nvramptr
[i
/2] = hex
<< 4;
265 nvramptr
[i
/2] |= hex
;
269 while (grub_isspace (*ptr
))
271 if (*ptr
!= (j
? ';' : ':'))
274 grub_dprintf ("efiemu", j
?"Not semicolon\n":"Not colon\n");
275 return grub_error (GRUB_ERR_BAD_OS
, "can't parse %s", filename
);
278 efivar
->size
= datalen
;
280 efivar
->namelen
= datalen
;
286 return GRUB_ERR_NONE
;
290 grub_efiemu_make_nvram (void)
294 err
= grub_efiemu_autocore ();
301 err
= grub_efiemu_register_prepare_hook (nvram_set
, nvram_unload
, 0);
308 = grub_efiemu_request_memalign (1, nvramsize
,
309 GRUB_EFI_RUNTIME_SERVICES_DATA
);
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
);
317 = grub_efiemu_request_memalign (1, sizeof (grub_uint16_t
),
318 GRUB_EFI_RUNTIME_SERVICES_DATA
);
320 = grub_efiemu_request_memalign (1, sizeof (grub_uint8_t
),
321 GRUB_EFI_RUNTIME_SERVICES_DATA
);
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
;
331 grub_efiemu_pnvram (void)
334 return GRUB_ERR_NONE
;
337 high_monotonic_count
= 1;
338 timezone
= GRUB_EFI_UNSPECIFIED_TIMEZONE
;
342 nvram
= grub_zalloc (nvramsize
);
344 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
345 "Couldn't allocate space for temporary pnvram storage");
347 return grub_efiemu_make_nvram ();
351 grub_cmd_efiemu_pnvram (struct grub_extcmd
*cmd
,
352 int argc
, char **args
)
354 struct grub_arg_list
*state
= cmd
->state
;
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
);
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])))
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);
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 "
397 grub_efiemu_pnvram_cmd_unregister (void)
399 grub_unregister_extcmd (cmd
);