[syslinux:firmware] efi: Reworked EFI boot loader on several fronts.

syslinux-bot for Chandramouli Narayanan chandramouli.narayanan at intel.com
Fri Nov 9 09:06:27 PST 2012


Commit-ID:  df799a583f07369c976a8f9e778c6787d0a1f610
Gitweb:     http://www.syslinux.org/commit/df799a583f07369c976a8f9e778c6787d0a1f610
Author:     Chandramouli Narayanan <chandramouli.narayanan at intel.com>
AuthorDate: Mon, 3 Sep 2012 05:36:32 +0100
Committer:  Matt Fleming <matt.fleming at intel.com>
CommitDate: Wed, 5 Sep 2012 09:22:32 +0100

efi: Reworked EFI boot loader on several fronts.

a) Added EFI timer handling in support of menu subsystem. This enables
the menu system to display and handle timeout properly.

b) Taking the cue from http://git.kernel.org/?p=boot/efilinux/efilinux.git
on the requirement that key kernel data structures need to be capped
at 0x3FFFFFFF, reworked the efi boot loader accordingly. The patch for the
boot loader is an adapted from the recent patches in efilinux as noted
above.

c) Arrow keys (up/down) in the menu system were not working earlier
requiring the user to use CTRL sequence keys instead. Implemented fixes to map
the EFI scan code to appropriate key sequences in order for get_key()
to handle the menu traversal in a user-friendly fashion.

d)efi/keymap.h has been added in support of key scan code mapping.

Signed-off-by: Chandramouli Narayanan <chandramouli.narayanan at intel.com>
Signed-off-by: Matt Fleming <matt.fleming at intel.com>

---
 com32/lib/sys/isatty.c => efi/keymap.h |   67 +++-
 efi/main.c                             |  604 ++++++++++++++++++++++++++------
 mk/efi.mk                              |    3 +-
 3 files changed, 540 insertions(+), 134 deletions(-)

diff --git a/com32/lib/sys/isatty.c b/efi/keymap.h
similarity index 52%
copy from com32/lib/sys/isatty.c
copy to efi/keymap.h
index d80214a..f44a40c 100644
--- a/com32/lib/sys/isatty.c
+++ b/efi/keymap.h
@@ -26,28 +26,57 @@
  * ----------------------------------------------------------------------- */
 
 /*
- * isatty.c
+ * keymap.h
  *
- * Return if this is a tty or not
+ * Map scan codes to key codes that key processing in com32/libutil expects to rely on.
+ * Scan codes that are part of EFI spec but not included in the map are:
+ * 	F13..F24
+ * 	VOLUME UP/DOWN
+ * 	BRIGHTNESS UP/DOWN
+ * 	SUSPEND/HIBERNATE
+ * 	TOGGLE DISPLAY
+ * 	RECOVERY
+ * 	EJECT
  */
 
-#include <errno.h>
-#include <string.h>
-#include <com32.h>
-#include <minmax.h>
-#include <unistd.h>
-#include <klibc/compiler.h>
-#include "file.h"
+#ifndef SCANKEY_MAP
+#define SCANKEY_MAP
 
-int isatty(int fd)
-{
-    struct file_info *fp = &__file_info[fd];
+#include <getkey.h>
 
-    if (fd >= NFILES || !fp->iop) {
-	errno = EBADF;
-	return -1;
-    }
+struct keycode {
+    int code;
+    int seqlen;
+    const unsigned char *seq;
+};
 
-    /* __DEV_TTY == 1 */
-    return (fp->iop->flags & __DEV_TTY);
-}
+#define CODE(x,y) { x, (sizeof y)-1, (const unsigned char *)(y) }
+
+const struct keycode keycodes[] = {
+    /* First, the BIOS combined codes */
+    CODE(KEY_UP, "\0\x48"),
+    CODE(KEY_DOWN, "\0\x50"),
+    CODE(KEY_RIGHT, "\0\x4D"),
+    CODE(KEY_LEFT, "\0\x4B"),
+    CODE(KEY_HOME, "\0\x47"),
+    CODE(KEY_END, "\0\x4F"),
+    CODE(KEY_INSERT, "\0\x52"),
+    CODE(KEY_DELETE, "\0\x53"),
+    CODE(KEY_PGUP, "\0\x49"),
+    CODE(KEY_PGDN, "\0\x51"),
+    CODE(KEY_F1, "\0\x3B"),
+    CODE(KEY_F2, "\0\x3C"),
+    CODE(KEY_F3, "\0\x3D"),
+    CODE(KEY_F4, "\0\x3E"),
+    CODE(KEY_F5, "\0\x3F"),
+    CODE(KEY_F6, "\0\x40"),
+    CODE(KEY_F7, "\0\x41"),
+    CODE(KEY_F8, "\0\x42"),
+    CODE(KEY_F9, "\0\x43"),
+    CODE(KEY_F10, "\0\x44"),
+    CODE(KEY_F11, "\0\x85"),
+    CODE(KEY_F12, "\0\x86"),
+};
+
+#define NCODES ((int)(sizeof keycodes/sizeof(struct keycode)))
+#endif /* SCANKEY_MAP */
diff --git a/efi/main.c b/efi/main.c
index 5241292..4e70cce 100644
--- a/efi/main.c
+++ b/efi/main.c
@@ -6,6 +6,7 @@
 #include <syslinux/linux.h>
 #include <sys/ansi.h>
 
+#include "keymap.h"
 #include "efi.h"
 #include "fio.h"
 
@@ -96,7 +97,7 @@ void pxe_int1a(void)
 uint8_t KeepPXE;
 
 
-volatile uint32_t __ms_timer = 0xdeadbeef;
+volatile uint32_t __ms_timer = 0;
 volatile uint32_t __jiffies = 0;
 
 static void efi_write_char(uint8_t ch, uint8_t attribute)
@@ -237,6 +238,9 @@ void efi_init(void)
 	mem_init();
 }
 
+static int seq_len = 0;
+static char *key_seq = NULL;
+
 char efi_getchar(char *hi)
 {
 	SIMPLE_INPUT_INTERFACE *in = ST->ConIn;
@@ -244,12 +248,33 @@ char efi_getchar(char *hi)
 	EFI_STATUS status;
 	char c;
 
-	WaitForSingleEvent(in->WaitForKey, 0);
+	if (seq_len) {
+		/* We are in the middle of key sequence for the scan code */
+		c = *key_seq++;
+		seq_len--;
+		if (!seq_len) {
+			/* end of key sequene, reset state */
+			seq_len = 0;
+			key_seq = NULL;
+		}
+		return c;
+	}
+	/* Fresh key processing */
 	do {
 		status = uefi_call_wrapper(in->ReadKeyStroke, 2, in, &key);
 	} while (status == EFI_NOT_READY);
 
-	c = (char)key.UnicodeChar;
+	if (!key.ScanCode)
+		return (char)key.UnicodeChar;
+
+	/* We need to generate a key sequence for the scan code */
+	if (key.ScanCode <= NCODES) {
+		key_seq = (char *)keycodes[key.ScanCode-1].seq;
+		seq_len = keycodes[key.ScanCode-1].seqlen;
+		seq_len--;
+		c = *key_seq++;
+	} else c = '\0';
+
 	return c;
 }
 
@@ -258,6 +283,10 @@ int efi_pollchar(void)
 	SIMPLE_INPUT_INTERFACE *in = ST->ConIn;
 	EFI_STATUS status;
 
+	if (seq_len) {
+		/* we are in the middle of a key sequence .. say so */
+		return 1;
+	}
 	status = WaitForSingleEvent(in->WaitForKey, 1);
 	return status != EFI_TIMEOUT;
 }
