loadcore_common.c 4.67 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
/* Load runtime image of EFIemu. Functions common to 32/64-bit mode */
/*
 *  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 <http://www.gnu.org/licenses/>.
 */

#include <grub/file.h>
#include <grub/err.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/efiemu/efiemu.h>
#include <grub/cpu/efiemu.h>

/* Are we in 32 or 64-bit mode?*/
static grub_efiemu_mode_t grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED;
/* Runtime ELF file */
static grub_ssize_t efiemu_core_size;
static void *efiemu_core = 0;
/* Linked list of segments */
static grub_efiemu_segment_t efiemu_segments = 0;

/* equivalent to sizeof (grub_efi_uintn_t) but taking the mode into account*/
int
grub_efiemu_sizeof_uintn_t (void)
{
  if (grub_efiemu_mode == GRUB_EFIEMU32)
    return 4;
  if (grub_efiemu_mode == GRUB_EFIEMU64)
    return 8;
  return 0;
}

/* Check the header and set mode */
static grub_err_t
48
grub_efiemu_check_header (void *ehdr, grub_size_t size,
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
			  grub_efiemu_mode_t *mode)
{
  /* Check the magic numbers.  */
  if ((*mode == GRUB_EFIEMU_NOTLOADED || *mode == GRUB_EFIEMU32)
      && grub_efiemu_check_header32 (ehdr,size))
    {
      *mode = GRUB_EFIEMU32;
      return GRUB_ERR_NONE;
    }
  if ((*mode == GRUB_EFIEMU_NOTLOADED || *mode == GRUB_EFIEMU64)
      && grub_efiemu_check_header64 (ehdr,size))
    {
      *mode = GRUB_EFIEMU64;
      return GRUB_ERR_NONE;
    }
  return grub_error (GRUB_ERR_BAD_OS, "invalid ELF magic");
}

/* Unload segments */
static int
grub_efiemu_unload_segs (grub_efiemu_segment_t seg)
{
  grub_efiemu_segment_t segn;
  for (; seg; seg = segn)
    {
      segn = seg->next;
      grub_efiemu_mm_return_request (seg->handle);
      grub_free (seg);
    }
  return 1;
}


grub_err_t
grub_efiemu_loadcore_unload(void)
{
  switch (grub_efiemu_mode)
    {
    case GRUB_EFIEMU32:
      grub_efiemu_loadcore_unload32 ();
      break;

    case GRUB_EFIEMU64:
      grub_efiemu_loadcore_unload64 ();
      break;
94

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
    default:
      break;
    }

  grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED;

  grub_free (efiemu_core);
  efiemu_core = 0;

  grub_efiemu_unload_segs (efiemu_segments);
  efiemu_segments = 0;

  grub_efiemu_free_syms ();

  return GRUB_ERR_NONE;
}

112
/* Load runtime file and do some initial preparations */
113
grub_err_t
114 115
grub_efiemu_loadcore_init (grub_file_t file,
			   const char *filename)
116 117 118 119 120 121 122 123 124
{
  grub_err_t err;

  efiemu_core_size = grub_file_size (file);
  efiemu_core = 0;
  efiemu_core = grub_malloc (efiemu_core_size);
  if (! efiemu_core)
    return grub_errno;

125
  if (grub_file_read (file, efiemu_core, efiemu_core_size)
126 127 128 129 130 131 132
      != (int) efiemu_core_size)
    {
      grub_free (efiemu_core);
      efiemu_core = 0;
      return grub_errno;
    }

133
  if (grub_efiemu_check_header (efiemu_core, efiemu_core_size,
134 135 136 137 138 139 140 141 142 143
				&grub_efiemu_mode))
    {
      grub_free (efiemu_core);
      efiemu_core = 0;
      return GRUB_ERR_BAD_MODULE;
    }

  switch (grub_efiemu_mode)
    {
    case GRUB_EFIEMU32:
144 145
      err = grub_efiemu_loadcore_init32 (efiemu_core, filename,
					 efiemu_core_size,
146 147
					 &efiemu_segments);
      if (err)
148 149 150 151 152 153 154 155 156
	{
	  grub_free (efiemu_core);
	  efiemu_core = 0;
	  grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED;
	  return err;
	}
      break;

    case GRUB_EFIEMU64:
157 158
      err = grub_efiemu_loadcore_init64 (efiemu_core, filename,
					 efiemu_core_size,
159 160
					 &efiemu_segments);
      if (err)
161 162 163 164 165 166 167
	{
	  grub_free (efiemu_core);
	  efiemu_core = 0;
	  grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED;
	  return err;
	}
      break;
168

169
    default:
170
      return grub_error (GRUB_ERR_BUG, "unknown EFI runtime");
171 172 173 174 175 176 177 178 179 180 181
    }
  return GRUB_ERR_NONE;
}

grub_err_t
grub_efiemu_loadcore_load (void)
{
  grub_err_t err;
  switch (grub_efiemu_mode)
    {
    case GRUB_EFIEMU32:
182 183 184 185
      err = grub_efiemu_loadcore_load32 (efiemu_core, efiemu_core_size,
					 efiemu_segments);
      if (err)
	grub_efiemu_loadcore_unload ();
186 187
      return err;
    case GRUB_EFIEMU64:
188 189 190 191
      err = grub_efiemu_loadcore_load64 (efiemu_core, efiemu_core_size,
					 efiemu_segments);
      if (err)
	grub_efiemu_loadcore_unload ();
192 193
      return err;
    default:
194
      return grub_error (GRUB_ERR_BUG, "unknown EFI runtime");
195 196
    }
}