x86: Add support for starting 64-bit kernel

Add code to jump to a 64-bit Linux kernel. We need to set up a flat page
table structure, a new GDT and then go through a few hoops in the right
order.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/arch/x86/cpu/call64.S b/arch/x86/cpu/call64.S
new file mode 100644
index 0000000..74dd5a8
--- /dev/null
+++ b/arch/x86/cpu/call64.S
@@ -0,0 +1,93 @@
+/*
+ * (C) Copyright 2014 Google, Inc
+ * Copyright (C) 1991, 1992, 1993  Linus Torvalds
+ *
+ * Parts of this copied from Linux arch/x86/boot/compressed/head_64.S
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <asm/global_data.h>
+#include <asm/msr-index.h>
+#include <asm/processor-flags.h>
+
+.code32
+.globl cpu_call64
+cpu_call64:
+	/*
+	 * cpu_call64(ulong pgtable, ulong setup_base, ulong target)
+	 *
+	 * eax - pgtable
+	 * edx - setup_base
+	 * ecx - target
+	 */
+	cli
+	push	%ecx		/* arg2 = target */
+	push	%edx		/* arg1 = setup_base */
+	mov	%eax, %ebx
+
+	/* Load new GDT with the 64bit segments using 32bit descriptor */
+	leal	gdt, %eax
+	movl	%eax, gdt+2
+	lgdt	gdt
+
+	/* Enable PAE mode */
+	movl	$(X86_CR4_PAE), %eax
+	movl	%eax, %cr4
+
+	/* Enable the boot page tables */
+	leal	(%ebx), %eax
+	movl	%eax, %cr3
+
+	/* Enable Long mode in EFER (Extended Feature Enable Register) */
+	movl	$MSR_EFER, %ecx
+	rdmsr
+	btsl	$_EFER_LME, %eax
+	wrmsr
+
+	/* After gdt is loaded */
+	xorl	%eax, %eax
+	lldt	%ax
+	movl    $0x20, %eax
+	ltr	%ax
+
+	/*
+	 * Setup for the jump to 64bit mode
+	 *
+	 * When the jump is performed we will be in long mode but
+	 * in 32bit compatibility mode with EFER.LME = 1, CS.L = 0, CS.D = 1
+	 * (and in turn EFER.LMA = 1). To jump into 64bit mode we use
+	 * the new gdt/idt that has __KERNEL_CS with CS.L = 1.
+	 * We place all of the values on our mini stack so lret can
+	 * used to perform that far jump. See the gdt below.
+	 */
+	pop	%esi			/* setup_base */
+
+	pushl	$0x10
+	leal	lret_target, %eax
+	pushl	%eax
+
+	/* Enter paged protected Mode, activating Long Mode */
+	movl	$(X86_CR0_PG | X86_CR0_PE), %eax
+	movl	%eax, %cr0
+
+	/* Jump from 32bit compatibility mode into 64bit mode. */
+	lret
+
+code64:
+lret_target:
+	pop	%eax			/* target */
+	mov	%eax, %eax		/* Clear bits 63:32 */
+	jmp	*%eax			/* Jump to the 64-bit target */
+
+	.data
+gdt:
+	.word	gdt_end - gdt
+	.long	gdt
+	.word	0
+	.quad	0x0000000000000000	/* NULL descriptor */
+	.quad	0x00af9a000000ffff	/* __KERNEL_CS */
+	.quad	0x00cf92000000ffff	/* __KERNEL_DS */
+	.quad	0x0080890000000000	/* TS descriptor */
+	.quad   0x0000000000000000	/* TS continued */
+gdt_end: