diff --git a/src/arch/x86/image/bzimage.c b/src/arch/x86/image/bzimage.c index 6bfac770ebb..18d9bf05ec8 100644 --- a/src/arch/x86/image/bzimage.c +++ b/src/arch/x86/image/bzimage.c @@ -673,7 +673,8 @@ static int bzimage_exec ( struct image *image ) { */ bzimg.pm_sz = ( bzimg.pm_sz + LZ_ALIGN - 1 ) & ~( LZ_ALIGN - 1 ); - lz_set_bzimage ( lz, bzimg.rm_kernel, bzimg.pm_kernel + bzimg.pm_sz ); + lz_set ( lz, bzimg.rm_kernel, bzimg.pm_kernel + bzimg.pm_sz, + LZ_PROTO_LINUX_BOOT ); bzimg.pm_sz += SLB_SIZE; } diff --git a/src/arch/x86/image/elfboot.c b/src/arch/x86/image/elfboot.c index dc35689293e..9a47e2cf2c5 100644 --- a/src/arch/x86/image/elfboot.c +++ b/src/arch/x86/image/elfboot.c @@ -51,7 +51,7 @@ static int elfboot_exec ( struct image *image ) { int rc; /* Load the image using core ELF support */ - if ( ( rc = elf_load ( image, &entry, &max ) ) != 0 ) { + if ( ( rc = elf_load ( image, NULL, &entry, &max ) ) != 0 ) { DBGC ( image, "ELF %p could not load: %s\n", image, strerror ( rc ) ); return rc; @@ -129,7 +129,7 @@ static int elfboot_probe ( struct image *image ) { /* Check that this image uses flat physical addressing */ if ( ( rc = elf_segments ( image, &ehdr, elfboot_check_segment, - &entry, &max ) ) != 0 ) { + NULL, &entry, &max ) ) != 0 ) { DBGC ( image, "Unloadable ELF image\n" ); return rc; } diff --git a/src/arch/x86/image/landing_zone.c b/src/arch/x86/image/landing_zone.c index f5f8701e36a..929f449d264 100644 --- a/src/arch/x86/image/landing_zone.c +++ b/src/arch/x86/image/landing_zone.c @@ -9,21 +9,22 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include +#include #include struct sl_header { - u16 lz_offet; - u16 lz_length; + u16 lz_entry_point; + u16 bootloader_data_offset; + u16 lz_info_offset; } __attribute__ (( packed )); -struct lz_header { +struct lz_info { u8 uuid[16]; - u32 slaunch_loader_size; - u32 zero_page_addr; - u32 event_log_addr; - u32 event_log_size; - u8 msb_key_hash[20]; - u8 lz_hashes[]; + u32 version; + u16 msb_key_algo; + u8 msb_key_hash[]; } __attribute__ (( packed )); const unsigned char @@ -44,6 +45,59 @@ struct drtm_t { u8 var_len_fields[]; } __attribute__ (( packed )); +#define LZ_TAG_CLASS_MASK 0xF0 + +/* Tags with no particular class */ +#define LZ_TAG_NO_CLASS 0x00 +#define LZ_TAG_END 0x00 +#define LZ_TAG_UNAWARE_OS 0x01 +#define LZ_TAG_TAGS_SIZE 0x0F /* Always first */ + +/* Tags specifying kernel type */ +#define LZ_TAG_BOOT_CLASS 0x10 +#define LZ_TAG_BOOT_LINUX 0x10 +#define LZ_TAG_BOOT_MB2 0x11 + +/* Tags specific to TPM event log */ +#define LZ_TAG_EVENT_LOG_CLASS 0x20 +#define LZ_TAG_EVENT_LOG 0x20 +#define LZ_TAG_HASH 0x21 + +struct lz_tag_hdr { + u8 type; + u8 len; +} __attribute__ (( packed )); + +struct lz_tag_tags_size { + struct lz_tag_hdr hdr; + u16 size; +} __attribute__ (( packed )); + +struct lz_tag_boot_linux { + struct lz_tag_hdr hdr; + u32 zero_page; +} __attribute__ (( packed )); + +struct lz_tag_boot_mb2 { + struct lz_tag_hdr hdr; + u32 mbi; + u32 kernel_entry; + u32 kernel_size; +} __attribute__ (( packed )); + +struct lz_tag_evtlog { + struct lz_tag_hdr hdr; + u32 address; + u32 size; +} __attribute__ (( packed )); + +struct lz_tag_hash { + struct lz_tag_hdr hdr; + u16 algo_id; + u8 digest[]; +} __attribute__ (( packed )); + + static physaddr_t target; /** @@ -52,7 +106,7 @@ static physaddr_t target; * @v image LZ file * @v zeropage Address of zero page */ -int lz_set_bzimage ( struct image *image, userptr_t zeropage, userptr_t tgt ) { +int lz_set ( struct image *image, userptr_t zeropage, userptr_t tgt, int proto ) { target = user_to_phys ( tgt, 0 ); int rc; @@ -68,23 +122,91 @@ int lz_set_bzimage ( struct image *image, userptr_t zeropage, userptr_t tgt ) { memcpy_user ( tgt, 0, image->data, 0, image->len ); struct sl_header *sl_hdr = ( struct sl_header *) tgt; - struct lz_header *hdr = ( struct lz_header *) ( tgt + sl_hdr->lz_length ); + struct lz_tag_tags_size *tags = ( struct lz_tag_tags_size *) + ( tgt + sl_hdr->bootloader_data_offset ); + + /* Tags header */ + tags->hdr.type = LZ_TAG_TAGS_SIZE; + tags->hdr.len = sizeof(struct lz_tag_tags_size); + tags->size = sizeof(struct lz_tag_tags_size); + + /* Hashes of LZ */ + { + u8 buff[SHA256_CTX_SIZE]; /* SHA1_CTX_SIZE is smaller */ + struct lz_tag_hash *h = ((void *)tags) + tags->size; + h->hdr.type = LZ_TAG_HASH; + h->hdr.len = sizeof(struct lz_tag_hash) + SHA256_DIGEST_SIZE; + h->algo_id = 0x000B; + sha256_algorithm.init ( buff ); + sha256_algorithm.update ( buff, (void *) tgt, + sl_hdr->bootloader_data_offset ); + sha256_algorithm.final ( buff, h->digest ); + tags->size += h->hdr.len; + + h = ((void *)tags) + tags->size; + h->hdr.type = LZ_TAG_HASH; + h->hdr.len = sizeof(struct lz_tag_hash) + SHA1_DIGEST_SIZE; + h->algo_id = 0x0004; + sha1_algorithm.init ( buff ); + sha1_algorithm.update ( buff, (void *) tgt, + sl_hdr->bootloader_data_offset ); + sha1_algorithm.final ( buff, h->digest ); + tags->size += h->hdr.len; + } + /* Boot protocol data */ DBGC ( image, "LZ %p writing zeropage address: 0x%lx\n", image, user_to_phys ( zeropage, 0 ) ); - hdr->zero_page_addr = user_to_phys ( zeropage, 0 ); + switch (proto) { + case LZ_PROTO_LINUX_BOOT: + { + struct lz_tag_boot_linux *b = ((void *)tags) + tags->size; + b->hdr.type = LZ_TAG_BOOT_LINUX; + b->hdr.len = sizeof(struct lz_tag_boot_linux); + b->zero_page = user_to_phys ( zeropage, 0 ); + tags->size += b->hdr.len; + break; + } + case LZ_PROTO_MULTIBOOT2: + { + struct lz_tag_boot_mb2 *b = ((void *)tags) + tags->size; + physaddr_t *args = (physaddr_t *)zeropage; + b->hdr.type = LZ_TAG_BOOT_MB2; + b->hdr.len = sizeof(struct lz_tag_boot_mb2); + b->mbi = user_to_phys ( zeropage, 0 ); + b->kernel_entry = args[0]; + b->kernel_size = args[1]; + tags->size += b->hdr.len; + break; + + } + default: + return 1; + } + /* DRTM Event log address and size */ struct drtm_t *drtm = ( struct drtm_t *) acpi_find ( ACPI_SIGNATURE ('D', 'R', 'T', 'M'), 0 ); if ( drtm != UNULL ) { DBGC ( image, "ACPI DRTM table at %p (0x%lx physical)\n", drtm, user_to_phys ( ( userptr_t ) drtm, 0 ) ); - hdr->event_log_addr = drtm->Log_Area_Start; - hdr->event_log_size = drtm->Log_Area_Length; + + struct lz_tag_evtlog *e = ((void *)tags) + tags->size; + e->hdr.type = LZ_TAG_EVENT_LOG; + e->hdr.len = sizeof(struct lz_tag_evtlog); + e->address = drtm->Log_Area_Start; + e->size = drtm->Log_Area_Length; + tags->size += e->hdr.len; } + /* Mark end of tags */ + struct lz_tag_hdr *end = ((void *)tags) + tags->size; + end->type = LZ_TAG_END; + end->len = sizeof(struct lz_tag_hdr); + tags->size += end->len; + return 0; } @@ -150,6 +272,7 @@ static int lz_exec ( struct image *image ) { /* There is no way for the image to return, since we provide * no return address. */ + DBGC ( image, "LZ %p SKINIT returned\n", image ); assert ( 0 ); return -ECANCELED; /* -EIMPOSSIBLE */ @@ -164,7 +287,7 @@ static int lz_exec ( struct image *image ) { static int lz_probe ( struct image *image ) { int rc; struct sl_header sl_hdr; - struct lz_header hdr; + struct lz_info lzi; uint32_t eax, ebx, ecx, edx; cpuid ( CPUID_AMD_CHECK, 0, &eax, &ebx, &ecx, &edx ); @@ -186,9 +309,9 @@ static int lz_probe ( struct image *image ) { return -ENOEXEC; } copy_from_user ( &sl_hdr, image->data, 0, sizeof ( sl_hdr ) ); - copy_from_user ( &hdr, image->data, sl_hdr.lz_length, sizeof ( hdr ) ); + copy_from_user ( &lzi, image->data, sl_hdr.lz_info_offset, sizeof ( lzi ) ); - rc = memcmp ( hdr.uuid, lz_header_uuid, sizeof ( lz_header_uuid ) ); + rc = memcmp ( lzi.uuid, lz_header_uuid, sizeof ( lz_header_uuid ) ); if ( rc == 0 ) { image_set_name ( image, "landing_zone" ); diff --git a/src/arch/x86/image/multiboot.c b/src/arch/x86/image/multiboot.c index 0c85df70864..c153bb7fd82 100644 --- a/src/arch/x86/image/multiboot.c +++ b/src/arch/x86/image/multiboot.c @@ -372,7 +372,7 @@ static int multiboot_load_elf ( struct image *image, physaddr_t *entry, int rc; /* Load ELF image*/ - if ( ( rc = elf_load ( image, entry, max ) ) != 0 ) { + if ( ( rc = elf_load ( image, NULL, entry, max ) ) != 0 ) { DBGC ( image, "MULTIBOOT %p ELF image failed to load: %s\n", image, strerror ( rc ) ); return rc; diff --git a/src/arch/x86/image/multiboot2.c b/src/arch/x86/image/multiboot2.c new file mode 100644 index 00000000000..69cb2ebcf66 --- /dev/null +++ b/src/arch/x86/image/multiboot2.c @@ -0,0 +1,654 @@ +/* + * Copyright (C) 2016 Star Lab Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Multiboot2 image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef EFIAPI +#include +#endif + +FEATURE ( FEATURE_IMAGE, "MBOOT2", DHCP_EB_FEATURE_MULTIBOOT2, 1 ); + +/** + * Maximum multiboot2 boot information size + */ +#define MB_MAX_BOOTINFO_SIZE 4096 + +/** Multiboot2 boot information buffer */ +static union { + uint64_t align; + char bib[MB_MAX_BOOTINFO_SIZE]; +} mb2_bib; + +/** A multiboot2 header descriptor */ +struct multiboot2_header_info { + /** The actual multiboot2 header */ + struct multiboot_header mb; + /** Offset of header within the multiboot2 image */ + size_t offset; +}; + +/** + * Find multiboot2 header + * + * @v image Multiboot file + * @v hdr Multiboot header descriptor to fill in + * @ret rc Return status code + */ +static int multiboot2_find_header ( struct image *image, + struct multiboot2_header_info *hdr ) { + uint32_t buf[64]; + size_t offset; + unsigned int buf_idx; + uint32_t checksum; + + /* Scan through first MULTIBOOT_SEARCH of image file 256 bytes at a time. + * (Use the buffering to avoid the overhead of a + * copy_from_user() for every dword.) + */ + for ( offset = 0 ; offset < MULTIBOOT_SEARCH ; offset += sizeof ( buf[0] ) ) { + /* Check for end of image */ + if ( offset > image->len ) + break; + /* Refill buffer if applicable */ + buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) ); + if ( buf_idx == 0 ) { + copy_from_user ( buf, image->data, offset, + sizeof ( buf ) ); + } + /* Check signature */ + if ( buf[buf_idx] != MULTIBOOT2_HEADER_MAGIC ) + continue; + /* Copy header and verify checksum */ + copy_from_user ( &hdr->mb, image->data, offset, + sizeof ( hdr->mb ) ); + checksum = ( hdr->mb.magic + hdr->mb.architecture + hdr->mb.header_length + + hdr->mb.checksum ); + if ( checksum != 0 ) + continue; + + /* Make sure that the multiboot architecture is x86 */ + if (hdr->mb.architecture != MULTIBOOT_ARCHITECTURE_I386) { + return -ENOEXEC; + } + + /* Record offset of multiboot header and return */ + hdr->offset = offset; + return 0; + } + + /* No multiboot header found */ + return -ENOEXEC; +} + +struct multiboot2_tags { + int module_align; + int boot_services; + + int entry_addr_valid; + int entry_addr_efi32_valid; + int entry_addr_efi64_valid; + int relocatable_valid; + + uint32_t entry_addr; + uint32_t entry_addr_efi32; + uint32_t entry_addr_efi64; + uint32_t reloc_min_addr; + uint32_t reloc_max_addr; + uint32_t reloc_align; + uint32_t reloc_preference; +}; + +static int multiboot2_validate_inforeq ( struct image *image, size_t offset, size_t num_reqs ) { + uint32_t inforeq; + + while (num_reqs) { + copy_from_user ( &inforeq, image->data, offset, sizeof ( inforeq ) ); + offset += sizeof(inforeq); + num_reqs--; + + switch (inforeq) { + case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: + case MULTIBOOT_TAG_TYPE_MMAP: + continue; + + default: + return -ENOTSUP; + } + } + + return 0; +} + +static int multiboot2_validate_tags ( struct image *image, struct multiboot2_header_info *hdr, + struct multiboot2_tags *tags ) { + size_t offset = hdr->offset + sizeof(struct multiboot_header); + size_t end_offset = offset + hdr->mb.header_length; + struct multiboot_header_tag tag; + + /* Clear out the multiboot2 tags structure */ + memset(tags, 0, sizeof(*tags)); + + while (offset < end_offset) { + copy_from_user ( &tag, image->data, offset, sizeof ( tag ) ); + + DBGC ( image, "MULTIBOOT2 %p (offset: %d) TAG type: %x flags: %x size: %d\n", image, + (int)(offset - hdr->offset), tag.type, tag.flags, tag.size ); + + if (tag.type == MULTIBOOT_HEADER_TAG_END) { + DBGC ( image, "MULTIBOOT2 %p tag end\n", image ); + return 0; + } + + switch (tag.type) { + case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST: + { + size_t num_inforeqs; + + DBGC ( image, "MULTIBOOT2 %p has an information request tag\n", + image ); + + num_inforeqs = (tag.size - sizeof(tag)) / sizeof(uint32_t); + + if (multiboot2_validate_inforeq ( image, offset + sizeof(tag), num_inforeqs ) != 0) { + DBGC ( image, "MULTIBOOT2 %p cannot support all information request tags\n", + image ); + return -ENOTSUP; + } + + break; + } + case MULTIBOOT_HEADER_TAG_ADDRESS: + DBGC ( image, "MULTIBOOT2 %p has an address tag\n", + image ); + + if ((tag.flags & MULTIBOOT_HEADER_TAG_OPTIONAL) != MULTIBOOT_HEADER_TAG_OPTIONAL) + return -ENOTSUP; + + break; + + case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS: + { + struct multiboot_header_tag_entry_address mb_tag = { 0 }; + copy_from_user ( &mb_tag, image->data, offset, tag.size ); + + DBGC ( image, "MULTIBOOT2 %p has an entry address tag\n", + image ); + + tags->entry_addr_valid = 1; + tags->entry_addr = mb_tag.entry_addr; + break; + } + case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS: + DBGC ( image, "MULTIBOOT2 %p has a console flags tag\n", + image ); + + if ((tag.flags & MULTIBOOT_HEADER_TAG_OPTIONAL) != MULTIBOOT_HEADER_TAG_OPTIONAL) + return -ENOTSUP; + + break; + + case MULTIBOOT_HEADER_TAG_FRAMEBUFFER: + DBGC ( image, "MULTIBOOT2 %p has a framebuffer tag\n", + image ); + + if ((tag.flags & MULTIBOOT_HEADER_TAG_OPTIONAL) != MULTIBOOT_HEADER_TAG_OPTIONAL) + return -ENOTSUP; + + break; + + case MULTIBOOT_HEADER_TAG_MODULE_ALIGN: + DBGC ( image, "MULTIBOOT2 %p has a module align tag\n", + image ); + tags->module_align = 1; + break; + + case MULTIBOOT_HEADER_TAG_EFI_BS: + DBGC ( image, "MULTIBOOT2 %p has a boot services tag\n", + image ); + tags->boot_services = 1; + break; + + case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32: + { + struct multiboot_header_tag_entry_address mb_tag = { 0 }; + copy_from_user ( &mb_tag, image->data, offset, tag.size ); + + DBGC ( image, "MULTIBOOT2 %p has an entry address EFI32 tag\n", + image ); + + tags->entry_addr_efi32_valid = 1; + tags->entry_addr_efi32 = mb_tag.entry_addr; + break; + } + case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64: + { + struct multiboot_header_tag_entry_address mb_tag = { 0 }; + copy_from_user ( &mb_tag, image->data, offset, tag.size ); + + DBGC ( image, "MULTIBOOT2 %p has an entry address EFI64 tag: %x\n", + image, mb_tag.entry_addr ); + + tags->entry_addr_efi64_valid = 1; + tags->entry_addr_efi64 = mb_tag.entry_addr; + break; + } + case MULTIBOOT_HEADER_TAG_RELOCATABLE: + { + struct multiboot_header_tag_relocatable mb_tag = { 0 }; + copy_from_user ( &mb_tag, image->data, offset, tag.size ); + + DBGC ( image, "MULTIBOOT2 %p has a relocatable tag\n", + image ); + + tags->relocatable_valid = 1; + tags->reloc_min_addr = mb_tag.min_addr; + tags->reloc_max_addr = mb_tag.max_addr; + tags->reloc_align = mb_tag.align; + tags->reloc_preference = mb_tag.preference; + break; + } + default: + DBGC ( image, "MULTIBOOT2 %p unknown tag %x\n", + image, tag.type ); + return -ENOTSUP; + } + + offset += tag.size + (MULTIBOOT_TAG_ALIGN - 1); + offset = offset & ~(MULTIBOOT_TAG_ALIGN - 1); + } + + /* If we did not get a MULTIBOOT_HEADER_TAG_END, fail out */ + DBGC ( image, "MULTIBOOT %p missing tag end\n", image ); + return -ENOTSUP; +} + +/** + * Add bootloader into bib + */ +static size_t multiboot2_add_bootloader ( struct image *image, size_t offset ) { + struct multiboot_tag_string *bootloader = (struct multiboot_tag_string *)&mb2_bib.bib[offset]; + size_t remaining = MB_MAX_BOOTINFO_SIZE - offset - sizeof(*bootloader); + size_t len; + char *buf = bootloader->string; + + len = ( snprintf ( buf, remaining, "iPXE %s", product_version ) + 1 /* NUL */ ); + if ( len > remaining ) + len = remaining; + + DBGC ( image, "MULTIBOOT2 %p bootloader: %s\n", image, bootloader->string ); + + bootloader->type = MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME; + bootloader->size = len + sizeof(*bootloader); + return bootloader->size; +} + +/** + * Add command line into bib + */ +static size_t multiboot2_add_cmdline ( struct image *image, size_t offset ) { + struct multiboot_tag_string *cmdline = (struct multiboot_tag_string *)&mb2_bib.bib[offset]; + size_t remaining = MB_MAX_BOOTINFO_SIZE - offset - sizeof(*cmdline); + size_t len; + char *buf = cmdline->string; + + cmdline->type = MULTIBOOT_TAG_TYPE_CMDLINE; + cmdline->size = sizeof(*cmdline); + + /* Copy image URI to base memory buffer as start of command line */ + len = ( format_uri ( image->uri, buf, remaining ) + 1 /* NUL */ ); + if ( len > remaining ) + len = remaining; + buf += len; + remaining -= len; + cmdline->size += len; + + /* Copy command line to base memory buffer, if present */ + if ( image->cmdline ) { + buf--; + cmdline->size--; + remaining++; + len = ( snprintf ( buf, remaining, " %s", image->cmdline ) + 1 /* NUL */ ); + if ( len > remaining ) + len = remaining; + } + + DBGC ( image, "MULTIBOOT2 %p cmdline: %s\n", image, cmdline->string ); + + cmdline->size += len; + return cmdline->size; +} + +/** + * Load multiboot2 image into memory + * + * @v image Multiboot file + * @v hdr Multiboot header descriptor + * @ret entry Entry point + * @ret max Maximum used address + * @ret rc Return status code + */ +static int multiboot2_load ( struct image *image, struct multiboot2_tags *tags, + physaddr_t *load, physaddr_t *entry, physaddr_t *max ) { + + int rc; + + if ( ( rc = elf_load ( image, load, entry, max ) ) < 0 ) { + DBGC ( image, "MULTIBOOT2 %p could not load elf image\n", image ); + return rc; + } +#ifdef EFIAPI + *entry = tags->entry_addr_efi64; +#else + (void)tags; +#endif + + return rc; +} + +static size_t adjust_tag_offset(size_t offset) { + if ((offset & 7) != 0) { + return ((offset + 8) & ~7); + } + return offset; +} + +/** + * Add multiboot modules + */ +static size_t multiboot2_add_modules ( struct image *image, size_t offset ) { + struct image *module_image; + struct multiboot_tag_module *module; + char *buf; + size_t remaining; + size_t len; + userptr_t memory; + + /* Add each image as a multiboot module */ + for_each_image ( module_image ) { + + /* Do not include kernel image itself as a module */ + if ( module_image == image ) + continue; + + memory = umalloc ( module_image->len ); + if ( memory == UNULL ) { + DBGC ( image, "MULTIBOOT2 %p could not allocate %zd bytes.\n", module_image, module_image->len ); + return 0; + } + + memcpy_user ( memory, 0, module_image->data, 0, module_image->len ); + + /* Add module to list */ + module = (struct multiboot_tag_module *)&mb2_bib.bib[offset]; + module->type = MULTIBOOT_TAG_TYPE_MODULE; + module->size = sizeof(*module); + module->mod_start = user_to_phys ( memory, 0 ); + module->mod_end = user_to_phys ( memory, module_image->len ); + + buf = module->cmdline; + remaining = MB_MAX_BOOTINFO_SIZE - offset - sizeof(*module); + + /* Copy image URI to base memory buffer as start of command line */ + len = ( format_uri ( module_image->uri, buf, remaining ) + 1 /* NUL */ ); + if ( len > remaining ) + len = remaining; + buf += len; + remaining -= len; + module->size += len; + + /* Copy command line to base memory buffer, if present */ + if ( module_image->cmdline ) { + buf--; + module->size--; + remaining++; + len = ( snprintf ( buf, remaining, " %s", module_image->cmdline ) + 1 /* NUL */ ); + if ( len > remaining ) + len = remaining; + module->size += len; + } + + offset += module->size; + offset = adjust_tag_offset(offset); + + DBGC ( image, "MULTIBOOT2 %p module %s is [%x,%x): %s\n", + image, module_image->name, module->mod_start, + module->mod_end, module->cmdline ); + } + + return offset; +} + +void multiboot2_boot(uint32_t *bib, uint32_t entry) { + __asm__ __volatile__ ( "push %%ebp\n\t" + "call *%%edi\n\t" + "pop %%ebp\n\t" + : : "a" ( MULTIBOOT2_BOOTLOADER_MAGIC ), + "b" ( bib ), + "D" ( entry ) + : "ecx", "edx", "esi", "memory" ); +} + +/** + * Execute multiboot2 image + * + * @v image Multiboot image + * @ret rc Return status code + */ +static int multiboot2_exec ( struct image *image ) { + struct multiboot2_header_info hdr; + struct multiboot2_tags mb_tags; + struct multiboot_tag *tag; + struct multiboot_tag_load_base_addr *load_base_addr_tag; +#ifdef EFIAPI + struct multiboot_tag_efi64 *tag_efi64; +#endif + struct image *lz; + uint32_t *total_size; + uint32_t *reserved; + physaddr_t load; + physaddr_t entry; + physaddr_t max; + size_t offset; + int rc; + + /* Locate multiboot2 header, if present */ + if ( ( rc = multiboot2_find_header ( image, &hdr ) ) != 0 ) { + DBGC ( image, "MULTIBOOT2 %p has no multiboot header\n", + image ); + return rc; + } + + /* Abort if we detect tags that we cannot support */ + if ( ( rc = multiboot2_validate_tags ( image, &hdr, &mb_tags ) ) != 0 ) { + DBGC ( image, "MULTIBOOT2 %p contains unsupported tags\n", + image ); + return -ENOTSUP; + } + + /* Attempt to load the image into memory of our choosing */ + if ( ( rc = multiboot2_load ( image, &mb_tags, &load, &entry, &max ) ) != 0) { + DBGC ( image, "MULTIBOOT2 %p could not load\n", image ); + return rc; + } + + if ( ( lz = find_image ( "landing_zone" ) ) != NULL ) { + physaddr_t *args = (physaddr_t *) &mb2_bib; + unregister_image ( image_get ( lz ) ); + + max = ( max + LZ_ALIGN - 1 ) & ~( LZ_ALIGN - 1 ); + + args[0] = entry; + Elf_Ehdr *Ehdr = (Elf_Ehdr *) image->data; + Elf_Phdr *Phdr = (Elf_Phdr *) ( image->data + Ehdr->e_phoff ); + args[1] = Phdr->p_filesz; + lz_set ( lz, ( userptr_t ) &mb2_bib, phys_to_user ( max ), + LZ_PROTO_MULTIBOOT2 ); + /* Doesn't seem that max is used anywhere... Can LZ and kernel be + * overwritten by modules? + */ + max += SLB_SIZE; + } + + /* Populate multiboot information structure */ + offset = 0; + + total_size = (uint32_t *)&mb2_bib.bib[offset]; + offset += sizeof(*total_size); + + reserved = (uint32_t *)&mb2_bib.bib[offset]; + offset += sizeof(*reserved); + + /* Clear out the reserved word */ + *reserved = 0; + + /* Add the load base address tag */ + load_base_addr_tag = (struct multiboot_tag_load_base_addr *)&mb2_bib.bib[offset]; + load_base_addr_tag->type = MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR; + load_base_addr_tag->size = sizeof(*load_base_addr_tag); + load_base_addr_tag->load_base_addr = load; + offset += load_base_addr_tag->size; + offset = adjust_tag_offset(offset); + +#ifdef EFIAPI + /* Add the EFI boot services not terminated tag */ + tag = (struct multiboot_tag *)&mb2_bib.bib[offset]; + tag->type = MULTIBOOT_TAG_TYPE_EFI_BS; + tag->size = sizeof(*tag); + offset += tag->size; + offset = adjust_tag_offset(offset); + + /* Add the EFI 64-bit image handle pointer */ + tag_efi64 = (struct multiboot_tag_efi64 *)&mb2_bib.bib[offset]; + tag_efi64->type = MULTIBOOT_TAG_TYPE_EFI64_IH; + tag_efi64->size = sizeof(*tag_efi64); + tag_efi64->pointer = (multiboot_uint64_t)efi_image_handle; + offset += tag_efi64->size; + offset = adjust_tag_offset(offset); + + /* Add the EFI 64-bit system table handle pointer */ + tag_efi64 = (struct multiboot_tag_efi64 *)&mb2_bib.bib[offset]; + tag_efi64->type = MULTIBOOT_TAG_TYPE_EFI64; + tag_efi64->size = sizeof(*tag_efi64); + tag_efi64->pointer = (multiboot_uint64_t)efi_systab; + offset += tag_efi64->size; + offset = adjust_tag_offset(offset); +#endif + + /* add the boot command line */ + offset += multiboot2_add_cmdline ( image, offset ); + offset = adjust_tag_offset(offset); + + /* add the bootloader */ + offset += multiboot2_add_bootloader ( image, offset ); + offset = adjust_tag_offset(offset); + + /* Add the modules */ + offset = multiboot2_add_modules ( image, offset ); + offset = adjust_tag_offset(offset); + + /* TODO: provide memory information */ + + /* Terminate the tags */ + tag = (struct multiboot_tag *)&mb2_bib.bib[offset]; + tag->type = 0; + tag->size = sizeof(*tag); + offset += tag->size; + + *total_size = offset; + + DBGC ( image, "MULTIBOOT2 %p BIB is %d bytes\n", image, *total_size ); + + /* Multiboot images may not return and have no callback + * interface, so shut everything down prior to booting the OS. + */ + shutdown_boot(); + + if ( lz != NULL ) { + register_image ( lz ); + image_put ( lz ); + + return image_replace ( lz ); + } + + /* Jump to OS with flat physical addressing */ + DBGC ( image, "MULTIBOOT2 %p starting execution at %lx\n", image, entry ); + + multiboot2_boot ( total_size, entry ); + DBGC ( image, "MULTIBOOT2 %p returned\n", image ); + + /* It isn't safe to continue after calling shutdown() */ + while ( 1 ) {} + + return -ECANCELED; /* -EIMPOSSIBLE, anyone? */ +} + +/** + * Probe multiboot2 image + * + * @v image Multiboot file + * @ret rc Return status code + */ +static int multiboot2_probe ( struct image *image ) { + struct multiboot2_header_info hdr; + int rc; + + /* Locate multiboot2 header, if present */ + if ( ( rc = multiboot2_find_header ( image, &hdr ) ) != 0 ) { + DBGC ( image, "MULTIBOOT2 %p has no multiboot2 header\n", + image ); + return rc; + } + DBGC ( image, "MULTIBOOT2 %p found header with architecture %08x and header_length %d\n", + image, hdr.mb.architecture, hdr.mb.header_length ); + return 0; +} + +/** Multiboot image type */ +struct image_type multiboot2_image_type __image_type ( PROBE_MULTIBOOT2 ) = { + .name = "Multiboot 2", + .probe = multiboot2_probe, + .exec = multiboot2_exec, +}; diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 8ae3e23e1c4..ef0ad95fd4d 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -43,6 +43,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_sdi ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000b0000 ) #define ERRFILE_initrd ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000c0000 ) #define ERRFILE_pxe_call ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000d0000 ) +#define ERRFILE_multiboot2 ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000e0000 ) #define ERRFILE_landing_zone ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000f0000 ) #define ERRFILE_undi ( ERRFILE_ARCH | ERRFILE_NET | 0x00000000 ) diff --git a/src/arch/x86/include/landing_zone.h b/src/arch/x86/include/landing_zone.h index f2e7763ea38..fdc19139e6c 100644 --- a/src/arch/x86/include/landing_zone.h +++ b/src/arch/x86/include/landing_zone.h @@ -16,7 +16,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define LZ_ALIGN ( 64 * 1024 ) #define SLB_SIZE ( 64 * 1024 ) -int lz_set_bzimage ( struct image *image, userptr_t zeropage, physaddr_t tgt ); +#define LZ_PROTO_LINUX_BOOT 0 +#define LZ_PROTO_MULTIBOOT2 2 + +int lz_set ( struct image *image, userptr_t zeropage, physaddr_t tgt, int proto ); #endif /* _LANDING_ZONE_H */ diff --git a/src/arch/x86/include/multiboot2.h b/src/arch/x86/include/multiboot2.h new file mode 100644 index 00000000000..2c93a2a6d01 --- /dev/null +++ b/src/arch/x86/include/multiboot2.h @@ -0,0 +1,417 @@ +/* multiboot2.h - Multiboot 2 header file. */ +/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY + * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MULTIBOOT_HEADER +#define MULTIBOOT_HEADER 1 + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 32768 +#define MULTIBOOT_HEADER_ALIGN 8 + +/* The magic field should contain this. */ +#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6 + +/* This should be in %eax. */ +#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289 + +/* Alignment of multiboot modules. */ +#define MULTIBOOT_MOD_ALIGN 0x00001000 + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT_INFO_ALIGN 0x00000008 + +/* Flags set in the 'flags' member of the multiboot header. */ + +#define MULTIBOOT_TAG_ALIGN 8 +#define MULTIBOOT_TAG_TYPE_END 0 +#define MULTIBOOT_TAG_TYPE_CMDLINE 1 +#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2 +#define MULTIBOOT_TAG_TYPE_MODULE 3 +#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4 +#define MULTIBOOT_TAG_TYPE_BOOTDEV 5 +#define MULTIBOOT_TAG_TYPE_MMAP 6 +#define MULTIBOOT_TAG_TYPE_VBE 7 +#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8 +#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9 +#define MULTIBOOT_TAG_TYPE_APM 10 +#define MULTIBOOT_TAG_TYPE_EFI32 11 +#define MULTIBOOT_TAG_TYPE_EFI64 12 +#define MULTIBOOT_TAG_TYPE_SMBIOS 13 +#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14 +#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15 +#define MULTIBOOT_TAG_TYPE_NETWORK 16 +#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17 +#define MULTIBOOT_TAG_TYPE_EFI_BS 18 +#define MULTIBOOT_TAG_TYPE_EFI32_IH 19 +#define MULTIBOOT_TAG_TYPE_EFI64_IH 20 +#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21 + +#define MULTIBOOT_HEADER_TAG_END 0 +#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1 +#define MULTIBOOT_HEADER_TAG_ADDRESS 2 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3 +#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4 +#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5 +#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6 +#define MULTIBOOT_HEADER_TAG_EFI_BS 7 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9 +#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10 + +#define MULTIBOOT_ARCHITECTURE_I386 0 +#define MULTIBOOT_ARCHITECTURE_MIPS32 4 +#define MULTIBOOT_HEADER_TAG_OPTIONAL 1 + +#define MULTIBOOT_LOAD_PREFERENCE_NONE 0 +#define MULTIBOOT_LOAD_PREFERENCE_LOW 1 +#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2 + +#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1 +#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2 + +#ifndef ASM_FILE + +typedef unsigned char multiboot_uint8_t; +typedef unsigned short multiboot_uint16_t; +typedef unsigned int multiboot_uint32_t; +typedef unsigned long long multiboot_uint64_t; + +struct multiboot_header +{ + /* Must be MULTIBOOT_MAGIC - see above. */ + multiboot_uint32_t magic; + + /* ISA */ + multiboot_uint32_t architecture; + + /* Total header length. */ + multiboot_uint32_t header_length; + + /* The above fields plus this one must equal 0 mod 2^32. */ + multiboot_uint32_t checksum; +}; + +struct multiboot_header_tag +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; +}; + +struct multiboot_header_tag_information_request +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t requests[0]; +}; + +struct multiboot_header_tag_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t header_addr; + multiboot_uint32_t load_addr; + multiboot_uint32_t load_end_addr; + multiboot_uint32_t bss_end_addr; +}; + +struct multiboot_header_tag_entry_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t entry_addr; +}; + +struct multiboot_header_tag_console_flags +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t console_flags; +}; + +struct multiboot_header_tag_framebuffer +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; +}; + +struct multiboot_header_tag_module_align +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; +}; + +struct multiboot_header_tag_relocatable +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t min_addr; + multiboot_uint32_t max_addr; + multiboot_uint32_t align; + multiboot_uint32_t preference; +}; + +struct multiboot_color +{ + multiboot_uint8_t red; + multiboot_uint8_t green; + multiboot_uint8_t blue; +}; + +struct multiboot_mmap_entry +{ + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_BADRAM 5 + multiboot_uint32_t type; + multiboot_uint32_t zero; +}; +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_tag +{ + multiboot_uint32_t type; + multiboot_uint32_t size; +}; + +struct multiboot_tag_string +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + char string[0]; +}; + +struct multiboot_tag_module +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + char cmdline[0]; +}; + +struct multiboot_tag_basic_meminfo +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; +}; + +struct multiboot_tag_bootdev +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t biosdev; + multiboot_uint32_t slice; + multiboot_uint32_t part; +}; + +struct multiboot_tag_mmap +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t entry_size; + multiboot_uint32_t entry_version; + struct multiboot_mmap_entry entries[0]; +}; + +struct multiboot_vbe_info_block +{ + multiboot_uint8_t external_specification[512]; +}; + +struct multiboot_vbe_mode_info_block +{ + multiboot_uint8_t external_specification[256]; +}; + +struct multiboot_tag_vbe +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; + + struct multiboot_vbe_info_block vbe_control_info; + struct multiboot_vbe_mode_info_block vbe_mode_info; +}; + +struct multiboot_tag_framebuffer_common +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint64_t framebuffer_addr; + multiboot_uint32_t framebuffer_pitch; + multiboot_uint32_t framebuffer_width; + multiboot_uint32_t framebuffer_height; + multiboot_uint8_t framebuffer_bpp; +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 +#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 +#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 + multiboot_uint8_t framebuffer_type; + multiboot_uint16_t reserved; +}; + +struct multiboot_tag_framebuffer +{ + struct multiboot_tag_framebuffer_common common; + + union + { + struct + { + multiboot_uint16_t framebuffer_palette_num_colors; + struct multiboot_color framebuffer_palette[0]; + }; + struct + { + multiboot_uint8_t framebuffer_red_field_position; + multiboot_uint8_t framebuffer_red_mask_size; + multiboot_uint8_t framebuffer_green_field_position; + multiboot_uint8_t framebuffer_green_mask_size; + multiboot_uint8_t framebuffer_blue_field_position; + multiboot_uint8_t framebuffer_blue_mask_size; + }; + }; +}; + +struct multiboot_tag_elf_sections +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t num; + multiboot_uint32_t entsize; + multiboot_uint32_t shndx; + char sections[0]; +}; + +struct multiboot_tag_apm +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint16_t version; + multiboot_uint16_t cseg; + multiboot_uint32_t offset; + multiboot_uint16_t cseg_16; + multiboot_uint16_t dseg; + multiboot_uint16_t flags; + multiboot_uint16_t cseg_len; + multiboot_uint16_t cseg_16_len; + multiboot_uint16_t dseg_len; +}; + +struct multiboot_tag_efi32 +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t pointer; +}; + +struct multiboot_tag_efi64 +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint64_t pointer; +}; + +struct multiboot_tag_smbios +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t major; + multiboot_uint8_t minor; + multiboot_uint8_t reserved[6]; + multiboot_uint8_t tables[0]; +}; + +struct multiboot_tag_old_acpi +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t rsdp[0]; +}; + +struct multiboot_tag_new_acpi +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t rsdp[0]; +}; + +struct multiboot_tag_network +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t dhcpack[0]; +}; + +struct multiboot_tag_efi_mmap +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t descr_size; + multiboot_uint32_t descr_vers; + multiboot_uint8_t efi_mmap[0]; +}; + +struct multiboot_tag_efi32_ih +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t pointer; +}; + +struct multiboot_tag_efi64_ih +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint64_t pointer; +}; + +struct multiboot_tag_load_base_addr +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t load_base_addr; +}; + +#endif /* ! ASM_FILE */ + +#endif /* ! MULTIBOOT_HEADER */ diff --git a/src/config/config.c b/src/config/config.c index 2ca05dff7d7..3c80363fd3e 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -156,6 +156,9 @@ REQUIRE_OBJECT ( elfboot ); #ifdef IMAGE_MULTIBOOT REQUIRE_OBJECT ( multiboot ); #endif +#ifdef IMAGE_MULTIBOOT2 +REQUIRE_OBJECT ( multiboot2 ); +#endif #ifdef IMAGE_PXE REQUIRE_OBJECT ( pxe_image ); #endif diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 53a7a7b4bd4..59ad7854004 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -28,6 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define IMAGE_EFI /* EFI image support */ #define IMAGE_SCRIPT /* iPXE script image support */ +#define IMAGE_MULTIBOOT2 /* Multiboot2 image support */ #define SANBOOT_PROTO_ISCSI /* iSCSI protocol */ #define SANBOOT_PROTO_AOE /* AoE protocol */ diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h index 21821c95cb0..83d60bbb4d0 100644 --- a/src/config/defaults/pcbios.h +++ b/src/config/defaults/pcbios.h @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define IMAGE_ELF /* ELF image support */ #define IMAGE_MULTIBOOT /* MultiBoot image support */ +#define IMAGE_MULTIBOOT2 /* MultiBoot image support */ #define IMAGE_PXE /* PXE image support */ #define IMAGE_SCRIPT /* iPXE script image support */ #define IMAGE_BZIMAGE /* Linux bzImage image support */ diff --git a/src/image/elf.c b/src/image/elf.c index 5c2f9db255d..f8e3b38e9c4 100644 --- a/src/image/elf.c +++ b/src/image/elf.c @@ -85,7 +85,7 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr, static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr, int ( * process ) ( struct image *image, Elf_Phdr *phdr, physaddr_t dest ), - physaddr_t *entry, physaddr_t *max ) { + physaddr_t *load, physaddr_t *entry, physaddr_t *max ) { physaddr_t dest; physaddr_t end; unsigned long e_offset; @@ -123,6 +123,10 @@ static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr, if ( ( rc = process ( image, phdr, dest ) ) != 0 ) return rc; + /* Set the load address if it hadn't been set yet */ + if ( load && *load == 0 ) + *load = dest; + /* Set execution address, if it lies within this segment */ if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) { *entry = ehdr->e_entry; @@ -154,7 +158,7 @@ static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr, int elf_segments ( struct image *image, Elf_Ehdr *ehdr, int ( * process ) ( struct image *image, Elf_Phdr *phdr, physaddr_t dest ), - physaddr_t *entry, physaddr_t *max ) { + physaddr_t *load, physaddr_t *entry, physaddr_t *max ) { Elf_Phdr phdr; Elf_Off phoff; unsigned int phnum; @@ -166,6 +170,10 @@ int elf_segments ( struct image *image, Elf_Ehdr *ehdr, /* Invalidate entry point */ *entry = 0; + /* Invalidate load address */ + if ( load ) + *load = 0; + /* Read and process ELF program headers */ for ( phoff = ehdr->e_phoff , phnum = ehdr->e_phnum ; phnum ; phoff += ehdr->e_phentsize, phnum-- ) { @@ -176,7 +184,7 @@ int elf_segments ( struct image *image, Elf_Ehdr *ehdr, } copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) ); if ( ( rc = elf_segment ( image, ehdr, &phdr, process, - entry, max ) ) != 0 ) + load, entry, max ) ) != 0 ) return rc; } @@ -198,7 +206,7 @@ int elf_segments ( struct image *image, Elf_Ehdr *ehdr, * @ret max Maximum used address * @ret rc Return status code */ -int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) { +int elf_load ( struct image *image, physaddr_t *load, physaddr_t *entry, physaddr_t *max ) { static const uint8_t e_ident[] = { [EI_MAG0] = ELFMAG0, [EI_MAG1] = ELFMAG1, @@ -219,7 +227,7 @@ int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) { /* Load ELF segments into memory */ if ( ( rc = elf_segments ( image, &ehdr, elf_load_segment, - entry, max ) ) != 0 ) + load, entry, max ) ) != 0 ) return rc; return 0; diff --git a/src/image/segment.c b/src/image/segment.c index 2d0f2f0fc60..875379c1ebb 100644 --- a/src/image/segment.c +++ b/src/image/segment.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +#include #include #include #include @@ -49,6 +50,53 @@ struct errortab segment_errors[] __errortab = { __einfo_errortab ( EINFO_ERANGE_SEGMENT ), }; +#ifdef EFIAPI +/** + * Prepare segment for loading + * + * @v segment Segment start + * @v filesz Size of the "allocated bytes" portion of the segment + * @v memsz Size of the segment + * @ret rc Return status code + */ +int prep_segment ( userptr_t segment, size_t filesz, size_t memsz ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + unsigned int pages; + EFI_PHYSICAL_ADDRESS phys_addr; + physaddr_t start = user_to_phys ( segment, 0 ); + physaddr_t mid = user_to_phys ( segment, filesz ); + physaddr_t end = user_to_phys ( segment, memsz ); + + DBG ( "Preparing segment [%lx,%lx,%lx)\n", start, mid, end ); + + /* Sanity check */ + if ( filesz > memsz ) { + DBG ( "Insane segment [%lx,%lx,%lx)\n", start, mid, end ); + return -EINVAL; + } + + /* Start address of the segment so that we know where to allocate from */ + phys_addr = start; + /* Size of the segment in pages */ + pages = EFI_SIZE_TO_PAGES ( memsz ); + /* Allocate the memory via EFI to ensure its reserved */ + if ( bs->AllocatePages ( AllocateAddress, + EfiLoaderData, + pages, + &phys_addr ) != 0 ) { + /* No suitable memory region found */ + DBG ( "Segment [%lx,%lx,%lx) does not fit into available memory\n", + start, mid, end ); + return -ERANGE_SEGMENT; + } + + assert ( phys_to_user ( phys_addr ) == segment ); + + /* Found valid region: zero bss and return */ + memset_user ( segment, filesz, 0, ( memsz - filesz ) ); + return 0; +} +#else /** * Prepare segment for loading * @@ -93,3 +141,4 @@ int prep_segment ( userptr_t segment, size_t filesz, size_t memsz ) { start, mid, end ); return -ERANGE_SEGMENT; } +#endif /* EFIAPI */ diff --git a/src/include/ipxe/elf.h b/src/include/ipxe/elf.h index 033c3f7a80d..4d511de7cd8 100644 --- a/src/include/ipxe/elf.h +++ b/src/include/ipxe/elf.h @@ -22,7 +22,8 @@ typedef Elf32_Off Elf_Off; extern int elf_segments ( struct image *image, Elf_Ehdr *ehdr, int ( * process ) ( struct image *image, Elf_Phdr *phdr, physaddr_t dest ), - physaddr_t *entry, physaddr_t *max ); -extern int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ); + physaddr_t *load, physaddr_t *entry, physaddr_t *max ); +extern int elf_load ( struct image *image, physaddr_t *load, + physaddr_t *entry, physaddr_t *max ); #endif /* _IPXE_ELF_H */ diff --git a/src/include/ipxe/features.h b/src/include/ipxe/features.h index e86a2d226cf..985c4146a7e 100644 --- a/src/include/ipxe/features.h +++ b/src/include/ipxe/features.h @@ -55,6 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define DHCP_EB_FEATURE_MENU 0x27 /**< Menu support */ #define DHCP_EB_FEATURE_SDI 0x28 /**< SDI image support */ #define DHCP_EB_FEATURE_NFS 0x29 /**< NFS protocol */ +#define DHCP_EB_FEATURE_MULTIBOOT2 0x2a /**< Multiboot2 format */ /** @} */ diff --git a/src/include/ipxe/image.h b/src/include/ipxe/image.h index 2e7eb4cee39..f0bc8c50c06 100644 --- a/src/include/ipxe/image.h +++ b/src/include/ipxe/image.h @@ -115,18 +115,27 @@ struct image_type { struct asn1_cursor **cursor ); }; +/** + * Multiboot2 image probe priority + * + * Multiboot2 images are also valid executables in another format + * (e.g. ELF) AND might also have multiboot headers on them, so we + * must perform the multiboot2 probe first. + */ +#define PROBE_MULTIBOOT2 01 + /** * Multiboot image probe priority * * Multiboot images are also valid executables in another format * (e.g. ELF), so we must perform the multiboot probe first. */ -#define PROBE_MULTIBOOT 01 +#define PROBE_MULTIBOOT 02 /** * Normal image probe priority */ -#define PROBE_NORMAL 02 +#define PROBE_NORMAL 03 /** * PXE image probe priority @@ -134,7 +143,7 @@ struct image_type { * PXE images have no signature checks, so will claim all image files. * They must therefore be tried last in the probe order list. */ -#define PROBE_PXE 03 +#define PROBE_PXE 04 /** Executable image type table */ #define IMAGE_TYPES __table ( struct image_type, "image_types" )