mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-11-04 12:55:16 +00:00 
			
		
		
		
	* commands/acpi.c (options): Fix capitalizations and/or full stops. (GRUB_MOD_INIT): Likewise. * commands/boot.c (GRUB_MOD_INIT): Likewise. * commands/cmp.c (grub_cmd_cmp): Likewise. * commands/echo.c (options): Likewise. * commands/efi/loadbios.c (enable_rom_area): Likewise. (enable_rom_area): Likewise. (GRUB_MOD_INIT): Likewise. * commands/gptsync.c (GRUB_MOD_INIT): Likewise. * commands/halt.c (GRUB_MOD_INIT): Improve the help message. * commands/handler.c (GRUB_MOD_INIT): Likewise. * commands/hdparm.c (options): Fix capitalizations and/or full stops. * commands/hexdump.c (options): Likewise. * commands/i386/cpuid.c (options): Likewise. (GRUB_MOD_INIT): Likewise. * commands/i386/pc/drivemap.c (options): Likewise. (GRUB_MOD_INIT): Likewise. * commands/i386/pc/halt (options): Likewise. (GRUB_MOD_INIT): Likewise. * commands/i386/pc/play.c (GRUB_MOD_INIT): Likewise. * commands/i386/pc/pxecmd.c (options): Likewise. * commands/i386/pc/vbetest.c (GRUB_MOD_INIT): Likewise. * commands/ieee1275/suspend.c (GRUB_MOD_INIT): Likewise. * commands/keystatus.c (options): Likewise. (GRUB_MOD_INIT): Likewise. * commands/loadenv.c (options): Likewise. * commands/ls.c (options): Likewise. * commands/lspci.c (GRUB_MOD_INIT): Likewise. * commands/memrw.c (GRUB_MOD_INIT): Likewise. * commands/minicmd.c (GRUB_MOD_INIT): Likewise. * commands/parttool.c (helpmsg): Likewise. * commands/probe.c (options): Likewise. * commands/read.c (GRUB_MOD_INIT): Likewise. * commands/reboot.c (GRUB_MOD_INIT): Likewise. * commands/search.c (options): Likewise. * commands/sleep.c (options): Likewise. * commands/test.c (GRUB_MOD_INIT): Likewise. * commands/true.c (GRUB_MOD_INIT): Likewise. * commands/usbtest.c (GRUB_MOD_INIT): Likewise. * commands/videotest.c (GRUB_MOD_INIT): Likewise. * lib/arg.c (help_options): Likewise.
		
			
				
	
	
		
			774 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			774 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* acpi.c - modify acpi tables. */
 | 
						|
/*
 | 
						|
 *  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/dl.h>
 | 
						|
#include <grub/extcmd.h>
 | 
						|
#include <grub/file.h>
 | 
						|
#include <grub/disk.h>
 | 
						|
#include <grub/term.h>
 | 
						|
#include <grub/misc.h>
 | 
						|
#include <grub/gzio.h>
 | 
						|
#include <grub/acpi.h>
 | 
						|
#include <grub/mm.h>
 | 
						|
#include <grub/machine/memory.h>
 | 
						|
#include <grub/memory.h>
 | 
						|
 | 
						|
#ifdef GRUB_MACHINE_EFI
 | 
						|
#include <grub/efi/efi.h>
 | 
						|
#include <grub/efi/api.h>
 | 
						|
#endif
 | 
						|
 | 
						|
static const struct grub_arg_option options[] = {
 | 
						|
  {"exclude", 'x', 0,
 | 
						|
   "Don't load host tables specified by comma-separated list.",
 | 
						|
   0, ARG_TYPE_STRING},
 | 
						|
  {"load-only", 'n', 0,
 | 
						|
   "Load only tables specified by comma-separated list.", 0, ARG_TYPE_STRING},
 | 
						|
  {"v1", '1', 0, "Expose v1 tables.", 0, ARG_TYPE_NONE},
 | 
						|
  {"v2", '2', 0, "Expose v2 and v3 tables.", 0, ARG_TYPE_NONE},
 | 
						|
  {"oemid", 'o', 0, "Set OEMID of RSDP, XSDT and RSDT.", 0, ARG_TYPE_STRING},
 | 
						|
  {"oemtable", 't', 0,
 | 
						|
   "Set OEMTABLE ID of RSDP, XSDT and RSDT.", 0, ARG_TYPE_STRING},
 | 
						|
  {"oemtablerev", 'r', 0,
 | 
						|
   "Set OEMTABLE revision of RSDP, XSDT and RSDT.", 0, ARG_TYPE_INT},
 | 
						|
  {"oemtablecreator", 'c', 0,
 | 
						|
   "Set creator field of RSDP, XSDT and RSDT.", 0, ARG_TYPE_STRING},
 | 
						|
  {"oemtablecreatorrev", 'd', 0,
 | 
						|
   "Set creator revision of RSDP, XSDT and RSDT.", 0, ARG_TYPE_INT},
 | 
						|
  {"no-ebda", 'e', 0, "Don't update EBDA. May fix failures or hangs on some."
 | 
						|
   " BIOSes but makes it ineffective with OS not receiving RSDP from GRUB.",
 | 
						|
   0, ARG_TYPE_NONE},
 | 
						|
  {0, 0, 0, 0, 0, 0}
 | 
						|
};
 | 
						|
 | 
						|
/* Simple checksum by summing all bytes. Used by ACPI and SMBIOS. */
 | 
						|