@@ -299,6 +328,10 @@ struct efi_info {
 #define E820_NVS	4
 #define E820_UNUSABLE	5
 
+#define BOOT_SIGNATURE	0xaa55
+#define SYSLINUX_EFILDR	0x30	/* Is this published value? */
+#define DEFAULT_TIMER_TICK_DURATION 	500000 /* 500000 == 500000 * 100 * 10^-9 == 50 msec */
+#define DEFAULT_MSTIMER_INC		0x32	/* 50 msec */
 struct e820_entry {
 	uint64_t start;
 	uint64_t len;
@@ -315,10 +348,84 @@ struct boot_params {
 	struct e820_entry e820_map[E820MAX];
 } __packed;
 
+/* Allocate boot parameter block aligned to page */
+#define BOOT_PARAM_BLKSIZE	EFI_SIZE_TO_PAGES(sizeof(struct boot_params)) * EFI_PAGE_SIZE
+
+/* Routines in support of efi boot loader were obtained from
+ * http://git.kernel.org/?p=boot/efilinux/efilinux.git:
+ * kernel_jump(), handover_jump(),
+ * emalloc()/efree, alloc_pages/free_pages
+ * allocate_pool()/free_pool()
+ * memory_map()
+ */ 
 #if __SIZEOF_POINTER__ == 4
 #define EFI_LOAD_SIG	"EL32"
+static inline void kernel_jump(EFI_PHYSICAL_ADDRESS kernel_start,
+			       struct boot_params *boot_params)
+{
+	asm volatile ("cli		\n"
+		      "movl %0, %%esi	\n"
+		      "movl %1, %%ecx	\n"
+		      "jmp *%%ecx	\n"
+		      :: "m" (boot_params), "m" (kernel_start));
+}
+
+static inline void handover_jump(EFI_HANDLE image, struct boot_params *bp,
+				 EFI_PHYSICAL_ADDRESS kernel_start)
+{
+	/* handover protocol not implemented yet; the linux header needs to be updated */
+#if 0
+	kernel_start += hdr->handover_offset;
+
+	asm volatile ("cli		\n"
+		      "pushl %0         \n"
+		      "pushl %1         \n"
+		      "pushl %2         \n"
+		      "movl %3, %%ecx	\n"
+		      "jmp *%%ecx	\n"
+		      :: "m" (bp), "m" (ST),
+		         "m" (image), "m" (kernel_start));
+#endif
+}
 #elif __SIZEOF_POINTER__ == 8
 #define EFI_LOAD_SIG	"EL64"
+typedef void(*kernel_func)(void *, struct boot_params *);
+typedef void(*handover_func)(void *, EFI_SYSTEM_TABLE *, struct boot_params *);
+static inline void kernel_jump(EFI_PHYSICAL_ADDRESS kernel_start,
+			       struct boot_params *boot_params)
+{
+	kernel_func kf;
+
+	asm volatile ("cli");
+
+	/* The 64-bit kernel entry is 512 bytes after the start. */
+	kf = (kernel_func)kernel_start + 512;
+
+	/*
+	 * The first parameter is a dummy because the kernel expects
+	 * boot_params in %[re]si.
+	 */
+	kf(NULL, boot_params);
+}
+
+static inline void handover_jump(EFI_HANDLE image, struct boot_params *bp,
+				 EFI_PHYSICAL_ADDRESS kernel_start)
+{
+#if 0
+	/* handover protocol not implemented yet the linux header needs to be updated */
+
+	UINT32 offset = bp->hdr.handover_offset;
+	handover_func hf;
+
+	asm volatile ("cli");
+
+	/* The 64-bit kernel entry is 512 bytes after the start. */
+	kernel_start += 512;
+
+	hf = (handover_func)(kernel_start + offset);
+	hf(image, ST, bp);
+#endif
+}
 #else
 #error "unsupported architecture"
 #endif
@@ -328,7 +435,7 @@ struct dt_desc {
 	uint64_t *base;
 } __packed;
 
-struct dt_desc gdt = { 0x800, 0 };
+struct dt_desc gdt = { 0x800, (uint64_t *)0 };
 struct dt_desc idt = { 0, 0 };
 
 static inline EFI_MEMORY_DESCRIPTOR *
@@ -424,6 +531,41 @@ static void find_addr(EFI_PHYSICAL_ADDRESS *first,
 	FreePool(map);
 }
 
+/**
+ * allocate_pages - Allocate memory pages from the system
+ * @atype: type of allocation to perform
+ * @mtype: type of memory to allocate
+ * @num_pages: number of contiguous 4KB pages to allocate
+ * @memory: used to return the address of allocated pages
+ *
+ * Allocate @num_pages physically contiguous pages from the system
+ * memory and return a pointer to the base of the allocation in
+ * @memory if the allocation succeeds. On success, the firmware memory
+ * map is updated accordingly.
+ *
+ * If @atype is AllocateAddress then, on input, @memory specifies the
+ * address at which to attempt to allocate the memory pages.
+ */
+static inline EFI_STATUS
+allocate_pages(EFI_ALLOCATE_TYPE atype, EFI_MEMORY_TYPE mtype,
+	       UINTN num_pages, EFI_PHYSICAL_ADDRESS *memory)
+{
+	return uefi_call_wrapper(BS->AllocatePages, 4, atype,
+				 mtype, num_pages, memory);
+}
+/**
+ * free_pages - Return memory allocated by allocate_pages() to the firmware
+ * @memory: physical base address of the page range to be freed
+ * @num_pages: number of contiguous 4KB pages to free
+ *
+ * On success, the firmware memory map is updated accordingly.
+ */
+static inline EFI_STATUS
+free_pages(EFI_PHYSICAL_ADDRESS memory, UINTN num_pages)
+{
+	return uefi_call_wrapper(BS->FreePages, 2, memory, num_pages);
+}
+
 static EFI_STATUS allocate_addr(EFI_PHYSICAL_ADDRESS *addr, size_t size)
 {
 	UINTN npages = EFI_SIZE_TO_PAGES(size);
@@ -433,6 +575,35 @@ static EFI_STATUS allocate_addr(EFI_PHYSICAL_ADDRESS *addr, size_t size)
 				   EfiLoaderData, npages,
 				   addr);
 }
+/**
+ * allocate_pool - Allocate pool memory
+ * @type: the type of pool to allocate
+ * @size: number of bytes to allocate from pool of @type
+ * @buffer: used to return the address of allocated memory
+ *
+ * Allocate memory from pool of @type. If the pool needs more memory
+ * pages are allocated from EfiConventionalMemory in order to grow the
+ * pool.
+ *
+ * All allocations are eight-byte aligned.
+ */
+static inline EFI_STATUS
+allocate_pool(EFI_MEMORY_TYPE type, UINTN size, void **buffer)
+{
+	return uefi_call_wrapper(BS->AllocatePool, 3, type, size, buffer);
+}
+
+/**
+ * free_pool - Return pool memory to the system
+ * @buffer: the buffer to free
+ *
+ * Return @buffer to the system. The returned memory is marked as
+ * EfiConventionalMemory.
+ */
+static inline EFI_STATUS free_pool(void *buffer)
+{
+	return uefi_call_wrapper(BS->FreePool, 1, buffer);
+}
 
 static void free_addr(EFI_PHYSICAL_ADDRESS addr, size_t size)
 {
@@ -441,75 +612,320 @@ static void free_addr(EFI_PHYSICAL_ADDRESS addr, size_t size)
 	uefi_call_wrapper(BS->FreePages, 2, addr, npages);
 }
 
+/* cancel the established timer */
+static void cancel_timer(EFI_EVENT ev)
+{
+	uefi_call_wrapper(BS->SetTimer, 3, ev, TimerCancel, 0);
+}
+
+/* Check if timer went off and update default timer counter */
+void timer_handler(EFI_EVENT ev, VOID *ctx)
+{
+	__ms_timer += DEFAULT_MSTIMER_INC;
+	++__jiffies;
+}
+
+/* Setup a default periodic timer */
+static EFI_STATUS setup_default_timer(EFI_EVENT ev)
+{
+	EFI_STATUS efi_status;
+
+	ev = NULL;
+    	efi_status = uefi_call_wrapper( BS->CreateEvent, 5, EVT_TIMER|EVT_NOTIFY_SIGNAL, TPL_NOTIFY, (EFI_EVENT_NOTIFY)timer_handler, NULL, &ev);
+	if (efi_status == EFI_SUCCESS) {
+		efi_status = uefi_call_wrapper(BS->SetTimer, 3, ev, TimerPeriodic, DEFAULT_TIMER_TICK_DURATION); 
+	}
+	return efi_status;
+}
+
+/**
+ * memory_map - Allocate and fill out an array of memory descriptors
+ * @map_buf: buffer containing the memory map
+ * @map_size: size of the buffer containing the memory map
+ * @map_key: key for the current memory map
+ * @desc_size: size of the desc
+ * @desc_version: memory descriptor version
+ *
+ * On success, @map_size contains the size of the memory map pointed
+ * to by @map_buf and @map_key, @desc_size and @desc_version are
+ * updated.
+ */
+EFI_STATUS
+memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, UINTN *map_size,
+	   UINTN *map_key, UINTN *desc_size, UINT32 *desc_version)
+{
+	EFI_STATUS err = EFI_SUCCESS;
+
+	*map_size = sizeof(**map_buf) * 31;
+
+	/*
+	 * Because we're about to allocate memory, we may
+	 * potentially create a new memory descriptor, thereby
+	 * increasing the size of the memory map. So increase
+	 * the buffer size by the size of one memory
+	 * descriptor, just in case.
+	 */
+	*map_size += sizeof(**map_buf);
+
+	err = allocate_pool(EfiLoaderData, *map_size,
+			    (void **)map_buf);
+	if (err == EFI_SUCCESS) {
+		*map_buf = get_memory_map(map_size, map_key, desc_size, desc_version);
+		if (*map_buf == (EFI_MEMORY_DESCRIPTOR *)NULL) {
+			Print(L"Failed to get memory map");
+			err = EFI_OUT_OF_RESOURCES;
+		}
+	} else {
+		Print(L"Failed to allocate pool for memory map");
+	}
+
+	return err;
+}
+/**
+ * emalloc - Allocate memory with a strict alignment requirement
+ * @size: size in bytes of the requested allocation
+ * @align: the required alignment of the allocation
+ * @addr: a pointer to the allocated address on success
+ *
+ * If we cannot satisfy @align we return 0.
+ */
+EFI_STATUS emalloc(UINTN size, UINTN align, EFI_PHYSICAL_ADDRESS *addr)
+{
+	UINTN map_size, map_key, desc_size;
+	EFI_MEMORY_DESCRIPTOR *map_buf;
+	UINTN d, map_end;
+	UINT32 desc_version;
+	EFI_STATUS err;
+	UINTN nr_pages = EFI_SIZE_TO_PAGES(size);
+
+	err = memory_map(&map_buf, &map_size, &map_key,
+			 &desc_size, &desc_version);
+	if (err != EFI_SUCCESS)
+		goto fail;
+
+	d = (UINTN)map_buf;
+	map_end = (UINTN)map_buf + map_size;
+
+	for (; d < map_end; d += desc_size) {
+		EFI_MEMORY_DESCRIPTOR *desc;
+		EFI_PHYSICAL_ADDRESS start, end, aligned;
+
+		desc = (EFI_MEMORY_DESCRIPTOR *)d;
+		if (desc->Type != EfiConventionalMemory)
+			continue;
+
+		if (desc->NumberOfPages < nr_pages)
+			continue;
+
+		start = desc->PhysicalStart;
+		end = start + (desc->NumberOfPages << EFI_PAGE_SHIFT);
+
+		/* Low-memory is super-precious! */
+		if (end <= 1 << 20)
+			continue;
+		if (start < 1 << 20) {
+			size -= (1 << 20) - start;
+			start = (1 << 20);
+		}
+
+		aligned = (start + align -1) & ~(align -1);
+
+		if ((aligned + size) <= end) {
+			err = allocate_pages(AllocateAddress, EfiLoaderData,
+					     nr_pages, &aligned);
+			if (err == EFI_SUCCESS) {
+				*addr = aligned;
+				break;
+			}
+		}
+	}
+
+	if (d == map_end)
+		err = EFI_OUT_OF_RESOURCES;
+
+	free_pool(map_buf);
+fail:
+	return err;
+}
+/**
+ * efree - Return memory allocated with emalloc
+ * @memory: the address of the emalloc() allocation
+ * @size: the size of the allocation
+ */
+void efree(EFI_PHYSICAL_ADDRESS memory, UINTN size)
+{
+	UINTN nr_pages = EFI_SIZE_TO_PAGES(size);
+
+	free_pages(memory, nr_pages);
+}
+
+/* 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)
 {
 	EFI_MEMORY_DESCRIPTOR *map;
-	struct linux_header *hdr;
+	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_STATUS status;
-	EFI_PHYSICAL_ADDRESS first, last;
+	EFI_PHYSICAL_ADDRESS last, addr, pref_address, kernel_start = 0;
+	UINT64 setup_sz, init_size = 0;
 	UINTN nr_entries, key, desc_sz;
 	UINT32 desc_ver;
 	uint32_t e820_type;
 	addr_t irf_size;
 	int i;
+	char *_cmdline = NULL; /* internal, in efi_physical below 0x3FFFFFFF */
 
 	hdr = (struct linux_header *)kernel_buf;
 	bp = (struct boot_params *)hdr;
