grub_err_t grub_multiboot_load (grub_file_t file) { char *buffer; grub_ssize_t len; struct multiboot_header *header; grub_err_t err; buffer = grub_malloc (MULTIBOOT_SEARCH); if (!buffer) return grub_errno; len = grub_file_read (file, buffer, MULTIBOOT_SEARCH); if (len < 32) { grub_free (buffer); return grub_error (GRUB_ERR_BAD_OS, "file too small"); } /* Look for the multiboot header in the buffer. The header should be at least 12 bytes and aligned on a 4-byte boundary. */ for (header = (struct multiboot_header *) buffer; ((char *) header <= buffer + len - 12) || (header = 0); header = (struct multiboot_header *) ((char *) header + MULTIBOOT_HEADER_ALIGN)) { if (header->magic == MULTIBOOT_HEADER_MAGIC && !(header->magic + header->flags + header->checksum)) break; } if (header == 0) { grub_free (buffer); return grub_error (GRUB_ERR_BAD_ARGUMENT, "no multiboot header found"); } if (header->flags & UNSUPPORTED_FLAGS) { grub_free (buffer); return grub_error (GRUB_ERR_UNKNOWN_OS, "unsupported flag: 0x%x", header->flags); } if (header->flags & MULTIBOOT_AOUT_KLUDGE) { int offset = ((char *) header - buffer - (header->header_addr - header->load_addr)); int load_size = ((header->load_end_addr == 0) ? file->size - offset : header->load_end_addr - header->load_addr); grub_size_t code_size; if (header->bss_end_addr) code_size = (header->bss_end_addr - header->load_addr); else code_size = load_size; grub_multiboot_payload_dest = header->load_addr; grub_multiboot_pure_size += code_size; /* Allocate a bit more to avoid relocations in most cases. */ grub_multiboot_alloc_mbi = grub_multiboot_get_mbi_size () + 65536; grub_multiboot_payload_orig = grub_relocator32_alloc (grub_multiboot_pure_size + grub_multiboot_alloc_mbi); if (! grub_multiboot_payload_orig) { grub_free (buffer); return grub_errno; } if ((grub_file_seek (file, offset)) == (grub_off_t) -1) { grub_free (buffer); return grub_errno; } grub_file_read (file, (void *) grub_multiboot_payload_orig, load_size); if (grub_errno) { grub_free (buffer); return grub_errno; } if (header->bss_end_addr) grub_memset ((void *) (grub_multiboot_payload_orig + load_size), 0, header->bss_end_addr - header->load_addr - load_size); grub_multiboot_payload_eip = header->entry_addr; } else { err = grub_multiboot_load_elf (file, buffer); if (err) { grub_free (buffer); return err; } } if (header->flags & MULTIBOOT_VIDEO_MODE) { switch (header->mode_type) { case 1: err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT, GRUB_MULTIBOOT_CONSOLE_EGA_TEXT | GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER, 0, 0, 0, 0); break; case 0: err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER, GRUB_MULTIBOOT_CONSOLE_EGA_TEXT | GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER, header->width, header->height, header->depth, 0); break; default: err = grub_error (GRUB_ERR_BAD_OS, "unsupported graphical mode type %d", header->mode_type); break; } } else err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT, GRUB_MULTIBOOT_CONSOLE_EGA_TEXT, 0, 0, 0, 0); return err; }
static grub_err_t CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, void *buffer) { Elf_Ehdr *ehdr = (Elf_Ehdr *) buffer; char *phdr_base; int lowest_segment = -1, highest_segment = -1; int i; grub_size_t code_size; if (ehdr->e_ident[EI_CLASS] != ELFCLASSXX) return grub_error (GRUB_ERR_UNKNOWN_OS, "invalid ELF class"); 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_version != EV_CURRENT || ehdr->e_ident[EI_DATA] != ELFDATA2LSB || ehdr->e_machine != E_MACHINE) return grub_error(GRUB_ERR_UNKNOWN_OS, "no valid ELF header found"); if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) return grub_error (GRUB_ERR_UNKNOWN_OS, "invalid ELF file 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"); #ifdef MULTIBOOT_LOAD_ELF64 /* We still in 32-bit mode. */ if (ehdr->e_entry > 0xffffffff) return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64"); #endif phdr_base = (char *) buffer + ehdr->e_phoff; #define phdr(i) ((Elf_Phdr *) (phdr_base + (i) * ehdr->e_phentsize)) for (i = 0; i < ehdr->e_phnum; i++) if (phdr(i)->p_type == PT_LOAD && phdr(i)->p_filesz != 0) { /* Beware that segment 0 isn't necessarily loadable */ if (lowest_segment == -1 || phdr(i)->p_paddr < phdr(lowest_segment)->p_paddr) lowest_segment = i; if (highest_segment == -1 || phdr(i)->p_paddr > phdr(highest_segment)->p_paddr) highest_segment = i; } if (lowest_segment == -1) return grub_error (GRUB_ERR_BAD_OS, "ELF contains no loadable segments"); code_size = (phdr(highest_segment)->p_paddr + phdr(highest_segment)->p_memsz) - phdr(lowest_segment)->p_paddr; grub_multiboot_payload_dest = phdr(lowest_segment)->p_paddr; grub_multiboot_pure_size += code_size; grub_multiboot_alloc_mbi = grub_multiboot_get_mbi_size () + 65536; grub_multiboot_payload_orig = grub_relocator32_alloc (grub_multiboot_pure_size + grub_multiboot_alloc_mbi); if (!grub_multiboot_payload_orig) return grub_errno; /* Load every loadable segment in memory. */ for (i = 0; i < ehdr->e_phnum; i++) { if (phdr(i)->p_type == PT_LOAD && phdr(i)->p_filesz != 0) { char *load_this_module_at = (char *) (grub_multiboot_payload_orig + (long) (phdr(i)->p_paddr - phdr(lowest_segment)->p_paddr)); 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); if (grub_file_seek (file, (grub_off_t) phdr(i)->p_offset) == (grub_off_t) -1) return grub_error (GRUB_ERR_BAD_OS, "invalid offset in program header"); if (grub_file_read (file, load_this_module_at, phdr(i)->p_filesz) != (grub_ssize_t) phdr(i)->p_filesz) return grub_error (GRUB_ERR_BAD_OS, "couldn't read segment from file"); if (phdr(i)->p_filesz < phdr(i)->p_memsz) grub_memset (load_this_module_at + 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 = grub_multiboot_payload_dest + (ehdr->e_entry - phdr(i)->p_vaddr) + (phdr(i)->p_paddr - phdr(lowest_segment)->p_paddr); break; } if (i == ehdr->e_phnum) return grub_error (GRUB_ERR_BAD_OS, "entry point isn't in a segment"); #undef phdr return grub_errno; }
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; }
grub_err_t grub_multiboot_make_mbi (void *orig, grub_uint32_t dest, grub_off_t buf_off, grub_size_t bufsize) { grub_uint8_t *ptrorig = (grub_uint8_t *) orig + buf_off; grub_uint32_t ptrdest = dest + buf_off; struct multiboot_info *mbi; struct multiboot_mod_list *modlist; unsigned i; struct module *cur; grub_size_t mmap_size; grub_err_t err; if (bufsize < grub_multiboot_get_mbi_size ()) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "mbi buffer is too small"); 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); 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; } err = retrieve_video_parameters (mbi, ptrorig, ptrdest); if (err) { grub_print_error (); grub_errno = GRUB_ERR_NONE; } return GRUB_ERR_NONE; }
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; }
grub_err_t grub_multiboot_load (grub_file_t file) { char *buffer; grub_ssize_t len; struct multiboot_header *header; grub_err_t err; struct multiboot_header_tag *tag; struct multiboot_header_tag_address *addr_tag = NULL; int entry_specified = 0; grub_addr_t entry = 0; grub_uint32_t console_required = 0; struct multiboot_header_tag_framebuffer *fbtag = NULL; int accepted_consoles = GRUB_MULTIBOOT_CONSOLE_EGA_TEXT; buffer = grub_malloc (MULTIBOOT_SEARCH); if (!buffer) return grub_errno; len = grub_file_read (file, buffer, MULTIBOOT_SEARCH); if (len < 32) { grub_free (buffer); return grub_error (GRUB_ERR_BAD_OS, "file too small"); } /* Look for the multiboot header in the buffer. The header should be at least 12 bytes and aligned on a 4-byte boundary. */ for (header = (struct multiboot_header *) buffer; ((char *) header <= buffer + len - 12) || (header = 0); header = (struct multiboot_header *) ((char *) header + MULTIBOOT_HEADER_ALIGN)) { if (header->magic == MULTIBOOT_HEADER_MAGIC && !(header->magic + header->architecture + header->header_length + header->checksum) && header->architecture == MULTIBOOT_ARCHITECTURE_CURRENT) break; } if (header == 0) { grub_free (buffer); return grub_error (GRUB_ERR_BAD_ARGUMENT, "no multiboot header found"); } for (tag = (struct multiboot_header_tag *) (header + 1); tag->type != MULTIBOOT_TAG_TYPE_END; tag = (struct multiboot_header_tag *) ((char *) tag + tag->size)) switch (tag->type) { case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST: { unsigned i; struct multiboot_header_tag_information_request *request_tag = (struct multiboot_header_tag_information_request *) tag; if (request_tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL) break; for (i = 0; i < (request_tag->size - sizeof (request_tag)) / sizeof (request_tag->requests[0]); i++) switch (request_tag->requests[i]) { case MULTIBOOT_TAG_TYPE_END: case MULTIBOOT_TAG_TYPE_CMDLINE: case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: case MULTIBOOT_TAG_TYPE_MODULE: case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: case MULTIBOOT_TAG_TYPE_BOOTDEV: case MULTIBOOT_TAG_TYPE_MMAP: case MULTIBOOT_TAG_TYPE_FRAMEBUFFER: break; case MULTIBOOT_TAG_TYPE_VBE: case MULTIBOOT_TAG_TYPE_ELF_SECTIONS: case MULTIBOOT_TAG_TYPE_APM: default: grub_free (buffer); return grub_error (GRUB_ERR_UNKNOWN_OS, "unsupported information tag: 0x%x", request_tag->requests[i]); } break; } case MULTIBOOT_HEADER_TAG_ADDRESS: addr_tag = (struct multiboot_header_tag_address *) tag; break; case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS: entry_specified = 1; entry = ((struct multiboot_header_tag_entry_address *) tag)->entry_addr; break; case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS: if (!(((struct multiboot_header_tag_console_flags *) tag)->console_flags & MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED)) accepted_consoles &= ~GRUB_MULTIBOOT_CONSOLE_EGA_TEXT; if (((struct multiboot_header_tag_console_flags *) tag)->console_flags & MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED) console_required = 1; break; case MULTIBOOT_HEADER_TAG_FRAMEBUFFER: fbtag = (struct multiboot_header_tag_framebuffer *) tag; accepted_consoles |= GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER; break; /* GRUB always page-aligns modules. */ case MULTIBOOT_HEADER_TAG_MODULE_ALIGN: break; default: if (! (tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL)) { grub_free (buffer); return grub_error (GRUB_ERR_UNKNOWN_OS, "unsupported tag: 0x%x", tag->type); } break; } if (addr_tag && !entry_specified) { grub_free (buffer); return grub_error (GRUB_ERR_UNKNOWN_OS, "load address tag without entry address tag"); } if (addr_tag) { int offset = ((char *) header - buffer - (addr_tag->header_addr - addr_tag->load_addr)); int load_size = ((addr_tag->load_end_addr == 0) ? file->size - offset : addr_tag->load_end_addr - addr_tag->load_addr); grub_size_t code_size; if (addr_tag->bss_end_addr) code_size = (addr_tag->bss_end_addr - addr_tag->load_addr); else code_size = load_size; grub_multiboot_payload_dest = addr_tag->load_addr; grub_multiboot_pure_size += code_size; /* Allocate a bit more to avoid relocations in most cases. */ grub_multiboot_alloc_mbi = grub_multiboot_get_mbi_size () + 65536; grub_multiboot_payload_orig = grub_relocator32_alloc (grub_multiboot_pure_size + grub_multiboot_alloc_mbi); if (! grub_multiboot_payload_orig) { grub_free (buffer); return grub_errno; } if ((grub_file_seek (file, offset)) == (grub_off_t) -1) { grub_free (buffer); return grub_errno; } grub_file_read (file, (void *) grub_multiboot_payload_orig, load_size); if (grub_errno) { grub_free (buffer); return grub_errno; } if (addr_tag->bss_end_addr) grub_memset ((void *) (grub_multiboot_payload_orig + load_size), 0, addr_tag->bss_end_addr - addr_tag->load_addr - load_size); } else { err = grub_multiboot_load_elf (file, buffer); if (err) { grub_free (buffer); return err; } } if (entry_specified) grub_multiboot_payload_eip = entry; if (fbtag) err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER, accepted_consoles, fbtag->width, fbtag->height, fbtag->depth, console_required); else err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT, accepted_consoles, 0, 0, 0, console_required); return err; }