2N/A/* Code for managing symbols and pointers in efiemu */
2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2009 Free Software Foundation, Inc.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <grub/err.h>
2N/A#include <grub/mm.h>
2N/A#include <grub/misc.h>
2N/A#include <grub/efiemu/efiemu.h>
2N/A#include <grub/efiemu/runtime.h>
2N/A
2N/Astatic int ptv_written = 0;
2N/Astatic int ptv_alloc = 0;
2N/Astatic int ptv_handle = 0;
2N/Astatic int relocated_handle = 0;
2N/Astatic int ptv_requested = 0;
2N/Astatic struct grub_efiemu_sym *efiemu_syms = 0;
2N/A
2N/Astruct grub_efiemu_sym
2N/A{
2N/A struct grub_efiemu_sym *next;
2N/A char *name;
2N/A int handle;
2N/A grub_off_t off;
2N/A};
2N/A
2N/Avoid
2N/Agrub_efiemu_free_syms (void)
2N/A{
2N/A struct grub_efiemu_sym *cur, *d;
2N/A for (cur = efiemu_syms; cur;)
2N/A {
2N/A d = cur->next;
2N/A grub_free (cur->name);
2N/A grub_free (cur);
2N/A cur = d;
2N/A }
2N/A efiemu_syms = 0;
2N/A ptv_written = 0;
2N/A ptv_alloc = 0;
2N/A ptv_requested = 0;
2N/A grub_efiemu_mm_return_request (ptv_handle);
2N/A ptv_handle = 0;
2N/A grub_efiemu_mm_return_request (relocated_handle);
2N/A relocated_handle = 0;
2N/A}
2N/A
2N/A/* Announce that the module will need NUM allocators */
2N/A/* Because of deferred memory allocation all the relocators have to be
2N/A announced during phase 1*/
2N/Agrub_err_t
2N/Agrub_efiemu_request_symbols (int num)
2N/A{
2N/A if (ptv_alloc)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT,
2N/A "symbols have already been allocated");
2N/A if (num < 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT,
2N/A "can't request negative symbols");
2N/A ptv_requested += num;
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/* Resolve the symbol name NAME and set HANDLE and OFF accordingly */
2N/Agrub_err_t
2N/Agrub_efiemu_resolve_symbol (const char *name, int *handle, grub_off_t *off)
2N/A{
2N/A struct grub_efiemu_sym *cur;
2N/A for (cur = efiemu_syms; cur; cur = cur->next)
2N/A if (!grub_strcmp (name, cur->name))
2N/A {
2N/A *handle = cur->handle;
2N/A *off = cur->off;
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A grub_dprintf ("efiemu", "%s not found\n", name);
2N/A return grub_error (GRUB_ERR_BAD_OS, "symbol %s isn't found", name);
2N/A}
2N/A
2N/A/* Register symbol named NAME in memory handle HANDLE at offset OFF */
2N/Agrub_err_t
2N/Agrub_efiemu_register_symbol (const char *name, int handle, grub_off_t off)
2N/A{
2N/A struct grub_efiemu_sym *cur;
2N/A cur = (struct grub_efiemu_sym *) grub_malloc (sizeof (*cur));
2N/A grub_dprintf ("efiemu", "registering symbol '%s'\n", name);
2N/A if (!cur)
2N/A return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't register symbol");
2N/A cur->name = grub_strdup (name);
2N/A cur->next = efiemu_syms;
2N/A cur->handle = handle;
2N/A cur->off = off;
2N/A efiemu_syms = cur;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A/* Go from phase 1 to phase 2. Must be called before similar function in mm.c */
2N/Agrub_err_t
2N/Agrub_efiemu_alloc_syms (void)
2N/A{
2N/A ptv_alloc = ptv_requested;
2N/A ptv_handle = grub_efiemu_request_memalign
2N/A (1, (ptv_requested + 1) * sizeof (struct grub_efiemu_ptv_rel),
2N/A GRUB_EFI_RUNTIME_SERVICES_DATA);
2N/A relocated_handle = grub_efiemu_request_memalign
2N/A (1, sizeof (grub_uint8_t), GRUB_EFI_RUNTIME_SERVICES_DATA);
2N/A
2N/A grub_efiemu_register_symbol ("efiemu_ptv_relocated", relocated_handle, 0);
2N/A grub_efiemu_register_symbol ("efiemu_ptv_relloc", ptv_handle, 0);
2N/A return grub_errno;
2N/A}
2N/A
2N/Agrub_err_t
2N/Agrub_efiemu_write_sym_markers (void)
2N/A{
2N/A struct grub_efiemu_ptv_rel *ptv_rels
2N/A = grub_efiemu_mm_obtain_request (ptv_handle);
2N/A grub_uint8_t *relocated = grub_efiemu_mm_obtain_request (relocated_handle);
2N/A grub_memset (ptv_rels, 0, (ptv_requested + 1)
2N/A * sizeof (struct grub_efiemu_ptv_rel));
2N/A *relocated = 0;
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/* Write value (pointer to memory PLUS_HANDLE)
2N/A - (pointer to memory MINUS_HANDLE) + VALUE to ADDR assuming that the
2N/A size SIZE bytes. If PTV_NEEDED is 1 then announce it to runtime that this
2N/A value needs to be recomputed before going to virtual mode
2N/A*/
2N/Agrub_err_t
2N/Agrub_efiemu_write_value (void *addr, grub_uint32_t value, int plus_handle,
2N/A int minus_handle, int ptv_needed, int size)
2N/A{
2N/A /* Announce relocator to runtime */
2N/A if (ptv_needed)
2N/A {
2N/A struct grub_efiemu_ptv_rel *ptv_rels
2N/A = grub_efiemu_mm_obtain_request (ptv_handle);
2N/A
2N/A if (ptv_needed && ptv_written >= ptv_alloc)
2N/A return grub_error (GRUB_ERR_OUT_OF_MEMORY,
2N/A "your module didn't declare efiemu "
2N/A " relocators correctly");
2N/A
2N/A if (minus_handle)
2N/A ptv_rels[ptv_written].minustype
2N/A = grub_efiemu_mm_get_type (minus_handle);
2N/A else
2N/A ptv_rels[ptv_written].minustype = 0;
2N/A
2N/A if (plus_handle)
2N/A ptv_rels[ptv_written].plustype
2N/A = grub_efiemu_mm_get_type (plus_handle);
2N/A else
2N/A ptv_rels[ptv_written].plustype = 0;
2N/A
2N/A ptv_rels[ptv_written].addr = (grub_addr_t) addr;
2N/A ptv_rels[ptv_written].size = size;
2N/A ptv_written++;
2N/A
2N/A /* memset next value to zero to mark the end */
2N/A grub_memset (&ptv_rels[ptv_written], 0, sizeof (ptv_rels[ptv_written]));
2N/A }
2N/A
2N/A /* Compute the value */
2N/A if (minus_handle)
2N/A value -= (grub_addr_t) grub_efiemu_mm_obtain_request (minus_handle);
2N/A
2N/A if (plus_handle)
2N/A value += (grub_addr_t) grub_efiemu_mm_obtain_request (plus_handle);
2N/A
2N/A /* Write the value */
2N/A switch (size)
2N/A {
2N/A case 8:
2N/A *((grub_uint64_t *) addr) = value;
2N/A break;
2N/A case 4:
2N/A *((grub_uint32_t *) addr) = value;
2N/A break;
2N/A case 2:
2N/A *((grub_uint16_t *) addr) = value;
2N/A break;
2N/A case 1:
2N/A *((grub_uint8_t *) addr) = value;
2N/A break;
2N/A default:
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "wrong symbol size");
2N/A }
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Agrub_err_t
2N/Agrub_efiemu_set_virtual_address_map (grub_efi_uintn_t memory_map_size,
2N/A grub_efi_uintn_t descriptor_size,
2N/A grub_efi_uint32_t descriptor_version
2N/A __attribute__ ((unused)),
2N/A grub_efi_memory_descriptor_t *virtual_map)
2N/A{
2N/A grub_uint8_t *ptv_relocated;
2N/A struct grub_efiemu_ptv_rel *cur_relloc;
2N/A struct grub_efiemu_ptv_rel *ptv_rels;
2N/A
2N/A ptv_relocated = grub_efiemu_mm_obtain_request (relocated_handle);
2N/A ptv_rels = grub_efiemu_mm_obtain_request (ptv_handle);
2N/A
2N/A /* Ensure that we are called only once */
2N/A if (*ptv_relocated)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "EfiEmu is already relocated");
2N/A *ptv_relocated = 1;
2N/A
2N/A /* Correct addresses using information supplied by grub */
2N/A for (cur_relloc = ptv_rels; cur_relloc->size; cur_relloc++)
2N/A {
2N/A grub_int64_t corr = 0;
2N/A grub_efi_memory_descriptor_t *descptr;
2N/A
2N/A /* Compute correction */
2N/A for (descptr = virtual_map;
2N/A (grub_size_t) ((grub_uint8_t *) descptr
2N/A - (grub_uint8_t *) virtual_map) < memory_map_size;
2N/A descptr = (grub_efi_memory_descriptor_t *)
2N/A ((grub_uint8_t *) descptr + descriptor_size))
2N/A {
2N/A if (descptr->type == cur_relloc->plustype)
2N/A corr += descptr->virtual_start - descptr->physical_start;
2N/A if (descptr->type == cur_relloc->minustype)
2N/A corr -= descptr->virtual_start - descptr->physical_start;
2N/A }
2N/A
2N/A /* Apply correction */
2N/A switch (cur_relloc->size)
2N/A {
2N/A case 8:
2N/A *((grub_uint64_t *) (grub_addr_t) cur_relloc->addr) += corr;
2N/A break;
2N/A case 4:
2N/A *((grub_uint32_t *) (grub_addr_t) cur_relloc->addr) += corr;
2N/A break;
2N/A case 2:
2N/A *((grub_uint16_t *) (grub_addr_t) cur_relloc->addr) += corr;
2N/A break;
2N/A case 1:
2N/A *((grub_uint8_t *) (grub_addr_t) cur_relloc->addr) += corr;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A /* Recompute crc32 of system table and runtime services */
2N/A
2N/A if (grub_efiemu_sizeof_uintn_t () == 4)
2N/A return grub_efiemu_crc32 ();
2N/A else
2N/A return grub_efiemu_crc64 ();
2N/A}