2N/A/* acpi.c - modify acpi tables. */
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/dl.h>
2N/A#include <grub/extcmd.h>
2N/A#include <grub/file.h>
2N/A#include <grub/disk.h>
2N/A#include <grub/term.h>
2N/A#include <grub/misc.h>
2N/A#include <grub/acpi.h>
2N/A#include <grub/mm.h>
2N/A#include <grub/memory.h>
2N/A#include <grub/i18n.h>
2N/A
2N/A#ifdef GRUB_MACHINE_EFI
2N/A#include <grub/efi/efi.h>
2N/A#include <grub/efi/api.h>
2N/A#endif
2N/A
2N/AGRUB_MOD_LICENSE ("GPLv3+");
2N/A
2N/Astatic const struct grub_arg_option options[] = {
2N/A {"exclude", 'x', 0,
2N/A N_("Don't load host tables specified by comma-separated list."),
2N/A 0, ARG_TYPE_STRING},
2N/A {"load-only", 'n', 0,
2N/A N_("Load only tables specified by comma-separated list."), 0, ARG_TYPE_STRING},
2N/A {"v1", '1', 0, N_("Expose v1 tables."), 0, ARG_TYPE_NONE},
2N/A {"v2", '2', 0, N_("Expose v2 and v3 tables."), 0, ARG_TYPE_NONE},
2N/A {"oemid", 'o', 0, N_("Set OEMID of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
2N/A {"oemtable", 't', 0,
2N/A N_("Set OEMTABLE ID of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
2N/A {"oemtablerev", 'r', 0,
2N/A N_("Set OEMTABLE revision of RSDP, XSDT and RSDT."), 0, ARG_TYPE_INT},
2N/A {"oemtablecreator", 'c', 0,
2N/A N_("Set creator field of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
2N/A {"oemtablecreatorrev", 'd', 0,
2N/A N_("Set creator revision of RSDP, XSDT and RSDT."), 0, ARG_TYPE_INT},
2N/A {"no-ebda", 'e', 0, N_("Don't update EBDA. May fix failures or hangs on some."
2N/A " BIOSes but makes it ineffective with OS not receiving RSDP from GRUB."),
2N/A 0, ARG_TYPE_NONE},
2N/A {0, 0, 0, 0, 0, 0}
2N/A};
2N/A
2N/A/* Simple checksum by summing all bytes. Used by ACPI and SMBIOS. */
2N/Agrub_uint8_t
2N/Agrub_byte_checksum (void *base, grub_size_t size)
2N/A{
2N/A grub_uint8_t *ptr;
2N/A grub_uint8_t ret = 0;
2N/A for (ptr = (grub_uint8_t *) base; ptr < ((grub_uint8_t *) base) + size;
2N/A ptr++)
2N/A ret += *ptr;
2N/A return ret;
2N/A}
2N/A
2N/A/* rev1 is 1 if ACPIv1 is to be generated, 0 otherwise.
2N/A rev2 contains the revision of ACPIv2+ to generate or 0 if none. */
2N/Astatic int rev1, rev2;
2N/A/* OEMID of RSDP, RSDT and XSDT. */
2N/Astatic char root_oemid[6];
2N/A/* OEMTABLE of the same tables. */
2N/Astatic char root_oemtable[8];
2N/A/* OEMREVISION of the same tables. */
2N/Astatic grub_uint32_t root_oemrev;
2N/A/* CreatorID of the same tables. */
2N/Astatic char root_creator_id[4];
2N/A/* CreatorRevision of the same tables. */
2N/Astatic grub_uint32_t root_creator_rev;
2N/Astatic struct grub_acpi_rsdp_v10 *rsdpv1_new = 0;
2N/Astatic struct grub_acpi_rsdp_v20 *rsdpv2_new = 0;
2N/Astatic char *playground = 0, *playground_ptr = 0;
2N/Astatic int playground_size = 0;
2N/A
2N/A/* Linked list of ACPI tables. */
2N/Astruct efiemu_acpi_table
2N/A{
2N/A void *addr;
2N/A grub_size_t size;
2N/A struct efiemu_acpi_table *next;
2N/A};
2N/Astatic struct efiemu_acpi_table *acpi_tables = 0;
2N/A
2N/A/* DSDT isn't in RSDT. So treat it specially. */
2N/Astatic void *table_dsdt = 0;
2N/A/* Pointer to recreated RSDT. */
2N/Astatic void *rsdt_addr = 0;
2N/A
2N/A/* Allocation handles for different tables. */
2N/Astatic grub_size_t dsdt_size = 0;
2N/A
2N/A/* Address of original FACS. */
2N/Astatic grub_uint32_t facs_addr = 0;
2N/A
2N/Astruct grub_acpi_rsdp_v20 *
2N/Agrub_acpi_get_rsdpv2 (void)
2N/A{
2N/A if (rsdpv2_new)
2N/A return rsdpv2_new;
2N/A if (rsdpv1_new)
2N/A return 0;
2N/A return grub_machine_acpi_get_rsdpv2 ();
2N/A}
2N/A
2N/Astruct grub_acpi_rsdp_v10 *
2N/Agrub_acpi_get_rsdpv1 (void)
2N/A{
2N/A if (rsdpv1_new)
2N/A return rsdpv1_new;
2N/A if (rsdpv2_new)
2N/A return 0;
2N/A return grub_machine_acpi_get_rsdpv1 ();
2N/A}
2N/A
2N/Astatic inline int
2N/Aiszero (grub_uint8_t *reg, int size)
2N/A{
2N/A int i;
2N/A for (i = 0; i < size; i++)
2N/A if (reg[i])
2N/A return 0;
2N/A return 1;
2N/A}
2N/A
2N/A#if defined (__i386__) || defined (__x86_64__)
2N/Agrub_err_t
2N/Agrub_acpi_create_ebda (void)
2N/A{
2N/A int ebda_kb_len;
2N/A int ebda_len;
2N/A int mmapregion = 0;
2N/A grub_uint8_t *ebda, *v1inebda = 0, *v2inebda = 0;
2N/A grub_uint64_t highestlow = 0;
2N/A grub_uint8_t *targetebda, *target;
2N/A struct grub_acpi_rsdp_v10 *v1;
2N/A struct grub_acpi_rsdp_v20 *v2;
2N/A auto int NESTED_FUNC_ATTR find_hook (grub_uint64_t, grub_uint64_t,
2N/A grub_uint32_t);
2N/A int NESTED_FUNC_ATTR find_hook (grub_uint64_t start, grub_uint64_t size,
2N/A grub_memory_type_t type)
2N/A {
2N/A grub_uint64_t end = start + size;
2N/A if (type != GRUB_MEMORY_AVAILABLE)
2N/A return 0;
2N/A if (end > 0x100000)
2N/A end = 0x100000;
2N/A if (end > start + ebda_len
2N/A && highestlow < ((end - ebda_len) & (~0xf)) )
2N/A highestlow = (end - ebda_len) & (~0xf);
2N/A return 0;
2N/A }
2N/A
2N/A ebda = (grub_uint8_t *) (grub_addr_t) ((*((grub_uint16_t *)0x40e)) << 4);
2N/A ebda_kb_len = *(grub_uint16_t *) ebda;
2N/A if (! ebda || ebda_kb_len > 16)
2N/A ebda_kb_len = 0;
2N/A ebda_len = (ebda_kb_len + 1) << 10;
2N/A
2N/A /* FIXME: use low-memory mm allocation once it's available. */
2N/A grub_mmap_iterate (find_hook);
2N/A targetebda = (grub_uint8_t *) (grub_addr_t) highestlow;
2N/A grub_dprintf ("acpi", "creating ebda @%llx\n",
2N/A (unsigned long long) highestlow);
2N/A if (! highestlow)
2N/A return grub_error (GRUB_ERR_OUT_OF_MEMORY,
2N/A "couldn't find space for the new EBDA");
2N/A
2N/A mmapregion = grub_mmap_register ((grub_addr_t) targetebda, ebda_len,
2N/A GRUB_MEMORY_RESERVED);
2N/A if (! mmapregion)
2N/A return grub_errno;
2N/A
2N/A /* XXX: EBDA is unstandardized, so this implementation is heuristical. */
2N/A if (ebda_kb_len)
2N/A grub_memcpy (targetebda, ebda, 0x400);
2N/A else
2N/A grub_memset (targetebda, 0, 0x400);
2N/A *((grub_uint16_t *) targetebda) = ebda_kb_len + 1;
2N/A target = targetebda;
2N/A
2N/A v1 = grub_acpi_get_rsdpv1 ();
2N/A v2 = grub_acpi_get_rsdpv2 ();
2N/A if (v2 && v2->length > 40)
2N/A v2 = 0;
2N/A
2N/A /* First try to replace already existing rsdp. */
2N/A if (v2)
2N/A {
2N/A grub_dprintf ("acpi", "Scanning EBDA for old rsdpv2\n");
2N/A for (; target < targetebda + 0x400 - v2->length; target += 0x10)
2N/A if (grub_memcmp (target, "RSD PTR ", 8) == 0
2N/A && grub_byte_checksum (target,
2N/A sizeof (struct grub_acpi_rsdp_v10)) == 0
2N/A && ((struct grub_acpi_rsdp_v10 *) target)->revision != 0
2N/A && ((struct grub_acpi_rsdp_v20 *) target)->length <= v2->length)
2N/A {
2N/A grub_memcpy (target, v2, v2->length);
2N/A grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target);
2N/A v2inebda = target;
2N/A target += v2->length;
2N/A target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
2N/A v2 = 0;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (v1)
2N/A {
2N/A grub_dprintf ("acpi", "Scanning EBDA for old rsdpv1\n");
2N/A for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
2N/A target += 0x10)
2N/A if (grub_memcmp (target, "RSD PTR ", 8) == 0
2N/A && grub_byte_checksum (target,
2N/A sizeof (struct grub_acpi_rsdp_v10)) == 0)
2N/A {
2N/A grub_memcpy (target, v1, sizeof (struct grub_acpi_rsdp_v10));
2N/A grub_dprintf ("acpi", "Copying rsdpv1 to %p\n", target);
2N/A v1inebda = target;
2N/A target += sizeof (struct grub_acpi_rsdp_v10);
2N/A target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
2N/A v1 = 0;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A target = targetebda + 0x100;
2N/A
2N/A /* Try contiguous zeros. */
2N/A if (v2)
2N/A {
2N/A grub_dprintf ("acpi", "Scanning EBDA for block of zeros\n");
2N/A for (; target < targetebda + 0x400 - v2->length; target += 0x10)
2N/A if (iszero (target, v2->length))
2N/A {
2N/A grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target);
2N/A grub_memcpy (target, v2, v2->length);
2N/A v2inebda = target;
2N/A target += v2->length;
2N/A target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
2N/A v2 = 0;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (v1)
2N/A {
2N/A grub_dprintf ("acpi", "Scanning EBDA for block of zeros\n");
2N/A for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
2N/A target += 0x10)
2N/A if (iszero (target, sizeof (struct grub_acpi_rsdp_v10)))
2N/A {
2N/A grub_dprintf ("acpi", "Copying rsdpv1 to %p\n", target);
2N/A grub_memcpy (target, v1, sizeof (struct grub_acpi_rsdp_v10));
2N/A v1inebda = target;
2N/A target += sizeof (struct grub_acpi_rsdp_v10);
2N/A target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
2N/A v1 = 0;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (v1 || v2)
2N/A {
2N/A grub_mmap_unregister (mmapregion);
2N/A return grub_error (GRUB_ERR_OUT_OF_MEMORY,
2N/A "couldn't find suitable spot in EBDA");
2N/A }
2N/A
2N/A /* Remove any other RSDT. */
2N/A for (target = targetebda;
2N/A target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
2N/A target += 0x10)
2N/A if (grub_memcmp (target, "RSD PTR ", 8) == 0
2N/A && grub_byte_checksum (target,
2N/A sizeof (struct grub_acpi_rsdp_v10)) == 0
2N/A && target != v1inebda && target != v2inebda)
2N/A *target = 0;
2N/A
2N/A grub_dprintf ("acpi", "Switching EBDA\n");
2N/A (*((grub_uint16_t *) 0x40e)) = ((long)targetebda) >> 4;
2N/A grub_dprintf ("acpi", "EBDA switched\n");
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A#endif
2N/A
2N/A/* Create tables common to ACPIv1 and ACPIv2+ */
2N/Astatic void
2N/Asetup_common_tables (void)
2N/A{
2N/A struct efiemu_acpi_table *cur;
2N/A struct grub_acpi_table_header *rsdt;
2N/A grub_uint32_t *rsdt_entry;
2N/A int numoftables;
2N/A
2N/A /* Treat DSDT. */
2N/A grub_memcpy (playground_ptr, table_dsdt, dsdt_size);
2N/A grub_free (table_dsdt);
2N/A table_dsdt = playground_ptr;
2N/A playground_ptr += dsdt_size;
2N/A
2N/A /* Treat other tables. */
2N/A for (cur = acpi_tables; cur; cur = cur->next)
2N/A {
2N/A struct grub_acpi_fadt *fadt;
2N/A
2N/A grub_memcpy (playground_ptr, cur->addr, cur->size);
2N/A grub_free (cur->addr);
2N/A cur->addr = playground_ptr;
2N/A playground_ptr += cur->size;
2N/A
2N/A /* If it's FADT correct DSDT and FACS addresses. */
2N/A fadt = (struct grub_acpi_fadt *) cur->addr;
2N/A if (grub_memcmp (fadt->hdr.signature, GRUB_ACPI_FADT_SIGNATURE,
2N/A sizeof (fadt->hdr.signature)) == 0)
2N/A {
2N/A fadt->dsdt_addr = (grub_addr_t) table_dsdt;
2N/A fadt->facs_addr = facs_addr;
2N/A
2N/A /* Does a revision 2 exist at all? */
2N/A if (fadt->hdr.revision >= 3)
2N/A {
2N/A fadt->dsdt_xaddr = (grub_addr_t) table_dsdt;
2N/A fadt->facs_xaddr = facs_addr;
2N/A }
2N/A
2N/A /* Recompute checksum. */
2N/A fadt->hdr.checksum = 0;
2N/A fadt->hdr.checksum = 1 + ~grub_byte_checksum (fadt, fadt->hdr.length);
2N/A }
2N/A }
2N/A
2N/A /* Fill RSDT entries. */
2N/A numoftables = 0;
2N/A for (cur = acpi_tables; cur; cur = cur->next)
2N/A numoftables++;
2N/A
2N/A rsdt_addr = rsdt = (struct grub_acpi_table_header *) playground_ptr;
2N/A playground_ptr += sizeof (struct grub_acpi_table_header) + 4 * numoftables;
2N/A
2N/A rsdt_entry = (grub_uint32_t *) (rsdt + 1);
2N/A
2N/A /* Fill RSDT header. */
2N/A grub_memcpy (&(rsdt->signature), "RSDT", 4);
2N/A rsdt->length = sizeof (struct grub_acpi_table_header) + 4 * numoftables;
2N/A rsdt->revision = 1;
2N/A grub_memcpy (&(rsdt->oemid), root_oemid, sizeof (rsdt->oemid));
2N/A grub_memcpy (&(rsdt->oemtable), root_oemtable, sizeof (rsdt->oemtable));
2N/A rsdt->oemrev = root_oemrev;
2N/A grub_memcpy (&(rsdt->creator_id), root_creator_id, sizeof (rsdt->creator_id));
2N/A rsdt->creator_rev = root_creator_rev;
2N/A
2N/A for (cur = acpi_tables; cur; cur = cur->next)
2N/A *(rsdt_entry++) = (grub_addr_t) cur->addr;
2N/A
2N/A /* Recompute checksum. */
2N/A rsdt->checksum = 0;
2N/A rsdt->checksum = 1 + ~grub_byte_checksum (rsdt, rsdt->length);
2N/A}
2N/A
2N/A/* Regenerate ACPIv1 RSDP */
2N/Astatic void
2N/Asetv1table (void)
2N/A{
2N/A /* Create RSDP. */
2N/A rsdpv1_new = (struct grub_acpi_rsdp_v10 *) playground_ptr;
2N/A playground_ptr += sizeof (struct grub_acpi_rsdp_v10);
2N/A grub_memcpy (&(rsdpv1_new->signature), "RSD PTR ",
2N/A sizeof (rsdpv1_new->signature));
2N/A grub_memcpy (&(rsdpv1_new->oemid), root_oemid, sizeof (rsdpv1_new->oemid));
2N/A rsdpv1_new->revision = 0;
2N/A rsdpv1_new->rsdt_addr = (grub_addr_t) rsdt_addr;
2N/A rsdpv1_new->checksum = 0;
2N/A rsdpv1_new->checksum = 1 + ~grub_byte_checksum (rsdpv1_new,
2N/A sizeof (*rsdpv1_new));
2N/A grub_dprintf ("acpi", "Generated ACPIv1 tables\n");
2N/A}
2N/A
2N/Astatic void
2N/Asetv2table (void)
2N/A{
2N/A struct grub_acpi_table_header *xsdt;
2N/A struct efiemu_acpi_table *cur;
2N/A grub_uint64_t *xsdt_entry;
2N/A int numoftables;
2N/A
2N/A numoftables = 0;
2N/A for (cur = acpi_tables; cur; cur = cur->next)
2N/A numoftables++;
2N/A
2N/A /* Create XSDT. */
2N/A xsdt = (struct grub_acpi_table_header *) playground_ptr;
2N/A playground_ptr += sizeof (struct grub_acpi_table_header) + 8 * numoftables;
2N/A
2N/A xsdt_entry = (grub_uint64_t *)(xsdt + 1);
2N/A for (cur = acpi_tables; cur; cur = cur->next)
2N/A *(xsdt_entry++) = (grub_addr_t) cur->addr;
2N/A grub_memcpy (&(xsdt->signature), "XSDT", 4);
2N/A xsdt->length = sizeof (struct grub_acpi_table_header) + 8 * numoftables;
2N/A xsdt->revision = 1;
2N/A grub_memcpy (&(xsdt->oemid), root_oemid, sizeof (xsdt->oemid));
2N/A grub_memcpy (&(xsdt->oemtable), root_oemtable, sizeof (xsdt->oemtable));
2N/A xsdt->oemrev = root_oemrev;
2N/A grub_memcpy (&(xsdt->creator_id), root_creator_id, sizeof (xsdt->creator_id));
2N/A xsdt->creator_rev = root_creator_rev;
2N/A xsdt->checksum = 0;
2N/A xsdt->checksum = 1 + ~grub_byte_checksum (xsdt, xsdt->length);
2N/A
2N/A /* Create RSDPv2. */
2N/A rsdpv2_new = (struct grub_acpi_rsdp_v20 *) playground_ptr;
2N/A playground_ptr += sizeof (struct grub_acpi_rsdp_v20);
2N/A grub_memcpy (&(rsdpv2_new->rsdpv1.signature), "RSD PTR ",
2N/A sizeof (rsdpv2_new->rsdpv1.signature));
2N/A grub_memcpy (&(rsdpv2_new->rsdpv1.oemid), root_oemid,
2N/A sizeof (rsdpv2_new->rsdpv1.oemid));
2N/A rsdpv2_new->rsdpv1.revision = rev2;
2N/A rsdpv2_new->rsdpv1.rsdt_addr = (grub_addr_t) rsdt_addr;
2N/A rsdpv2_new->rsdpv1.checksum = 0;
2N/A rsdpv2_new->rsdpv1.checksum = 1 + ~grub_byte_checksum
2N/A (&(rsdpv2_new->rsdpv1), sizeof (rsdpv2_new->rsdpv1));
2N/A rsdpv2_new->length = sizeof (*rsdpv2_new);
2N/A rsdpv2_new->xsdt_addr = (grub_addr_t) xsdt;
2N/A rsdpv2_new->checksum = 0;
2N/A rsdpv2_new->checksum = 1 + ~grub_byte_checksum (rsdpv2_new,
2N/A rsdpv2_new->length);
2N/A grub_dprintf ("acpi", "Generated ACPIv2 tables\n");
2N/A}
2N/A
2N/Astatic void
2N/Afree_tables (void)
2N/A{
2N/A struct efiemu_acpi_table *cur, *t;
2N/A if (table_dsdt)
2N/A grub_free (table_dsdt);
2N/A for (cur = acpi_tables; cur;)
2N/A {
2N/A t = cur;
2N/A grub_free (cur->addr);
2N/A cur = cur->next;
2N/A grub_free (t);
2N/A }
2N/A acpi_tables = 0;
2N/A table_dsdt = 0;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_cmd_acpi (struct grub_extcmd_context *ctxt, int argc, char **args)
2N/A{
2N/A struct grub_arg_list *state = ctxt->state;
2N/A struct grub_acpi_rsdp_v10 *rsdp;
2N/A struct efiemu_acpi_table *cur, *t;
2N/A int i, mmapregion;
2N/A int numoftables;
2N/A
2N/A /* Default values if no RSDP is found. */
2N/A rev1 = 1;
2N/A rev2 = 3;
2N/A
2N/A facs_addr = 0;
2N/A playground = playground_ptr = 0;
2N/A playground_size = 0;
2N/A
2N/A rsdp = (struct grub_acpi_rsdp_v10 *) grub_machine_acpi_get_rsdpv2 ();
2N/A
2N/A if (! rsdp)
2N/A rsdp = grub_machine_acpi_get_rsdpv1 ();
2N/A
2N/A if (rsdp)
2N/A {
2N/A grub_uint32_t *entry_ptr;
2N/A char *exclude = 0;
2N/A char *load_only = 0;
2N/A char *ptr;
2N/A /* RSDT consists of header and an array of 32-bit pointers. */
2N/A struct grub_acpi_table_header *rsdt;
2N/A
2N/A exclude = state[0].set ? grub_strdup (state[0].arg) : 0;
2N/A if (exclude)
2N/A {
2N/A for (ptr = exclude; *ptr; ptr++)
2N/A *ptr = grub_tolower (*ptr);
2N/A }
2N/A
2N/A load_only = state[1].set ? grub_strdup (state[1].arg) : 0;
2N/A if (load_only)
2N/A {
2N/A for (ptr = load_only; *ptr; ptr++)
2N/A *ptr = grub_tolower (*ptr);
2N/A }
2N/A
2N/A /* Set revision variables to replicate the same version as host. */
2N/A rev1 = ! rsdp->revision;
2N/A rev2 = rsdp->revision;
2N/A rsdt = (struct grub_acpi_table_header *) (grub_addr_t) rsdp->rsdt_addr;
2N/A /* Load host tables. */
2N/A for (entry_ptr = (grub_uint32_t *) (rsdt + 1);
2N/A entry_ptr < (grub_uint32_t *) (((grub_uint8_t *) rsdt)
2N/A + rsdt->length);
2N/A entry_ptr++)
2N/A {
2N/A char signature[5];
2N/A struct efiemu_acpi_table *table;
2N/A struct grub_acpi_table_header *curtable
2N/A = (struct grub_acpi_table_header *) (grub_addr_t) *entry_ptr;
2N/A signature[4] = 0;
2N/A for (i = 0; i < 4;i++)
2N/A signature[i] = grub_tolower (curtable->signature[i]);
2N/A
2N/A /* If it's FADT it contains addresses of DSDT and FACS. */
2N/A if (grub_strcmp (signature, "facp") == 0)
2N/A {
2N/A struct grub_acpi_table_header *dsdt;
2N/A struct grub_acpi_fadt *fadt = (struct grub_acpi_fadt *) curtable;
2N/A
2N/A /* Set root header variables to the same values
2N/A as FADT by default. */
2N/A grub_memcpy (&root_oemid, &(fadt->hdr.oemid),
2N/A sizeof (root_oemid));
2N/A grub_memcpy (&root_oemtable, &(fadt->hdr.oemtable),
2N/A sizeof (root_oemtable));
2N/A root_oemrev = fadt->hdr.oemrev;
2N/A grub_memcpy (&root_creator_id, &(fadt->hdr.creator_id),
2N/A sizeof (root_creator_id));
2N/A root_creator_rev = fadt->hdr.creator_rev;
2N/A
2N/A /* Load DSDT if not excluded. */
2N/A dsdt = (struct grub_acpi_table_header *)
2N/A (grub_addr_t) fadt->dsdt_addr;
2N/A if (dsdt && (! exclude || ! grub_strword (exclude, "dsdt"))
2N/A && (! load_only || grub_strword (load_only, "dsdt"))
2N/A && dsdt->length >= sizeof (*dsdt))
2N/A {
2N/A dsdt_size = dsdt->length;
2N/A table_dsdt = grub_malloc (dsdt->length);
2N/A if (! table_dsdt)
2N/A {
2N/A free_tables ();
2N/A grub_free (exclude);
2N/A grub_free (load_only);
2N/A return grub_error (GRUB_ERR_OUT_OF_MEMORY,
2N/A "couldn't allocate table");
2N/A }
2N/A grub_memcpy (table_dsdt, dsdt, dsdt->length);
2N/A }
2N/A
2N/A /* Save FACS address. FACS shouldn't be overridden. */
2N/A facs_addr = fadt->facs_addr;
2N/A }
2N/A
2N/A /* Skip excluded tables. */
2N/A if (exclude && grub_strword (exclude, signature))
2N/A continue;
2N/A if (load_only && ! grub_strword (load_only, signature))
2N/A continue;
2N/A
2N/A /* Sanity check. */
2N/A if (curtable->length < sizeof (*curtable))
2N/A continue;
2N/A
2N/A table = (struct efiemu_acpi_table *) grub_malloc
2N/A (sizeof (struct efiemu_acpi_table));
2N/A if (! table)
2N/A {
2N/A free_tables ();
2N/A grub_free (exclude);
2N/A grub_free (load_only);
2N/A return grub_error (GRUB_ERR_OUT_OF_MEMORY,
2N/A "couldn't allocate table structure");
2N/A }
2N/A table->size = curtable->length;
2N/A table->addr = grub_malloc (table->size);
2N/A playground_size += table->size;
2N/A if (! table->addr)
2N/A {
2N/A free_tables ();
2N/A return grub_error (GRUB_ERR_OUT_OF_MEMORY,
2N/A "couldn't allocate table");
2N/A }
2N/A table->next = acpi_tables;
2N/A acpi_tables = table;
2N/A grub_memcpy (table->addr, curtable, table->size);
2N/A }
2N/A grub_free (exclude);
2N/A grub_free (load_only);
2N/A }
2N/A
2N/A /* Does user specify versions to generate? */
2N/A if (state[2].set || state[3].set)
2N/A {
2N/A rev1 = state[2].set;
2N/A if (state[3].set)
2N/A rev2 = rev2 ? : 2;
2N/A else
2N/A rev2 = 0;
2N/A }
2N/A
2N/A /* Does user override root header information? */
2N/A if (state[4].set)
2N/A grub_strncpy (root_oemid, state[4].arg, sizeof (root_oemid));
2N/A if (state[5].set)
2N/A grub_strncpy (root_oemtable, state[5].arg, sizeof (root_oemtable));
2N/A if (state[6].set)
2N/A root_oemrev = grub_strtoul (state[6].arg, 0, 0);
2N/A if (state[7].set)
2N/A grub_strncpy (root_creator_id, state[7].arg, sizeof (root_creator_id));
2N/A if (state[8].set)
2N/A root_creator_rev = grub_strtoul (state[8].arg, 0, 0);
2N/A
2N/A /* Load user tables */
2N/A for (i = 0; i < argc; i++)
2N/A {
2N/A grub_file_t file;
2N/A grub_size_t size;
2N/A char *buf;
2N/A
2N/A file = grub_file_open (args[i]);
2N/A if (! file)
2N/A {
2N/A free_tables ();
2N/A return grub_error (GRUB_ERR_BAD_OS, "couldn't open file %s", args[i]);
2N/A }
2N/A
2N/A size = grub_file_size (file);
2N/A if (size < sizeof (struct grub_acpi_table_header))
2N/A {
2N/A grub_file_close (file);
2N/A free_tables ();
2N/A return grub_error (GRUB_ERR_BAD_OS, "file %s is too small", args[i]);
2N/A }
2N/A
2N/A buf = (char *) grub_malloc (size);
2N/A if (! buf)
2N/A {
2N/A grub_file_close (file);
2N/A free_tables ();
2N/A return grub_errno;
2N/A }
2N/A
2N/A if (grub_file_read (file, buf, size) != (int) size)
2N/A {
2N/A grub_file_close (file);
2N/A free_tables ();
2N/A if (!grub_errno)
2N/A grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
2N/A args[i]);
2N/A return grub_errno;
2N/A }
2N/A grub_file_close (file);
2N/A
2N/A if (grub_memcmp (((struct grub_acpi_table_header *) buf)->signature,
2N/A "DSDT", 4) == 0)
2N/A {
2N/A grub_free (table_dsdt);
2N/A table_dsdt = buf;
2N/A dsdt_size = size;
2N/A }
2N/A else
2N/A {
2N/A struct efiemu_acpi_table *table;
2N/A table = (struct efiemu_acpi_table *) grub_malloc
2N/A (sizeof (struct efiemu_acpi_table));
2N/A if (! table)
2N/A {
2N/A free_tables ();
2N/A return grub_error (GRUB_ERR_OUT_OF_MEMORY,
2N/A "couldn't allocate table structure");
2N/A }
2N/A
2N/A table->size = size;
2N/A table->addr = buf;
2N/A playground_size += table->size;
2N/A
2N/A table->next = acpi_tables;
2N/A acpi_tables = table;
2N/A }
2N/A }
2N/A
2N/A numoftables = 0;
2N/A for (cur = acpi_tables; cur; cur = cur->next)
2N/A numoftables++;
2N/A
2N/A /* DSDT. */
2N/A playground_size += dsdt_size;
2N/A /* RSDT. */
2N/A playground_size += sizeof (struct grub_acpi_table_header) + 4 * numoftables;
2N/A /* RSDPv1. */
2N/A playground_size += sizeof (struct grub_acpi_rsdp_v10);
2N/A /* XSDT. */
2N/A playground_size += sizeof (struct grub_acpi_table_header) + 8 * numoftables;
2N/A /* RSDPv2. */
2N/A playground_size += sizeof (struct grub_acpi_rsdp_v20);
2N/A
2N/A playground = playground_ptr
2N/A = grub_mmap_malign_and_register (1, playground_size, &mmapregion,
2N/A GRUB_MEMORY_ACPI, 0);
2N/A
2N/A if (! playground)
2N/A {
2N/A free_tables ();
2N/A return grub_error (GRUB_ERR_OUT_OF_MEMORY,
2N/A "couldn't allocate space for ACPI tables");
2N/A }
2N/A
2N/A setup_common_tables ();
2N/A
2N/A /* Request space for RSDPv1. */
2N/A if (rev1)
2N/A setv1table ();
2N/A
2N/A /* Request space for RSDPv2+ and XSDT. */
2N/A if (rev2)
2N/A setv2table ();
2N/A
2N/A for (cur = acpi_tables; cur;)
2N/A {
2N/A t = cur;
2N/A cur = cur->next;
2N/A grub_free (t);
2N/A }
2N/A acpi_tables = 0;
2N/A
2N/A#if defined (__i386__) || defined (__x86_64__)
2N/A if (! state[9].set)
2N/A {
2N/A grub_err_t err;
2N/A err = grub_acpi_create_ebda ();
2N/A if (err)
2N/A {
2N/A rsdpv1_new = 0;
2N/A rsdpv2_new = 0;
2N/A grub_mmap_free_and_unregister (mmapregion);
2N/A return err;
2N/A }
2N/A }
2N/A#endif
2N/A
2N/A#ifdef GRUB_MACHINE_EFI
2N/A {
2N/A struct grub_efi_guid acpi = GRUB_EFI_ACPI_TABLE_GUID;
2N/A struct grub_efi_guid acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID;
2N/A
2N/A grub_efi_system_table->boot_services->install_configuration_table
2N/A (&acpi20, grub_acpi_get_rsdpv2 ());
2N/A grub_efi_system_table->boot_services->install_configuration_table
2N/A (&acpi, grub_acpi_get_rsdpv1 ());
2N/A }
2N/A#endif
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_extcmd_t cmd;
2N/A
2N/AGRUB_MOD_INIT(acpi)
2N/A{
2N/A cmd = grub_register_extcmd ("acpi", grub_cmd_acpi, 0,
2N/A N_("[-1|-2] [--exclude=TABLE1,TABLE2|"
2N/A "--load-only=table1,table2] FILE1"
2N/A " [FILE2] [...]"),
2N/A N_("Load host ACPI tables and tables "
2N/A "specified by arguments."),
2N/A options);
2N/A}
2N/A
2N/AGRUB_MOD_FINI(acpi)
2N/A{
2N/A grub_unregister_extcmd (cmd);
2N/A}