2009-07-07 Pavel Roskin <proski@gnu.org>
[grub2/bean.git] / commands / acpi.c
blobd903d446daaa644df2a82e1259b06882e030282d
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/gzio.h>
27 #include <grub/acpi.h>
28 #include <grub/mm.h>
29 #include <grub/machine/machine.h>
30 #include <grub/machine/memory.h>
31 #include <grub/memory.h>
33 #ifdef GRUB_MACHINE_EFI
34 #include <grub/efi/efi.h>
35 #include <grub/efi/api.h>
36 #endif
38 static const struct grub_arg_option options[] = {
39 {"exclude", 'x', 0,
40 "Don't load host tables specified by comma-separated list",
41 0, ARG_TYPE_STRING},
42 {"load-only", 'n', 0,
43 "Load only tables specified by comma-separated list", 0, ARG_TYPE_STRING},
44 {"v1", '1', 0, "Expose v1 tables", 0, ARG_TYPE_NONE},
45 {"v2", '2', 0, "Expose v2 and v3 tables", 0, ARG_TYPE_NONE},
46 {"oemid", 'o', 0, "Set OEMID of RSDP, XSDT and RSDT", 0, ARG_TYPE_STRING},
47 {"oemtable", 't', 0,
48 "Set OEMTABLE ID of RSDP, XSDT and RSDT", 0, ARG_TYPE_STRING},
49 {"oemtablerev", 'r', 0,
50 "Set OEMTABLE revision of RSDP, XSDT and RSDT", 0, ARG_TYPE_INT},
51 {"oemtablecreator", 'c', 0,
52 "Set creator field of RSDP, XSDT and RSDT", 0, ARG_TYPE_STRING},
53 {"oemtablecreatorrev", 'd', 0,
54 "Set creator revision of RSDP, XSDT and RSDT", 0, ARG_TYPE_INT},
55 {"no-ebda", 'e', 0, "Don't update EBDA. May fix failures or hangs on some"
56 " BIOSes but makes it ineffective with OS not receiving RSDP from GRUB",
57 0, ARG_TYPE_NONE},
58 {0, 0, 0, 0, 0, 0}
61 /* Simple checksum by summing all bytes. Used by ACPI and SMBIOS. */
62 grub_uint8_t
63 grub_byte_checksum (void *base, grub_size_t size)
65 grub_uint8_t *ptr;
66 grub_uint8_t ret = 0;
67 for (ptr = (grub_uint8_t *) base; ptr < ((grub_uint8_t *) base) + size;
68 ptr++)
69 ret += *ptr;
70 return ret;
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
94 void *addr;
95 grub_size_t size;
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)
114 if (rsdpv2_new)
115 return rsdpv2_new;
116 if (rsdpv1_new)
117 return 0;
118 return grub_machine_acpi_get_rsdpv2 ();
121 struct grub_acpi_rsdp_v10 *
122 grub_acpi_get_rsdpv1 (void)
124 if (rsdpv1_new)
125 return rsdpv1_new;
126 if (rsdpv2_new)
127 return 0;
128 return grub_machine_acpi_get_rsdpv1 ();
131 static inline int
132 iszero (grub_uint8_t *reg, int size)
134 int i;
135 for (i = 0; i < size; i++)
136 if (reg[i])
137 return 0;
138 return 1;
141 grub_err_t
142 grub_acpi_create_ebda (void)
144 int ebda_kb_len;
145 int ebda_len;
146 int mmapregion = 0;
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,
153 grub_uint32_t);
154 int NESTED_FUNC_ATTR find_hook (grub_uint64_t start, grub_uint64_t size,
155 grub_uint32_t type)
157 grub_uint64_t end = start + size;
158 if (type != GRUB_MACHINE_MEMORY_AVAILABLE)
159 return 0;
160 if (end > 0x100000)
161 end = 0x100000;
162 if (end > start + ebda_len
163 && highestlow < ((end - ebda_len) & (~0xf)) )
164 highestlow = (end - ebda_len) & (~0xf);
165 return 0;
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)
171 ebda_kb_len = 0;
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);
179 if (! 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);
185 if (! mmapregion)
186 return grub_errno;
188 /* XXX: EBDA is unstandardized, so this implementation is heuristical. */
189 if (ebda_kb_len)
190 grub_memcpy (targetebda, ebda, 0x400);
191 else
192 grub_memset (targetebda, 0, 0x400);
193 *((grub_uint16_t *) targetebda) = ebda_kb_len + 1;
194 target = targetebda;
196 v1 = grub_acpi_get_rsdpv1 ();
197 v2 = grub_acpi_get_rsdpv2 ();
198 if (v2 && v2->length > 40)
199 v2 = 0;
201 /* First try to replace already existing rsdp. */
202 if (v2)
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);
214 v2inebda = target;
215 target += v2->length;
216 target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
217 v2 = 0;
218 break;
222 if (v1)
224 grub_dprintf ("acpi", "Scanning EBDA for old rsdpv1\n");
225 for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
226 target += 0x10)
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 rsdpv2 to %p\n", target);
233 v1inebda = target;
234 target += sizeof (struct grub_acpi_rsdp_v10);
235 target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
236 v1 = 0;
237 break;
241 target = targetebda + 0x100;
243 /* Try contiguous zeros. */
244 if (v2)
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);
252 v2inebda = target;
253 target += v2->length;
254 target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
255 v2 = 0;
256 break;
260 if (v1)
262 grub_dprintf ("acpi", "Scanning EBDA for block of zeros\n");
263 for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10);
264 target += 0x10)
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));
269 v1inebda = target;
270 target += sizeof (struct grub_acpi_rsdp_v10);
271 target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1);
272 v1 = 0;
273 break;
277 if (v1 || v2)
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);
287 target += 0x10)
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)
292 *target = 0;
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+ */
302 static void
303 setup_common_tables (void)
305 struct efiemu_acpi_table *cur;
306 struct grub_acpi_table_header *rsdt;
307 grub_uint32_t *rsdt_entry;
308 int numoftables;
310 /* Treat DSDT. */
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", 4) == 0)
330 fadt->dsdt_addr = PTR_TO_UINT32 (table_dsdt);
331 fadt->facs_addr = facs_addr;
333 /* Does a revision 2 exist at all? */
334 if (fadt->hdr.revision >= 3)
336 fadt->dsdt_xaddr = PTR_TO_UINT64 (table_dsdt);
337 fadt->facs_xaddr = facs_addr;
340 /* Recompute checksum. */
341 fadt->hdr.checksum = 0;
342 fadt->hdr.checksum = 1 + ~grub_byte_checksum (fadt, fadt->hdr.length);
346 /* Fill RSDT entries. */
347 numoftables = 0;
348 for (cur = acpi_tables; cur; cur = cur->next)
349 numoftables++;
351 rsdt_addr = rsdt = (struct grub_acpi_table_header *) playground_ptr;
352 playground_ptr += sizeof (struct grub_acpi_table_header) + 4 * numoftables;
354 rsdt_entry = (grub_uint32_t *)(rsdt + 1);
356 /* Fill RSDT header. */
357 grub_memcpy (&(rsdt->signature), "RSDT", 4);
358 rsdt->length = sizeof (struct grub_acpi_table_header) + 4 * numoftables;
359 rsdt->revision = 1;
360 grub_memcpy (&(rsdt->oemid), root_oemid, 6);
361 grub_memcpy (&(rsdt->oemtable), root_oemtable, 4);
362 rsdt->oemrev = root_oemrev;
363 grub_memcpy (&(rsdt->creator_id), root_creator_id, 6);
364 rsdt->creator_rev = root_creator_rev;
366 for (cur = acpi_tables; cur; cur = cur->next)
367 *(rsdt_entry++) = PTR_TO_UINT32 (cur->addr);
369 /* Recompute checksum. */
370 rsdt->checksum = 0;
371 rsdt->checksum = 1 + ~grub_byte_checksum (rsdt, rsdt->length);
374 /* Regenerate ACPIv1 RSDP */
375 static void
376 setv1table (void)
378 /* Create RSDP. */
379 rsdpv1_new = (struct grub_acpi_rsdp_v10 *) playground_ptr;
380 playground_ptr += sizeof (struct grub_acpi_rsdp_v10);
381 grub_memcpy (&(rsdpv1_new->signature), "RSD PTR ", 8);
382 grub_memcpy (&(rsdpv1_new->oemid), root_oemid, sizeof (rsdpv1_new->oemid));
383 rsdpv1_new->revision = 0;
384 rsdpv1_new->rsdt_addr = PTR_TO_UINT32 (rsdt_addr);
385 rsdpv1_new->checksum = 0;
386 rsdpv1_new->checksum = 1 + ~grub_byte_checksum (rsdpv1_new,
387 sizeof (*rsdpv1_new));
388 grub_dprintf ("acpi", "Generated ACPIv1 tables\n");
391 static void
392 setv2table (void)
394 struct grub_acpi_table_header *xsdt;
395 struct efiemu_acpi_table *cur;
396 grub_uint64_t *xsdt_entry;
397 int numoftables;
399 numoftables = 0;
400 for (cur = acpi_tables; cur; cur = cur->next)
401 numoftables++;
403 /* Create XSDT. */
404 xsdt = (struct grub_acpi_table_header *) playground_ptr;
405 playground_ptr += sizeof (struct grub_acpi_table_header) + 8 * numoftables;
407 xsdt_entry = (grub_uint64_t *)(xsdt + 1);
408 for (cur = acpi_tables; cur; cur = cur->next)
409 *(xsdt_entry++) = PTR_TO_UINT64 (cur->addr);
410 grub_memcpy (&(xsdt->signature), "XSDT", 4);
411 xsdt->length = sizeof (struct grub_acpi_table_header) + 8 * numoftables;
412 xsdt->revision = 1;
413 grub_memcpy (&(xsdt->oemid), root_oemid, sizeof (xsdt->oemid));
414 grub_memcpy (&(xsdt->oemtable), root_oemtable, sizeof (xsdt->oemtable));
415 xsdt->oemrev = root_oemrev;
416 grub_memcpy (&(xsdt->creator_id), root_creator_id, sizeof (xsdt->creator_id));
417 xsdt->creator_rev = root_creator_rev;
418 xsdt->checksum = 0;
419 xsdt->checksum = 1 + ~grub_byte_checksum (xsdt, xsdt->length);
421 /* Create RSDPv2. */
422 rsdpv2_new = (struct grub_acpi_rsdp_v20 *) playground_ptr;
423 playground_ptr += sizeof (struct grub_acpi_rsdp_v20);
424 grub_memcpy (&(rsdpv2_new->rsdpv1.signature), "RSD PTR ",
425 sizeof (rsdpv2_new->rsdpv1.signature));
426 grub_memcpy (&(rsdpv2_new->rsdpv1.oemid), root_oemid,
427 sizeof (rsdpv2_new->rsdpv1.oemid));
428 rsdpv2_new->rsdpv1.revision = rev2;
429 rsdpv2_new->rsdpv1.rsdt_addr = PTR_TO_UINT32 (rsdt_addr);
430 rsdpv2_new->rsdpv1.checksum = 0;
431 rsdpv2_new->rsdpv1.checksum = 1 + ~grub_byte_checksum
432 (&(rsdpv2_new->rsdpv1), sizeof (rsdpv2_new->rsdpv1));
433 rsdpv2_new->length = sizeof (*rsdpv2_new);
434 rsdpv2_new->xsdt_addr = PTR_TO_UINT64 (xsdt);
435 rsdpv2_new->checksum = 0;
436 rsdpv2_new->checksum = 1 + ~grub_byte_checksum (rsdpv2_new,
437 rsdpv2_new->length);
438 grub_dprintf ("acpi", "Generated ACPIv2 tables\n");
441 static void
442 free_tables (void)
444 struct efiemu_acpi_table *cur, *t;
445 if (table_dsdt)
446 grub_free (table_dsdt);
447 for (cur = acpi_tables; cur;)
449 t = cur;
450 grub_free (cur->addr);
451 cur = cur->next;
452 grub_free (t);
454 acpi_tables = 0;
455 table_dsdt = 0;
458 static grub_err_t
459 grub_cmd_acpi (struct grub_extcmd *cmd,
460 int argc, char **args)
462 struct grub_arg_list *state = cmd->state;
463 struct grub_acpi_rsdp_v10 *rsdp;
464 struct efiemu_acpi_table *cur, *t;
465 grub_err_t err;
466 int i, mmapregion;
467 int numoftables;
469 /* Default values if no RSDP is found. */
470 rev1 = 1;
471 rev2 = 3;
473 facs_addr = 0;
474 playground = playground_ptr = 0;
475 playground_size = 0;
477 rsdp = (struct grub_acpi_rsdp_v10 *) grub_machine_acpi_get_rsdpv2 ();
479 if (! rsdp)
480 rsdp = grub_machine_acpi_get_rsdpv1 ();
482 if (rsdp)
484 grub_uint32_t *entry_ptr;
485 char *exclude = 0;
486 char *load_only = 0;
487 char *ptr;
488 /* RSDT consists of header and an array of 32-bit pointers. */
489 struct grub_acpi_table_header *rsdt;
491 exclude = state[0].set ? grub_strdup (state[0].arg) : 0;
492 if (exclude)
494 for (ptr = exclude; *ptr; ptr++)
495 *ptr = grub_tolower (*ptr);
498 load_only = state[1].set ? grub_strdup (state[1].arg) : 0;
499 if (load_only)
501 for (ptr = load_only; *ptr; ptr++)
502 *ptr = grub_tolower (*ptr);
505 /* Set revision variables to replicate the same version as host. */
506 rev1 = ! rsdp->revision;
507 rev2 = rsdp->revision;
508 rsdt = (struct grub_acpi_table_header *) UINT_TO_PTR (rsdp->rsdt_addr);
509 /* Load host tables. */
510 for (entry_ptr = (grub_uint32_t *) (rsdt + 1);
511 entry_ptr < (grub_uint32_t *) (((grub_uint8_t *) rsdt)
512 + rsdt->length);
513 entry_ptr++)
515 char signature[5];
516 struct efiemu_acpi_table *table;
517 struct grub_acpi_table_header *curtable
518 = (struct grub_acpi_table_header *) UINT_TO_PTR (*entry_ptr);
519 signature[4] = 0;
520 for (i = 0; i < 4;i++)
521 signature[i] = grub_tolower (curtable->signature[i]);
523 /* If it's FADT it contains addresses of DSDT and FACS. */
524 if (grub_strcmp (signature, "facp") == 0)
526 struct grub_acpi_table_header *dsdt;
527 struct grub_acpi_fadt *fadt = (struct grub_acpi_fadt *) curtable;
529 /* Set root header variables to the same values
530 as FACP by default. */
531 grub_memcpy (&root_oemid, &(fadt->hdr.oemid),
532 sizeof (root_oemid));
533 grub_memcpy (&root_oemtable, &(fadt->hdr.oemtable),
534 sizeof (root_oemtable));
535 root_oemrev = fadt->hdr.oemrev;
536 grub_memcpy (&root_creator_id, &(fadt->hdr.creator_id),
537 sizeof (root_creator_id));
538 root_creator_rev = fadt->hdr.creator_rev;
540 /* Load DSDT if not excluded. */
541 dsdt = (struct grub_acpi_table_header *)
542 UINT_TO_PTR (fadt->dsdt_addr);
543 if (dsdt && (! exclude || ! grub_strword (exclude, "dsdt"))
544 && (! load_only || grub_strword (load_only, "dsdt"))
545 && dsdt->length >= sizeof (*dsdt))
547 dsdt_size = dsdt->length;
548 table_dsdt = grub_malloc (dsdt->length);
549 if (! table_dsdt)
551 free_tables ();
552 grub_free (exclude);
553 grub_free (load_only);
554 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
555 "Could allocate table");
557 grub_memcpy (table_dsdt, dsdt, dsdt->length);
560 /* Save FACS address. FACS shouldn't be overridden. */
561 facs_addr = fadt->facs_addr;
564 /* Skip excluded tables. */
565 if (exclude && grub_strword (exclude, signature))
566 continue;
567 if (load_only && ! grub_strword (load_only, signature))
568 continue;
570 /* Sanity check. */
571 if (curtable->length < sizeof (*curtable))
572 continue;
574 table = (struct efiemu_acpi_table *) grub_malloc
575 (sizeof (struct efiemu_acpi_table));
576 if (! table)
578 free_tables ();
579 grub_free (exclude);
580 grub_free (load_only);
581 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
582 "Could allocate table structure");
584 table->size = curtable->length;
585 table->addr = grub_malloc (table->size);
586 playground_size += table->size;
587 if (! table->addr)
589 free_tables ();
590 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
591 "Could allocate table");
593 table->next = acpi_tables;
594 acpi_tables = table;
595 grub_memcpy (table->addr, curtable, table->size);
597 grub_free (exclude);
598 grub_free (load_only);
601 /* Does user specify versions to generate? */
602 if (state[2].set || state[3].set)
604 rev1 = state[2].set;
605 if (state[3].set)
606 rev2 = rev2 ? : 2;
607 else
608 rev2 = 0;
611 /* Does user override root header information? */
612 if (state[4].set)
613 grub_strncpy (root_oemid, state[4].arg, sizeof (root_oemid));
614 if (state[5].set)
615 grub_strncpy (root_oemtable, state[5].arg, sizeof (root_oemtable));
616 if (state[6].set)
617 root_oemrev = grub_strtoul (state[6].arg, 0, 0);
618 if (state[7].set)
619 grub_strncpy (root_creator_id, state[7].arg, sizeof (root_creator_id));
620 if (state[8].set)
621 root_creator_rev = grub_strtoul (state[8].arg, 0, 0);
623 /* Load user tables */
624 for (i = 0; i < argc; i++)
626 grub_file_t file;
627 grub_size_t size;
628 char *buf;
630 file = grub_gzfile_open (args[i], 1);
631 if (! file)
633 free_tables ();
634 return grub_error (GRUB_ERR_BAD_OS, "couldn't open file %s", args[i]);
637 size = grub_file_size (file);
638 if (size < sizeof (struct grub_acpi_table_header))
640 grub_file_close (file);
641 free_tables ();
642 return grub_error (GRUB_ERR_BAD_OS, "file %s is too small", args[i]);
645 buf = (char *) grub_malloc (size);
646 if (! buf)
648 grub_file_close (file);
649 free_tables ();
650 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
651 "couldn't read file %s", args[i]);
654 if (grub_file_read (file, buf, size) != (int) size)
656 grub_file_close (file);
657 free_tables ();
658 return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[i]);
660 grub_file_close (file);
662 if (grub_memcmp (((struct grub_acpi_table_header *) buf)->signature,
663 "DSDT", 4) == 0)
665 grub_free (table_dsdt);
666 table_dsdt = buf;
667 dsdt_size = size;
669 else
671 struct efiemu_acpi_table *table;
672 table = (struct efiemu_acpi_table *) grub_malloc
673 (sizeof (struct efiemu_acpi_table));
674 if (! table)
676 free_tables ();
677 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
678 "Could allocate table structure");
681 table->size = size;
682 table->addr = buf;
683 playground_size += table->size;
687 numoftables = 0;
688 for (cur = acpi_tables; cur; cur = cur->next)
689 numoftables++;
691 /* DSDT. */
692 playground_size += dsdt_size;
693 /* RSDT. */
694 playground_size += sizeof (struct grub_acpi_table_header) + 4 * numoftables;
695 /* RSDPv1. */
696 playground_size += sizeof (struct grub_acpi_rsdp_v10);
697 /* XSDT. */
698 playground_size += sizeof (struct grub_acpi_table_header) + 8 * numoftables;
699 /* RSDPv2. */
700 playground_size += sizeof (struct grub_acpi_rsdp_v20);
702 playground = playground_ptr
703 = grub_mmap_malign_and_register (1, playground_size, &mmapregion,
704 GRUB_MACHINE_MEMORY_ACPI, 0);
706 if (! playground)
708 free_tables ();
709 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
710 "Couldn't allocate space for ACPI tables");
713 setup_common_tables ();
715 /* Request space for RSDPv1. */
716 if (rev1)
717 setv1table ();
719 /* Request space for RSDPv2+ and XSDT. */
720 if (rev2)
721 setv2table ();
723 for (cur = acpi_tables; cur;)
725 t = cur;
726 cur = cur->next;
727 grub_free (t);
729 acpi_tables = 0;
731 if (! state[9].set && (err = grub_acpi_create_ebda ()))
733 rsdpv1_new = 0;
734 rsdpv2_new = 0;
735 grub_mmap_free_and_unregister (mmapregion);
736 return err;
739 #ifdef GRUB_MACHINE_EFI
741 struct grub_efi_guid acpi = GRUB_EFI_ACPI_TABLE_GUID;
742 struct grub_efi_guid acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID;
744 grub_efi_system_table->boot_services->install_configuration_table
745 (&acpi20, grub_acpi_get_rsdpv2 ());
746 grub_efi_system_table->boot_services->install_configuration_table
747 (&acpi, grub_acpi_get_rsdpv1 ());
749 #endif
751 return GRUB_ERR_NONE;
754 static grub_extcmd_t cmd;
756 GRUB_MOD_INIT(acpi)
758 cmd = grub_register_extcmd ("acpi", grub_cmd_acpi,
759 GRUB_COMMAND_FLAG_BOTH,
760 "acpi [-1|-2] [--exclude=table1,table2|"
761 "--load-only=table1,table2] filename1 "
762 " [filename2] [...]",
763 "Load host acpi tables and tables "
764 "specified by arguments",
765 options);
768 GRUB_MOD_FINI(acpi)
770 grub_unregister_extcmd (cmd);