-
 	/*
 	 * We require a relocatable kernel because we have no control
 	 * over free memory in the memory map.
 	 */
 	if (hdr->version < 0x20a || !hdr->relocatable_kernel) {
-		printf("bzImage version unsupported\n");
+		printf("bzImage version 0x%x unsupported\n", hdr->version);
+		goto bail;
+	}
+	if (hdr->version >= 0x20b) {
+		printf("bzImage version 0x%x handover protocol unimplemented \n", hdr->version);
+		goto bail;
+	}
+
+	/* 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,
+	 *
+	 *	"The kernel command line can be located anywhere
+	 *	between the end of the setup heap and 0xA0000"
+	 */
+	addr = 0xA0000;
+	status = allocate_pages(AllocateMaxAddress, EfiLoaderData,
+			     EFI_SIZE_TO_PAGES(strlen(cmdline) + 1),
+			     &addr);
+	if (status != EFI_SUCCESS) {
+		printf("Failed to allocate memory for kernel command line, bailing out\n");
 		goto bail;
 	}
+	_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.
+		 */
+		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;
 
-	hdr->type_of_loader = 0x30;	/* SYSLINUX unknown module */
-	hdr->cmd_line_ptr = cmdline;
+	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);
 
-	si = &bp->screen_info;
+	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));
 	setup_screen(si);
 
