Manually reimported XNU branch
This commit is contained in:
parent
32764030b5
commit
1d3c6f1de7
29 changed files with 1128 additions and 633 deletions
451
efiemu/pnvram.c
451
efiemu/pnvram.c
|
@ -22,6 +22,7 @@
|
|||
#include <grub/normal.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/charset.h>
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
#include <grub/efiemu/runtime.h>
|
||||
#include <grub/extcmd.h>
|
||||
|
@ -34,62 +35,181 @@ static int timezone_handle = 0;
|
|||
static int accuracy_handle = 0;
|
||||
static int daylight_handle = 0;
|
||||
|
||||
/* Temporary place */
|
||||
static grub_uint8_t *nvram;
|
||||
static grub_size_t nvramsize;
|
||||
static grub_uint32_t high_monotonic_count;
|
||||
static grub_int16_t timezone;
|
||||
static grub_uint8_t daylight;
|
||||
static grub_uint32_t accuracy;
|
||||
|
||||
static const struct grub_arg_option options[] = {
|
||||
{"size", 's', 0, "number of bytes to reserve for pseudo NVRAM", 0,
|
||||
ARG_TYPE_INT},
|
||||
{"high-monotonic-count", 'm', 0,
|
||||
"Initial value of high monotonic count", 0, ARG_TYPE_INT},
|
||||
{"timezone", 't', 0,
|
||||
"Timezone, offset in minutes from GMT", 0, ARG_TYPE_INT},
|
||||
{"accuracy", 'a', 0,
|
||||
"Accuracy of clock, in 1e-12 units", 0, ARG_TYPE_INT},
|
||||
{"daylight", 'd', 0,
|
||||
"Daylight value, as per EFI specifications", 0, ARG_TYPE_INT},
|
||||
{0, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
/* Parse signed value */
|
||||
static int
|
||||
grub_strtosl (char *arg, char **end, int base)
|
||||
grub_strtosl (const char *arg, char **end, int base)
|
||||
{
|
||||
if (arg[0] == '-')
|
||||
return -grub_strtoul (arg + 1, end, base);
|
||||
return grub_strtoul (arg, end, base);
|
||||
}
|
||||
|
||||
static inline int
|
||||
hextoval (char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return c - 'A' + 10;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline grub_err_t
|
||||
unescape (char *in, char *out, char *outmax, int *len)
|
||||
{
|
||||
char *ptr, *dptr;
|
||||
dptr = out;
|
||||
for (ptr = in; *ptr && dptr < outmax; )
|
||||
if (*ptr == '%' && ptr[1] && ptr[2])
|
||||
{
|
||||
*dptr = (hextoval (ptr[1]) << 4) | (hextoval (ptr[2]));
|
||||
ptr += 3;
|
||||
dptr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dptr = *ptr;
|
||||
ptr++;
|
||||
dptr++;
|
||||
}
|
||||
if (dptr == outmax)
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"Too many NVRAM variables for reserved variable space."
|
||||
" Try increasing EfiEmu.pnvram.size.");
|
||||
*len = dptr - out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Export stuff for efiemu */
|
||||
static grub_err_t
|
||||
nvram_set (void * data __attribute__ ((unused)))
|
||||
{
|
||||
const char *env;
|
||||
/* Take definitive pointers */
|
||||
grub_uint8_t *nvram_def = grub_efiemu_mm_obtain_request (nvram_handle);
|
||||
char *nvram = grub_efiemu_mm_obtain_request (nvram_handle);
|
||||
grub_uint32_t *nvramsize_def
|
||||
= grub_efiemu_mm_obtain_request (nvramsize_handle);
|
||||
grub_uint32_t *high_monotonic_count_def
|
||||
grub_uint32_t *high_monotonic_count
|
||||
= grub_efiemu_mm_obtain_request (high_monotonic_count_handle);
|
||||
grub_int16_t *timezone_def
|
||||
grub_int16_t *timezone
|
||||
= grub_efiemu_mm_obtain_request (timezone_handle);
|
||||
grub_uint8_t *daylight_def
|
||||
grub_uint8_t *daylight
|
||||
= grub_efiemu_mm_obtain_request (daylight_handle);
|
||||
grub_uint32_t *accuracy_def
|
||||
grub_uint32_t *accuracy
|
||||
= grub_efiemu_mm_obtain_request (accuracy_handle);
|
||||
char *nvramptr;
|
||||
|
||||
auto int iterate_env (struct grub_env_var *var);
|
||||
int iterate_env (struct grub_env_var *var)
|
||||
{
|
||||
char *guid, *attr, *name, *varname;
|
||||
struct efi_variable *efivar;
|
||||
int len = 0;
|
||||
|
||||
if (grub_memcmp (var->name, "EfiEmu.pnvram.",
|
||||
sizeof ("EfiEmu.pnvram.") - 1) != 0)
|
||||
return 0;
|
||||
|
||||
guid = var->name + sizeof ("EfiEmu.pnvram.") - 1;
|
||||
|
||||
attr = grub_strchr (guid, '.');
|
||||
if (!attr)
|
||||
return 0;
|
||||
attr++;
|
||||
|
||||
name = grub_strchr (attr, '.');
|
||||
if (!name)
|
||||
return 0;
|
||||
name++;
|
||||
|
||||
efivar = (struct efi_variable *) nvramptr;
|
||||
if (nvramptr - nvram + sizeof (struct efi_variable) > nvramsize)
|
||||
{
|
||||
grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"Too many NVRAM variables for reserved variable space."
|
||||
" Try increasing EfiEmu.pnvram.size.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
nvramptr += sizeof (struct efi_variable);
|
||||
|
||||
efivar->guid.data1 = grub_cpu_to_le32 (grub_strtoul (guid, &guid, 16));
|
||||
if (*guid != '-')
|
||||
return 0;
|
||||
guid++;
|
||||
|
||||
efivar->guid.data2 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16));
|
||||
if (*guid != '-')
|
||||
return 0;
|
||||
guid++;
|
||||
|
||||
efivar->guid.data3 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16));
|
||||
if (*guid != '-')
|
||||
return 0;
|
||||
guid++;
|
||||
|
||||
*(grub_uint64_t *) &(efivar->guid.data4)
|
||||
= grub_cpu_to_be64 (grub_strtoull (guid, 0, 16));
|
||||
|
||||
efivar->attributes = grub_strtoull (attr, 0, 16);
|
||||
|
||||
varname = grub_malloc (grub_strlen (name) + 1);
|
||||
if (! varname)
|
||||
return 1;
|
||||
|
||||
if (unescape (name, varname, varname + grub_strlen (name) + 1, &len))
|
||||
return 1;
|
||||
|
||||
len = grub_utf8_to_utf16 ((grub_uint16_t *) nvramptr,
|
||||
(nvramsize - (nvramptr - nvram)) / 2,
|
||||
(grub_uint8_t *) varname, len, NULL);
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, "Broken UTF-8 in variable name\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
nvramptr += 2 * len;
|
||||
*((grub_uint16_t *) nvramptr) = 0;
|
||||
nvramptr += 2;
|
||||
efivar->namelen = 2 * len + 2;
|
||||
|
||||
if (unescape (var->value, nvramptr, nvram + nvramsize, &len))
|
||||
{
|
||||
efivar->namelen = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
nvramptr += len;
|
||||
|
||||
efivar->size = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy to definitive loaction */
|
||||
grub_dprintf ("efiemu", "preparing pnvram\n");
|
||||
grub_memcpy (nvram_def, nvram, nvramsize);
|
||||
|
||||
env = grub_env_get ("EfiEmu.pnvram.high_monotonic_count");
|
||||
*high_monotonic_count = env ? grub_strtoul (env, 0, 0) : 1;
|
||||
env = grub_env_get ("EfiEmu.pnvram.timezone");
|
||||
*timezone = env ? grub_strtosl (env, 0, 0) : GRUB_EFI_UNSPECIFIED_TIMEZONE;
|
||||
env = grub_env_get ("EfiEmu.pnvram.accuracy");
|
||||
*accuracy = env ? grub_strtoul (env, 0, 0) : 50000000;
|
||||
env = grub_env_get ("EfiEmu.pnvram.daylight");
|
||||
*daylight = env ? grub_strtoul (env, 0, 0) : 0;
|
||||
|
||||
nvramptr = nvram;
|
||||
grub_memset (nvram, 0, nvramsize);
|
||||
grub_env_iterate (iterate_env);
|
||||
if (grub_errno)
|
||||
return grub_errno;
|
||||
*nvramsize_def = nvramsize;
|
||||
*high_monotonic_count_def = high_monotonic_count;
|
||||
*timezone_def = timezone;
|
||||
*daylight_def = daylight;
|
||||
*accuracy_def = accuracy;
|
||||
|
||||
/* Register symbols */
|
||||
grub_efiemu_register_symbol ("efiemu_variables", nvram_handle, 0);
|
||||
|
@ -113,197 +233,27 @@ nvram_unload (void * data __attribute__ ((unused)))
|
|||
grub_efiemu_mm_return_request (timezone_handle);
|
||||
grub_efiemu_mm_return_request (accuracy_handle);
|
||||
grub_efiemu_mm_return_request (daylight_handle);
|
||||
|
||||
grub_free (nvram);
|
||||
nvram = 0;
|
||||
}
|
||||
|
||||
/* Load the variables file It's in format
|
||||
guid1:attr1:name1:data1;
|
||||
guid2:attr2:name2:data2;
|
||||
...
|
||||
Where all fields are in hex
|
||||
*/
|
||||
static grub_err_t
|
||||
read_pnvram (char *filename)
|
||||
{
|
||||
char *buf, *ptr, *ptr2;
|
||||
grub_file_t file;
|
||||
grub_size_t size;
|
||||
grub_uint8_t *nvramptr = nvram;
|
||||
struct efi_variable *efivar;
|
||||
grub_size_t guidlen, datalen;
|
||||
unsigned i, j;
|
||||
|
||||
file = grub_file_open (filename);
|
||||
if (!file)
|
||||
return grub_error (GRUB_ERR_BAD_OS, "couldn't read pnvram");
|
||||
size = grub_file_size (file);
|
||||
buf = grub_malloc (size + 1);
|
||||
if (!buf)
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't read pnvram");
|
||||
if (grub_file_read (file, buf, size) != (grub_ssize_t) size)
|
||||
return grub_error (GRUB_ERR_BAD_OS, "couldn't read pnvram");
|
||||
buf[size] = 0;
|
||||
grub_file_close (file);
|
||||
|
||||
for (ptr = buf; *ptr; )
|
||||
{
|
||||
if (grub_isspace (*ptr))
|
||||
{
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
efivar = (struct efi_variable *) nvramptr;
|
||||
if (nvramptr - nvram + sizeof (struct efi_variable) > nvramsize)
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"file is too large for reserved variable space");
|
||||
|
||||
nvramptr += sizeof (struct efi_variable);
|
||||
|
||||
/* look ahow long guid field is*/
|
||||
guidlen = 0;
|
||||
for (ptr2 = ptr; (grub_isspace (*ptr2)
|
||||
|| (*ptr2 >= '0' && *ptr2 <= '9')
|
||||
|| (*ptr2 >= 'a' && *ptr2 <= 'f')
|
||||
|| (*ptr2 >= 'A' && *ptr2 <= 'F'));
|
||||
ptr2++)
|
||||
if (!grub_isspace (*ptr2))
|
||||
guidlen++;
|
||||
guidlen /= 2;
|
||||
|
||||
/* Read guid */
|
||||
if (guidlen != sizeof (efivar->guid))
|
||||
{
|
||||
grub_free (buf);
|
||||
return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
|
||||
}
|
||||
for (i = 0; i < 2 * sizeof (efivar->guid); i++)
|
||||
{
|
||||
int hex = 0;
|
||||
while (grub_isspace (*ptr))
|
||||
ptr++;
|
||||
if (*ptr >= '0' && *ptr <= '9')
|
||||
hex = *ptr - '0';
|
||||
if (*ptr >= 'a' && *ptr <= 'f')
|
||||
hex = *ptr - 'a' + 10;
|
||||
if (*ptr >= 'A' && *ptr <= 'F')
|
||||
hex = *ptr - 'A' + 10;
|
||||
|
||||
if (i%2 == 0)
|
||||
((grub_uint8_t *)&(efivar->guid))[i/2] = hex << 4;
|
||||
else
|
||||
((grub_uint8_t *)&(efivar->guid))[i/2] |= hex;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
while (grub_isspace (*ptr))
|
||||
ptr++;
|
||||
if (*ptr != ':')
|
||||
{
|
||||
grub_dprintf ("efiemu", "Not colon\n");
|
||||
grub_free (buf);
|
||||
return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
|
||||
}
|
||||
ptr++;
|
||||
while (grub_isspace (*ptr))
|
||||
ptr++;
|
||||
|
||||
/* Attributes can be just parsed by existing functions */
|
||||
efivar->attributes = grub_strtoul (ptr, &ptr, 16);
|
||||
|
||||
while (grub_isspace (*ptr))
|
||||
ptr++;
|
||||
if (*ptr != ':')
|
||||
{
|
||||
grub_dprintf ("efiemu", "Not colon\n");
|
||||
grub_free (buf);
|
||||
return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
|
||||
}
|
||||
ptr++;
|
||||
while (grub_isspace (*ptr))
|
||||
ptr++;
|
||||
|
||||
/* Read name and value */
|
||||
for (j = 0; j < 2; j++)
|
||||
{
|
||||
/* Look the length */
|
||||
datalen = 0;
|
||||
for (ptr2 = ptr; *ptr2 && (grub_isspace (*ptr2)
|
||||
|| (*ptr2 >= '0' && *ptr2 <= '9')
|
||||
|| (*ptr2 >= 'a' && *ptr2 <= 'f')
|
||||
|| (*ptr2 >= 'A' && *ptr2 <= 'F'));
|
||||
ptr2++)
|
||||
if (!grub_isspace (*ptr2))
|
||||
datalen++;
|
||||
datalen /= 2;
|
||||
|
||||
if (nvramptr - nvram + datalen > nvramsize)
|
||||
{
|
||||
grub_free (buf);
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"file is too large for reserved "
|
||||
" variable space");
|
||||
}
|
||||
|
||||
for (i = 0; i < 2 * datalen; i++)
|
||||
{
|
||||
int hex = 0;
|
||||
while (grub_isspace (*ptr))
|
||||
ptr++;
|
||||
if (*ptr >= '0' && *ptr <= '9')
|
||||
hex = *ptr - '0';
|
||||
if (*ptr >= 'a' && *ptr <= 'f')
|
||||
hex = *ptr - 'a' + 10;
|
||||
if (*ptr >= 'A' && *ptr <= 'F')
|
||||
hex = *ptr - 'A' + 10;
|
||||
|
||||
if (i%2 == 0)
|
||||
nvramptr[i/2] = hex << 4;
|
||||
else
|
||||
nvramptr[i/2] |= hex;
|
||||
ptr++;
|
||||
}
|
||||
nvramptr += datalen;
|
||||
while (grub_isspace (*ptr))
|
||||
ptr++;
|
||||
if (*ptr != (j ? ';' : ':'))
|
||||
{
|
||||
grub_free (buf);
|
||||
grub_dprintf ("efiemu", j?"Not semicolon\n":"Not colon\n");
|
||||
return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
|
||||
}
|
||||
if (j)
|
||||
efivar->size = datalen;
|
||||
else
|
||||
efivar->namelen = datalen;
|
||||
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
grub_free (buf);
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_efiemu_make_nvram (void)
|
||||
grub_err_t
|
||||
grub_efiemu_pnvram (void)
|
||||
{
|
||||
const char *size;
|
||||
grub_err_t err;
|
||||
|
||||
err = grub_efiemu_autocore ();
|
||||
if (err)
|
||||
{
|
||||
grub_free (nvram);
|
||||
return err;
|
||||
}
|
||||
nvramsize = 0;
|
||||
|
||||
size = grub_env_get ("EfiEmu.pnvram.size");
|
||||
if (size)
|
||||
nvramsize = grub_strtoul (size, 0, 0);
|
||||
|
||||
if (!nvramsize)
|
||||
nvramsize = 2048;
|
||||
|
||||
err = grub_efiemu_register_prepare_hook (nvram_set, nvram_unload, 0);
|
||||
if (err)
|
||||
{
|
||||
grub_free (nvram);
|
||||
return err;
|
||||
}
|
||||
return err;
|
||||
|
||||
nvram_handle
|
||||
= grub_efiemu_request_memalign (1, nvramsize,
|
||||
GRUB_EFI_RUNTIME_SERVICES_DATA);
|
||||
|
@ -323,78 +273,5 @@ grub_efiemu_make_nvram (void)
|
|||
= grub_efiemu_request_memalign (1, sizeof (grub_uint32_t),
|
||||
GRUB_EFI_RUNTIME_SERVICES_DATA);
|
||||
|
||||
grub_efiemu_request_symbols (6);
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
grub_efiemu_pnvram (void)
|
||||
{
|
||||
if (nvram)
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
nvramsize = 2048;
|
||||
high_monotonic_count = 1;
|
||||
timezone = GRUB_EFI_UNSPECIFIED_TIMEZONE;
|
||||
accuracy = 50000000;
|
||||
daylight = 0;
|
||||
|
||||
nvram = grub_zalloc (nvramsize);
|
||||
if (!nvram)
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"Couldn't allocate space for temporary pnvram storage");
|
||||
|
||||
return grub_efiemu_make_nvram ();
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_efiemu_pnvram (struct grub_extcmd *cmd,
|
||||
int argc, char **args)
|
||||
{
|
||||
struct grub_arg_list *state = cmd->state;
|
||||
grub_err_t err;
|
||||
|
||||
if (argc > 1)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one argument expected");
|
||||
|
||||
nvramsize = state[0].set ? grub_strtoul (state[0].arg, 0, 0) : 2048;
|
||||
high_monotonic_count = state[1].set ? grub_strtoul (state[1].arg, 0, 0) : 1;
|
||||
timezone = state[2].set ? grub_strtosl (state[2].arg, 0, 0)
|
||||
: GRUB_EFI_UNSPECIFIED_TIMEZONE;
|
||||
accuracy = state[3].set ? grub_strtoul (state[3].arg, 0, 0) : 50000000;
|
||||
daylight = state[4].set ? grub_strtoul (state[4].arg, 0, 0) : 0;
|
||||
|
||||
nvram = grub_zalloc (nvramsize);
|
||||
if (!nvram)
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"Couldn't allocate space for temporary pnvram storage");
|
||||
|
||||
if (argc == 1 && (err = read_pnvram (args[0])))
|
||||
{
|
||||
grub_free (nvram);
|
||||
return err;
|
||||
}
|
||||
return grub_efiemu_make_nvram ();
|
||||
}
|
||||
|
||||
static grub_extcmd_t cmd;
|
||||
|
||||
void grub_efiemu_pnvram_cmd_register (void);
|
||||
void grub_efiemu_pnvram_cmd_unregister (void);
|
||||
|
||||
void
|
||||
grub_efiemu_pnvram_cmd_register (void)
|
||||
{
|
||||
cmd = grub_register_extcmd ("efiemu_pnvram", grub_cmd_efiemu_pnvram,
|
||||
GRUB_COMMAND_FLAG_BOTH,
|
||||
"efiemu_pnvram [FILENAME]",
|
||||
"Initialise pseudo-NVRAM and load variables "
|
||||
"from FILE",
|
||||
options);
|
||||
}
|
||||
|
||||
void
|
||||
grub_efiemu_pnvram_cmd_unregister (void)
|
||||
{
|
||||
grub_unregister_extcmd (cmd);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue