blob: 5a1499aa0e30ef7b5a683fea603bb2cd3bf30b49 [file] [log] [blame] [edit]
/* 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/acpi.h>
#include <grub/mm.h>
#include <grub/memory.h>
#include <grub/i18n.h>
#ifdef GRUB_MACHINE_EFI
#include <grub/efi/efi.h>
#include <grub/efi/api.h>
#endif
#pragma GCC diagnostic ignored "-Wcast-align"
GRUB_MOD_LICENSE ("GPLv3+");
static const struct grub_arg_option options[] = {
{"exclude", 'x', 0,
N_("Don't load host tables specified by comma-separated list."),
0, ARG_TYPE_STRING},
{"load-only", 'n', 0,
N_("Load only tables specified by comma-separated list."), 0, ARG_TYPE_STRING},
{"v1", '1', 0, N_("Export version 1 tables to the OS."), 0, ARG_TYPE_NONE},
{"v2", '2', 0, N_("Export version 2 and version 3 tables to the OS."), 0, ARG_TYPE_NONE},
{"oemid", 'o', 0, N_("Set OEMID of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
{"oemtable", 't', 0,
N_("Set OEMTABLE ID of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
{"oemtablerev", 'r', 0,
N_("Set OEMTABLE revision of RSDP, XSDT and RSDT."), 0, ARG_TYPE_INT},
{"oemtablecreator", 'c', 0,
N_("Set creator field of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
{"oemtablecreatorrev", 'd', 0,
N_("Set creator revision of RSDP, XSDT and RSDT."), 0, ARG_TYPE_INT},
/* TRANSLATORS: "hangs" here is a noun, not a verb. */
{"no-ebda", 'e', 0, N_("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}
};
/* 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 ();
}
#if defined (__i386__) || defined (__x86_64__)
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;
}
/* Context for grub_acpi_create_ebda. */
struct grub_acpi_create_ebda_ctx {
int ebda_len;
grub_uint64_t highestlow;
};
/* Helper for grub_acpi_create_ebda. */
static int
find_hook (grub_uint64_t start, grub_uint64_t size, grub_memory_type_t type,
void *data)
{
struct grub_acpi_create_ebda_ctx *ctx = data;
grub_uint64_t end = start + size;
if (type != GRUB_MEMORY_AVAILABLE)
return 0;
if (end > 0x100000)
end = 0x100000;
if (end > start + ctx->ebda_len
&& ctx->highestlow < ((end - ctx->ebda_len) & (~0xf)) )
ctx->highestlow = (end - ctx->ebda_len) & (~0xf);
return 0;
}
grub_err_t
grub_acpi_create_ebda (void)
{
struct grub_acpi_create_ebda_ctx ctx = {
.highestlow = 0
};
int ebda_kb_len = 0;
int mmapregion = 0;
grub_uint8_t *ebda, *v1inebda = 0, *v2inebda = 0;
grub_uint8_t *targetebda, *target;
struct grub_acpi_rsdp_v10 *v1;
struct grub_acpi_rsdp_v20 *v2;
ebda = (grub_uint8_t *) (grub_addr_t) ((*((grub_uint16_t *)0x40e)) << 4);
grub_dprintf ("acpi", "EBDA @%p\n", ebda);
if (ebda)
ebda_kb_len = *(grub_uint16_t *) ebda;
grub_dprintf ("acpi", "EBDA length 0x%x\n", ebda_kb_len);
if (ebda_kb_len > 16)
ebda_kb_len = 0;
ctx.ebda_len = (ebda_kb_len + 1) << 10;
/* FIXME: use low-memory mm allocation once it's available. */
grub_mmap_iterate (find_hook, &ctx);
targetebda = (grub_uint8_t *) (grub_addr_t) ctx.highestlow;
grub_dprintf ("acpi", "creating ebda @%llx\n",
(unsigned long long) ctx.highestlow);
if (! ctx.highestlow)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't find space for the new EBDA");
mmapregion = grub_mmap_register ((grub_addr_t) targetebda, ctx.ebda_len,
GRUB_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, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 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 *) ALIGN_UP((grub_addr_t) target, 16);
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, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 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 rsdpv1 to %p\n", target);
v1inebda = target;
target += sizeof (struct grub_acpi_rsdp_v10);
target = (grub_uint8_t *) ALIGN_UP((grub_addr_t) target, 16);
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 *) ALIGN_UP((grub_addr_t) target, 16);
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 *) ALIGN_UP((grub_addr_t) target, 16);
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, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 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)) = ((grub_addr_t) targetebda) >> 4;
grub_dprintf ("acpi", "EBDA switched\n");
return GRUB_ERR_NONE;
}
#endif
/* 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, GRUB_ACPI_FADT_SIGNATURE,
sizeof (fadt->hdr.signature)) == 0)
{
fadt->dsdt_addr = (grub_addr_t) table_dsdt;
fadt->facs_addr = facs_addr;
/* Does a revision 2 exist at all? */
if (fadt->hdr.revision >= 3)
{
fadt->dsdt_xaddr = (grub_addr_t) 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) + sizeof (grub_uint32_t) * 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) + sizeof (grub_uint32_t) * numoftables;
rsdt->revision = 1;
grub_memcpy (&(rsdt->oemid), root_oemid, sizeof (rsdt->oemid));
grub_memcpy (&(rsdt->oemtable), root_oemtable, sizeof (rsdt->oemtable));
rsdt->oemrev = root_oemrev;
grub_memcpy (&(rsdt->creator_id), root_creator_id, sizeof (rsdt->creator_id));
rsdt->creator_rev = root_creator_rev;
for (cur = acpi_tables; cur; cur = cur->next)
*(rsdt_entry++) = (grub_addr_t) 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), GRUB_RSDP_SIGNATURE,
sizeof (rsdpv1_new->signature));
grub_memcpy (&(rsdpv1_new->oemid), root_oemid, sizeof (rsdpv1_new->oemid));
rsdpv1_new->revision = 0;
rsdpv1_new->rsdt_addr = (grub_addr_t) 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) + sizeof (grub_uint64_t) * numoftables;
xsdt_entry = (grub_uint64_t *)(xsdt + 1);
for (cur = acpi_tables; cur; cur = cur->next)
*(xsdt_entry++) = (grub_addr_t) cur->addr;
grub_memcpy (&(xsdt->signature), "XSDT", 4);
xsdt->length = sizeof (struct grub_acpi_table_header) + sizeof (grub_uint64_t) * 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), GRUB_RSDP_SIGNATURE,
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 = (grub_addr_t) 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 = (grub_addr_t) 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_context *ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
struct grub_acpi_rsdp_v10 *rsdp;
struct efiemu_acpi_table *cur, *t;
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 ();
grub_dprintf ("acpi", "RSDP @%p\n", rsdp);
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 *) (grub_addr_t) 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 *) (grub_addr_t) *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 FADT 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 *)
(grub_addr_t) 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_errno;
}
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_errno;
}
table->size = curtable->length;
table->addr = grub_malloc (table->size);
playground_size += table->size;
if (! table->addr)
{
free_tables ();
grub_free (exclude);
grub_free (load_only);
grub_free (table);
return grub_errno;
}
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_file_open (args[i], GRUB_FILE_TYPE_ACPI_TABLE);
if (! file)
{
free_tables ();
return grub_errno;
}
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, N_("premature end of file %s"),
args[i]);
}
buf = (char *) grub_malloc (size);
if (! buf)
{
grub_file_close (file);
free_tables ();
return grub_errno;
}
if (grub_file_read (file, buf, size) != (int) size)
{
grub_file_close (file);
free_tables ();
if (!grub_errno)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
args[i]);
return grub_errno;
}
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_errno;
}
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) + sizeof (grub_uint32_t) * numoftables;
/* RSDPv1. */
playground_size += sizeof (struct grub_acpi_rsdp_v10);
/* XSDT. */
playground_size += sizeof (struct grub_acpi_table_header) + sizeof (grub_uint64_t) * numoftables;
/* RSDPv2. */
playground_size += sizeof (struct grub_acpi_rsdp_v20);
playground = playground_ptr
= grub_mmap_malign_and_register (1, playground_size, &mmapregion,
GRUB_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 defined (__i386__) || defined (__x86_64__)
if (! state[9].set)
{
grub_err_t err;
err = grub_acpi_create_ebda ();
if (err)
{
rsdpv1_new = 0;
rsdpv2_new = 0;
grub_mmap_free_and_unregister (mmapregion);
return err;
}
}
#endif
#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;
efi_call_2 (grub_efi_system_table->boot_services->install_configuration_table,
&acpi20, grub_acpi_get_rsdpv2 ());
efi_call_2 (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, 0,
N_("[-1|-2] [--exclude=TABLE1,TABLE2|"
"--load-only=TABLE1,TABLE2] FILE1"
" [FILE2] [...]"),
N_("Load host ACPI tables and tables "
"specified by arguments."),
options);
}
GRUB_MOD_FINI(acpi)
{
grub_unregister_extcmd (cmd);
}