1
/* acpi.c - modify acpi tables. */
3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 2009 Free Software Foundation, Inc.
6
* GRUB is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation, either version 3 of the License, or
9
* (at your option) any later version.
11
* GRUB is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
21
#include <grub/extcmd.h>
22
#include <grub/file.h>
23
#include <grub/disk.h>
24
#include <grub/term.h>
25
#include <grub/misc.h>
26
#include <grub/gzio.h>
27
#include <grub/acpi.h>
29
#include <grub/machine/memory.h>
30
#include <grub/memory.h>
31
#include <grub/i18n.h>
33
#ifdef GRUB_MACHINE_EFI
34
#include <grub/efi/efi.h>
35
#include <grub/efi/api.h>
38
static const struct grub_arg_option options[] = {
40
N_("Don't load host tables specified by comma-separated list."),
43
N_("Load only tables specified by comma-separated list."), 0, ARG_TYPE_STRING},
44
{"v1", '1', 0, N_("Expose v1 tables."), 0, ARG_TYPE_NONE},
45
{"v2", '2', 0, N_("Expose v2 and v3 tables."), 0, ARG_TYPE_NONE},
46
{"oemid", 'o', 0, N_("Set OEMID of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
48
N_("Set OEMTABLE ID of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
49
{"oemtablerev", 'r', 0,
50
N_("Set OEMTABLE revision of RSDP, XSDT and RSDT."), 0, ARG_TYPE_INT},
51
{"oemtablecreator", 'c', 0,
52
N_("Set creator field of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
53
{"oemtablecreatorrev", 'd', 0,
54
N_("Set creator revision of RSDP, XSDT and RSDT."), 0, ARG_TYPE_INT},
55
{"no-ebda", 'e', 0, N_("Don't update EBDA. May fix failures or hangs on some."
56
" BIOSes but makes it ineffective with OS not receiving RSDP from GRUB."),
61
/* Simple checksum by summing all bytes. Used by ACPI and SMBIOS. */
63
grub_byte_checksum (void *base, grub_size_t size)
67
for (ptr = (grub_uint8_t *) base; ptr < ((grub_uint8_t *) base) + size;
73
/* rev1 is 1 if ACPIv1 is to be generated, 0 otherwise.
74
rev2 contains the revision of ACPIv2+ to generate or 0 if none. */
75
static int rev1, rev2;
76
/* OEMID of RSDP, RSDT and XSDT. */
77
static char root_oemid[6];
78
/* OEMTABLE of the same tables. */
79
static char root_oemtable[8];
80
/* OEMREVISION of the same tables. */
81
static grub_uint32_t root_oemrev;
82
/* CreatorID of the same tables. */
83
static char root_creator_id[4];
84
/* CreatorRevision of the same tables. */
85
static grub_uint32_t root_creator_rev;
86
static struct grub_acpi_rsdp_v10 *rsdpv1_new = 0;
87
static struct grub_acpi_rsdp_v20 *rsdpv2_new = 0;
88
static char *playground = 0, *playground_ptr = 0;
89
static int playground_size = 0;
91
/* Linked list of ACPI tables. */
92
struct efiemu_acpi_table
96
struct efiemu_acpi_table *next;
98
static struct efiemu_acpi_table *acpi_tables = 0;
100
/* DSDT isn't in RSDT. So treat it specially. */
101
static void *table_dsdt = 0;
102
/* Pointer to recreated RSDT. */
103
static void *rsdt_addr = 0;
105
/* Allocation handles for different tables. */
106
static grub_size_t dsdt_size = 0;
108
/* Address of original FACS. */
109
static grub_uint32_t facs_addr = 0;
111
struct grub_acpi_rsdp_v20 *
112
grub_acpi_get_rsdpv2 (void)
118
return grub_machine_acpi_get_rsdpv2 ();
121
struct grub_acpi_rsdp_v10 *
122
grub_acpi_get_rsdpv1 (void)
128
return grub_machine_acpi_get_rsdpv1 ();
132
iszero (grub_uint8_t *reg, int size)
135
for (i = 0; i < size; i++)
142
grub_acpi_create_ebda (void)
147
grub_uint8_t *ebda, *v1inebda = 0, *v2inebda = 0;
148
grub_uint64_t highestlow = 0;
149
grub_uint8_t *targetebda, *target;
150
struct grub_acpi_rsdp_v10 *v1;
151
struct grub_acpi_rsdp_v20 *v2;
152
auto int NESTED_FUNC_ATTR find_hook (grub_uint64_t, grub_uint64_t,
154
int NESTED_FUNC_ATTR find_hook (grub_uint64_t start, grub_uint64_t size,
157
grub_uint64_t end = start + size;
158
if (type != GRUB_MACHINE_MEMORY_AVAILABLE)
162
if (end > start + ebda_len
163
&& highestlow < ((end - ebda_len) & (~0xf)) )
164
highestlow = (end - ebda_len) & (~0xf);
168
ebda = (grub_uint8_t *) UINT_TO_PTR ((*((grub_uint16_t *)0x40e)) << 4);
169
ebda_kb_len = *(grub_uint16_t *) ebda;
170
if (! ebda || ebda_kb_len > 16)
172
ebda_len = (ebda_kb_len + 1) << 10;
174
/* FIXME: use low-memory mm allocation once it's available. */
175
grub_mmap_iterate (find_hook);
176
targetebda = (grub_uint8_t *) UINT_TO_PTR (highestlow);
177
grub_dprintf ("acpi", "creating ebda @%llx\n",
178
(unsigned long long) highestlow);
180
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
181
"couldn't find space for the new EBDA");
183
mmapregion = grub_mmap_register (PTR_TO_UINT64 (targetebda), ebda_len,
184
GRUB_MACHINE_MEMORY_RESERVED);
188
/* XXX: EBDA is unstandardized, so this implementation is heuristical. */
190
grub_memcpy (targetebda, ebda, 0x400);
192
grub_memset (targetebda, 0, 0x400);
193
*((grub_uint16_t *) targetebda) = ebda_kb_len + 1;
196
v1 = grub_acpi_get_rsdpv1 ();
197
v2 = grub_acpi_get_rsdpv2 ();
198
if (v2 && v2->length > 40)
201
/* First try to replace already existing rsdp. */
204
grub_dprintf ("acpi", "Scanning EBDA for old rsdpv2\n");
205
for (; target < targetebda + 0x400 - v2->length; target += 0x10)
206
if (grub_memcmp (target, "RSD PTR ", 8) == 0
207
&& grub_byte_checksum (target,
208
sizeof (struct grub_acpi_rsdp_v10)) == 0
209
&& ((struct grub_acpi_rsdp_v10 *) target)->revision != 0
210
&& ((struct grub_acpi_rsdp_v20 *) target)->length <= v2->length)
212
grub_memcpy (target, v2, v2->length);
213
grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target);
215
target += v2->length;
216
target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
224
grub_dprintf ("acpi", "Scanning EBDA for old rsdpv1\n");
225
for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
227
if (grub_memcmp (target, "RSD PTR ", 8) == 0
228
&& grub_byte_checksum (target,
229
sizeof (struct grub_acpi_rsdp_v10)) == 0)
231
grub_memcpy (target, v1, sizeof (struct grub_acpi_rsdp_v10));
232
grub_dprintf ("acpi", "Copying rsdpv1 to %p\n", target);
234
target += sizeof (struct grub_acpi_rsdp_v10);
235
target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
241
target = targetebda + 0x100;
243
/* Try contiguous zeros. */
246
grub_dprintf ("acpi", "Scanning EBDA for block of zeros\n");
247
for (; target < targetebda + 0x400 - v2->length; target += 0x10)
248
if (iszero (target, v2->length))
250
grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target);
251
grub_memcpy (target, v2, v2->length);
253
target += v2->length;
254
target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
262
grub_dprintf ("acpi", "Scanning EBDA for block of zeros\n");
263
for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
265
if (iszero (target, sizeof (struct grub_acpi_rsdp_v10)))
267
grub_dprintf ("acpi", "Copying rsdpv1 to %p\n", target);
268
grub_memcpy (target, v1, sizeof (struct grub_acpi_rsdp_v10));
270
target += sizeof (struct grub_acpi_rsdp_v10);
271
target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
279
grub_mmap_unregister (mmapregion);
280
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
281
"couldn't find suitable spot in EBDA");
284
/* Remove any other RSDT. */
285
for (target = targetebda;
286
target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
288
if (grub_memcmp (target, "RSD PTR ", 8) == 0
289
&& grub_byte_checksum (target,
290
sizeof (struct grub_acpi_rsdp_v10)) == 0
291
&& target != v1inebda && target != v2inebda)
294
grub_dprintf ("acpi", "Switching EBDA\n");
295
(*((grub_uint16_t *) 0x40e)) = ((long)targetebda) >> 4;
296
grub_dprintf ("acpi", "EBDA switched\n");
298
return GRUB_ERR_NONE;
301
/* Create tables common to ACPIv1 and ACPIv2+ */
303
setup_common_tables (void)
305
struct efiemu_acpi_table *cur;
306
struct grub_acpi_table_header *rsdt;
307
grub_uint32_t *rsdt_entry;
311
grub_memcpy (playground_ptr, table_dsdt, dsdt_size);
312
grub_free (table_dsdt);
313
table_dsdt = playground_ptr;
314
playground_ptr += dsdt_size;
316
/* Treat other tables. */
317
for (cur = acpi_tables; cur; cur = cur->next)
319
struct grub_acpi_fadt *fadt;
321
grub_memcpy (playground_ptr, cur->addr, cur->size);
322
grub_free (cur->addr);
323
cur->addr = playground_ptr;
324
playground_ptr += cur->size;
326
/* If it's FADT correct DSDT and FACS addresses. */
327
fadt = (struct grub_acpi_fadt *) cur->addr;
328
if (grub_memcmp (fadt->hdr.signature, "FACP",
329
sizeof (fadt->hdr.signature)) == 0)
331
fadt->dsdt_addr = PTR_TO_UINT32 (table_dsdt);
332
fadt->facs_addr = facs_addr;
334
/* Does a revision 2 exist at all? */
335
if (fadt->hdr.revision >= 3)
337
fadt->dsdt_xaddr = PTR_TO_UINT64 (table_dsdt);
338
fadt->facs_xaddr = facs_addr;
341
/* Recompute checksum. */
342
fadt->hdr.checksum = 0;
343
fadt->hdr.checksum = 1 + ~grub_byte_checksum (fadt, fadt->hdr.length);
347
/* Fill RSDT entries. */
349
for (cur = acpi_tables; cur; cur = cur->next)
352
rsdt_addr = rsdt = (struct grub_acpi_table_header *) playground_ptr;
353
playground_ptr += sizeof (struct grub_acpi_table_header) + 4 * numoftables;
355
rsdt_entry = (grub_uint32_t *) (rsdt + 1);
357
/* Fill RSDT header. */
358
grub_memcpy (&(rsdt->signature), "RSDT", 4);
359
rsdt->length = sizeof (struct grub_acpi_table_header) + 4 * numoftables;
361
grub_memcpy (&(rsdt->oemid), root_oemid, sizeof (rsdt->oemid));
362
grub_memcpy (&(rsdt->oemtable), root_oemtable, sizeof (rsdt->oemtable));
363
rsdt->oemrev = root_oemrev;
364
grub_memcpy (&(rsdt->creator_id), root_creator_id, sizeof (rsdt->creator_id));
365
rsdt->creator_rev = root_creator_rev;
367
for (cur = acpi_tables; cur; cur = cur->next)
368
*(rsdt_entry++) = PTR_TO_UINT32 (cur->addr);
370
/* Recompute checksum. */
372
rsdt->checksum = 1 + ~grub_byte_checksum (rsdt, rsdt->length);
375
/* Regenerate ACPIv1 RSDP */
380
rsdpv1_new = (struct grub_acpi_rsdp_v10 *) playground_ptr;
381
playground_ptr += sizeof (struct grub_acpi_rsdp_v10);
382
grub_memcpy (&(rsdpv1_new->signature), "RSD PTR ",
383
sizeof (rsdpv1_new->signature));
384
grub_memcpy (&(rsdpv1_new->oemid), root_oemid, sizeof (rsdpv1_new->oemid));
385
rsdpv1_new->revision = 0;
386
rsdpv1_new->rsdt_addr = PTR_TO_UINT32 (rsdt_addr);
387
rsdpv1_new->checksum = 0;
388
rsdpv1_new->checksum = 1 + ~grub_byte_checksum (rsdpv1_new,
389
sizeof (*rsdpv1_new));
390
grub_dprintf ("acpi", "Generated ACPIv1 tables\n");
396
struct grub_acpi_table_header *xsdt;
397
struct efiemu_acpi_table *cur;
398
grub_uint64_t *xsdt_entry;
402
for (cur = acpi_tables; cur; cur = cur->next)
406
xsdt = (struct grub_acpi_table_header *) playground_ptr;
407
playground_ptr += sizeof (struct grub_acpi_table_header) + 8 * numoftables;
409
xsdt_entry = (grub_uint64_t *)(xsdt + 1);
410
for (cur = acpi_tables; cur; cur = cur->next)
411
*(xsdt_entry++) = PTR_TO_UINT64 (cur->addr);
412
grub_memcpy (&(xsdt->signature), "XSDT", 4);
413
xsdt->length = sizeof (struct grub_acpi_table_header) + 8 * numoftables;
415
grub_memcpy (&(xsdt->oemid), root_oemid, sizeof (xsdt->oemid));
416
grub_memcpy (&(xsdt->oemtable), root_oemtable, sizeof (xsdt->oemtable));
417
xsdt->oemrev = root_oemrev;
418
grub_memcpy (&(xsdt->creator_id), root_creator_id, sizeof (xsdt->creator_id));
419
xsdt->creator_rev = root_creator_rev;
421
xsdt->checksum = 1 + ~grub_byte_checksum (xsdt, xsdt->length);
424
rsdpv2_new = (struct grub_acpi_rsdp_v20 *) playground_ptr;
425
playground_ptr += sizeof (struct grub_acpi_rsdp_v20);
426
grub_memcpy (&(rsdpv2_new->rsdpv1.signature), "RSD PTR ",
427
sizeof (rsdpv2_new->rsdpv1.signature));
428
grub_memcpy (&(rsdpv2_new->rsdpv1.oemid), root_oemid,
429
sizeof (rsdpv2_new->rsdpv1.oemid));
430
rsdpv2_new->rsdpv1.revision = rev2;
431
rsdpv2_new->rsdpv1.rsdt_addr = PTR_TO_UINT32 (rsdt_addr);
432
rsdpv2_new->rsdpv1.checksum = 0;
433
rsdpv2_new->rsdpv1.checksum = 1 + ~grub_byte_checksum
434
(&(rsdpv2_new->rsdpv1), sizeof (rsdpv2_new->rsdpv1));
435
rsdpv2_new->length = sizeof (*rsdpv2_new);
436
rsdpv2_new->xsdt_addr = PTR_TO_UINT64 (xsdt);
437
rsdpv2_new->checksum = 0;
438
rsdpv2_new->checksum = 1 + ~grub_byte_checksum (rsdpv2_new,
440
grub_dprintf ("acpi", "Generated ACPIv2 tables\n");
446
struct efiemu_acpi_table *cur, *t;
448
grub_free (table_dsdt);
449
for (cur = acpi_tables; cur;)
452
grub_free (cur->addr);
461
grub_cmd_acpi (struct grub_extcmd *cmd,
462
int argc, char **args)
464
struct grub_arg_list *state = cmd->state;
465
struct grub_acpi_rsdp_v10 *rsdp;
466
struct efiemu_acpi_table *cur, *t;
471
/* Default values if no RSDP is found. */
476
playground = playground_ptr = 0;
479
rsdp = (struct grub_acpi_rsdp_v10 *) grub_machine_acpi_get_rsdpv2 ();
482
rsdp = grub_machine_acpi_get_rsdpv1 ();
486
grub_uint32_t *entry_ptr;
490
/* RSDT consists of header and an array of 32-bit pointers. */
491
struct grub_acpi_table_header *rsdt;
493
exclude = state[0].set ? grub_strdup (state[0].arg) : 0;
496
for (ptr = exclude; *ptr; ptr++)
497
*ptr = grub_tolower (*ptr);
500
load_only = state[1].set ? grub_strdup (state[1].arg) : 0;
503
for (ptr = load_only; *ptr; ptr++)
504
*ptr = grub_tolower (*ptr);
507
/* Set revision variables to replicate the same version as host. */
508
rev1 = ! rsdp->revision;
509
rev2 = rsdp->revision;
510
rsdt = (struct grub_acpi_table_header *) UINT_TO_PTR (rsdp->rsdt_addr);
511
/* Load host tables. */
512
for (entry_ptr = (grub_uint32_t *) (rsdt + 1);
513
entry_ptr < (grub_uint32_t *) (((grub_uint8_t *) rsdt)
518
struct efiemu_acpi_table *table;
519
struct grub_acpi_table_header *curtable
520
= (struct grub_acpi_table_header *) UINT_TO_PTR (*entry_ptr);
522
for (i = 0; i < 4;i++)
523
signature[i] = grub_tolower (curtable->signature[i]);
525
/* If it's FADT it contains addresses of DSDT and FACS. */
526
if (grub_strcmp (signature, "facp") == 0)
528
struct grub_acpi_table_header *dsdt;
529
struct grub_acpi_fadt *fadt = (struct grub_acpi_fadt *) curtable;
531
/* Set root header variables to the same values
532
as FACP by default. */
533
grub_memcpy (&root_oemid, &(fadt->hdr.oemid),
534
sizeof (root_oemid));
535
grub_memcpy (&root_oemtable, &(fadt->hdr.oemtable),
536
sizeof (root_oemtable));
537
root_oemrev = fadt->hdr.oemrev;
538
grub_memcpy (&root_creator_id, &(fadt->hdr.creator_id),
539
sizeof (root_creator_id));
540
root_creator_rev = fadt->hdr.creator_rev;
542
/* Load DSDT if not excluded. */
543
dsdt = (struct grub_acpi_table_header *)
544
UINT_TO_PTR (fadt->dsdt_addr);
545
if (dsdt && (! exclude || ! grub_strword (exclude, "dsdt"))
546
&& (! load_only || grub_strword (load_only, "dsdt"))
547
&& dsdt->length >= sizeof (*dsdt))
549
dsdt_size = dsdt->length;
550
table_dsdt = grub_malloc (dsdt->length);
555
grub_free (load_only);
556
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
557
"couldn't allocate table");
559
grub_memcpy (table_dsdt, dsdt, dsdt->length);
562
/* Save FACS address. FACS shouldn't be overridden. */
563
facs_addr = fadt->facs_addr;
566
/* Skip excluded tables. */
567
if (exclude && grub_strword (exclude, signature))
569
if (load_only && ! grub_strword (load_only, signature))
573
if (curtable->length < sizeof (*curtable))
576
table = (struct efiemu_acpi_table *) grub_malloc
577
(sizeof (struct efiemu_acpi_table));
582
grub_free (load_only);
583
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
584
"couldn't allocate table structure");
586
table->size = curtable->length;
587
table->addr = grub_malloc (table->size);
588
playground_size += table->size;
592
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
593
"couldn't allocate table");
595
table->next = acpi_tables;
597
grub_memcpy (table->addr, curtable, table->size);
600
grub_free (load_only);
603
/* Does user specify versions to generate? */
604
if (state[2].set || state[3].set)
613
/* Does user override root header information? */
615
grub_strncpy (root_oemid, state[4].arg, sizeof (root_oemid));
617
grub_strncpy (root_oemtable, state[5].arg, sizeof (root_oemtable));
619
root_oemrev = grub_strtoul (state[6].arg, 0, 0);
621
grub_strncpy (root_creator_id, state[7].arg, sizeof (root_creator_id));
623
root_creator_rev = grub_strtoul (state[8].arg, 0, 0);
625
/* Load user tables */
626
for (i = 0; i < argc; i++)
632
file = grub_gzfile_open (args[i], 1);
636
return grub_error (GRUB_ERR_BAD_OS, "couldn't open file %s", args[i]);
639
size = grub_file_size (file);
640
if (size < sizeof (struct grub_acpi_table_header))
642
grub_file_close (file);
644
return grub_error (GRUB_ERR_BAD_OS, "file %s is too small", args[i]);
647
buf = (char *) grub_malloc (size);
650
grub_file_close (file);
652
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
653
"couldn't read file %s", args[i]);
656
if (grub_file_read (file, buf, size) != (int) size)
658
grub_file_close (file);
660
return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[i]);
662
grub_file_close (file);
664
if (grub_memcmp (((struct grub_acpi_table_header *) buf)->signature,
667
grub_free (table_dsdt);
673
struct efiemu_acpi_table *table;
674
table = (struct efiemu_acpi_table *) grub_malloc
675
(sizeof (struct efiemu_acpi_table));
679
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
680
"couldn't allocate table structure");
685
playground_size += table->size;
687
table->next = acpi_tables;
693
for (cur = acpi_tables; cur; cur = cur->next)
697
playground_size += dsdt_size;
699
playground_size += sizeof (struct grub_acpi_table_header) + 4 * numoftables;
701
playground_size += sizeof (struct grub_acpi_rsdp_v10);
703
playground_size += sizeof (struct grub_acpi_table_header) + 8 * numoftables;
705
playground_size += sizeof (struct grub_acpi_rsdp_v20);
707
playground = playground_ptr
708
= grub_mmap_malign_and_register (1, playground_size, &mmapregion,
709
GRUB_MACHINE_MEMORY_ACPI, 0);
714
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
715
"couldn't allocate space for ACPI tables");
718
setup_common_tables ();
720
/* Request space for RSDPv1. */
724
/* Request space for RSDPv2+ and XSDT. */
728
for (cur = acpi_tables; cur;)
736
if (! state[9].set && (err = grub_acpi_create_ebda ()))
740
grub_mmap_free_and_unregister (mmapregion);
744
#ifdef GRUB_MACHINE_EFI
746
struct grub_efi_guid acpi = GRUB_EFI_ACPI_TABLE_GUID;
747
struct grub_efi_guid acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID;
749
grub_efi_system_table->boot_services->install_configuration_table
750
(&acpi20, grub_acpi_get_rsdpv2 ());
751
grub_efi_system_table->boot_services->install_configuration_table
752
(&acpi, grub_acpi_get_rsdpv1 ());
756
return GRUB_ERR_NONE;
759
static grub_extcmd_t cmd;
763
cmd = grub_register_extcmd ("acpi", grub_cmd_acpi,
764
GRUB_COMMAND_FLAG_BOTH,
765
N_("[-1|-2] [--exclude=TABLE1,TABLE2|"
766
"--load-only=table1,table2] FILE1"
768
N_("Load host ACPI tables and tables "
769
"specified by arguments."),
775
grub_unregister_extcmd (cmd);