grub_uint8_t
 | 
						|
grub_byte_checksum (void *base, grub_size_t size)
 | 
						|
{
 | 
						|
  grub_uint8_t *ptr;
 | 
						|
  grub_uint8_t ret = 0;
 | 
						|
  for (ptr = (grub_uint8_t *) base; ptr < ((grub_uint8_t *) base) + size;
 | 
						|
       ptr++)
 | 
						|
    ret += *ptr;
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* rev1 is 1 if ACPIv1 is to be generated, 0 otherwise.
 | 
						|
   rev2 contains the revision of ACPIv2+ to generate or 0 if none. */
 | 
						|
static int rev1, rev2;
 | 
						|
/* OEMID of RSDP, RSDT and XSDT. */
 | 
						|
static char root_oemid[6];
 | 
						|
/* OEMTABLE of the same tables. */
 | 
						|
static char root_oemtable[8];
 | 
						|
/* OEMREVISION of the same tables. */
 | 
						|
static grub_uint32_t root_oemrev;
 | 
						|
/* CreatorID of the same tables. */
 | 
						|
static char root_creator_id[4];
 | 
						|
/* CreatorRevision of the same tables. */
 | 
						|
static grub_uint32_t root_creator_rev;
 | 
						|
static struct grub_acpi_rsdp_v10 *rsdpv1_new = 0;
 | 
						|
static struct grub_acpi_rsdp_v20 *rsdpv2_new = 0;
 | 
						|
static char *playground = 0, *playground_ptr = 0;
 | 
						|
static int playground_size = 0;
 | 
						|
 | 
						|
/* Linked list of ACPI tables. */
 | 
						|
struct efiemu_acpi_table
 | 
						|
{
 | 
						|
  void *addr;
 | 
						|
  grub_size_t size;
 | 
						|
  struct efiemu_acpi_table *next;
 | 
						|
};
 | 
						|
static struct efiemu_acpi_table *acpi_tables = 0;
 | 
						|
 | 
						|
/* DSDT isn't in RSDT. So treat it specially. */
 | 
						|
static void *table_dsdt = 0;
 | 
						|
/* Pointer to recreated RSDT. */
 | 
						|
static void *rsdt_addr = 0;
 | 
						|
 | 
						|
/* Allocation handles for different tables. */
 | 
						|
static grub_size_t dsdt_size = 0;
 | 
						|
 | 
						|
/* Address of original FACS. */
 | 
						|
static grub_uint32_t facs_addr = 0;
 | 
						|
 | 
						|
struct grub_acpi_rsdp_v20 *
 | 
						|
grub_acpi_get_rsdpv2 (void)
 | 
						|
{
 | 
						|
  if (rsdpv2_new)
 | 
						|
    return rsdpv2_new;
 | 
						|
  if (rsdpv1_new)
 | 
						|
    return 0;
 | 
						|
  return grub_machine_acpi_get_rsdpv2 ();
 | 
						|
}
 | 
						|
 | 
						|
struct grub_acpi_rsdp_v10 *
 | 
						|
grub_acpi_get_rsdpv1 (void)
 | 
						|
{
 | 
						|
  if (rsdpv1_new)
 | 
						|
    return rsdpv1_new;
 | 
						|
  if (rsdpv2_new)
 | 
						|
    return 0;
 | 
						|
  return grub_machine_acpi_get_rsdpv1 ();
 | 
						|
}
 | 
						|
 | 
						|
static inline int
 | 
						|
iszero (grub_uint8_t *reg, int size)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  for (i = 0; i < size; i++)
 | 
						|
    if (reg[i])
 | 
						|
      return 0;
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
grub_err_t
 | 
						|
grub_acpi_create_ebda (void)
 | 
						|
{
 | 
						|
  int ebda_kb_len;
 | 
						|
  int ebda_len;
 | 
						|
  int mmapregion = 0;
 | 
						|
  grub_uint8_t *ebda, *v1inebda = 0, *v2inebda = 0;
 | 
						|
  grub_uint64_t highestlow = 0;
 | 
						|
  grub_uint8_t *targetebda, *target;
 | 
						|
  struct grub_acpi_rsdp_v10 *v1;
 | 
						|
  struct grub_acpi_rsdp_v20 *v2;
 | 
						|
  auto int NESTED_FUNC_ATTR find_hook (grub_uint64_t, grub_uint64_t,
 | 
						|
				       grub_uint32_t);
 | 
						|
  int NESTED_FUNC_ATTR find_hook (grub_uint64_t start, grub_uint64_t size,
 | 
						|
				  grub_uint32_t type)
 | 
						|
  {
 | 
						|
    grub_uint64_t end = start + size;
 | 
						|
    if (type != GRUB_MACHINE_MEMORY_AVAILABLE)
 | 
						|
      return 0;
 | 
						|
    if (end > 0x100000)
 | 
						|
      end = 0x100000;
 | 
						|
    if (end > start + ebda_len
 | 
						|
	&& highestlow < ((end - ebda_len) & (~0xf)) )
 | 
						|
      highestlow = (end - ebda_len) & (~0xf);
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  ebda = (grub_uint8_t *) UINT_TO_PTR ((*((grub_uint16_t *)0x40e)) << 4);
 | 
						|
  ebda_kb_len = *(grub_uint16_t *) ebda;
 | 
						|
  if (! ebda || ebda_kb_len > 16)
 | 
						|
    ebda_kb_len = 0;
 | 
						|
  ebda_len = (ebda_kb_len + 1) << 10;
 | 
						|
 | 
						|
  /* FIXME: use low-memory mm allocation once it's available. */
 | 
						|
  grub_mmap_iterate (find_hook);
 | 
						|
  targetebda = (grub_uint8_t *) UINT_TO_PTR (highestlow);
 | 
						|
  grub_dprintf ("acpi", "creating ebda @%llx\n",
 | 
						|
		(unsigned long long) highestlow);
 | 
						|
  if (! highestlow)
 | 
						|
    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
 | 
						|
		       "couldn't find space for the new EBDA");
 | 
						|
 | 
						|
  mmapregion = grub_mmap_register (PTR_TO_UINT64 (targetebda), ebda_len,
 | 
						|
				   GRUB_MACHINE_MEMORY_RESERVED);
 | 
						|
  if (! mmapregion)
 | 
						|
    return grub_errno;
 | 
						|
 | 
						|
  /* XXX: EBDA is unstandardized, so this implementation is heuristical. */
 | 
						|
  if (ebda_kb_len)
 | 
						|
    grub_memcpy (targetebda, ebda, 0x400);
 | 
						|
  else
 | 
						|
    grub_memset (targetebda, 0, 0x400);
 | 
						|
  *((grub_uint16_t *) targetebda) = ebda_kb_len + 1;
 | 
						|
  target = targetebda;
 | 
						|
 | 
						|
  v1 = grub_acpi_get_rsdpv1 ();
 | 
						|
  v2 = grub_acpi_get_rsdpv2 ();
 | 
						|
  if (v2 && v2->length > 40)
 | 
						|
    v2 = 0;
 | 
						|
 | 
						|
  /* First try to replace already existing rsdp. */
 | 
						|
  if (v2)
 | 
						|
    {
 | 
						|
      grub_dprintf ("acpi", "Scanning EBDA for old rsdpv2\n");
 | 
						|
      for (; target < targetebda + 0x400 - v2->length; target += 0x10)
 | 
						|
	if (grub_memcmp (target, "RSD PTR ", 8) == 0
 | 
						|
	    && grub_byte_checksum (target,
 | 
						|
				   sizeof (struct grub_acpi_rsdp_v10)) == 0
 | 
						|
	    && ((struct grub_acpi_rsdp_v10 *) target)->revision != 0
 | 
						|
	    && ((struct grub_acpi_rsdp_v20 *) target)->length <= v2->length)
 | 
						|
	  {
 | 
						|
	    grub_memcpy (target, v2, v2->length);
 | 
						|
	    grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target);
 | 
						|
	    v2inebda = target;
 | 
						|
	    target += v2->length;
 | 
						|
	    target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
 | 
						|
	    v2 = 0;
 | 
						|
	    break;
 | 
						|
	  }
 | 
						|
    }
 | 
						|
 | 
						|
  if (v1)
 | 
						|
    {
 | 
						|
      grub_dprintf ("acpi", "Scanning EBDA for old rsdpv1\n");
 | 
						|
      for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
 | 
						|
	   target += 0x10)
 | 
						|
	if (grub_memcmp (target, "RSD PTR ", 8) == 0
 | 
						|
	    && grub_byte_checksum (target,
 | 
						|
				   sizeof (struct grub_acpi_rsdp_v10)) == 0)
 | 
						|
	  {
 | 
						|
	    grub_memcpy (target, v1, sizeof (struct grub_acpi_rsdp_v10));
 | 
						|
	    grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target);
 | 
						|
	    v1inebda = target;
 | 
						|
	    target += sizeof (struct grub_acpi_rsdp_v10);
 | 
						|
	    target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
 | 
						|
	    v1 = 0;
 | 
						|
	    break;
 | 
						|
	  }
 | 
						|
    }
 | 
						|
 | 
						|
  target = targetebda + 0x100;
 | 
						|
 | 
						|
  /* Try contiguous zeros. */
 | 
						|
  if (v2)
 | 
						|
    {
 | 
						|
      grub_dprintf ("acpi", "Scanning EBDA for block of zeros\n");
 | 
						|
      for (; target < targetebda + 0x400 - v2->length; target += 0x10)
 | 
						|
	if (iszero (target, v2->length))
 | 
						|
	  {
 | 
						|
	    grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target);
 | 
						|
	    grub_memcpy (target, v2, v2->length);
 | 
						|
	    v2inebda = target;
 | 
						|
	    target += v2->length;
 | 
						|
	    target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
 | 
						|
	    v2 = 0;
 | 
						|
	    break;
 | 
						|
	  }
 | 
						|
    }
 | 
						|
 | 
						|
  if (v1)
 | 
						|
    {
 | 
						|
      grub_dprintf ("acpi", "Scanning EBDA for block of zeros\n");
 | 
						|
      for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
 | 
						|
	   target += 0x10)
 | 
						|
	if (iszero (target, sizeof (struct grub_acpi_rsdp_v10)))
 | 
						|
	  {
 | 
						|
	    grub_dprintf ("acpi", "Copying rsdpv1 to %p\n", target);
 | 
						|
	    grub_memcpy (target, v1, sizeof (struct grub_acpi_rsdp_v10));
 | 
						|
	    v1inebda = target;
 | 
						|
	    target += sizeof (struct grub_acpi_rsdp_v10);
 | 
						|
	    target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
 | 
						|
	    v1 = 0;
 | 
						|
	    break;
 | 
						|
	  }
 | 
						|
    }
 | 
						|
 | 
						|
  if (v1 || v2)
 | 
						|
    {
 | 
						|
      grub_mmap_unregister (mmapregion);
 | 
						|
      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
 | 
						|
			 "Couldn't find suitable spot in EBDA");
 | 
						|
    }
 | 
						|
 | 
						|
  /* Remove any other RSDT. */
 | 
						|
  for (target = targetebda;
 | 
						|
       target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
 | 
						|
       target += 0x10)
 | 
						|
    if (grub_memcmp (target, "RSD PTR ", 8) == 0
 | 
						|
	&& grub_byte_checksum (target,
 | 
						|
			       sizeof (struct grub_acpi_rsdp_v10)) == 0
 | 
						|
	&& target != v1inebda && target != v2inebda)
 | 
						|
      *target = 0;
 | 
						|
 | 
						|
  grub_dprintf ("acpi", "Switching EBDA\n");
 | 
						|
  (*((grub_uint16_t *) 0x40e)) = ((long)targetebda) >> 4;
 | 
						|
  grub_dprintf ("acpi", "EBDA switched\n");
 | 
						|
 | 
						|
  return GRUB_ERR_NONE;
 | 
						|
}
 | 
						|
 | 
						|
