/* Export pnvram and some variables for runtime */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2009 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GRUB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ #include #include #include #include #include #include #include #include /* Place for final location of variables */ static int nvram_handle = 0; static int nvramsize_handle = 0; static int high_monotonic_count_handle = 0; 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) { if (arg[0] == '-') return -grub_strtoul (arg + 1, end, base); return grub_strtoul (arg, end, base); } /* Export stuff for efiemu */ static grub_err_t nvram_set (void * data __attribute__ ((unused))) { /* Take definitive pointers */ grub_uint8_t *nvram_def = 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_efiemu_mm_obtain_request (high_monotonic_count_handle); grub_int16_t *timezone_def = grub_efiemu_mm_obtain_request (timezone_handle); grub_uint8_t *daylight_def = grub_efiemu_mm_obtain_request (daylight_handle); grub_uint32_t *accuracy_def = grub_efiemu_mm_obtain_request (accuracy_handle); /* Copy to definitive loaction */ grub_dprintf ("efiemu", "preparing pnvram\n"); grub_memcpy (nvram_def, nvram, nvramsize); *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); grub_efiemu_register_symbol ("efiemu_varsize", nvramsize_handle, 0); grub_efiemu_register_symbol ("efiemu_high_monotonic_count", high_monotonic_count_handle, 0); grub_efiemu_register_symbol ("efiemu_time_zone", timezone_handle, 0); grub_efiemu_register_symbol ("efiemu_time_daylight", daylight_handle, 0); grub_efiemu_register_symbol ("efiemu_time_accuracy", accuracy_handle, 0); return GRUB_ERR_NONE; } static void nvram_unload (void * data __attribute__ ((unused))) { grub_efiemu_mm_return_request (nvram_handle); grub_efiemu_mm_return_request (nvramsize_handle); grub_efiemu_mm_return_request (high_monotonic_count_handle); 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 err; err = grub_efiemu_autocore (); if (err) { grub_free (nvram); return err; } err = grub_efiemu_register_prepare_hook (nvram_set, nvram_unload, 0); if (err) { grub_free (nvram); return err; } nvram_handle = grub_efiemu_request_memalign (1, nvramsize, GRUB_EFI_RUNTIME_SERVICES_DATA); nvramsize_handle = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t), GRUB_EFI_RUNTIME_SERVICES_DATA); high_monotonic_count_handle = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t), GRUB_EFI_RUNTIME_SERVICES_DATA); timezone_handle = grub_efiemu_request_memalign (1, sizeof (grub_uint16_t), GRUB_EFI_RUNTIME_SERVICES_DATA); daylight_handle = grub_efiemu_request_memalign (1, sizeof (grub_uint8_t), GRUB_EFI_RUNTIME_SERVICES_DATA); accuracy_handle = 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_malloc (nvramsize); if (!nvram) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Couldn't allocate space for temporary pnvram storage"); grub_memset (nvram, 0, nvramsize); 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_malloc (nvramsize); if (!nvram) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Couldn't allocate space for temporary pnvram storage"); grub_memset (nvram, 0, nvramsize); 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); }