-#if __SIZEOF_POINTER__ == 4
-	gdt.base = (uint16_t *)malloc(gdt.limit);
-#elif __SIZEOF_POINTER__ == 8
-	gdt.base = (uint64_t *)malloc(gdt.limit);
-#else
-#error "unsupported architecture"
-#endif
+	/*
+	 * FIXME: implement handover protocol 
+	 * Use the kernel's EFI boot stub by invoking the handover
+	 * protocol.
+	 */
+	/* 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;
+	}
 	memset(gdt.base, 0x0, gdt.limit);
 
-	first = -1ULL;
-	find_addr(&first, NULL, 0x1000, -1ULL, kernel_size,
-		  hdr->kernel_alignment);
-	if (first != -1ULL) {
-		status = allocate_addr(&first, kernel_size);
-	}
+	/*
+         * 4Gb - (0x100000*0x1000 = 4Gb)
+         * base address=0
+         * code read/exec
+         * granularity=4096, 386 (+5th nibble of limit)
+         */
+        gdt.base[2] = 0x00cf9a000000ffff;
 
-	if (first == -1ULL || status != EFI_SUCCESS) {
-		printf("Failed to allocate memory for kernel\n");
-		goto bail;
-	}
+        /*
+         * 4Gb - (0x100000*0x1000 = 4Gb)
+         * base address=0
+         * data read/write
+         * granularity=4096, 386 (+5th nibble of limit)
+         */
+        gdt.base[3] = 0x00cf92000000ffff;
 
