main.c 7.84 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 *  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/>.
 */

/* This is an emulation of EFI runtime services.
20 21
   This allows a more uniform boot on i386 machines.
   As it emulates only runtime service it isn't able
22 23 24 25 26 27 28 29 30 31 32
   to chainload EFI bootloader on non-EFI system. */


#include <grub/file.h>
#include <grub/err.h>
#include <grub/normal.h>
#include <grub/mm.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/efiemu/efiemu.h>
#include <grub/command.h>
33
#include <grub/i18n.h>
34

35 36
GRUB_MOD_LICENSE ("GPLv3+");

37 38 39 40 41 42 43
/* System table. Two version depending on mode */
grub_efi_system_table32_t *grub_efiemu_system_table32 = 0;
grub_efi_system_table64_t *grub_efiemu_system_table64 = 0;
/* Modules may need to execute some actions after memory allocation happens */
static struct grub_efiemu_prepare_hook *efiemu_prepare_hooks = 0;
/* Linked list of configuration tables */
static struct grub_efiemu_configuration_table *efiemu_config_tables = 0;
44
static int prepared = 0;
45 46 47 48 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

/* Free all allocated space */
grub_err_t
grub_efiemu_unload (void)
{
  struct grub_efiemu_configuration_table *cur, *d;
  struct grub_efiemu_prepare_hook *curhook, *d2;
  grub_efiemu_loadcore_unload ();

  grub_efiemu_mm_unload ();

  for (cur = efiemu_config_tables; cur;)
    {
      d = cur->next;
      if (cur->unload)
	cur->unload (cur->data);
      grub_free (cur);
      cur = d;
    }
  efiemu_config_tables = 0;

  for (curhook = efiemu_prepare_hooks; curhook;)
    {
      d2 = curhook->next;
      if (curhook->unload)
	curhook->unload (curhook->data);
      grub_free (curhook);
      curhook = d2;
    }
  efiemu_prepare_hooks = 0;

76 77
  prepared = 0;

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
  return GRUB_ERR_NONE;
}

/* Remove previously registered table from the list */
grub_err_t
grub_efiemu_unregister_configuration_table (grub_efi_guid_t guid)
{
  struct grub_efiemu_configuration_table *cur, *prev;

  /* Special treating if head is to remove */
  while (efiemu_config_tables
	 && !grub_memcmp (&(efiemu_config_tables->guid), &guid, sizeof (guid)))
    {
      if (efiemu_config_tables->unload)
	  efiemu_config_tables->unload (efiemu_config_tables->data);
	cur = efiemu_config_tables->next;
	grub_free (efiemu_config_tables);
	efiemu_config_tables = cur;
    }
  if (!efiemu_config_tables)
    return GRUB_ERR_NONE;

  /* Remove from chain */
  for (prev = efiemu_config_tables, cur = prev->next; cur;)
    if (grub_memcmp (&(cur->guid), &guid, sizeof (guid)) == 0)
      {
	if (cur->unload)
	  cur->unload (cur->data);
	prev->next = cur->next;
	grub_free (cur);
	cur = prev->next;
      }
    else
      {
	prev = cur;
	cur = cur->next;
      }
  return GRUB_ERR_NONE;
}

grub_err_t
grub_efiemu_register_prepare_hook (grub_err_t (*hook) (void *data),
120
				   void (*unload) (void *data),
121 122 123 124 125
				   void *data)
{
  struct grub_efiemu_prepare_hook *nhook;
  nhook = (struct grub_efiemu_prepare_hook *) grub_malloc (sizeof (*nhook));
  if (! nhook)
126
    return grub_errno;
127 128 129 130 131 132 133 134
  nhook->hook = hook;
  nhook->unload = unload;
  nhook->data = data;
  nhook->next = efiemu_prepare_hooks;
  efiemu_prepare_hooks = nhook;
  return GRUB_ERR_NONE;
}

135
/* Register a configuration table either supplying the address directly
136 137 138
   or with a hook
*/
grub_err_t
139
grub_efiemu_register_configuration_table (grub_efi_guid_t guid,
140
					  void * (*get_table) (void *data),
141
					  void (*unload) (void *data),
142 143 144 145
					  void *data)
{
  struct grub_efiemu_configuration_table *tbl;
  grub_err_t err;
146

147 148
 err = grub_efiemu_unregister_configuration_table (guid);
  if (err)
149 150 151 152
    return err;

  tbl = (struct grub_efiemu_configuration_table *) grub_malloc (sizeof (*tbl));
  if (! tbl)
153
    return grub_errno;
154 155 156 157 158 159 160 161 162 163 164 165 166

  tbl->guid = guid;
  tbl->get_table = get_table;
  tbl->unload = unload;
  tbl->data = data;
  tbl->next = efiemu_config_tables;
  efiemu_config_tables = tbl;

  return GRUB_ERR_NONE;
}

