/* acpi.c - modify acpi tables. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* 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/>.
*/
#ifdef GRUB_MACHINE_EFI
#endif
GRUB_MOD_LICENSE ("GPLv3+");
{"exclude", 'x', 0,
N_("Don't load host tables specified by comma-separated list."),
0, ARG_TYPE_STRING},
{"load-only", 'n', 0,
{"oemtable", 't', 0,
{"oemtablerev", 'r', 0,
{"oemtablecreator", 'c', 0,
{"oemtablecreatorrev", 'd', 0,
" 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. */
{
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. */
/* OEMID of RSDP, RSDT and XSDT. */
/* OEMTABLE of the same tables. */
/* OEMREVISION of the same tables. */
/* CreatorID of the same tables. */
/* CreatorRevision of the same tables. */
static int playground_size = 0;
/* Linked list of ACPI tables. */
struct efiemu_acpi_table
{
void *addr;
};
/* 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. */
/* Address of original FACS. */
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
{
int i;
for (i = 0; i < size; i++)
if (reg[i])
return 0;
return 1;
}
#if defined (__i386__) || defined (__x86_64__)
grub_acpi_create_ebda (void)
{
int ebda_kb_len;
int ebda_len;
int mmapregion = 0;
{
if (type != GRUB_MEMORY_AVAILABLE)
return 0;
if (end > 0x100000)
end = 0x100000;
return 0;
}
ebda_kb_len = 0;
/* FIXME: use low-memory mm allocation once it's available. */
(unsigned long long) highestlow);
if (! highestlow)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't find space for the new EBDA");
if (! mmapregion)
return grub_errno;
/* XXX: EBDA is unstandardized, so this implementation is heuristical. */
if (ebda_kb_len)
else
target = targetebda;
v1 = grub_acpi_get_rsdpv1 ();
v2 = grub_acpi_get_rsdpv2 ();
v2 = 0;
/* First try to replace already existing rsdp. */
if (v2)
{
&& grub_byte_checksum (target,
sizeof (struct grub_acpi_rsdp_v10)) == 0
{
v2 = 0;
break;
}
}
if (v1)
{
target += 0x10)
&& grub_byte_checksum (target,
sizeof (struct grub_acpi_rsdp_v10)) == 0)
{
target += sizeof (struct grub_acpi_rsdp_v10);
v1 = 0;
break;
}
}
/* Try contiguous zeros. */
if (v2)
{
{
v2 = 0;
break;
}
}
if (v1)
{
target += 0x10)
{
target += sizeof (struct grub_acpi_rsdp_v10);
v1 = 0;
break;
}
}
{
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't find suitable spot in EBDA");
}
/* Remove any other RSDT. */
for (target = targetebda;
target += 0x10)
&& grub_byte_checksum (target,
sizeof (struct grub_acpi_rsdp_v10)) == 0
*target = 0;
return GRUB_ERR_NONE;
}
#endif
/* Create tables common to ACPIv1 and ACPIv2+ */
static void
setup_common_tables (void)
{
int numoftables;
/* Treat DSDT. */
/* Treat other tables. */
{
/* If it's FADT correct DSDT and FACS addresses. */
{
/* Does a revision 2 exist at all? */
{
}
/* Recompute checksum. */
}
}
/* Fill RSDT entries. */
numoftables = 0;
numoftables++;
/* Fill RSDT header. */
/* Recompute checksum. */
}
/* Regenerate ACPIv1 RSDP */
static void
setv1table (void)
{
/* Create RSDP. */
playground_ptr += sizeof (struct grub_acpi_rsdp_v10);
sizeof (rsdpv1_new->signature));
rsdpv1_new->revision = 0;
rsdpv1_new->checksum = 0;
sizeof (*rsdpv1_new));
}
static void
setv2table (void)
{
int numoftables;
numoftables = 0;
numoftables++;
/* Create XSDT. */
/* Create RSDPv2. */
playground_ptr += sizeof (struct grub_acpi_rsdp_v20);
rsdpv2_new->checksum = 0;
rsdpv2_new->length);
}
static void
free_tables (void)
{
if (table_dsdt)
{
t = cur;
grub_free (t);
}
acpi_tables = 0;
table_dsdt = 0;
}
static grub_err_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;
if (! rsdp)
if (rsdp)
{
char *exclude = 0;
char *load_only = 0;
char *ptr;
/* RSDT consists of header and an array of 32-bit pointers. */
if (exclude)
{
}
if (load_only)
{
}
/* Set revision variables to replicate the same version as host. */
/* Load host tables. */
entry_ptr++)
{
signature[4] = 0;
for (i = 0; i < 4;i++)
/* If it's FADT it contains addresses of DSDT and FACS. */
{
/* Set root header variables to the same values
as FADT by default. */
sizeof (root_oemid));
sizeof (root_oemtable));
sizeof (root_creator_id));
/* Load DSDT if not excluded. */
dsdt = (struct grub_acpi_table_header *)
{
if (! table_dsdt)
{
free_tables ();
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't allocate table");
}
}
/* Save FACS address. FACS shouldn't be overridden. */
}
/* Skip excluded tables. */
continue;
continue;
/* Sanity check. */
continue;
(sizeof (struct efiemu_acpi_table));
if (! table)
{
free_tables ();
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't allocate table structure");
}
{
free_tables ();
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't allocate table");
}
acpi_tables = table;
}
}
/* Does user specify versions to generate? */
{
else
rev2 = 0;
}
/* Does user override root header information? */
/* Load user tables */
for (i = 0; i < argc; i++)
{
char *buf;
if (! file)
{
free_tables ();
}
if (size < sizeof (struct grub_acpi_table_header))
{
free_tables ();
}
if (! buf)
{
free_tables ();
return grub_errno;
}
{
free_tables ();
if (!grub_errno)
args[i]);
return grub_errno;
}
"DSDT", 4) == 0)
{
table_dsdt = buf;
}
else
{
(sizeof (struct efiemu_acpi_table));
if (! table)
{
free_tables ();
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't allocate table structure");
}
acpi_tables = table;
}
}
numoftables = 0;
numoftables++;
/* DSDT. */
/* RSDT. */
/* RSDPv1. */
playground_size += sizeof (struct grub_acpi_rsdp_v10);
/* XSDT. */
/* RSDPv2. */
playground_size += sizeof (struct grub_acpi_rsdp_v20);
GRUB_MEMORY_ACPI, 0);
if (! playground)
{
free_tables ();
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't allocate space for ACPI tables");
}
/* Request space for RSDPv1. */
if (rev1)
setv1table ();
/* Request space for RSDPv2+ and XSDT. */
if (rev2)
setv2table ();
{
t = cur;
grub_free (t);
}
acpi_tables = 0;
#if defined (__i386__) || defined (__x86_64__)
{
err = grub_acpi_create_ebda ();
if (err)
{
rsdpv1_new = 0;
rsdpv2_new = 0;
return err;
}
}
#endif
#ifdef GRUB_MACHINE_EFI
{
(&acpi20, grub_acpi_get_rsdpv2 ());
(&acpi, grub_acpi_get_rsdpv1 ());
}
#endif
return GRUB_ERR_NONE;
}
{
N_("[-1|-2] [--exclude=TABLE1,TABLE2|"
"--load-only=table1,table2] FILE1"
" [FILE2] [...]"),
N_("Load host ACPI tables and tables "
"specified by arguments."),
options);
}
{
}