void grub_reboot (void) { struct grub_relocator *relocator = NULL; grub_relocator_chunk_t ch; grub_err_t err; void *buf; struct grub_relocator16_state state; grub_uint16_t segment; relocator = grub_relocator_new (); if (!relocator) while (1); err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000, 0x1000, grub_reboot_end - grub_reboot_start, 16, GRUB_RELOCATOR_PREFERENCE_NONE, 0); if (err) while (1); buf = get_virtual_current_address (ch); grub_memcpy (buf, grub_reboot_start, grub_reboot_end - grub_reboot_start); segment = ((grub_addr_t) get_physical_target_address (ch)) >> 4; state.gs = state.fs = state.es = state.ds = state.ss = segment; state.sp = 0; state.cs = segment; state.ip = 0; state.a20 = 0; grub_stop_floppy (); err = grub_relocator16_boot (relocator, state); while (1); }
grub_err_t grub_relocator16_boot (struct grub_relocator *rel, struct grub_relocator16_state state) { grub_err_t err; void *relst; grub_relocator_chunk_t ch; /* Put it higher than the byte it checks for A20 check. */ err = grub_relocator_alloc_chunk_align (rel, &ch, 0x8010, 0xa0000 - RELOCATOR_SIZEOF (16) - GRUB_RELOCATOR16_STACK_SIZE, RELOCATOR_SIZEOF (16) + GRUB_RELOCATOR16_STACK_SIZE, 16, GRUB_RELOCATOR_PREFERENCE_NONE, 0); if (err) return err; grub_relocator16_cs = state.cs; grub_relocator16_ip = state.ip; grub_relocator16_ds = state.ds; grub_relocator16_es = state.es; grub_relocator16_fs = state.fs; grub_relocator16_gs = state.gs; grub_relocator16_ss = state.ss; grub_relocator16_sp = state.sp; grub_relocator16_ebp = state.ebp; grub_relocator16_ebx = state.ebx; grub_relocator16_edx = state.edx; grub_relocator16_esi = state.esi; #ifdef GRUB_MACHINE_PCBIOS grub_relocator16_idt = *grub_realidt; #else grub_relocator16_idt.base = 0; grub_relocator16_idt.limit = 0; #endif grub_relocator16_keep_a20_enabled = state.a20; grub_memmove (get_virtual_current_address (ch), &grub_relocator16_start, RELOCATOR_SIZEOF (16)); err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch), &relst, NULL); if (err) return err; asm volatile ("cli"); ((void (*) (void)) relst) (); /* Not reached. */ return GRUB_ERR_NONE; }
grub_err_t grub_relocator32_boot (struct grub_relocator *rel, struct grub_relocator32_state state, int avoid_efi_bootservices) { grub_err_t err; void *relst; grub_relocator_chunk_t ch; err = grub_relocator_alloc_chunk_align (rel, &ch, 0, (0xffffffff - RELOCATOR_SIZEOF (32)) + 1, RELOCATOR_SIZEOF (32), 16, GRUB_RELOCATOR_PREFERENCE_NONE, avoid_efi_bootservices); if (err) return err; grub_relocator32_eax = state.eax; grub_relocator32_ebx = state.ebx; grub_relocator32_ecx = state.ecx; grub_relocator32_edx = state.edx; grub_relocator32_eip = state.eip; grub_relocator32_esp = state.esp; grub_relocator32_ebp = state.ebp; grub_relocator32_esi = state.esi; grub_relocator32_edi = state.edi; grub_memmove (get_virtual_current_address (ch), &grub_relocator32_start, RELOCATOR_SIZEOF (32)); err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch), &relst, NULL); if (err) return err; asm volatile ("cli"); ((void (*) (void)) relst) (); /* Not reached. */ return GRUB_ERR_NONE; }
grub_err_t grub_relocator64_boot (struct grub_relocator *rel, struct grub_relocator64_state state, grub_addr_t min_addr, grub_addr_t max_addr) { grub_err_t err; void *relst; grub_relocator_chunk_t ch; err = grub_relocator_alloc_chunk_align (rel, &ch, min_addr, max_addr - RELOCATOR_SIZEOF (64), RELOCATOR_SIZEOF (64), 16, GRUB_RELOCATOR_PREFERENCE_NONE, 0); if (err) return err; grub_relocator64_rax = state.rax; grub_relocator64_rbx = state.rbx; grub_relocator64_rcx = state.rcx; grub_relocator64_rdx = state.rdx; grub_relocator64_rip = state.rip; grub_relocator64_rsp = state.rsp; grub_relocator64_rsi = state.rsi; grub_relocator64_cr3 = state.cr3; grub_memmove (get_virtual_current_address (ch), &grub_relocator64_start, RELOCATOR_SIZEOF (64)); err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch), &relst, NULL); if (err) return err; asm volatile ("cli"); ((void (*) (void)) relst) (); /* Not reached. */ return GRUB_ERR_NONE; }
grub_err_t grub_multiboot_make_mbi (grub_uint32_t *target) { grub_properly_aligned_t *ptrorig; grub_properly_aligned_t *mbistart; grub_err_t err; grub_size_t bufsize; grub_relocator_chunk_t ch; bufsize = grub_multiboot_get_mbi_size (); COMPILE_TIME_ASSERT (MULTIBOOT_TAG_ALIGN % sizeof (grub_properly_aligned_t) == 0); err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch, 0, 0xffffffff - bufsize, bufsize, MULTIBOOT_TAG_ALIGN, GRUB_RELOCATOR_PREFERENCE_NONE, 1); if (err) return err; ptrorig = get_virtual_current_address (ch); #if defined (__i386__) || defined (__x86_64__) *target = get_physical_target_address (ch); #elif defined (__mips) *target = get_physical_target_address (ch) | 0x80000000; #else #error Please complete this #endif mbistart = ptrorig; COMPILE_TIME_ASSERT ((2 * sizeof (grub_uint32_t)) % sizeof (grub_properly_aligned_t) == 0); COMPILE_TIME_ASSERT (MULTIBOOT_TAG_ALIGN % sizeof (grub_properly_aligned_t) == 0); ptrorig += (2 * sizeof (grub_uint32_t)) / sizeof (grub_properly_aligned_t); { struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_CMDLINE; tag->size = sizeof (struct multiboot_tag_string) + cmdline_size; grub_memcpy (tag->string, cmdline, cmdline_size); ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } { struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME; tag->size = sizeof (struct multiboot_tag_string) + sizeof (PACKAGE_STRING); grub_memcpy (tag->string, PACKAGE_STRING, sizeof (PACKAGE_STRING)); ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } #ifdef GRUB_MACHINE_PCBIOS { struct grub_apm_info info; if (grub_apm_get_info (&info)) { struct multiboot_tag_apm *tag = (struct multiboot_tag_apm *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_APM; tag->size = sizeof (struct multiboot_tag_apm); tag->cseg = info.cseg; tag->offset = info.offset; tag->cseg_16 = info.cseg_16; tag->dseg = info.dseg; tag->flags = info.flags; tag->cseg_len = info.cseg_len; tag->dseg_len = info.dseg_len; tag->cseg_16_len = info.cseg_16_len; tag->version = info.version; ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } } #endif { unsigned i; struct module *cur; for (i = 0, cur = modules; i < modcnt; i++, cur = cur->next) { struct multiboot_tag_module *tag = (struct multiboot_tag_module *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_MODULE; tag->size = sizeof (struct multiboot_tag_module) + cur->cmdline_size; tag->mod_start = cur->start; tag->mod_end = tag->mod_start + cur->size; grub_memcpy (tag->cmdline, cur->cmdline, cur->cmdline_size); ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } } { struct multiboot_tag_mmap *tag = (struct multiboot_tag_mmap *) ptrorig; grub_fill_multiboot_mmap (tag); ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } { struct multiboot_tag_elf_sections *tag = (struct multiboot_tag_elf_sections *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_ELF_SECTIONS; tag->size = sizeof (struct multiboot_tag_elf_sections) + elf_sec_entsize * elf_sec_num; grub_memcpy (tag->sections, elf_sections, elf_sec_entsize * elf_sec_num); tag->num = elf_sec_num; tag->entsize = elf_sec_entsize; tag->shndx = elf_sec_shstrndx; ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } { struct multiboot_tag_basic_meminfo *tag = (struct multiboot_tag_basic_meminfo *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_BASIC_MEMINFO; tag->size = sizeof (struct multiboot_tag_basic_meminfo); /* Convert from bytes to kilobytes. */ tag->mem_lower = grub_mmap_get_lower () / 1024; tag->mem_upper = grub_mmap_get_upper () / 1024; ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } { struct grub_net_network_level_interface *net; FOR_NET_NETWORK_LEVEL_INTERFACES(net) if (net->dhcp_ack) { struct multiboot_tag_network *tag = (struct multiboot_tag_network *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_NETWORK; tag->size = sizeof (struct multiboot_tag_network) + net->dhcp_acklen; grub_memcpy (tag->dhcpack, net->dhcp_ack, net->dhcp_acklen); ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } } if (bootdev_set) { struct multiboot_tag_bootdev *tag = (struct multiboot_tag_bootdev *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_BOOTDEV; tag->size = sizeof (struct multiboot_tag_bootdev); tag->biosdev = biosdev; tag->slice = slice; tag->part = part; ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } { err = retrieve_video_parameters (&ptrorig); if (err) { grub_print_error (); grub_errno = GRUB_ERR_NONE; } } #if defined (GRUB_MACHINE_EFI) && defined (__x86_64__) { struct multiboot_tag_efi64 *tag = (struct multiboot_tag_efi64 *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_EFI64; tag->size = sizeof (*tag); tag->pointer = (grub_addr_t) grub_efi_system_table; ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } #endif #if defined (GRUB_MACHINE_EFI) && defined (__i386__) { struct multiboot_tag_efi32 *tag = (struct multiboot_tag_efi32 *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_EFI32; tag->size = sizeof (*tag); tag->pointer = (grub_addr_t) grub_efi_system_table; ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } #endif #if GRUB_MACHINE_HAS_ACPI { struct multiboot_tag_old_acpi *tag = (struct multiboot_tag_old_acpi *) ptrorig; struct grub_acpi_rsdp_v10 *a = grub_acpi_get_rsdpv1 (); if (a) { tag->type = MULTIBOOT_TAG_TYPE_ACPI_OLD; tag->size = sizeof (*tag) + sizeof (*a); grub_memcpy (tag->rsdp, a, sizeof (*a)); ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } } { struct multiboot_tag_new_acpi *tag = (struct multiboot_tag_new_acpi *) ptrorig; struct grub_acpi_rsdp_v20 *a = grub_acpi_get_rsdpv2 (); if (a) { tag->type = MULTIBOOT_TAG_TYPE_ACPI_NEW; tag->size = sizeof (*tag) + a->length; grub_memcpy (tag->rsdp, a, a->length); ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } } #endif #ifdef GRUB_MACHINE_EFI { struct multiboot_tag_efi_mmap *tag = (struct multiboot_tag_efi_mmap *) ptrorig; grub_efi_uintn_t efi_desc_size; grub_efi_uint32_t efi_desc_version; tag->type = MULTIBOOT_TAG_TYPE_EFI_MMAP; tag->size = sizeof (*tag) + efi_mmap_size; if (!keep_bs) err = grub_efi_finish_boot_services (&efi_mmap_size, tag->efi_mmap, NULL, &efi_desc_size, &efi_desc_version); else { if (grub_efi_get_memory_map (&efi_mmap_size, (void *) tag->efi_mmap, NULL, &efi_desc_size, &efi_desc_version) <= 0) err = grub_error (GRUB_ERR_IO, "couldn't retrieve memory map"); } if (err) return err; tag->descr_size = efi_desc_size; tag->descr_vers = efi_desc_version; tag->size = sizeof (*tag) + efi_mmap_size; ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } if (keep_bs) { struct multiboot_tag *tag = (struct multiboot_tag *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_EFI_BS; tag->size = sizeof (struct multiboot_tag); ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } #endif { struct multiboot_tag *tag = (struct multiboot_tag *) ptrorig; tag->type = MULTIBOOT_TAG_TYPE_END; tag->size = sizeof (struct multiboot_tag); ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (grub_properly_aligned_t); } ((grub_uint32_t *) mbistart)[0] = (char *) ptrorig - (char *) mbistart; ((grub_uint32_t *) mbistart)[1] = 0; return GRUB_ERR_NONE; }
static grub_err_t grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t file = 0; grub_ssize_t size; void *module = NULL; grub_addr_t target; grub_err_t err; int nounzip = 0; grub_uint64_t lowest_addr = 0; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); if (grub_strcmp (argv[0], "--nounzip") == 0) { argv++; argc--; nounzip = 1; } if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); if (!grub_multiboot_relocator) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); if (nounzip) grub_file_filter_disable_compression (); file = grub_file_open (argv[0]); if (! file) return grub_errno; #ifndef GRUB_USE_MULTIBOOT2 lowest_addr = 0x100000; if (grub_multiboot_quirks & GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL) lowest_addr = ALIGN_UP (highest_load + 1048576, 4096); #endif size = grub_file_size (file); if (size) { grub_relocator_chunk_t ch; err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch, lowest_addr, (0xffffffff - size) + 1, size, MULTIBOOT_MOD_ALIGN, GRUB_RELOCATOR_PREFERENCE_NONE, 1); if (err) { grub_file_close (file); return err; } module = get_virtual_current_address (ch); target = get_physical_target_address (ch); } else { module = 0; target = 0; } err = grub_multiboot_add_module (target, size, argc - 1, argv + 1); if (err) { grub_file_close (file); return err; } if (size && grub_file_read (file, module, size) != size) { grub_file_close (file); if (!grub_errno) grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), argv[0]); return grub_errno; } /* Begin TCG Extension */ DEBUG_PRINT( ("measured multiboot module: %s \n", argv[0]) ); grub_TPM_measure_buffer( module, size, TPM_LOADER_MEASUREMENT_PCR ); /* End TCG Extension */ grub_file_close (file); return GRUB_ERR_NONE; }
/* Allocate pages for the real mode code and the protected mode code for linux as well as a memory map buffer. */ static grub_err_t allocate_pages (grub_size_t prot_size, grub_size_t *align, grub_size_t min_align, int relocatable, grub_uint64_t preferred_address) { grub_err_t err; if (prot_size == 0) prot_size = 1; prot_size = page_align (prot_size); /* Initialize the memory pointers with NULL for convenience. */ free_pages (); relocator = grub_relocator_new (); if (!relocator) { err = grub_errno; goto fail; } /* FIXME: Should request low memory from the heap when this feature is implemented. */ { grub_relocator_chunk_t ch; if (relocatable) { err = grub_relocator_alloc_chunk_align (relocator, &ch, preferred_address, preferred_address, prot_size, 1, GRUB_RELOCATOR_PREFERENCE_LOW, 1); for (; err && *align + 1 > min_align; (*align)--) { grub_errno = GRUB_ERR_NONE; err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000, 0xffffffff & ~prot_size, prot_size, 1 << *align, GRUB_RELOCATOR_PREFERENCE_LOW, 1); } if (err) goto fail; } else err = grub_relocator_alloc_chunk_addr (relocator, &ch, preferred_address, prot_size); if (err) goto fail; prot_mode_mem = get_virtual_current_address (ch); prot_mode_target = get_physical_target_address (ch); } grub_dprintf ("linux", "prot_mode_mem = %p, prot_mode_target = %lx, prot_size = %x\n", prot_mode_mem, (unsigned long) prot_mode_target, (unsigned) prot_size); return GRUB_ERR_NONE; fail: free_pages (); return err; }
grub_err_t grub_multiboot_make_mbi (grub_uint32_t *target) { struct multiboot_info *mbi; struct multiboot_mod_list *modlist; unsigned i; struct module *cur; grub_size_t mmap_size; grub_uint8_t *ptrorig; grub_addr_t ptrdest; grub_err_t err; grub_size_t bufsize; grub_relocator_chunk_t ch; bufsize = grub_multiboot_get_mbi_size (); err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch, 0x10000, 0xa0000 - bufsize, bufsize, 4, GRUB_RELOCATOR_PREFERENCE_NONE, 0); if (err) return err; ptrorig = get_virtual_current_address (ch); ptrdest = get_physical_target_address (ch); *target = ptrdest; mbi = (struct multiboot_info *) ptrorig; ptrorig += sizeof (*mbi); ptrdest += sizeof (*mbi); grub_memset (mbi, 0, sizeof (*mbi)); grub_memcpy (ptrorig, cmdline, cmdline_size); mbi->flags |= MULTIBOOT_INFO_CMDLINE; mbi->cmdline = ptrdest; ptrorig += ALIGN_UP (cmdline_size, 4); ptrdest += ALIGN_UP (cmdline_size, 4); grub_memcpy (ptrorig, PACKAGE_STRING, sizeof(PACKAGE_STRING)); mbi->flags |= MULTIBOOT_INFO_BOOT_LOADER_NAME; mbi->boot_loader_name = ptrdest; ptrorig += ALIGN_UP (sizeof(PACKAGE_STRING), 4); ptrdest += ALIGN_UP (sizeof(PACKAGE_STRING), 4); #ifdef GRUB_MACHINE_PCBIOS { struct grub_apm_info info; if (grub_apm_get_info (&info)) { struct multiboot_apm_info *mbinfo = (void *) ptrorig; mbinfo->cseg = info.cseg; mbinfo->offset = info.offset; mbinfo->cseg_16 = info.cseg_16; mbinfo->dseg = info.dseg; mbinfo->flags = info.flags; mbinfo->cseg_len = info.cseg_len; mbinfo->dseg_len = info.dseg_len; mbinfo->cseg_16_len = info.cseg_16_len; mbinfo->version = info.version; ptrorig += ALIGN_UP (sizeof (struct multiboot_apm_info), 4); ptrdest += ALIGN_UP (sizeof (struct multiboot_apm_info), 4); } } #endif if (modcnt) { mbi->flags |= MULTIBOOT_INFO_MODS; mbi->mods_addr = ptrdest; mbi->mods_count = modcnt; modlist = (struct multiboot_mod_list *) ptrorig; ptrorig += modcnt * sizeof (struct multiboot_mod_list); ptrdest += modcnt * sizeof (struct multiboot_mod_list); for (i = 0, cur = modules; i < modcnt; i++, cur = cur->next) { modlist[i].mod_start = cur->start; modlist[i].mod_end = modlist[i].mod_start + cur->size; modlist[i].cmdline = ptrdest; grub_memcpy (ptrorig, cur->cmdline, cur->cmdline_size); ptrorig += ALIGN_UP (cur->cmdline_size, 4); ptrdest += ALIGN_UP (cur->cmdline_size, 4); } } else { mbi->mods_addr = 0; mbi->mods_count = 0; } mmap_size = grub_get_multiboot_mmap_count () * sizeof (struct multiboot_mmap_entry); grub_fill_multiboot_mmap ((struct multiboot_mmap_entry *) ptrorig); mbi->mmap_length = mmap_size; mbi->mmap_addr = ptrdest; mbi->flags |= MULTIBOOT_INFO_MEM_MAP; ptrorig += mmap_size; ptrdest += mmap_size; /* Convert from bytes to kilobytes. */ mbi->mem_lower = grub_mmap_get_lower () / 1024; mbi->mem_upper = grub_mmap_get_upper () / 1024; mbi->flags |= MULTIBOOT_INFO_MEMORY; if (bootdev_set) { mbi->boot_device = bootdev; mbi->flags |= MULTIBOOT_INFO_BOOTDEV; } { struct grub_net_network_level_interface *net; FOR_NET_NETWORK_LEVEL_INTERFACES(net) if (net->dhcp_ack) { grub_memcpy (ptrorig, net->dhcp_ack, net->dhcp_acklen); mbi->drives_addr = ptrdest; mbi->drives_length = net->dhcp_acklen; ptrorig += net->dhcp_acklen; ptrdest += net->dhcp_acklen; break; } } if (elf_sec_num) { mbi->u.elf_sec.addr = ptrdest; grub_memcpy (ptrorig, elf_sections, elf_sec_entsize * elf_sec_num); mbi->u.elf_sec.num = elf_sec_num; mbi->u.elf_sec.size = elf_sec_entsize; mbi->u.elf_sec.shndx = elf_sec_shstrndx; mbi->flags |= MULTIBOOT_INFO_ELF_SHDR; ptrorig += elf_sec_entsize * elf_sec_num; ptrdest += elf_sec_entsize * elf_sec_num; } err = retrieve_video_parameters (mbi, ptrorig, ptrdest); if (err) { grub_print_error (); grub_errno = GRUB_ERR_NONE; } if ((mbi->flags & MULTIBOOT_INFO_FRAMEBUFFER_INFO) && mbi->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED) { ptrorig += mbi->framebuffer_palette_num_colors * sizeof (struct multiboot_color); ptrdest += mbi->framebuffer_palette_num_colors * sizeof (struct multiboot_color); } #if GRUB_MACHINE_HAS_VBE ptrorig += sizeof (struct grub_vbe_info_block); ptrdest += sizeof (struct grub_vbe_info_block); ptrorig += sizeof (struct grub_vbe_mode_info_block); ptrdest += sizeof (struct grub_vbe_mode_info_block); #endif #ifdef GRUB_MACHINE_EFI err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL); if (err) return err; #endif return GRUB_ERR_NONE; }
static grub_err_t CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, const char *filename, void *buffer) { Elf_Ehdr *ehdr = (Elf_Ehdr *) buffer; char *phdr_base; int i; if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || ehdr->e_ident[EI_MAG1] != ELFMAG1 || ehdr->e_ident[EI_MAG2] != ELFMAG2 || ehdr->e_ident[EI_MAG3] != ELFMAG3 || ehdr->e_ident[EI_DATA] != ELFDATA2LSB) return grub_error(GRUB_ERR_UNKNOWN_OS, N_("invalid arch-independent ELF magic")); if (ehdr->e_ident[EI_CLASS] != ELFCLASSXX || ehdr->e_machine != E_MACHINE || ehdr->e_version != EV_CURRENT) return grub_error (GRUB_ERR_UNKNOWN_OS, N_("invalid arch-dependent ELF magic")); if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) return grub_error (GRUB_ERR_UNKNOWN_OS, N_("this ELF file is not of the right type")); /* FIXME: Should we support program headers at strange locations? */ if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH) return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset"); phdr_base = (char *) buffer + ehdr->e_phoff; #define phdr(i) ((Elf_Phdr *) (phdr_base + (i) * ehdr->e_phentsize)) /* Load every loadable segment in memory. */ for (i = 0; i < ehdr->e_phnum; i++) { if (phdr(i)->p_type == PT_LOAD) { grub_err_t err; void *source; grub_dprintf ("multiboot_loader", "segment %d: paddr=0x%lx, memsz=0x%lx, vaddr=0x%lx\n", i, (long) phdr(i)->p_paddr, (long) phdr(i)->p_memsz, (long) phdr(i)->p_vaddr); { grub_relocator_chunk_t ch; err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator, &ch, phdr(i)->p_paddr, phdr(i)->p_memsz); if (err) { grub_dprintf ("multiboot_loader", "Error loading phdr %d\n", i); return err; } source = get_virtual_current_address (ch); } if (phdr(i)->p_filesz != 0) { if (grub_file_seek (file, (grub_off_t) phdr(i)->p_offset) == (grub_off_t) -1) return grub_errno; if (grub_file_read (file, source, phdr(i)->p_filesz) != (grub_ssize_t) phdr(i)->p_filesz) { if (!grub_errno) grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), filename); return grub_errno; } } if (phdr(i)->p_filesz < phdr(i)->p_memsz) grub_memset ((grub_uint8_t *) source + phdr(i)->p_filesz, 0, phdr(i)->p_memsz - phdr(i)->p_filesz); } } for (i = 0; i < ehdr->e_phnum; i++) if (phdr(i)->p_vaddr <= ehdr->e_entry && phdr(i)->p_vaddr + phdr(i)->p_memsz > ehdr->e_entry) { grub_multiboot_payload_eip = (ehdr->e_entry - phdr(i)->p_vaddr) + phdr(i)->p_paddr; #ifdef MULTIBOOT_LOAD_ELF64 # ifdef __mips /* We still in 32-bit mode. */ if ((ehdr->e_entry - phdr(i)->p_vaddr) + phdr(i)->p_paddr < 0xffffffff80000000ULL) return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64"); # else /* We still in 32-bit mode. */ if ((ehdr->e_entry - phdr(i)->p_vaddr) + phdr(i)->p_paddr > 0xffffffff) return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64"); # endif #endif break; } if (i == ehdr->e_phnum) return grub_error (GRUB_ERR_BAD_OS, "entry point isn't in a segment"); #if defined (__i386__) || defined (__x86_64__) #elif defined (__mips) grub_multiboot_payload_eip |= 0x80000000; #else #error Please complete this #endif if (ehdr->e_shnum) { grub_uint8_t *shdr, *shdrptr; shdr = grub_malloc (ehdr->e_shnum * ehdr->e_shentsize); if (!shdr) return grub_errno; if (grub_file_seek (file, ehdr->e_shoff) == (grub_off_t) -1) return grub_errno; if (grub_file_read (file, shdr, ehdr->e_shnum * ehdr->e_shentsize) != (grub_ssize_t) ehdr->e_shnum * ehdr->e_shentsize) { if (!grub_errno) grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), filename); return grub_errno; } for (shdrptr = shdr, i = 0; i < ehdr->e_shnum; shdrptr += ehdr->e_shentsize, i++) { Elf_Shdr *sh = (Elf_Shdr *) shdrptr; void *src; grub_addr_t target; grub_err_t err; /* This section is a loaded section, so we don't care. */ if (sh->sh_addr != 0) continue; /* This section is empty, so we don't care. */ if (sh->sh_size == 0) continue; { grub_relocator_chunk_t ch; err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch, 0, (0xffffffff - sh->sh_size) + 1, sh->sh_size, sh->sh_addralign, GRUB_RELOCATOR_PREFERENCE_NONE, 0); if (err) { grub_dprintf ("multiboot_loader", "Error loading shdr %d\n", i); return err; } src = get_virtual_current_address (ch); target = get_physical_target_address (ch); } if (grub_file_seek (file, sh->sh_offset) == (grub_off_t) -1) return grub_errno; if (grub_file_read (file, src, sh->sh_size) != (grub_ssize_t) sh->sh_size) { if (!grub_errno) grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), filename); return grub_errno; } sh->sh_addr = target; } grub_multiboot_add_elfsyms (ehdr->e_shnum, ehdr->e_shentsize, ehdr->e_shstrndx, shdr); } #undef phdr return grub_errno; }