static grub_err_t
grub_cmd_efiemu_unload (grub_command_t cmd __attribute__ ((unused)),
167
			int argc __attribute__ ((unused)),
168 169 170 171 172 173 174
			char *args[] __attribute__ ((unused)))
{
  return grub_efiemu_unload ();
}

static grub_err_t
grub_cmd_efiemu_prepare (grub_command_t cmd __attribute__ ((unused)),
175
			 int argc __attribute__ ((unused)),
176 177 178 179 180 181 182 183 184 185 186 187 188
			 char *args[] __attribute__ ((unused)))
{
  return grub_efiemu_prepare ();
}



/* Load the runtime from the file FILENAME.  */
static grub_err_t
grub_efiemu_load_file (const char *filename)
{
  grub_file_t file;
  grub_err_t err;
189

190 191
  file = grub_file_open (filename);
  if (! file)
192
    return grub_errno;
193

194 195 196 197 198
  err = grub_efiemu_mm_init ();
  if (err)
    {
      grub_file_close (file);
      grub_efiemu_unload ();
199
      return err;
200 201
    }

202
  grub_dprintf ("efiemu", "mm initialized\n");
203

204
  err = grub_efiemu_loadcore_init (file, filename);
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
  if (err)
    {
      grub_file_close (file);
      grub_efiemu_unload ();
      return err;
    }

  grub_file_close (file);

  /* For configuration tables entry in system table. */
  grub_efiemu_request_symbols (1);

  return GRUB_ERR_NONE;
}

grub_err_t
grub_efiemu_autocore (void)
{
  const char *prefix;
  char *filename;
225
  const char *suffix;
226 227 228 229 230 231
  grub_err_t err;

  if (grub_efiemu_sizeof_uintn_t () != 0)
    return GRUB_ERR_NONE;

  prefix = grub_env_get ("prefix");
232

233
  if (! prefix)
234
    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
235
		       N_("variable `%s' isn't set"), "prefix");
236

237
  suffix = grub_efiemu_get_default_core_name ();
238

239 240
  filename = grub_xasprintf ("%s/" GRUB_TARGET_CPU "-" GRUB_PLATFORM "/%s",
			     prefix, suffix);
241
  if (! filename)
242
    return grub_errno;
243

244 245 246 247
  err = grub_efiemu_load_file (filename);
  grub_free (filename);
  if (err)
    return err;
248
#ifndef GRUB_MACHINE_EMU
249 250 251 252 253 254 255 256 257 258 259 260 261
  err = grub_machine_efiemu_init_tables ();
  if (err)
    return err;
#endif

  return GRUB_ERR_NONE;
}

grub_err_t
grub_efiemu_prepare (void)
{
  grub_err_t err;

262 263 264
  if (prepared)
    return GRUB_ERR_NONE;

265 266 267 268
  err = grub_efiemu_autocore ();
  if (err)
    return err;

269
  grub_dprintf ("efiemu", "Preparing %d-bit efiemu\n",
270 271
		8 * grub_efiemu_sizeof_uintn_t ());

272
  /* Create NVRAM. */
273 274
  grub_efiemu_pnvram ();

275 276
  prepared = 1;

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
  if (grub_efiemu_sizeof_uintn_t () == 4)
    return grub_efiemu_prepare32 (efiemu_prepare_hooks, efiemu_config_tables);
  else
    return grub_efiemu_prepare64 (efiemu_prepare_hooks, efiemu_config_tables);
}


static grub_err_t
grub_cmd_efiemu_load (grub_command_t cmd __attribute__ ((unused)),
		      int argc, char *args[])
{
  grub_err_t err;

  grub_efiemu_unload ();

  if (argc != 1)
293
    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
294 295 296 297

  err = grub_efiemu_load_file (args[0]);
  if (err)
    return err;
298
#ifndef GRUB_MACHINE_EMU
299 300 301 302 303 304 305 306 307 308 309
  err = grub_machine_efiemu_init_tables ();
  if (err)
    return err;
#endif
  return GRUB_ERR_NONE;
}

static grub_command_t cmd_loadcore, cmd_prepare, cmd_unload;

GRUB_MOD_INIT(efiemu)
{
310 311
  cmd_loadcore = grub_register_command ("efiemu_loadcore",
					grub_cmd_efiemu_load,
312 313
					N_("FILE"),
					N_("Load and initialize EFI emulator."));
314 315
  cmd_prepare = grub_register_command ("efiemu_prepare",
				       grub_cmd_efiemu_prepare,
316
				       0,
317
				       N_("Finalize loading of EFI emulator."));
318
  cmd_unload = grub_register_command ("efiemu_unload", grub_cmd_efiemu_unload,
319
				      0,
320
				      N_("Unload EFI emulator."));
321 322 323 324 325 326 327 328
}

GRUB_MOD_FINI(efiemu)
{
  grub_unregister_command (cmd_loadcore);
  grub_unregister_command (cmd_prepare);
  grub_unregister_command (cmd_unload);
}