-#if __SIZEOF_POINTER__ == 4
-	hdr->code32_start = (uint32_t)first;
-#elif __SIZEOF_POINTER__ == 8
-	hdr->code32_start = (uint32_t)(uint64_t)first;
-#else
-#error "unsupported architecture"
-#endif
+        /* Task segment value */
+        gdt.base[4] = 0x0080890000000000;
 
-	/* Skip the setup headers and copy the code */
-	kernel_buf += (hdr->setup_sects + 1) * 512;
-	memcpy(hdr->code32_start, kernel_buf, kernel_size);
+	dprintf("efi_boot_linux: setup_sects %d kernel_size %d\n", hdr->setup_sects, kernel_size);
 
 	/*
 	 * Figure out the size of the initramfs, and where to put it.
@@ -528,8 +944,8 @@ int efi_boot_linux(void *kernel_buf, size_t kernel_size,
 			status = allocate_addr(&last, irf_size);
 
 		if (!last || status != EFI_SUCCESS) {
-			printf("Failed to allocate initramfs memory\n");
-			goto free_kernel;
+			printf("Failed to allocate initramfs memory, bailing out\n");
+			goto free_map;
 		}
 
 		hdr->ramdisk_image = (uint32_t)last;
@@ -552,10 +968,10 @@ int efi_boot_linux(void *kernel_buf, size_t kernel_size,
 			}
 
 			if (ip->data_len)
-				memcpy(last, ip->data, ip->data_len);
+				memcpy((void *)(UINTN)last, ip->data, ip->data_len);
 
 			if (len > ip->data_len)
-				memset(last + ip->data_len, 0,
+				memset((void *)(UINTN)(last + ip->data_len), 0,
 				       len - ip->data_len);
 
 			last = next_addr;
@@ -565,42 +981,33 @@ int efi_boot_linux(void *kernel_buf, size_t kernel_size,
 	/* Build efi memory map */
 	map = get_memory_map(&nr_entries, &key, &desc_sz, &desc_ver);
 	if (!map)
