/* Fill previously allocated Multiboot mmap. */ static void grub_fill_multiboot_mmap (struct multiboot_mmap_entry *first_entry) { struct multiboot_mmap_entry *mmap_entry = (struct multiboot_mmap_entry *) first_entry; grub_mmap_iterate (grub_fill_multiboot_mmap_hook, &mmap_entry); }
/* Count the continuous bytes after 64 MiB. */ grub_uint64_t grub_mmap_get_post64 (void) { grub_uint64_t post64 = 0; grub_mmap_iterate (post64_hook, &post64); return post64; }
grub_uint64_t grub_mmap_get_upper (void) { grub_uint64_t upper = 0; grub_mmap_iterate (upper_hook, &upper); return upper; }
/* Return the length of the Multiboot mmap that will be needed to allocate our platform's map. */ grub_uint32_t grub_get_multiboot_mmap_count (void) { grub_size_t count = 0; grub_mmap_iterate (count_hook, &count); return count; }
grub_uint64_t grub_mmap_get_lower (void) { grub_uint64_t lower = 0; grub_mmap_iterate (lower_hook, &lower); if (lower > 0x100000) lower = 0x100000; return lower; }
/* Fill previously allocated Multiboot mmap. */ static void grub_fill_multiboot_mmap (struct multiboot_tag_mmap *tag) { struct multiboot_mmap_entry *mmap_entry = tag->entries; tag->type = MULTIBOOT_TAG_TYPE_MMAP; tag->size = sizeof (struct multiboot_tag_mmap) + sizeof (struct multiboot_mmap_entry) * grub_get_multiboot_mmap_count (); tag->entry_size = sizeof (struct multiboot_mmap_entry); tag->entry_version = 0; grub_mmap_iterate (grub_fill_multiboot_mmap_iter, &mmap_entry); }
/* Find the optimal number of pages for the memory map. */ static grub_size_t find_mmap_size (void) { grub_size_t count = 0, mmap_size; grub_mmap_iterate (count_hook, &count); mmap_size = count * sizeof (struct grub_e820_mmap); /* Increase the size a bit for safety, because GRUB allocates more on later. */ mmap_size += (1 << 12); return page_align (mmap_size); }
void * grub_mmap_malign_and_register (grub_uint64_t align, grub_uint64_t size, int *handle, int type, int flags) { void *ret; if (flags & (GRUB_MMAP_MALLOC_LOW | GRUB_MMAP_MALLOC_HIGH)) { struct grub_mmap_malign_and_register_closure c; c.align = align; c.size = size; c.highestlow = 0; if (flags & GRUB_MMAP_MALLOC_LOW) { c.min = 0; c.max = 0x100000; } else { c.min = grub_mmap_high; c.max = 0x100000000ll; } /* FIXME: use low-memory mm allocation once it's available. */ grub_mmap_iterate (find_hook, &c); ret = UINT_TO_PTR (c.highestlow); } else ret = grub_memalign (align, size); if (! ret) { *handle = 0; return 0; } *handle = grub_mmap_register (PTR_TO_UINT64 (ret), size, type); if (! *handle) { grub_free (ret); return 0; } return ret; }
static grub_err_t grub_cmd_badram (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) { char * str; if (argc != 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, "badram string required"); grub_dprintf ("badram", "executing badram\n"); str = args[0]; while (1) { struct grub_cmd_badram_closure c; /* Parse address and mask. */ c.badaddr = grub_strtoull (str, &str, 16); if (*str == ',') str++; c.badmask = grub_strtoull (str, &str, 16); if (*str == ',') str++; if (grub_errno == GRUB_ERR_BAD_NUMBER) { grub_errno = 0; return GRUB_ERR_NONE; } /* When part of a page is tainted, we discard the whole of it. There's no point in providing sub-page chunks. */ c.badmask &= ~(CHUNK_SIZE - 1); grub_dprintf ("badram", "badram %llx:%llx\n", (unsigned long long) c.badaddr, (unsigned long long) c.badmask); grub_mmap_iterate (grub_cmd_badram_hook, &c); } }
static grub_err_t grub_linux_boot (void) { grub_err_t err = 0; const char *modevar; char *tmp; struct grub_relocator32_state state; void *real_mode_mem; struct grub_linux_boot_ctx ctx = { .real_mode_target = 0 }; grub_size_t mmap_size; grub_size_t cl_offset; #ifdef GRUB_MACHINE_IEEE1275 { const char *bootpath; grub_ssize_t len; bootpath = grub_env_get ("root"); if (bootpath) grub_ieee1275_set_property (grub_ieee1275_chosen, "bootpath", bootpath, grub_strlen (bootpath) + 1, &len); linux_params.ofw_signature = GRUB_LINUX_OFW_SIGNATURE; linux_params.ofw_num_items = 1; linux_params.ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn; linux_params.ofw_idt = 0; } #endif modevar = grub_env_get ("gfxpayload"); /* Now all graphical modes are acceptable. May change in future if we have modes without framebuffer. */ if (modevar && *modevar != 0) { tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar); if (! tmp) return grub_errno; #if ACCEPTS_PURE_TEXT err = grub_video_set_mode (tmp, 0, 0); #else err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0); #endif grub_free (tmp); } else /* We can't go back to text mode from coreboot fb. */ #ifdef GRUB_MACHINE_COREBOOT if (grub_video_get_driver_id () == GRUB_VIDEO_DRIVER_COREBOOT) err = GRUB_ERR_NONE; else #endif { #if ACCEPTS_PURE_TEXT err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0); #else err = grub_video_set_mode (DEFAULT_VIDEO_MODE, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0); #endif } if (err) { grub_print_error (); grub_puts_ (N_("Booting in blind mode")); grub_errno = GRUB_ERR_NONE; } if (grub_linux_setup_video (&linux_params)) { #if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU) linux_params.have_vga = GRUB_VIDEO_LINUX_TYPE_TEXT; linux_params.video_mode = 0x3; #else linux_params.have_vga = 0; linux_params.video_mode = 0; linux_params.video_width = 0; linux_params.video_height = 0; #endif } #ifndef GRUB_MACHINE_IEEE1275 if (linux_params.have_vga == GRUB_VIDEO_LINUX_TYPE_TEXT) #endif { grub_term_output_t term; int found = 0; FOR_ACTIVE_TERM_OUTPUTS(term) if (grub_strcmp (term->name, "vga_text") == 0 || grub_strcmp (term->name, "console") == 0 || grub_strcmp (term->name, "ofconsole") == 0) { struct grub_term_coordinate pos = grub_term_getxy (term); linux_params.video_cursor_x = pos.x; linux_params.video_cursor_y = pos.y; linux_params.video_width = grub_term_width (term); linux_params.video_height = grub_term_height (term); found = 1; break; } if (!found) { linux_params.video_cursor_x = 0; linux_params.video_cursor_y = 0; linux_params.video_width = 80; linux_params.video_height = 25; } } mmap_size = find_mmap_size (); /* Make sure that each size is aligned to a page boundary. */ cl_offset = ALIGN_UP (mmap_size + sizeof (linux_params), 4096); if (cl_offset < ((grub_size_t) linux_params.setup_sects << GRUB_DISK_SECTOR_BITS)) cl_offset = ALIGN_UP ((grub_size_t) (linux_params.setup_sects << GRUB_DISK_SECTOR_BITS), 4096); ctx.real_size = ALIGN_UP (cl_offset + maximal_cmdline_size, 4096); #ifdef GRUB_MACHINE_EFI efi_mmap_size = find_efi_mmap_size (); if (efi_mmap_size == 0) return grub_errno; #endif grub_dprintf ("linux", "real_size = %x, mmap_size = %x\n", (unsigned) ctx.real_size, (unsigned) mmap_size); #ifdef GRUB_MACHINE_EFI grub_efi_mmap_iterate (grub_linux_boot_mmap_find, &ctx, 1); if (! ctx.real_mode_target) grub_efi_mmap_iterate (grub_linux_boot_mmap_find, &ctx, 0); #else grub_mmap_iterate (grub_linux_boot_mmap_find, &ctx); #endif grub_dprintf ("linux", "real_mode_target = %lx, real_size = %x, efi_mmap_size = %x\n", (unsigned long) ctx.real_mode_target, (unsigned) ctx.real_size, (unsigned) efi_mmap_size); if (! ctx.real_mode_target) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages"); { grub_relocator_chunk_t ch; err = grub_relocator_alloc_chunk_addr (relocator, &ch, ctx.real_mode_target, (ctx.real_size + efi_mmap_size)); if (err) return err; real_mode_mem = get_virtual_current_address (ch); } efi_mmap_buf = (grub_uint8_t *) real_mode_mem + ctx.real_size; grub_dprintf ("linux", "real_mode_mem = %p\n", real_mode_mem); ctx.params = real_mode_mem; *ctx.params = linux_params; ctx.params->cmd_line_ptr = ctx.real_mode_target + cl_offset; grub_memcpy ((char *) ctx.params + cl_offset, linux_cmdline, maximal_cmdline_size); grub_dprintf ("linux", "code32_start = %x\n", (unsigned) ctx.params->code32_start); ctx.e820_num = 0; if (grub_mmap_iterate (grub_linux_boot_mmap_fill, &ctx)) return grub_errno; ctx.params->mmap_size = ctx.e820_num; #ifdef GRUB_MACHINE_EFI { grub_efi_uintn_t efi_desc_size; grub_size_t efi_mmap_target; grub_efi_uint32_t efi_desc_version; err = grub_efi_finish_boot_services (&efi_mmap_size, efi_mmap_buf, NULL, &efi_desc_size, &efi_desc_version); if (err) return err; /* Note that no boot services are available from here. */ efi_mmap_target = ctx.real_mode_target + ((grub_uint8_t *) efi_mmap_buf - (grub_uint8_t *) real_mode_mem); /* Pass EFI parameters. */ if (grub_le_to_cpu16 (ctx.params->version) >= 0x0208) { ctx.params->v0208.efi_mem_desc_size = efi_desc_size; ctx.params->v0208.efi_mem_desc_version = efi_desc_version; ctx.params->v0208.efi_mmap = efi_mmap_target; ctx.params->v0208.efi_mmap_size = efi_mmap_size; #ifdef __x86_64__ ctx.params->v0208.efi_mmap_hi = (efi_mmap_target >> 32); #endif } else if (grub_le_to_cpu16 (ctx.params->version) >= 0x0206)
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"); }