Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / commands / acpi.c
blob5507ffa0885834e037c9ab98ce13e732fc7100ad
1 /* acpi.c - modify acpi tables. */
2 /*
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/>.
20 #include <grub/dl.h>
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/acpi.h>
27 #include <grub/mm.h>
28 #include <grub/memory.h>
29 #include <grub/i18n.h>
31 #ifdef GRUB_MACHINE_EFI
32 #include <grub/efi/efi.h>
33 #include <grub/efi/api.h>
34 #endif
36 #pragma GCC diagnostic ignored "-Wcast-align"
38 GRUB_MOD_LICENSE ("GPLv3+");
40 static const struct grub_arg_option options[] = {
41 {"exclude", 'x', 0,
42 N_("Don't load host tables specified by comma-separated list."),
43 0, ARG_TYPE_STRING},
44 {"load-only", 'n', 0,
45 N_("Load only tables specified by comma-separated list."), 0, ARG_TYPE_STRING},
46 {"v1", '1', 0, N_("Export version 1 tables to the OS."), 0, ARG_TYPE_NONE},
47 {"v2", '2', 0, N_("Export version 2 and version 3 tables to the OS."), 0, ARG_TYPE_NONE},
48 {"oemid", 'o', 0, N_("Set OEMID of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
49 {"oemtable", 't', 0,
50 N_("Set OEMTABLE ID of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
51 {"oemtablerev", 'r', 0,
52 N_("Set OEMTABLE revision of RSDP, XSDT and RSDT."), 0, ARG_TYPE_INT},
53 {"oemtablecreator", 'c', 0,
54 N_("Set creator field of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING},
55 {"oemtablecreatorrev", 'd', 0,
56 N_("Set creator revision of RSDP, XSDT and RSDT."), 0, ARG_TYPE_INT},
57 /* TRANSLATORS: "hangs" here is a noun, not a verb. */
58 {"no-ebda", 'e', 0, N_("Don't update EBDA. May fix failures or hangs on some "
59 "BIOSes but makes it ineffective with OS not receiving RSDP from GRUB."),
60 0, ARG_TYPE_NONE},
61 {0, 0, 0, 0, 0, 0}
64 /* Simple checksum by summing all bytes. Used by ACPI and SMBIOS. */
65 grub_uint8_t
66 grub_byte_checksum (void *base, grub_size_t size)
68 grub_uint8_t *ptr;
69 grub_uint8_t ret = 0;
70 for (ptr = (grub_uint8_t *) base; ptr < ((grub_uint8_t *) base) + size;
71 ptr++)
72 ret += *ptr;
73 return ret;
76 /* rev1 is 1 if ACPIv1 is to be generated, 0 otherwise.
77 rev2 contains the revision of ACPIv2+ to generate or 0 if none. */
78 static int rev1, rev2;
79 /* OEMID of RSDP, RSDT and XSDT. */
80 static char root_oemid[6];
81 /* OEMTABLE of the same tables. */
82 static char root_oemtable[8];
83 /* OEMREVISION of the same tables. */
84 static grub_uint32_t root_oemrev;
85 /* CreatorID of the same tables. */
86 static char root_creator_id[4];
87 /* CreatorRevision of the same tables. */
88 static grub_uint32_t root_creator_rev;
89 static struct grub_acpi_rsdp_v10 *rsdpv1_new = 0;
90 static struct grub_acpi_rsdp_v20 *rsdpv2_new = 0;
91 static char *playground = 0, *playground_ptr = 0;
92 static int playground_size = 0;
94 /* Linked list of ACPI tables. */
95 struct efiemu_acpi_table
97 void *addr;
98 grub_size_t size;
99 struct efiemu_acpi_table *next;
101 static struct efiemu_acpi_table *acpi_tables = 0;
103 /* DSDT isn't in RSDT. So treat it specially. */
104 static void *table_dsdt = 0;
105 /* Pointer to recreated RSDT. */
106 static void *rsdt_addr = 0;
108 /* Allocation handles for different tables. */
109 static grub_size_t dsdt_size = 0;
111 /* Address of original FACS. */
112 static grub_uint32_t facs_addr = 0;
114 struct grub_acpi_rsdp_v20 *
115 grub_acpi_get_rsdpv2 (void)
117 if (rsdpv2_new)
118 return rsdpv2_new;
119 if (rsdpv1_new)
120 return 0;
121 return grub_machine_acpi_get_rsdpv2 ();
124 struct grub_acpi_rsdp_v10 *
125 grub_acpi_get_rsdpv1 (void)
127 if (rsdpv1_new)
128 return rsdpv1_new;
129 if (rsdpv2_new)
130 return 0;
131 return grub_machine_acpi_get_rsdpv1 ();
134 static inline int
135 iszero (grub_uint8_t *reg, int size)
137 int i;
138 for (i = 0; i < size; i++)
139 if (reg[i])
140 return 0;
141 return 1;
144 #if defined (__i386__) || defined (__x86_64__)
145 /* Context for grub_acpi_create_ebda. */
146 struct grub_acpi_create_ebda_ctx {
147 int ebda_len;
148 grub_uint64_t highestlow;
151 /* Helper for grub_acpi_create_ebda. */
152 static int
153 find_hook (grub_uint64_t start, grub_uint64_t size, grub_memory_type_t type,
154 void *data)
156 struct grub_acpi_create_ebda_ctx *ctx = data;
157 grub_uint64_t end = start + size;
158 if (type != GRUB_MEMORY_AVAILABLE)
159 return 0;
160 if (end > 0x100000)
161 end = 0x100000;
162 if (end > start + ctx->ebda_len
163 && ctx->highestlow < ((end - ctx->ebda_len) & (~0xf)) )
164 ctx->highestlow = (end - ctx->ebda_len) & (~0xf);
165 return 0;
168 grub_err_t
169 grub_acpi_create_ebda (void)
171 struct grub_acpi_create_ebda_ctx ctx = {
172 .highestlow = 0
174 int ebda_kb_len = 0;
175 int mmapregion = 0;
176 grub_uint8_t *ebda, *v1inebda = 0, *v2inebda = 0;
177 grub_uint8_t *targetebda, *target;
178 struct grub_acpi_rsdp_v10 *v1;
179 struct grub_acpi_rsdp_v20 *v2;
181 ebda = (grub_uint8_t *) (grub_addr_t) ((*((grub_uint16_t *)0x40e)) << 4);
182 if (ebda)
183 ebda_kb_len = *(grub_uint16_t *) ebda;
184 if (ebda_kb_len > 16)
185 ebda_kb_len = 0;
186 ctx.ebda_len = (ebda_kb_len + 1) << 10;
188 /* FIXME: use low-memory mm allocation once it's available. */
189 grub_mmap_iterate (find_hook, &ctx);
190 targetebda = (grub_uint8_t *) (grub_addr_t) ctx.highestlow;
191 grub_dprintf ("acpi", "creating ebda @%llx\n",
192 (unsigned long long) ctx.highestlow);
193 if (! ctx.highestlow)
194 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
195 "couldn't find space for the new EBDA");
197 mmapregion = grub_mmap_register ((grub_addr_t) targetebda, ctx.ebda_len,
198 GRUB_MEMORY_RESERVED);
199 if (! mmapregion)
200 return grub_errno;
202 /* XXX: EBDA is unstandardized, so this implementation is heuristical. */
203 if (ebda_kb_len)
204 grub_memcpy (targetebda, ebda, 0x400);
205 else
206 grub_memset (targetebda, 0, 0x400);
207 *((grub_uint16_t *) targetebda) = ebda_kb_len + 1;
208 target = targetebda;
210 v1 = grub_acpi_get_rsdpv1 ();
211 v2 = grub_acpi_get_rsdpv2 ();
212 if (v2 && v2->length > 40)
213 v2 = 0;
215 /* First try to replace already existing rsdp. */
216 if (v2)
218 grub_dprintf ("acpi", "Scanning EBDA for old rsdpv2\n");
219 for (; target < targetebda + 0x400 - v2->length; target += 0x10)
220 if (grub_memcmp (target, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0
221 && grub_byte_checksum (target,
222 sizeof (struct grub_acpi_rsdp_v10)) == 0
223 && ((struct grub_acpi_rsdp_v10 *) target)->revision != 0
224 && ((struct grub_acpi_rsdp_v20 *) target)->length <= v2->length)
226 grub_memcpy (target, v2, v2->length);
227 grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target);
228 v2inebda = target;
229 target += v2->length;
230 target = (grub_uint8_t *) ALIGN_UP((grub_addr_t) target, 16);
231 v2 = 0;
232 break;
236 if (v1)
238 grub_dprintf ("acpi", "Scanning EBDA for old rsdpv1\n");
239 for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
240 target += 0x10)
241 if (grub_memcmp (target, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0
242 && grub_byte_checksum (target,
243 sizeof (struct grub_acpi_rsdp_v10)) == 0)
245 grub_memcpy (target, v1, sizeof (struct grub_acpi_rsdp_v10));
246 grub_dprintf ("acpi", "Copying rsdpv1 to %p\n", target);
247 v1inebda = target;
248 target += sizeof (struct grub_acpi_rsdp_v10);
249 target = (grub_uint8_t *) ALIGN_UP((grub_addr_t) target, 16);
250 v1 = 0;
251 break;
255 target = targetebda + 0x100;
257 /* Try contiguous zeros. */
258 if (v2)
260 grub_dprintf ("acpi", "Scanning EBDA for block of zeros\n");
261 for (; target < targetebda + 0x400 - v2->length; target += 0x10)
262 if (iszero (target, v2->length))
264 grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target);
265 grub_memcpy (target, v2, v2->length);
266 v2inebda = target;
267 target += v2->length;
268 target = (grub_uint8_t *) ALIGN_UP((grub_addr_t) target, 16);
269 v2 = 0;
270 break;
274 if (v1)
276 grub_dprintf ("acpi", "Scanning EBDA for block of zeros\n");
277 for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
278 target += 0x10)
279 if (iszero (target, sizeof (struct grub_acpi_rsdp_v10)))
281 grub_dprintf ("acpi", "Copying rsdpv1 to %p\n", target);
282 grub_memcpy (target, v1, sizeof (struct grub_acpi_rsdp_v10));
283 v1inebda = target;
284 target += sizeof (struct grub_acpi_rsdp_v10);
285 target = (grub_uint8_t *) ALIGN_UP((grub_addr_t) target, 16);
286 v1 = 0;
287 break;
291 if (v1 || v2)
293 grub_mmap_unregister (mmapregion);
294 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
295 "couldn't find suitable spot in EBDA");
298 /* Remove any other RSDT. */
299 for (target = targetebda;
300 target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
301 target += 0x10)
302 if (grub_memcmp (target, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0
303 && grub_byte_checksum (target,
304 sizeof (struct grub_acpi_rsdp_v10)) == 0
305 && target != v1inebda && target != v2inebda)
306 *target = 0;
308 grub_dprintf ("acpi", "Switching EBDA\n");
309 (*((grub_uint16_t *) 0x40e)) = ((grub_addr_t) targetebda) >> 4;
310 grub_dprintf ("acpi", "EBDA switched\n");
312 return GRUB_ERR_NONE;
314 #endif
316 /* Create tables common to ACPIv1 and ACPIv2+ */
317 static void
318 setup_common_tables (void)
320 struct efiemu_acpi_table *cur;
321 struct grub_acpi_table_header *rsdt;
322 grub_uint32_t *rsdt_entry;
323 int numoftables;
325 /* Treat DSDT. */
326 grub_memcpy (playground_ptr, table_dsdt, dsdt_size);
327 grub_free (table_dsdt);
328 table_dsdt = playground_ptr;
329 playground_ptr += dsdt_size;
331 /* Treat other tables. */
332 for (cur = acpi_tables; cur; cur = cur->next)
334 struct grub_acpi_fadt *fadt;
336 grub_memcpy (playground_ptr, cur->addr, cur->size);
337 grub_free (cur->addr);
338 cur->addr = playground_ptr;
339 playground_ptr += cur->size;
341 /* If it's FADT correct DSDT and FACS addresses. */
342 fadt = (struct grub_acpi_fadt *) cur->addr;
343 if (grub_memcmp (fadt->hdr.signature, GRUB_ACPI_FADT_SIGNATURE,
344 sizeof (fadt->hdr.signature)) == 0)
346 fadt->dsdt_addr = (grub_addr_t) table_dsdt;
347 fadt->facs_addr = facs_addr;
349 /* Does a revision 2 exist at all? */
350 if (fadt->hdr.revision >= 3)
352 fadt->dsdt_xaddr = (grub_addr_t) table_dsdt;
353 fadt->facs_xaddr = facs_addr;
356 /* Recompute checksum. */
357 fadt->hdr.checksum = 0;
358 fadt->hdr.checksum = 1 + ~grub_byte_checksum (fadt, fadt->hdr.length);
362 /* Fill RSDT entries. */
363 numoftables = 0;
364 for (cur = acpi_tables; cur; cur = cur->next)
365 numoftables++;
367 rsdt_addr = rsdt = (struct grub_acpi_table_header *) playground_ptr;
368 playground_ptr += sizeof (struct grub_acpi_table_header) + sizeof (grub_uint32_t) * numoftables;
370 rsdt_entry = (grub_uint32_t *) (rsdt + 1);
372 /* Fill RSDT header. */
373 grub_memcpy (&(rsdt->signature), "RSDT", 4);
374 rsdt->length = sizeof (struct grub_acpi_table_header) + sizeof (grub_uint32_t) * numoftables;
375 rsdt->revision = 1;
376 grub_memcpy (&(rsdt->oemid), root_oemid, sizeof (rsdt->oemid));
377 grub_memcpy (&(rsdt->oemtable), root_oemtable, sizeof (rsdt->oemtable));
378 rsdt->oemrev = root_oemrev;
379 grub_memcpy (&(rsdt->creator_id), root_creator_id, sizeof (rsdt->creator_id));
380 rsdt->creator_rev = root_creator_rev;
382 for (cur = acpi_tables; cur; cur = cur->next)
383 *(rsdt_entry++) = (grub_addr_t) cur->addr;
385 /* Recompute checksum. */
386 rsdt->checksum = 0;
387 rsdt->checksum = 1 + ~grub_byte_checksum (rsdt, rsdt->length);
390 /* Regenerate ACPIv1 RSDP */
391 static void
392 setv1table (void)
394 /* Create RSDP. */
395 rsdpv1_new = (struct grub_acpi_rsdp_v10 *) playground_ptr;
396 playground_ptr += sizeof (struct grub_acpi_rsdp_v10);
397 grub_memcpy (&(rsdpv1_new->signature), GRUB_RSDP_SIGNATURE,
398 sizeof (rsdpv1_new->signature));
399 grub_memcpy (&(rsdpv1_new->oemid), root_oemid, sizeof (rsdpv1_new->oemid));
400 rsdpv1_new->revision = 0;
401 rsdpv1_new->rsdt_addr = (grub_addr_t) rsdt_addr;
402 rsdpv1_new->checksum = 0;
403 rsdpv1_new->checksum = 1 + ~grub_byte_checksum (rsdpv1_new,
404 sizeof (*rsdpv1_new));
405 grub_dprintf ("acpi", "Generated ACPIv1 tables\n");
408 static void
409 setv2table (void)
411 struct grub_acpi_table_header *xsdt;
412 struct efiemu_acpi_table *cur;
413 grub_uint64_t *xsdt_entry;
414 int numoftables;
416 numoftables = 0;
417 for (cur = acpi_tables; cur; cur = cur->next)
418 numoftables++;
420 /* Create XSDT. */
421 xsdt = (struct grub_acpi_table_header *) playground_ptr;
422 playground_ptr += sizeof (struct grub_acpi_table_header) + sizeof (grub_uint64_t) * numoftables;
424 xsdt_entry = (grub_uint64_t *)(xsdt + 1);
425 for (cur = acpi_tables; cur; cur = cur->next)
426 *(xsdt_entry++) = (grub_addr_t) cur->addr;
427 grub_memcpy (&(xsdt->signature), "XSDT", 4);
428 xsdt->length = sizeof (struct grub_acpi_table_header) + sizeof (grub_uint64_t) * numoftables;
429 xsdt->revision = 1;
430 grub_memcpy (&(xsdt->oemid), root_oemid, sizeof (xsdt->oemid));
431 grub_memcpy (&(xsdt->oemtable), root_oemtable, sizeof (xsdt->oemtable));
432 xsdt->oemrev = root_oemrev;
433 grub_memcpy (&(xsdt->creator_id), root_creator_id, sizeof (xsdt->creator_id));
434 xsdt->creator_rev = root_creator_rev;
435 xsdt->checksum = 0;
436 xsdt->checksum = 1 + ~grub_byte_checksum (xsdt, xsdt->length);
438 /* Create RSDPv2. */
439 rsdpv2_new = (struct grub_acpi_rsdp_v20 *) playground_ptr;
440 playground_ptr += sizeof (struct grub_acpi_rsdp_v20);
441 grub_memcpy (&(rsdpv2_new->rsdpv1.signature), GRUB_RSDP_SIGNATURE,
442 sizeof (rsdpv2_new->rsdpv1.signature));
443 grub_memcpy (&(rsdpv2_new->rsdpv1.oemid), root_oemid,
444 sizeof (rsdpv2_new->rsdpv1.oemid));
445 rsdpv2_new->rsdpv1.revision = rev2;
446 rsdpv2_new->rsdpv1.rsdt_addr = (grub_addr_t) rsdt_addr;
447 rsdpv2_new->rsdpv1.checksum = 0;
448 rsdpv2_new->rsdpv1.checksum = 1 + ~grub_byte_checksum
449 (&(rsdpv2_new->rsdpv1), sizeof (rsdpv2_new->rsdpv1));
450 rsdpv2_new->length = sizeof (*rsdpv2_new);
451 rsdpv2_new->xsdt_addr = (grub_addr_t) xsdt;
452 rsdpv2_new->checksum = 0;
453 rsdpv2_new->checksum = 1 + ~grub_byte_checksum (rsdpv2_new,
454 rsdpv2_new->length);
455 grub_dprintf ("acpi", "Generated ACPIv2 tables\n");
458 static void
459 free_tables (void)
461 struct efiemu_acpi_table *cur, *t;
462 if (table_dsdt)
463 grub_free (table_dsdt);
464 for (cur = acpi_tables; cur;)
466 t = cur;
467 grub_free (cur->addr);
468 cur = cur->next;
469 grub_free (t);
471 acpi_tables = 0;
472 table_dsdt = 0;
475 static grub_err_t
476 grub_cmd_acpi (struct grub_extcmd_context *ctxt, int argc, char **args)
478 struct grub_arg_list *state = ctxt->state;
479 struct grub_acpi_rsdp_v10 *rsdp;
480 struct efiemu_acpi_table *cur, *t;
481 int i, mmapregion;
482 int numoftables;
484 /* Default values if no RSDP is found. */
485 rev1 = 1;
486 rev2 = 3;
488 facs_addr = 0;
489 playground = playground_ptr = 0;
490 playground_size = 0;
492 rsdp = (struct grub_acpi_rsdp_v10 *) grub_machine_acpi_get_rsdpv2 ();
494 if (! rsdp)
495 rsdp = grub_machine_acpi_get_rsdpv1 ();
497 if (rsdp)
499 grub_uint32_t *entry_ptr;
500 char *exclude = 0;
501 char *load_only = 0;
502 char *ptr;
503 /* RSDT consists of header and an array of 32-bit pointers. */
504 struct grub_acpi_table_header *rsdt;
506 exclude = state[0].set ? grub_strdup (state[0].arg) : 0;
507 if (exclude)
509 for (ptr = exclude; *ptr; ptr++)
510 *ptr = grub_tolower (*ptr);
513 load_only = state[1].set ? grub_strdup (state[1].arg) : 0;
514 if (load_only)
516 for (ptr = load_only; *ptr; ptr++)
517 *ptr = grub_tolower (*ptr);
520 /* Set revision variables to replicate the same version as host. */
521 rev1 = ! rsdp->revision;
522 rev2 = rsdp->revision;
523 rsdt = (struct grub_acpi_table_header *) (grub_addr_t) rsdp->rsdt_addr;
524 /* Load host tables. */
525 for (entry_ptr = (grub_uint32_t *) (rsdt + 1);
526 entry_ptr < (grub_uint32_t *) (((grub_uint8_t *) rsdt)
527 + rsdt->length);
528 entry_ptr++)
530 char signature[5];
531 struct efiemu_acpi_table *table;
532 struct grub_acpi_table_header *curtable
533 = (struct grub_acpi_table_header *) (grub_addr_t) *entry_ptr;
534 signature[4] = 0;
535 for (i = 0; i < 4;i++)
536 signature[i] = grub_tolower (curtable->signature[i]);
538 /* If it's FADT it contains addresses of DSDT and FACS. */
539 if (grub_strcmp (signature, "facp") == 0)
541 struct grub_acpi_table_header *dsdt;
542 struct grub_acpi_fadt *fadt = (struct grub_acpi_fadt *) curtable;
544 /* Set root header variables to the same values
545 as FADT by default. */
546 grub_memcpy (&root_oemid, &(fadt->hdr.oemid),
547 sizeof (root_oemid));
548 grub_memcpy (&root_oemtable, &(fadt->hdr.oemtable),
549 sizeof (root_oemtable));
550 root_oemrev = fadt->hdr.oemrev;
551 grub_memcpy (&root_creator_id, &(fadt->hdr.creator_id),
552 sizeof (root_creator_id));
553 root_creator_rev = fadt->hdr.creator_rev;
555 /* Load DSDT if not excluded. */
556 dsdt = (struct grub_acpi_table_header *)
557 (grub_addr_t) fadt->dsdt_addr;
558 if (dsdt && (! exclude || ! grub_strword (exclude, "dsdt"))
559 && (! load_only || grub_strword (load_only, "dsdt"))
560 && dsdt->length >= sizeof (*dsdt))
562 dsdt_size = dsdt->length;
563 table_dsdt = grub_malloc (dsdt->length);
564 if (! table_dsdt)
566 free_tables ();
567 grub_free (exclude);
568 grub_free (load_only);
569 return grub_errno;
571 grub_memcpy (table_dsdt, dsdt, dsdt->length);
574 /* Save FACS address. FACS shouldn't be overridden. */
575 facs_addr = fadt->facs_addr;
578 /* Skip excluded tables. */
579 if (exclude && grub_strword (exclude, signature))
580 continue;
581 if (load_only && ! grub_strword (load_only, signature))
582 continue;
584 /* Sanity check. */
585 if (curtable->length < sizeof (*curtable))
586 continue;
588 table = (struct efiemu_acpi_table *) grub_malloc
589 (sizeof (struct efiemu_acpi_table));
590 if (! table)
592 free_tables ();
593 grub_free (exclude);
594 grub_free (load_only);
595 return grub_errno;
597 table->size = curtable->length;
598 table->addr = grub_malloc (table->size);
599 playground_size += table->size;
600 if (! table->addr)
602 free_tables ();
603 return grub_errno;
605 table->next = acpi_tables;
606 acpi_tables = table;
607 grub_memcpy (table->addr, curtable, table->size);
609 grub_free (exclude);
610 grub_free (load_only);
613 /* Does user specify versions to generate? */
614 if (state[2].set || state[3].set)
616 rev1 = state[2].set;
617 if (state[3].set)
618 rev2 = rev2 ? : 2;
619 else
620 rev2 = 0;
623 /* Does user override root header information? */
624 if (state[4].set)
625 grub_strncpy (root_oemid, state[4].arg, sizeof (root_oemid));
626 if (state[5].set)
627 grub_strncpy (root_oemtable, state[5].arg, sizeof (root_oemtable));
628 if (state[6].set)
629 root_oemrev = grub_strtoul (state[6].arg, 0, 0);
630 if (state[7].set)
631 grub_strncpy (root_creator_id, state[7].arg, sizeof (root_creator_id));
632 if (state[8].set)
633 root_creator_rev = grub_strtoul (state[8].arg, 0, 0);
635 /* Load user tables */
636 for (i = 0; i < argc; i++)
638 grub_file_t file;
639 grub_size_t size;
640 char *buf;
642 file = grub_file_open (args[i]);
643 if (! file)
645 free_tables ();
646 return grub_errno;
649 size = grub_file_size (file);
650 if (size < sizeof (struct grub_acpi_table_header))
652 grub_file_close (file);
653 free_tables ();
654 return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
655 args[i]);
658 buf = (char *) grub_malloc (size);
659 if (! buf)
661 grub_file_close (file);
662 free_tables ();
663 return grub_errno;
666 if (grub_file_read (file, buf, size) != (int) size)
668 grub_file_close (file);
669 free_tables ();
670 if (!grub_errno)
671 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
672 args[i]);
673 return grub_errno;
675 grub_file_close (file);
677 if (grub_memcmp (((struct grub_acpi_table_header *) buf)->signature,
678 "DSDT", 4) == 0)
680 grub_free (table_dsdt);
681 table_dsdt = buf;
682 dsdt_size = size;
684 else
686 struct efiemu_acpi_table *table;
687 table = (struct efiemu_acpi_table *) grub_malloc
688 (sizeof (struct efiemu_acpi_table));
689 if (! table)
691 free_tables ();
692 return grub_errno;
695 table->size = size;
696 table->addr = buf;
697 playground_size += table->size;
699 table->next = acpi_tables;
700 acpi_tables = table;
704 numoftables = 0;
705 for (cur = acpi_tables; cur; cur = cur->next)
706 numoftables++;
708 /* DSDT. */
709 playground_size += dsdt_size;
710 /* RSDT. */
711 playground_size += sizeof (struct grub_acpi_table_header) + sizeof (grub_uint32_t) * numoftables;
712 /* RSDPv1. */
713 playground_size += sizeof (struct grub_acpi_rsdp_v10);
714 /* XSDT. */
715 playground_size += sizeof (struct grub_acpi_table_header) + sizeof (grub_uint64_t) * numoftables;
716 /* RSDPv2. */
717 playground_size += sizeof (struct grub_acpi_rsdp_v20);
719 playground = playground_ptr
720 = grub_mmap_malign_and_register (1, playground_size, &mmapregion,
721 GRUB_MEMORY_ACPI, 0);
723 if (! playground)
725 free_tables ();
726 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
727 "couldn't allocate space for ACPI tables");
730 setup_common_tables ();
732 /* Request space for RSDPv1. */
733 if (rev1)
734 setv1table ();
736 /* Request space for RSDPv2+ and XSDT. */
737 if (rev2)
738 setv2table ();
740 for (cur = acpi_tables; cur;)
742 t = cur;
743 cur = cur->next;
744 grub_free (t);
746 acpi_tables = 0;
748 #if defined (__i386__) || defined (__x86_64__)
749 if (! state[9].set)
751 grub_err_t err;
752 err = grub_acpi_create_ebda ();
753 if (err)
755 rsdpv1_new = 0;
756 rsdpv2_new = 0;
757 grub_mmap_free_and_unregister (mmapregion);
758 return err;
761 #endif
763 #ifdef GRUB_MACHINE_EFI
765 struct grub_efi_guid acpi = GRUB_EFI_ACPI_TABLE_GUID;
766 struct grub_efi_guid acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID;
768 grub_efi_system_table->boot_services->install_configuration_table
769 (&acpi20, grub_acpi_get_rsdpv2 ());
770 grub_efi_system_table->boot_services->install_configuration_table
771 (&acpi, grub_acpi_get_rsdpv1 ());
773 #endif
775 return GRUB_ERR_NONE;
778 static grub_extcmd_t cmd;
780 GRUB_MOD_INIT(acpi)
782 cmd = grub_register_extcmd ("acpi", grub_cmd_acpi, 0,
783 N_("[-1|-2] [--exclude=TABLE1,TABLE2|"
784 "--load-only=TABLE1,TABLE2] FILE1"
785 " [FILE2] [...]"),
786 N_("Load host ACPI tables and tables "
787 "specified by arguments."),
788 options);
791 GRUB_MOD_FINI(acpi)
793 grub_unregister_extcmd (cmd);