/* Create tables common to ACPIv1 and ACPIv2+ */
 | 
						|
static void
 | 
						|
setup_common_tables (void)
 | 
						|
{
 | 
						|
  struct efiemu_acpi_table *cur;
 | 
						|
  struct grub_acpi_table_header *rsdt;
 | 
						|
  grub_uint32_t *rsdt_entry;
 | 
						|
  int numoftables;
 | 
						|
 | 
						|
  /* Treat DSDT. */
 | 
						|
  grub_memcpy (playground_ptr, table_dsdt, dsdt_size);
 | 
						|
  grub_free (table_dsdt);
 | 
						|
  table_dsdt = playground_ptr;
 | 
						|
  playground_ptr += dsdt_size;
 | 
						|
 | 
						|
  /* Treat other tables. */
 | 
						|
  for (cur = acpi_tables; cur; cur = cur->next)
 | 
						|
    {
 | 
						|
      struct grub_acpi_fadt *fadt;
 | 
						|
 | 
						|
      grub_memcpy (playground_ptr, cur->addr, cur->size);
 | 
						|
      grub_free (cur->addr);
 | 
						|
      cur->addr = playground_ptr;
 | 
						|
      playground_ptr += cur->size;
 | 
						|
 | 
						|
      /* If it's FADT correct DSDT and FACS addresses. */
 | 
						|
      fadt = (struct grub_acpi_fadt *) cur->addr;
 | 
						|
      if (grub_memcmp (fadt->hdr.signature, "FACP", 4) == 0)
 | 
						|
	{
 | 
						|
	  fadt->dsdt_addr = PTR_TO_UINT32 (table_dsdt);
 | 
						|
	  fadt->facs_addr = facs_addr;
 | 
						|
 | 
						|
	  /* Does a revision 2 exist at all? */
 | 
						|
	  if (fadt->hdr.revision >= 3)
 | 
						|
	    {
 | 
						|
	      fadt->dsdt_xaddr = PTR_TO_UINT64 (table_dsdt);
 | 
						|
	      fadt->facs_xaddr = facs_addr;
 | 
						|
	    }
 | 
						|
 | 
						|
	  /* Recompute checksum. */
 | 
						|
	  fadt->hdr.checksum = 0;
 | 
						|
	  fadt->hdr.checksum = 1 + ~grub_byte_checksum (fadt, fadt->hdr.length);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* Fill RSDT entries. */
 | 
						|
  numoftables = 0;
 | 
						|
  for (cur = acpi_tables; cur; cur = cur->next)
 | 
						|
    numoftables++;
 | 
						|
 | 
						|
  rsdt_addr = rsdt = (struct grub_acpi_table_header *) playground_ptr;
 | 
						|
  playground_ptr += sizeof (struct grub_acpi_table_header) + 4 * numoftables;
 | 
						|
 | 
						|
  rsdt_entry = (grub_uint32_t *)(rsdt + 1);
 | 
						|
 | 
						|
  /* Fill RSDT header. */
 | 
						|
  grub_memcpy (&(rsdt->signature), "RSDT", 4);
 | 
						|
  rsdt->length = sizeof (struct grub_acpi_table_header) + 4 * numoftables;
 | 
						|
  rsdt->revision = 1;
 | 
						|
  grub_memcpy (&(rsdt->oemid), root_oemid, 6);
 | 
						|
  grub_memcpy (&(rsdt->oemtable), root_oemtable, 4);
 | 
						|
  rsdt->oemrev = root_oemrev;
 | 
						|
  grub_memcpy (&(rsdt->creator_id), root_creator_id, 6);
 | 
						|
  rsdt->creator_rev = root_creator_rev;
 | 
						|
 | 
						|
  for (cur = acpi_tables; cur; cur = cur->next)
 | 
						|
    *(rsdt_entry++) = PTR_TO_UINT32 (cur->addr);
 | 
						|
 | 
						|
  /* Recompute checksum. */
 | 
						|
  rsdt->checksum = 0;
 | 
						|
  rsdt->checksum = 1 + ~grub_byte_checksum (rsdt, rsdt->length);
 | 
						|
}
 | 
						|
 | 
						|
/* Regenerate ACPIv1 RSDP */
 | 
						|
static void
 | 
						|
setv1table (void)
 | 
						|
{
 | 
						|
  /* Create RSDP. */
 | 
						|
  rsdpv1_new = (struct grub_acpi_rsdp_v10 *) playground_ptr;
 | 
						|
  playground_ptr += sizeof (struct grub_acpi_rsdp_v10);
 | 
						|
  grub_memcpy (&(rsdpv1_new->signature), "RSD PTR ", 8);
 | 
						|
  grub_memcpy (&(rsdpv1_new->oemid), root_oemid, sizeof  (rsdpv1_new->oemid));
 | 
						|
  rsdpv1_new->revision = 0;
 | 
						|
  rsdpv1_new->rsdt_addr = PTR_TO_UINT32 (rsdt_addr);
 | 
						|
  rsdpv1_new->checksum = 0;
 | 
						|
  rsdpv1_new->checksum = 1 + ~grub_byte_checksum (rsdpv1_new,
 | 
						|
						  sizeof (*rsdpv1_new));
 | 
						|
  grub_dprintf ("acpi", "Generated ACPIv1 tables\n");
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
setv2table (void)
 | 
						|
{
 | 
						|
  struct grub_acpi_table_header *xsdt;
 | 
						|
  struct efiemu_acpi_table *cur;
 | 
						|
  grub_uint64_t *xsdt_entry;
 | 
						|
  int numoftables;
 | 
						|
 | 
						|
  numoftables = 0;
 | 
						|
  for (cur = acpi_tables; cur; cur = cur->next)
 | 
						|
    numoftables++;
 | 
						|
 | 
						|
  /* Create XSDT. */
 | 
						|
  xsdt = (struct grub_acpi_table_header *) playground_ptr;
 | 
						|
  playground_ptr += sizeof (struct grub_acpi_table_header) + 8 * numoftables;
 | 
						|
 | 
						|
  xsdt_entry = (grub_uint64_t *)(xsdt + 1);
 | 
						|
  for (cur = acpi_tables; cur; cur = cur->next)
 | 
						|
    *(xsdt_entry++) = PTR_TO_UINT64 (cur->addr);
 | 
						|
  grub_memcpy (&(xsdt->signature), "XSDT", 4);
 | 
						|
  xsdt->length = sizeof (struct grub_acpi_table_header) + 8 * numoftables;
 | 
						|
  xsdt->revision = 1;
 | 
						|
  grub_memcpy (&(xsdt->oemid), root_oemid, sizeof (xsdt->oemid));
 | 
						|
  grub_memcpy (&(xsdt->oemtable), root_oemtable, sizeof (xsdt->oemtable));
 | 
						|
  xsdt->oemrev = root_oemrev;
 | 
						|
  grub_memcpy (&(xsdt->creator_id), root_creator_id, sizeof (xsdt->creator_id));
 | 
						|
  xsdt->creator_rev = root_creator_rev;
 | 
						|
  xsdt->checksum = 0;
 | 
						|
  xsdt->checksum = 1 + ~grub_byte_checksum (xsdt, xsdt->length);
 | 
						|
 | 
						|
  /* Create RSDPv2. */
 | 
						|
  rsdpv2_new = (struct grub_acpi_rsdp_v20 *) playground_ptr;
 | 
						|
  playground_ptr += sizeof (struct grub_acpi_rsdp_v20);
 | 
						|
  grub_memcpy (&(rsdpv2_new->rsdpv1.signature), "RSD PTR ",
 | 
						|
	       sizeof (rsdpv2_new->rsdpv1.signature));
 | 
						|
  grub_memcpy (&(rsdpv2_new->rsdpv1.oemid), root_oemid,
 | 
						|
	       sizeof (rsdpv2_new->rsdpv1.oemid));
 | 
						|
  rsdpv2_new->rsdpv1.revision = rev2;
 | 
						|
  rsdpv2_new->rsdpv1.rsdt_addr = PTR_TO_UINT32 (rsdt_addr);
 | 
						|
  rsdpv2_new->rsdpv1.checksum = 0;
 | 
						|
  rsdpv2_new->rsdpv1.checksum = 1 + ~grub_byte_checksum
 | 
						|
    (&(rsdpv2_new->rsdpv1), sizeof (rsdpv2_new->rsdpv1));
 | 
						|
  rsdpv2_new->length = sizeof (*rsdpv2_new);
 | 
						|
  rsdpv2_new->xsdt_addr = PTR_TO_UINT64 (xsdt);
 | 
						|
  rsdpv2_new->checksum = 0;
 | 
						|
  rsdpv2_new->checksum = 1 + ~grub_byte_checksum (rsdpv2_new,
 | 
						|
						  rsdpv2_new->length);
 | 
						|
  grub_dprintf ("acpi", "Generated ACPIv2 tables\n");
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
free_tables (void)
 | 
						|
{
 | 
						|
  struct efiemu_acpi_table *cur, *t;
 | 
						|
  if (table_dsdt)
 | 
						|
    grub_free (table_dsdt);
 | 
						|
  for (cur = acpi_tables; cur;)
 | 
						|
    {
 | 
						|
      t = cur;
 | 
						|
      grub_free (cur->addr);
 | 
						|
      cur = cur->next;
 | 
						|
      grub_free (t);
 | 
						|
    }
 | 
						|
  acpi_tables = 0;
 | 
						|
  table_dsdt = 0;
 | 
						|
}
 | 
						|
 | 
						|
static grub_err_t
 | 
						|
grub_cmd_acpi (struct grub_extcmd *cmd,
 | 
						|
		      int argc, char **args)
 | 
						|
{
 | 
						|
  struct grub_arg_list *state = cmd->state;
 | 
						|
  struct grub_acpi_rsdp_v10 *rsdp;
 | 
						|
  struct efiemu_acpi_table *cur, *t;
 | 
						|
  grub_err_t err;
 | 
						|
  int i, mmapregion;
 | 
						|
  int numoftables;
 | 
						|
 | 
						|
  /* Default values if no RSDP is found. */
 | 
						|
  rev1 = 1;
 | 
						|
  rev2 = 3;
 | 
						|
 | 
						|
  facs_addr = 0;
 | 
						|
  playground = playground_ptr = 0;
 | 
						|
  playground_size = 0;
 | 
						|
 | 
						|
  rsdp = (struct grub_acpi_rsdp_v10 *) grub_machine_acpi_get_rsdpv2 ();
 | 
						|
 | 
						|
  if (! rsdp)
 | 
						|
    rsdp = grub_machine_acpi_get_rsdpv1 ();
 | 
						|
 | 
						|
  if (rsdp)
 | 
						|
    {
 | 
						|
      grub_uint32_t *entry_ptr;
 | 
						|
      char *exclude = 0;
 | 
						|
      char *load_only = 0;
 | 
						|
      char *ptr;
 | 
						|
      /* RSDT consists of header and an array of 32-bit pointers. */
 | 
						|
      struct grub_acpi_table_header *rsdt;
 | 
						|
 | 
						|
      exclude = state[0].set ? grub_strdup (state[0].arg) : 0;
 | 
						|
      if (exclude)
 | 
						|
	{
 | 
						|
	  for (ptr = exclude; *ptr; ptr++)
 | 
						|
	    *ptr = grub_tolower (*ptr);
 | 
						|
	}
 | 
						|
 | 
						|
      load_only = state[1].set ? grub_strdup (state[1].arg) : 0;
 | 
						|
      if (load_only)
 | 
						|
	{
 | 
						|
	  for (ptr = load_only; *ptr; ptr++)
 | 
						|
	    *ptr = grub_tolower (*ptr);
 | 
						|
	}
 | 
						|
 | 
						|
      /* Set revision variables to replicate the same version as host. */
 | 
						|
      rev1 = ! rsdp->revision;
 | 
						|
      rev2 = rsdp->revision;
 | 
						|
      rsdt = (struct grub_acpi_table_header *) UINT_TO_PTR (rsdp->rsdt_addr);
 | 
						|
      /* Load host tables. */
 | 
						|
      for (entry_ptr = (grub_uint32_t *) (rsdt + 1);
 | 
						|
	   entry_ptr < (grub_uint32_t *) (((grub_uint8_t *) rsdt)
 | 
						|
					  + rsdt->length);
 | 
						|
	   entry_ptr++)
 | 
						|
	{
 | 
						|
	  char signature[5];
 | 
						|
	  struct efiemu_acpi_table *table;
 | 
						|
	  struct grub_acpi_table_header *curtable
 | 
						|
	    = (struct grub_acpi_table_header *) UINT_TO_PTR (*entry_ptr);
 | 
						|
	  signature[4] = 0;
 | 
						|
	  for (i = 0; i < 4;i++)
 | 
						|
	    signature[i] = grub_tolower (curtable->signature[i]);
 | 
						|
 | 
						|
	  /* If it's FADT it contains addresses of DSDT and FACS. */
 | 
						|
	  if (grub_strcmp (signature, "facp") == 0)
 | 
						|
	    {
 | 
						|
	      struct grub_acpi_table_header *dsdt;
 | 
						|
	      struct grub_acpi_fadt *fadt = (struct grub_acpi_fadt *) curtable;
 | 
						|
 | 
						|
	      /* Set root header variables to the same values
 | 
						|
		 as FACP by default. */
 | 
						|
	      grub_memcpy (&root_oemid, &(fadt->hdr.oemid),
 | 
						|
			   sizeof (root_oemid));
 | 
						|
	      grub_memcpy (&root_oemtable, &(fadt->hdr.oemtable),
 | 
						|
			   sizeof (root_oemtable));
 | 
						|
	      root_oemrev = fadt->hdr.oemrev;
 | 
						|
	      grub_memcpy (&root_creator_id, &(fadt->hdr.creator_id),
 | 
						|
			   sizeof (root_creator_id));
 | 
						|
	      root_creator_rev = fadt->hdr.creator_rev;
 | 
						|
 | 
						|
	      /* Load DSDT if not excluded. */
 | 
						|
	      dsdt = (struct grub_acpi_table_header *)
 | 
						|
		UINT_TO_PTR (fadt->dsdt_addr);
 | 
						|
	      if (dsdt && (! exclude || ! grub_strword (exclude, "dsdt"))
 | 
						|
		  && (! load_only || grub_strword (load_only, "dsdt"))
 | 
						|
		  && dsdt->length >= sizeof (*dsdt))
 | 
						|
		{
 | 
						|
		  dsdt_size = dsdt->length;
 | 
						|
		  table_dsdt = grub_malloc (dsdt->length);
 | 
						|
		  if (! table_dsdt)
 | 
						|
		    {
 | 
						|
		      free_tables ();
 | 
						|
		      grub_free (exclude);
 | 
						|
		      grub_free (load_only);
 | 
						|
		      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
 | 
						|
					 "Couldn't allocate table");
 | 
						|
		    }
 | 
						|
		  grub_memcpy (table_dsdt, dsdt, dsdt->length);
 | 
						|
		}
 | 
						|
 | 
						|
	      /* Save FACS address. FACS shouldn't be overridden. */
 | 
						|
	      facs_addr = fadt->facs_addr;
 | 
						|
	    }
 | 
						|
 | 
						|
	  /* Skip excluded tables. */
 | 
						|
	  if (exclude && grub_strword (exclude, signature))
 | 
						|
	    continue;
 | 
						|
	  if (load_only && ! grub_strword (load_only, signature))
 | 
						|
	    continue;
 | 
						|
 | 
						|
	  /* Sanity check. */
 | 
						|
	  if (curtable->length < sizeof (*curtable))
 | 
						|
	    continue;
 | 
						|
 | 
						|
	  table = (struct efiemu_acpi_table *) grub_malloc
 | 
						|
	    (sizeof (struct efiemu_acpi_table));
 | 
						|
	  if (! table)
 | 
						|
	    {
 | 
						|
	      free_tables ();
 | 
						|
	      grub_free (exclude);
 | 
						|
	      grub_free (load_only);
 | 
						|
	      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
 | 
						|
				 "Couldn't allocate table structure");
 | 
						|
	    }
 | 
						|
	  table->size = curtable->length;
 | 
						|
	  table->addr = grub_malloc (table->size);
 | 
						|
	  playground_size += table->size;
 | 
						|
	  if (! table->addr)
 | 
						|
	    {
 | 
						|
	      free_tables ();
 | 
						|
	      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
 | 
						|
				 "Couldn't allocate table");
 | 
						|
	    }
 | 
						|
	  table->next = acpi_tables;
 | 
						|
	  acpi_tables = table;
 | 
						|
	  grub_memcpy (table->addr, curtable, table->size);
 | 
						|
	}
 | 
						|
      grub_free (exclude);
 | 
						|
      grub_free (load_only);
 | 
						|
    }
 | 
						|
 | 
						|
  /* Does user specify versions to generate? */
 | 
						|
  if (state[2].set || state[3].set)
 | 
						|
    {
 | 
						|
      rev1 = state[2].set;
 | 
						|
      if (state[3].set)
 | 
						|
	rev2 = rev2 ? : 2;
 | 
						|
      else
 | 
						|
	rev2 = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Does user override root header information? */
 | 
						|
  if (state[4].set)
 | 
						|
    grub_strncpy (root_oemid, state[4].arg, sizeof (root_oemid));
 | 
						|
  if (state[5].set)
 | 
						|
    grub_strncpy (root_oemtable, state[5].arg, sizeof (root_oemtable));
 | 
						|
  if (state[6].set)
 | 
						|
    root_oemrev = grub_strtoul (state[6].arg, 0, 0);
 | 
						|
  if (state[7].set)
 | 
						|
    grub_strncpy (root_creator_id, state[7].arg, sizeof (root_creator_id));
 | 
						|
  if (state[8].set)
 | 
						|
    root_creator_rev = grub_strtoul (state[8].arg, 0, 0);
 | 
						|
 | 
						|
  /* Load user tables */
 | 
						|
  for (i = 0; i < argc; i++)
 | 
						|
    {
 | 
						|
      grub_file_t file;
 | 
						|
      grub_size_t size;
 | 
						|
      char *buf;
 | 
						|
 | 
						|
      file = grub_gzfile_open (args[i], 1);
 | 
						|
      if (! file)
 | 
						|
	{
 | 
						|
	  free_tables ();
 | 
						|
	  return grub_error (GRUB_ERR_BAD_OS, "couldn't open file %s", args[i]);
 | 
						|
	}
 | 
						|
 | 
						|
      size = grub_file_size (file);
 | 
						|
      if (size < sizeof (struct grub_acpi_table_header))
 | 
						|
	{
 | 
						|
	  grub_file_close (file);
 | 
						|
	  free_tables ();
 | 
						|
	  return grub_error (GRUB_ERR_BAD_OS, "file %s is too small", args[i]);
 | 
						|
	}
 | 
						|
 | 
						|
      buf = (char *) grub_malloc (size);
 | 
						|
      if (! buf)
 | 
						|
	{
 | 
						|
	  grub_file_close (file);
 | 
						|
	  free_tables ();
 | 
						|
	  return grub_error (GRUB_ERR_OUT_OF_MEMORY,
 | 
						|
			     "couldn't read file %s", args[i]);
 | 
						|
	}
 | 
						|
 | 
						|
      if (grub_file_read (file, buf, size) != (int) size)
 | 
						|
	{
 | 
						|
	  grub_file_close (file);
 | 
						|
	  free_tables ();
 | 
						|
	  return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[i]);
 | 
						|
	}
 | 
						|
      grub_file_close (file);
 | 
						|
 | 
						|
      if (grub_memcmp (((struct grub_acpi_table_header *) buf)->signature,
 | 
						|
		       "DSDT", 4) == 0)
 | 
						|
	{
 | 
						|
	  grub_free (table_dsdt);
 | 
						|
	  table_dsdt = buf;
 | 
						|
	  dsdt_size = size;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  struct efiemu_acpi_table *table;
 | 
						|
	  table = (struct efiemu_acpi_table *) grub_malloc
 | 
						|
	    (sizeof (struct efiemu_acpi_table));
 | 
						|
	  if (! table)
 | 
						|
	    {
 | 
						|
	      free_tables ();
 | 
						|
	      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
 | 
						|
				 "Couldn't allocate table structure");
 | 
						|
	    }
 | 
						|
 | 
						|
	  table->size = size;
 | 
						|
	  table->addr = buf;
 | 
						|
	  playground_size += table->size;
 | 
						|
 | 
						|
	  table->next = acpi_tables;
 | 
						|
	  acpi_tables = table;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  numoftables = 0;
 | 
						|
  for (cur = acpi_tables; cur; cur = cur->next)
 | 
						|
    numoftables++;
 | 
						|
 | 
						|
  /* DSDT. */
 | 
						|
  playground_size += dsdt_size;
 | 
						|
  /* RSDT. */
 | 
						|
  playground_size += sizeof (struct grub_acpi_table_header) + 4 * numoftables;
 | 
						|
  /* RSDPv1. */
 | 
						|
  playground_size += sizeof (struct grub_acpi_rsdp_v10);
 | 
						|
  /* XSDT. */
 | 
						|
  playground_size += sizeof (struct grub_acpi_table_header) + 8 * numoftables;
 | 
						|
  /* RSDPv2. */
 | 
						|
  playground_size += sizeof (struct grub_acpi_rsdp_v20);
 | 
						|
 | 
						|
  playground = playground_ptr
 | 
						|
    = grub_mmap_malign_and_register (1, playground_size, &mmapregion,
 | 
						|
				     GRUB_MACHINE_MEMORY_ACPI, 0);
 | 
						|
 | 
						|
  if (! playground)
 | 
						|
    {
 | 
						|
      free_tables ();
 | 
						|
      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
 | 
						|
			 "Couldn't allocate space for ACPI tables");
 | 
						|
    }
 | 
						|
 | 
						|
  setup_common_tables ();
 | 
						|
 | 
						|
  /* Request space for RSDPv1. */
 | 
						|
  if (rev1)
 | 
						|
    setv1table ();
 | 
						|
 | 
						|
  /* Request space for RSDPv2+ and XSDT. */
 | 
						|
  if (rev2)
 | 
						|
    setv2table ();
 | 
						|
 | 
						|
  for (cur = acpi_tables; cur;)
 | 
						|
    {
 | 
						|
      t = cur;
 | 
						|
      cur = cur->next;
 | 
						|
      grub_free (t);
 | 
						|
    }
 | 
						|
  acpi_tables = 0;
 | 
						|
 | 
						|
  if (! state[9].set && (err = grub_acpi_create_ebda ()))
 | 
						|
    {
 | 
						|
      rsdpv1_new = 0;
 | 
						|
      rsdpv2_new = 0;
 | 
						|
      grub_mmap_free_and_unregister (mmapregion);
 | 
						|
      return err;
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef GRUB_MACHINE_EFI
 | 
						|
  {
 | 
						|
    struct grub_efi_guid acpi = GRUB_EFI_ACPI_TABLE_GUID;
 | 
						|
    struct grub_efi_guid acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID;
 | 
						|
 | 
						|
    grub_efi_system_table->boot_services->install_configuration_table
 | 
						|
      (&acpi20, grub_acpi_get_rsdpv2 ());
 | 
						|
    grub_efi_system_table->boot_services->install_configuration_table
 | 
						|
      (&acpi, grub_acpi_get_rsdpv1 ());
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  return GRUB_ERR_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static grub_extcmd_t cmd;
 | 
						|
 | 
						|
GRUB_MOD_INIT(acpi)
 | 
						|
{
 | 
						|
  cmd = grub_register_extcmd ("acpi", grub_cmd_acpi,
 | 
						|
			      GRUB_COMMAND_FLAG_BOTH,
 | 
						|
			      "acpi [-1|-2] [--exclude=table1,table2|"
 | 
						|
			      "--load-only=table1,table2] filename1 "
 | 
						|
			      " [filename2] [...]",
 | 
						|
			      "Load host acpi tables and tables "
 | 
						|
			      "specified by arguments.",
 | 
						|
			      options);
 | 
						|
}
 | 
						|
 | 
						|
GRUB_MOD_FINI(acpi)
 | 
						|
{
 | 
						|
  grub_unregister_extcmd (cmd);
 | 
						|
}
 |