[syslinux:firmware] efi: refactor into smaller functions

syslinux-bot for Matt Fleming matt.fleming at intel.com
Mon Jul 8 09:30:06 PDT 2013


Commit-ID:  db450870c4b14e7c685ff8cf705ed9a008ea3564
Gitweb:     http://www.syslinux.org/commit/db450870c4b14e7c685ff8cf705ed9a008ea3564
Author:     Matt Fleming <matt.fleming at intel.com>
AuthorDate: Sat, 6 Jul 2013 20:15:45 +0100
Committer:  Matt Fleming <matt.fleming at intel.com>
CommitDate: Mon, 8 Jul 2013 15:49:16 +0100

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 at intel.com>

---
 efi/main.c | 395 +++++++++++++++++++++++++++++++++++--------------------------
 1 file changed, 225 insertions(+), 170 deletions(-)

diff --git a/efi/main.c b/efi/main.c
index 7f45ecc..1f38ef4 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;
 }


More information about the Syslinux-commits mailing list