aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2013-07-06 20:15:45 +0100
committerMatt Fleming <matt.fleming@intel.com>2013-07-08 15:49:16 +0100
commitdb450870c4b14e7c685ff8cf705ed9a008ea3564 (patch)
tree63c95072c8efd8c8694c321c0e508b125bef17e6
parentf5c665e0acd6953aa3c75864eaefc4b18a6e6694 (diff)
downloadsyslinux-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.c395
1 files changed, 225 insertions, 170 deletions
diff --git a/efi/main.c b/efi/main.c
index 7f45ecc2..1f38ef4f 100644
--- a/efi/main.c
+++ b/efi/main.c
@@ -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;
}