bzimage: Hand off initialisation to the EFI boot stub
Linux kernel v3.6-rc1 support an "EFI handover protocol" that allows
most of the initialisation necessary for booting a kernel under EFI to
be performed by a boot stub in the kernel image itself.
Use the boot stub if it is supported by the kernel.
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
diff --git a/loaders/bzimage/bzimage.c b/loaders/bzimage/bzimage.c
index 3b9c560..efdb884 100644
--- a/loaders/bzimage/bzimage.c
+++ b/loaders/bzimage/bzimage.c
@@ -323,6 +323,15 @@
memcpy((char *)boot_params, (char *)buf, 2 * 512);
boot_params->hdr.code32_start = (UINT32)((UINT64)kernel_start);
+ /*
+ * Use the kernel's EFI boot stub by invoking the handover
+ * protocol.
+ */
+ if (buf->hdr.version >= 0x20b) {
+ handover_jump(image, boot_params, kernel_start);
+ goto out;
+ }
+
err = setup_graphics(buf);
if (err != EFI_SUCCESS)
goto out;
diff --git a/loaders/bzimage/bzimage.h b/loaders/bzimage/bzimage.h
index 9d84725..cd11fd3 100644
--- a/loaders/bzimage/bzimage.h
+++ b/loaders/bzimage/bzimage.h
@@ -78,6 +78,7 @@
UINT64 setup_data;
UINT64 pref_address;
UINT32 init_size;
+ UINT32 handover_offset;
} __attribute__((packed));
struct efi_info {
diff --git a/loaders/bzimage/i386.h b/loaders/bzimage/i386.h
index 6f1cf8f..593e2af 100644
--- a/loaders/bzimage/i386.h
+++ b/loaders/bzimage/i386.h
@@ -42,4 +42,19 @@
:: "m" (boot_params), "m" (kernel_start));
}
+static inline void handover_jump(EFI_HANDLE image, struct boot_params *bp,
+ EFI_PHYSICAL_ADDRESS kernel_start)
+{
+ kernel_start += bp->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 /* __I386_H__ */
diff --git a/loaders/bzimage/x86_64.h b/loaders/bzimage/x86_64.h
index a7ba3af..b63710e 100644
--- a/loaders/bzimage/x86_64.h
+++ b/loaders/bzimage/x86_64.h
@@ -33,6 +33,7 @@
#define EFI_LOADER_SIGNATURE "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)
@@ -51,4 +52,19 @@
kf(NULL, boot_params);
}
+static inline void handover_jump(EFI_HANDLE image, struct boot_params *bp,
+ EFI_PHYSICAL_ADDRESS kernel_start)
+{
+ 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 /* __X86_64_H__ */