void grub_mmap_free_and_unregister (int handle) { struct grub_mmap_region *cur; grub_uint64_t addr; for (cur = grub_mmap_overlays; cur; cur = cur->next) if (cur->handle == handle) break; if (! cur) return; addr = cur->start; grub_mmap_unregister (handle); if (addr >= 0x100000) grub_free (UINT_TO_PTR (addr)); }
grub_err_t grub_acpi_create_ebda (void) { struct grub_acpi_create_ebda_ctx ctx = { .highestlow = 0 }; int ebda_kb_len = 0; int mmapregion = 0; grub_uint8_t *ebda, *v1inebda = 0, *v2inebda = 0; grub_uint8_t *targetebda, *target; struct grub_acpi_rsdp_v10 *v1; struct grub_acpi_rsdp_v20 *v2; ebda = (grub_uint8_t *) (grub_addr_t) ((*((grub_uint16_t *)0x40e)) << 4); if (ebda) ebda_kb_len = *(grub_uint16_t *) ebda; if (ebda_kb_len > 16) ebda_kb_len = 0; ctx.ebda_len = (ebda_kb_len + 1) << 10; /* FIXME: use low-memory mm allocation once it's available. */ grub_mmap_iterate (find_hook, &ctx); targetebda = (grub_uint8_t *) (grub_addr_t) ctx.highestlow; grub_dprintf ("acpi", "creating ebda @%llx\n", (unsigned long long) ctx.highestlow); if (! ctx.highestlow) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for the new EBDA"); mmapregion = grub_mmap_register ((grub_addr_t) targetebda, ctx.ebda_len, GRUB_MEMORY_RESERVED); if (! mmapregion) return grub_errno; /* XXX: EBDA is unstandardized, so this implementation is heuristical. */ if (ebda_kb_len) grub_memcpy (targetebda, ebda, 0x400); else grub_memset (targetebda, 0, 0x400); *((grub_uint16_t *) targetebda) = ebda_kb_len + 1; target = targetebda; v1 = grub_acpi_get_rsdpv1 (); v2 = grub_acpi_get_rsdpv2 (); if (v2 && v2->length > 40) v2 = 0; /* First try to replace already existing rsdp. */ if (v2) { grub_dprintf ("acpi", "Scanning EBDA for old rsdpv2\n"); for (; target < targetebda + 0x400 - v2->length; target += 0x10) if (grub_memcmp (target, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 && grub_byte_checksum (target, sizeof (struct grub_acpi_rsdp_v10)) == 0 && ((struct grub_acpi_rsdp_v10 *) target)->revision != 0 && ((struct grub_acpi_rsdp_v20 *) target)->length <= v2->length) { grub_memcpy (target, v2, v2->length); grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target); v2inebda = target; target += v2->length; target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1); v2 = 0; break; } } if (v1) { grub_dprintf ("acpi", "Scanning EBDA for old rsdpv1\n"); for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10); target += 0x10) if (grub_memcmp (target, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 && grub_byte_checksum (target, sizeof (struct grub_acpi_rsdp_v10)) == 0) { grub_memcpy (target, v1, sizeof (struct grub_acpi_rsdp_v10)); grub_dprintf ("acpi", "Copying rsdpv1 to %p\n", target); v1inebda = target; target += sizeof (struct grub_acpi_rsdp_v10); target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1); v1 = 0; break; } } target = targetebda + 0x100; /* Try contiguous zeros. */ if (v2) { grub_dprintf ("acpi", "Scanning EBDA for block of zeros\n"); for (; target < targetebda + 0x400 - v2->length; target += 0x10) if (iszero (target, v2->length)) { grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target); grub_memcpy (target, v2, v2->length); v2inebda = target; target += v2->length; target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1); v2 = 0; break; } } if (v1) { grub_dprintf ("acpi", "Scanning EBDA for block of zeros\n"); for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10); target += 0x10) if (iszero (target, sizeof (struct grub_acpi_rsdp_v10))) { grub_dprintf ("acpi", "Copying rsdpv1 to %p\n", target); grub_memcpy (target, v1, sizeof (struct grub_acpi_rsdp_v10)); v1inebda = target; target += sizeof (struct grub_acpi_rsdp_v10); target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1); v1 = 0; break; } } if (v1 || v2) { grub_mmap_unregister (mmapregion); return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find suitable spot in EBDA"); } /* Remove any other RSDT. */ for (target = targetebda; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10); target += 0x10) if (grub_memcmp (target, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 && grub_byte_checksum (target, sizeof (struct grub_acpi_rsdp_v10)) == 0 && target != v1inebda && target != v2inebda) *target = 0; grub_dprintf ("acpi", "Switching EBDA\n"); (*((grub_uint16_t *) 0x40e)) = ((long)targetebda) >> 4; grub_dprintf ("acpi", "EBDA switched\n"); return GRUB_ERR_NONE; } #endif /* Create tables common to ACPIv1 and ACPIv2+ */ static void setup_common_tables (void) { struct efiemu_acpi_table *cur; struct grub_acpi_table_header *rsdt; grub_uint32_t *rsdt_entry; int numoftables; /* Treat DSDT. */ grub_memcpy (playground_ptr, table_dsdt, dsdt_size); grub_free (table_dsdt); table_dsdt = playground_ptr; playground_ptr += dsdt_size; /* Treat other tables. */ for (cur = acpi_tables; cur; cur = cur->next) { struct grub_acpi_fadt *fadt; grub_memcpy (playground_ptr, cur->addr, cur->size); grub_free (cur->addr); cur->addr = playground_ptr; playground_ptr += cur->size; /* If it's FADT correct DSDT and FACS addresses. */ fadt = (struct grub_acpi_fadt *) cur->addr; if (grub_memcmp (fadt->hdr.signature, GRUB_ACPI_FADT_SIGNATURE, sizeof (fadt->hdr.signature)) == 0) { fadt->dsdt_addr = (grub_addr_t) table_dsdt; fadt->facs_addr = facs_addr; /* Does a revision 2 exist at all? */ if (fadt->hdr.revision >= 3) { fadt->dsdt_xaddr = (grub_addr_t) table_dsdt; fadt->facs_xaddr = facs_addr; } /* Recompute checksum. */ fadt->hdr.checksum = 0; fadt->hdr.checksum = 1 + ~grub_byte_checksum (fadt, fadt->hdr.length); } } /* Fill RSDT entries. */ numoftables = 0; for (cur = acpi_tables; cur; cur = cur->next) numoftables++; rsdt_addr = rsdt = (struct grub_acpi_table_header *) playground_ptr; playground_ptr += sizeof (struct grub_acpi_table_header) + sizeof (grub_uint32_t) * numoftables; rsdt_entry = (grub_uint32_t *) (rsdt + 1); /* Fill RSDT header. */ grub_memcpy (&(rsdt->signature), "RSDT", 4); rsdt->length = sizeof (struct grub_acpi_table_header) + sizeof (grub_uint32_t) * numoftables; rsdt->revision = 1; grub_memcpy (&(rsdt->oemid), root_oemid, sizeof (rsdt->oemid)); grub_memcpy (&(rsdt->oemtable), root_oemtable, sizeof (rsdt->oemtable)); rsdt->oemrev = root_oemrev; grub_memcpy (&(rsdt->creator_id), root_creator_id, sizeof (rsdt->creator_id)); rsdt->creator_rev = root_creator_rev; for (cur = acpi_tables; cur; cur = cur->next) *(rsdt_entry++) = (grub_addr_t) cur->addr; /* Recompute checksum. */ rsdt->checksum = 0; rsdt->checksum = 1 + ~grub_byte_checksum (rsdt, rsdt->length); } /* Regenerate ACPIv1 RSDP */ static void setv1table (void) { /* Create RSDP. */ rsdpv1_new = (struct grub_acpi_rsdp_v10 *) playground_ptr; playground_ptr += sizeof (struct grub_acpi_rsdp_v10); grub_memcpy (&(rsdpv1_new->signature), GRUB_RSDP_SIGNATURE, sizeof (rsdpv1_new->signature)); grub_memcpy (&(rsdpv1_new->oemid), root_oemid, sizeof (rsdpv1_new->oemid)); rsdpv1_new->revision = 0; rsdpv1_new->rsdt_addr = (grub_addr_t) rsdt_addr; rsdpv1_new->checksum = 0; rsdpv1_new->checksum = 1 + ~grub_byte_checksum (rsdpv1_new, sizeof (*rsdpv1_new)); grub_dprintf ("acpi", "Generated ACPIv1 tables\n"); }