-		goto free_irf;
-
-#if __SIZEOF_POINTER__ == 4
-	bp->efi.memmap = map;
-	bp->efi.memmap_size = nr_entries * desc_sz;
+		goto free_map;
 
-	bp->efi.systab = ST;
-	bp->efi.desc_size = desc_sz;
-	bp->efi.desc_version = desc_ver;
-#elif __SIZEOF_POINTER__ == 8
-	bp->efi.memmap = (uint32_t)(unsigned long)map;
-	bp->efi.memmap_hi = ((unsigned long)map) >> 32;
-	bp->efi.memmap_size = nr_entries * desc_sz;
-
-	bp->efi.systab = (uint32_t)(unsigned long)ST;
-	bp->efi.systab_hi = ((unsigned long)ST) >> 32;
-	bp->efi.desc_size = desc_sz;
-	bp->efi.desc_version = desc_ver;
-#else
-#error "unsupported architecture"
+	_bp->efi.memmap = (uint32_t)(uint64_t)map;
+	_bp->efi.memmap_size = nr_entries * desc_sz;
+	_bp->efi.systab = (uint32_t)(uint64_t)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;
 #endif
 
+
 	/*
 	 * Even though 'memmap' contains the memory map we provided
 	 * previously in efi_scan_memory(), we should recalculate the
 	 * 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;
 
@@ -647,73 +1054,30 @@ 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);
 		goto free_map;
 	}
-
-	memcpy(&bp->efi.load_signature, EFI_LOAD_SIG, sizeof(uint32_t));
-
-	/*
-         * 4Gb - (0x100000*0x1000 = 4Gb)
-         * base address=0
-         * code read/exec
-         * granularity=4096, 386 (+5th nibble of limit)
-         */
-        gdt.base[2] = 0x00cf9a000000ffff;
-
-        /*
-         * 4Gb - (0x100000*0x1000 = 4Gb)
-         * base address=0
-         * data read/write
-         * granularity=4096, 386 (+5th nibble of limit)
-         */
-        gdt.base[3] = 0x00cf92000000ffff;
-
-        /* Task segment value */
-        gdt.base[4] = 0x0080890000000000;
+	memcpy(&_bp->efi.load_signature, EFI_LOAD_SIG, sizeof(uint32_t));
 
 	asm volatile ("lidt %0" :: "m" (idt));
 	asm volatile ("lgdt %0" :: "m" (gdt));
 
