diff options
author | Matt Fleming <matt.fleming@intel.com> | 2013-07-06 20:15:45 +0100 |
---|---|---|
committer | Matt Fleming <matt.fleming@intel.com> | 2013-07-08 15:49:16 +0100 |
commit | db450870c4b14e7c685ff8cf705ed9a008ea3564 (patch) | |
tree | 63c95072c8efd8c8694c321c0e508b125bef17e6 | |
parent | f5c665e0acd6953aa3c75864eaefc4b18a6e6694 (diff) | |
download | syslinux-db450870c4b14e7c685ff8cf705ed9a008ea3564.tar.gz syslinux-db450870c4b14e7c685ff8cf705ed9a008ea3564.tar.xz syslinux-db450870c4b14e7c685ff8cf705ed9a008ea3564.zip |
efi: refactor into smaller functions
efi_main() is pretty large and could definitely be made clearer by
moving various chunks into their own functions.
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
-rw-r--r-- | efi/main.c | 395 |
1 files changed, 225 insertions, 170 deletions
@@ -790,66 +790,12 @@ static void handover_boot(struct linux_header *hdr, struct boot_params *bp) func(image_handle, ST, bp, address); } -/* efi_boot_linux: - * Boots the linux kernel using the image and parameters to boot with. - * The EFI boot loader is reworked taking the cue from - * http://git.kernel.org/?p=boot/efilinux/efilinux.git on the need to - * cap key kernel data structures at * 0x3FFFFFFF. - * The kernel image, kernel command line and boot parameter block are copied - * into allocated memory areas that honor the address capping requirement - * prior to kernel handoff. - * - * FIXME - * Can we move this allocation requirement to com32 linux loader in order - * to avoid double copying kernel image? - */ -int efi_boot_linux(void *kernel_buf, size_t kernel_size, - struct initramfs *initramfs, - struct setup_data *setup_data, - char *cmdline) +static char *build_cmdline(char *str) { - EFI_MEMORY_DESCRIPTOR *map; - struct linux_header *hdr, *bhdr; - struct boot_params *bp; - struct boot_params *_bp; /* internal, in efi_physical below 0x3FFFFFFF */ - struct screen_info *si; - struct e820_entry *e820buf, *e; + EFI_PHYSICAL_ADDRESS addr; EFI_STATUS status; - EFI_PHYSICAL_ADDRESS last, addr, pref_address, kernel_start = 0; - UINT64 setup_sz, init_size = 0; - UINTN i, nr_entries, key, desc_sz; - UINT32 desc_ver; - uint32_t e820_type; - addr_t irf_size; - char *_cmdline = NULL; /* internal, in efi_physical below 0x3FFFFFFF */ - - hdr = (struct linux_header *)kernel_buf; - bp = (struct boot_params *)hdr; + char *cmdline = NULL; /* internal, in efi_physical below 0x3FFFFFFF */ - if (hdr->version < 0x205) - hdr->relocatable_kernel = 0; - - /* FIXME: check boot sector signature */ - if (hdr->boot_flag != BOOT_SIGNATURE) { - printf("Invalid Boot signature 0x%x, bailing out\n", hdr->boot_flag); - goto bail; - } - - setup_sz = (hdr->setup_sects + 1) * 512; - if (hdr->version >= 0x20a) { - pref_address = hdr->pref_address; - init_size = hdr->init_size; - } else { - pref_address = 0x100000; - - /* - * We need to account for the fact that the kernel - * needs room for decompression, otherwise we could - * end up trashing other chunks of allocated memory. - */ - init_size = (kernel_size - setup_sz) * 3; - } - hdr->type_of_loader = SYSLINUX_EFILDR; /* SYSLINUX boot loader module */ /* * The kernel expects cmdline to be allocated pretty low, * Documentation/x86/boot.txt says, @@ -859,76 +805,26 @@ int efi_boot_linux(void *kernel_buf, size_t kernel_size, */ addr = 0xA0000; status = allocate_pages(AllocateMaxAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(strlen(cmdline) + 1), + EFI_SIZE_TO_PAGES(strlen(str) + 1), &addr); if (status != EFI_SUCCESS) { printf("Failed to allocate memory for kernel command line, bailing out\n"); - goto bail; + return NULL; } - _cmdline = (char *)(UINTN)addr; - memcpy(_cmdline, cmdline, strlen(cmdline) + 1); - hdr->cmd_line_ptr = (UINT32)(UINTN)_cmdline; - memset((char *)&bp->screen_info, 0x0, sizeof(bp->screen_info)); - - addr = pref_address; - status = allocate_pages(AllocateAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(init_size), &addr); - if (status != EFI_SUCCESS) { - /* - * We failed to allocate the preferred address, so - * just allocate some memory and hope for the best. - */ - if (!hdr->relocatable_kernel) { - printf("Cannot relocate kernel, bailing out\n"); - goto bail; - } - - status = emalloc(init_size, hdr->kernel_alignment, &addr); - if (status != EFI_SUCCESS) { - printf("Failed to allocate memory for kernel image, bailing out\n"); - goto free_map; - } - } - kernel_start = addr; - /* FIXME: we copy the kernel into the physical memory allocated here - * The syslinux kernel image load elsewhere could allocate the EFI memory from here - * prior to copying kernel and save an extra copy - */ - memcpy((void *)(UINTN)kernel_start, kernel_buf+setup_sz, kernel_size-setup_sz); - - /* allocate for boot parameter block */ - addr = 0x3FFFFFFF; - status = allocate_pages(AllocateMaxAddress, EfiLoaderData, - BOOT_PARAM_BLKSIZE, &addr); - if (status != EFI_SUCCESS) { - printf("Failed to allocate memory for kernel boot parameter block, bailing out\n"); - goto free_map; - } - - _bp = (struct boot_params *)(UINTN)addr; - - memset((void *)_bp, 0x0, BOOT_PARAM_BLKSIZE); - /* Copy the first two sectors to boot_params */ - memcpy((char *)_bp, kernel_buf, 2 * 512); - bhdr = (struct linux_header *)_bp; - bhdr->code32_start = (UINT32)((UINT64)kernel_start); - - dprintf("efi_boot_linux: kernel_start 0x%x kernel_size 0x%x initramfs 0x%x setup_data 0x%x cmdline 0x%x\n", - kernel_start, kernel_size, initramfs, setup_data, _cmdline); - si = &_bp->screen_info; - memset(si, 0, sizeof(*si)); - - /* Attempt to use the handover protocol if available */ - if (hdr->version >= 0x20b && hdr->handover_offset) - handover_boot(bhdr, _bp); + cmdline = (char *)(UINTN)addr; + memcpy(cmdline, str, strlen(str) + 1); + return cmdline; +} - setup_screen(si); +static int build_gdt(void) +{ + EFI_STATUS status; /* Allocate gdt consistent with the alignment for architecture */ status = emalloc(gdt.limit, __SIZEOF_POINTER__ , (EFI_PHYSICAL_ADDRESS *)&gdt.base); if (status != EFI_SUCCESS) { printf("Failed to allocate memory for GDT, bailing out\n"); - goto free_map; + return -1; } memset(gdt.base, 0x0, gdt.limit); @@ -951,7 +847,26 @@ int efi_boot_linux(void *kernel_buf, size_t kernel_size, /* Task segment value */ gdt.base[4] = 0x0080890000000000; - dprintf("efi_boot_linux: setup_sects %d kernel_size %d\n", hdr->setup_sects, kernel_size); + return 0; +} + +/* + * Callers use ->ramdisk_size to check whether any memory was + * allocated (and therefore needs free'ing). The return value indicates + * hard error conditions, such as failing to alloc memory for the + * ramdisk image. Having no initramfs is not an error. + */ +static int handle_ramdisks(struct linux_header *hdr, + struct initramfs *initramfs) +{ + EFI_PHYSICAL_ADDRESS last; + struct initramfs *ip; + EFI_STATUS status; + addr_t irf_size; + addr_t next_addr, len, pad; + + hdr->ramdisk_image = 0; + hdr->ramdisk_size = 0; /* * Figure out the size of the initramfs, and where to put it. @@ -959,64 +874,73 @@ int efi_boot_linux(void *kernel_buf, size_t kernel_size, * <= hdr->initrd_addr_max, which fits the entire initramfs. */ irf_size = initramfs_size(initramfs); /* Handles initramfs == NULL */ - if (irf_size) { - struct initramfs *ip; - addr_t next_addr, len, pad; - - last = 0; - find_addr(NULL, &last, 0x1000, hdr->initrd_addr_max, - irf_size, INITRAMFS_MAX_ALIGN); - if (last) - status = allocate_addr(&last, irf_size); - - if (!last || status != EFI_SUCCESS) { - printf("Failed to allocate initramfs memory, bailing out\n"); - goto free_map; - } + if (!irf_size) + return 0; - bhdr->ramdisk_image = (uint32_t)last; - bhdr->ramdisk_size = irf_size; - - /* Copy initramfs into allocated memory */ - for (ip = initramfs->next; ip->len; ip = ip->next) { - len = ip->len; - next_addr = last + len; - - /* - * If this isn't the last entry, extend the - * zero-pad region to enforce the alignment of - * the next chunk. - */ - if (ip->next->len) { - pad = -next_addr & (ip->next->align - 1); - len += pad; - next_addr += pad; - } + last = 0; + find_addr(NULL, &last, 0x1000, hdr->initrd_addr_max, + irf_size, INITRAMFS_MAX_ALIGN); + if (last) + status = allocate_addr(&last, irf_size); - if (ip->data_len) - memcpy((void *)(UINTN)last, ip->data, ip->data_len); + if (!last || status != EFI_SUCCESS) { + printf("Failed to allocate initramfs memory, bailing out\n"); + return -1; + } - if (len > ip->data_len) - memset((void *)(UINTN)(last + ip->data_len), 0, - len - ip->data_len); + hdr->ramdisk_image = (uint32_t)last; + hdr->ramdisk_size = irf_size; - last = next_addr; + /* Copy initramfs into allocated memory */ + for (ip = initramfs->next; ip->len; ip = ip->next) { + len = ip->len; + next_addr = last + len; + + /* + * If this isn't the last entry, extend the + * zero-pad region to enforce the alignment of + * the next chunk. + */ + if (ip->next->len) { + pad = -next_addr & (ip->next->align - 1); + len += pad; + next_addr += pad; } + + if (ip->data_len) + memcpy((void *)(UINTN)last, ip->data, ip->data_len); + + if (len > ip->data_len) + memset((void *)(UINTN)(last + ip->data_len), 0, + len - ip->data_len); + + last = next_addr; } + return 0; +} + +static int exit_boot(struct boot_params *bp) +{ + struct e820_entry *e820buf, *e; + EFI_MEMORY_DESCRIPTOR *map; + EFI_STATUS status; + uint32_t e820_type; + UINTN i, nr_entries, key, desc_sz; + UINT32 desc_ver; /* Build efi memory map */ map = get_memory_map(&nr_entries, &key, &desc_sz, &desc_ver); if (!map) - goto free_map; + return -1; - _bp->efi.memmap = (uint32_t)(unsigned long)map; - _bp->efi.memmap_size = nr_entries * desc_sz; - _bp->efi.systab = (uint32_t)(unsigned long)ST; - _bp->efi.desc_size = desc_sz; - _bp->efi.desc_version = desc_ver; + bp->efi.memmap = (uint32_t)(unsigned long)map; + bp->efi.memmap_size = nr_entries * desc_sz; + bp->efi.systab = (uint32_t)(unsigned long)ST; + bp->efi.desc_size = desc_sz; + bp->efi.desc_version = desc_ver; #if defined(__x86_64__) - _bp->efi.systab_hi = ((unsigned long)ST) >> 32; - _bp->efi.memmap_hi = ((unsigned long)map) >> 32; + bp->efi.systab_hi = ((unsigned long)ST) >> 32; + bp->efi.memmap_hi = ((unsigned long)map) >> 32; #endif @@ -1026,14 +950,14 @@ int efi_boot_linux(void *kernel_buf, size_t kernel_size, * e820 map because it will most likely have changed in the * interim. */ - e = e820buf = _bp->e820_map; + e = e820buf = bp->e820_map; for (i = 0; i < nr_entries && i < E820MAX; i++) { struct e820_entry *prev = NULL; if (e > e820buf) prev = e - 1; - map = get_mem_desc(_bp->efi.memmap, desc_sz, i); + map = get_mem_desc(bp->efi.memmap, desc_sz, i); e->start = map->PhysicalStart; e->len = map->NumberOfPages << EFI_PAGE_SHIFT; @@ -1080,14 +1004,146 @@ int efi_boot_linux(void *kernel_buf, size_t kernel_size, e++; } - _bp->e820_entries = e - e820buf; + bp->e820_entries = e - e820buf; dprintf("efi_boot_linux: exit boot services\n"); status = uefi_call_wrapper(BS->ExitBootServices, 2, image_handle, key); if (status != EFI_SUCCESS) { printf("Failed to exit boot services: 0x%016lx\n", status); + FreePool(map); + return -1; + } + + return 0; +} + +/* efi_boot_linux: + * Boots the linux kernel using the image and parameters to boot with. + * The EFI boot loader is reworked taking the cue from + * http://git.kernel.org/?p=boot/efilinux/efilinux.git on the need to + * cap key kernel data structures at * 0x3FFFFFFF. + * The kernel image, kernel command line and boot parameter block are copied + * into allocated memory areas that honor the address capping requirement + * prior to kernel handoff. + * + * FIXME + * Can we move this allocation requirement to com32 linux loader in order + * to avoid double copying kernel image? + */ +int efi_boot_linux(void *kernel_buf, size_t kernel_size, + struct initramfs *initramfs, + struct setup_data *setup_data, + char *cmdline) +{ + struct linux_header *hdr, *bhdr; + struct boot_params *bp; + struct boot_params *_bp; /* internal, in efi_physical below 0x3FFFFFFF */ + struct screen_info *si; + EFI_STATUS status; + EFI_PHYSICAL_ADDRESS addr, pref_address, kernel_start = 0; + UINT64 setup_sz, init_size = 0; + char *_cmdline; + + hdr = (struct linux_header *)kernel_buf; + bp = (struct boot_params *)hdr; + + if (hdr->version < 0x205) + hdr->relocatable_kernel = 0; + + /* FIXME: check boot sector signature */ + if (hdr->boot_flag != BOOT_SIGNATURE) { + printf("Invalid Boot signature 0x%x, bailing out\n", hdr->boot_flag); + goto bail; + } + + setup_sz = (hdr->setup_sects + 1) * 512; + if (hdr->version >= 0x20a) { + pref_address = hdr->pref_address; + init_size = hdr->init_size; + } else { + pref_address = 0x100000; + + /* + * We need to account for the fact that the kernel + * needs room for decompression, otherwise we could + * end up trashing other chunks of allocated memory. + */ + init_size = (kernel_size - setup_sz) * 3; + } + hdr->type_of_loader = SYSLINUX_EFILDR; /* SYSLINUX boot loader module */ + _cmdline = build_cmdline(cmdline); + if (!_cmdline) + goto bail; + + hdr->cmd_line_ptr = (UINT32)(UINTN)_cmdline; + + memset((char *)&bp->screen_info, 0x0, sizeof(bp->screen_info)); + + addr = pref_address; + status = allocate_pages(AllocateAddress, EfiLoaderData, + EFI_SIZE_TO_PAGES(init_size), &addr); + if (status != EFI_SUCCESS) { + /* + * We failed to allocate the preferred address, so + * just allocate some memory and hope for the best. + */ + if (!hdr->relocatable_kernel) { + printf("Cannot relocate kernel, bailing out\n"); + goto bail; + } + + status = emalloc(init_size, hdr->kernel_alignment, &addr); + if (status != EFI_SUCCESS) { + printf("Failed to allocate memory for kernel image, bailing out\n"); + goto free_map; + } + } + kernel_start = addr; + /* FIXME: we copy the kernel into the physical memory allocated here + * The syslinux kernel image load elsewhere could allocate the EFI memory from here + * prior to copying kernel and save an extra copy + */ + memcpy((void *)(UINTN)kernel_start, kernel_buf+setup_sz, kernel_size-setup_sz); + + /* allocate for boot parameter block */ + addr = 0x3FFFFFFF; + status = allocate_pages(AllocateMaxAddress, EfiLoaderData, + BOOT_PARAM_BLKSIZE, &addr); + if (status != EFI_SUCCESS) { + printf("Failed to allocate memory for kernel boot parameter block, bailing out\n"); goto free_map; } + + _bp = (struct boot_params *)(UINTN)addr; + + memset((void *)_bp, 0x0, BOOT_PARAM_BLKSIZE); + /* Copy the first two sectors to boot_params */ + memcpy((char *)_bp, kernel_buf, 2 * 512); + bhdr = (struct linux_header *)_bp; + bhdr->code32_start = (UINT32)((UINT64)kernel_start); + + dprintf("efi_boot_linux: kernel_start 0x%x kernel_size 0x%x initramfs 0x%x setup_data 0x%x cmdline 0x%x\n", + kernel_start, kernel_size, initramfs, setup_data, _cmdline); + si = &_bp->screen_info; + memset(si, 0, sizeof(*si)); + + /* Attempt to use the handover protocol if available */ + if (hdr->version >= 0x20b && hdr->handover_offset) + handover_boot(bhdr, _bp); + + setup_screen(si); + + if (build_gdt()) + goto free_map; + + dprintf("efi_boot_linux: setup_sects %d kernel_size %d\n", hdr->setup_sects, kernel_size); + + if (handle_ramdisks(bhdr, initramfs)) + goto free_map; + + if (exit_boot(_bp)) + goto free_map; + memcpy(&_bp->efi.load_signature, EFI_LOAD_SIG, sizeof(uint32_t)); asm volatile ("lidt %0" :: "m" (idt)); @@ -1106,9 +1162,8 @@ free_map: efree((EFI_PHYSICAL_ADDRESS)(unsigned long)_bp, BOOT_PARAM_BLKSIZE); if (kernel_start) efree(kernel_start, init_size); - FreePool(map); - if (irf_size) - free_addr(last, irf_size); + if (bhdr->ramdisk_size) + free_addr(bhdr->ramdisk_image, bhdr->ramdisk_size); bail: return -1; } |