-#if __SIZEOF_POINTER__ == 4
-	asm volatile ("cli              \n"
-                      "movl %0, %%esi   \n"
-                      "movl %1, %%ecx   \n"
-                      "jmp *%%ecx       \n"
-                      :: "m" (bp), "m" (hdr->code32_start));
-#elif __SIZEOF_POINTER__ == 8
-	{
-		struct {
-			uint32_t  kernel_entry;
-			uint16_t  kernel_cs;
-		} kernel_vector;
-		void *kstart;
-
-		kernel_vector.kernel_entry = hdr->code32_start;
-		kernel_vector.kernel_cs = 0x10;
-		kstart = (VOID *)&kernel_vector;
-		asm volatile ("cli              \n"
-                      "mov %0, %%rsi   \n"
-                      "mov %1, %%rcx   \n"
-                      "ljmp *(%%rcx)       \n"
-                      :: "m" (bp), "m" (kstart));
-	}
-#else
-#error "unsupported architecture"
-#endif
+	kernel_jump(kernel_start, _bp);
+
 	/* NOTREACHED */
 
 free_map:
+	if (_cmdline) efree((EFI_PHYSICAL_ADDRESS)_cmdline, strlen(_cmdline) + 1);
+	if (_bp) efree((EFI_PHYSICAL_ADDRESS)_bp, BOOT_PARAM_BLKSIZE);
+	if (kernel_start) efree(kernel_start, init_size);
 	FreePool(map);
-free_irf:
 	if (irf_size)
 		free_addr(last, irf_size);
-free_kernel:
-	free_addr(first, kernel_size);
 bail:
 	return -1;
 }
@@ -721,6 +1085,8 @@ bail:
 extern struct disk *efi_disk_init(EFI_HANDLE);
 extern void serialcfg(uint16_t *, uint16_t *, uint16_t *);
 
+
+
 struct firmware efi_fw = {
 	.init = efi_init,
 	.scan_memory = efi_scan_memory,
@@ -745,6 +1111,7 @@ char free_high_memory[4096];
 
 extern char __bss_start[];
 extern char __bss_end[];
+
 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *table)
 {
 	EFI_LOADED_IMAGE *info;
@@ -752,6 +1119,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *table)
 	const struct fs_ops *ops[] = { &vfat_fs_ops, NULL };
 	unsigned long len = (unsigned long)__bss_end - (unsigned long)__bss_start;
 	static struct disk_private priv;
+	EFI_EVENT timer_ev;
 
 	memset(__bss_start, 0, len);
 	InitializeLib(image, table);
@@ -772,6 +1140,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *table)
 		Print(L"Failed to locate root device to prep for file operations & ADV initialization\n");
 		goto out;
 	}
+	/* setup timer for boot menu system support */
+	status = setup_default_timer(timer_ev);
+	if (status != EFI_SUCCESS) {
+		printf("Failed to set up EFI timer support, bailing out\n");
+		goto out;
+	}
 
 	/* TODO: once all errors are captured in efi_errno, bail out if necessary */
 
@@ -779,6 +1153,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *table)
 	priv.dev_handle = info->DeviceHandle;
 	fs_init(ops, &priv);
 	load_env32();
+	/* load_env32() failed.. cancel timer and bailout */
+	cancel_timer(timer_ev);
 
 out:
 	return status;
diff --git a/mk/efi.mk b/mk/efi.mk
index ede87d9..c3c4ca4 100644
--- a/mk/efi.mk
+++ b/mk/efi.mk
@@ -33,7 +33,8 @@ CFLAGS = -I$(EFIINC) -I$(EFIINC)/$(EFI_SUBARCH) \
 		-DEFI_FUNCTION_WRAPPER -fPIC -fshort-wchar -ffreestanding \
 		-Wall -I$(com32)/include -I$(com32)/include/sys \
 		-I$(core)/include $(CARCHOPT) \
-		-I$(com32)/lib/ -std=gnu99 -DELF_DEBUG -DSYSLINUX_EFI
+		-I$(com32)/lib/ -I$(com32)/libutil/include -std=gnu99 -DELF_DEBUG -DSYSLINUX_EFI \
+		$(GCCWARN) -D__COM32__
 
 # gnuefi sometimes installs these under a gnuefi/ directory, and sometimes not
 CRT0 := $(shell find $(LIBDIR) -name crt0-efi-$(EFI_SUBARCH).o 2>/dev/null | tail -n1)


More information about the Syslinux-commits mailing list