x86: Move GDT to a safe location in RAM

Currently, the GDT is either located in FLASH or in the non-relocated
U-Boot image in RAM. Both of these locations are unsafe as those
locations can be erased during a U-Boot update. Move the GDT into the
highest available memory location and relocate U-Boot to just below it

Signed-off-by: Graeme Russ <graeme.russ@gmail.com>
diff --git a/arch/i386/cpu/start.S b/arch/i386/cpu/start.S
index 3cea04b..7def8de 100644
--- a/arch/i386/cpu/start.S
+++ b/arch/i386/cpu/start.S
@@ -100,6 +100,53 @@
 	jmp     get_mem_size
 get_mem_size_ret:
 
+	/*
+	 * We are now in 'Flat Protected Mode' and we know how much memory
+	 * the board has. The (temporary) Global Descriptor Table is not
+	 * in a 'Safe' place (it is either in Flash which can be erased or
+	 * reprogrammed or in a fail-safe boot-strap image which could be
+	 * over-written).
+	 *
+	 * Move the final gdt to a safe place (top of RAM) and load it.
+	 * This is not a trivial excercise - the lgdt instruction does not
+	 * have a register operand (memory only) and we may well be
+	 * running from Flash, so self modifying code will not work here.
+	 * To overcome this, we copy a stub into upper memory along with
+	 * the GDT.
+	 */
+
+	/* Reduce upper memory limit by (Stub + GDT Pointer + GDT) */
+	subl	$(end_gdt_setup - start_gdt_setup), %eax
+
+	/* Copy the GDT and Stub */
+	movl	$start_gdt_setup, %esi
+	movl	%eax, %edi
+	movl	$(end_gdt_setup - start_gdt_setup), %ecx
+	shrl	$2, %ecx
+	cld
+	rep	movsl
+
+	/* write the lgdt 'parameter' */
+	subl	$(jmp_instr - start_gdt_setup - 4), %ebp
+	addl	%eax, %ebp
+	movl	$(gdt_ptr - start_gdt_setup), %ebx
+	addl	%eax, %ebx
+	movl	%ebx, (%ebp)
+
+	/* write the gdt address into the pointer */
+	movl	$(gdt_addr - start_gdt_setup), %ebp
+	addl	%eax, %ebp
+	movl	$(gdt - start_gdt_setup), %ebx
+	addl	%eax, %ebx
+	movl	%ebx, (%ebp)
+
+	/* Save the return address */
+	movl	$load_gdt_ret, %ebp
+
+	/* Load the new (safe) Global Descriptor Table */
+	jmp	*%eax
+
+load_gdt_ret:
 	/* Check we have enough memory for stack */
 	movl	$CONFIG_SYS_STACK_SIZE, %ecx
 	cmpl	%ecx, %eax
@@ -174,3 +221,52 @@
 blank_idt_ptr:
 	.word	0		/* limit */
 	.long	0		/* base */
+
+.align 4
+start_gdt_setup:
+	lgdt	gdt_ptr
+jmp_instr:
+	jmp	*%ebp
+
+.align 4
+gdt_ptr:
+	.word	0x30		/* limit (48 bytes = 6 GDT entries) */
+gdt_addr:
+	.long	gdt		/* base */
+
+	/* The GDT table ...
+	 *
+	 *	 Selector	Type
+	 *	 0x00		NULL
+	 *	 0x08		Unused
+	 *	 0x10		32bit code
+	 *	 0x18		32bit data/stack
+	 *	 0x20		16bit code
+	 *	 0x28		16bit data/stack
+	 */
+
+.align 4
+gdt:
+	.word	0, 0, 0, 0	/* NULL  */
+	.word	0, 0, 0, 0	/* unused */
+
+	.word	0xFFFF		/* 4Gb - (0x100000*0x1000 = 4Gb) */
+	.word	0		/* base address = 0 */
+	.word	0x9B00		/* code read/exec */
+	.word	0x00CF		/* granularity = 4096, 386 (+5th nibble of limit) */
+
+	.word	0xFFFF		/* 4Gb - (0x100000*0x1000 = 4Gb) */
+	.word	0x0		/* base address = 0 */
+	.word	0x9300		/* data read/write */
+	.word	0x00CF		/* granularity = 4096, 386 (+5th nibble of limit) */
+
+	.word	0xFFFF		/* 64kb */
+	.word	0		/* base address = 0 */
+	.word	0x9b00		/* data read/write */
+	.word	0x0010		/* granularity = 1  (+5th nibble of limit) */
+
+	.word	0xFFFF		/* 64kb */
+	.word	0		/* base address = 0 */
+	.word	0x9300		/* data read/write */
+	.word	0x0010		/* granularity = 1 (+5th nibble of limit) */
+end_gdt_setup:
diff --git a/arch/i386/cpu/start16.S b/arch/i386/cpu/start16.S
index 5e33aa1..3e8b2cc 100644
--- a/arch/i386/cpu/start16.S
+++ b/arch/i386/cpu/start16.S
@@ -44,7 +44,7 @@
 	movl	%eax, %cr0
 	wbinvd
 
-	/* load the descriptor tables */
+	/* load the temporary Global Descriptor Table */
 o32 cs	lgdt	gdt_ptr
 
 	/* Now, we enter protected mode */
@@ -68,8 +68,13 @@
 	.long	_start		/* offset */
 	.word	0x10		/* segment */
 
+/*
+ * The following Global Descriptor Table is just enough to get us into
+ * 'Flat Protected Mode' - It will be discarded as soon as the final
+ * GDT is setup in a safe location in RAM
+ */
 gdt_ptr:
-	.word	0x30		/* limit (48 bytes = 6 GDT entries) */
+	.word	0x20		/* limit (32 bytes = 4 GDT entries) */
 	.long	BOOT_SEG + gdt	/* base */
 
 	/* The GDT table ...
@@ -79,8 +84,6 @@
 	 *	 0x08		Unused
 	 *	 0x10		32bit code
 	 *	 0x18		32bit data/stack
-	 *	 0x20		16bit code
-	 *	 0x28		16bit data/stack
 	 */
 
 gdt:
@@ -96,13 +99,3 @@
 	.word	0x0		/* base address = 0 */
 	.word	0x9300		/* data read/write */
 	.word	0x00CF		/* granularity = 4096, 386 (+5th nibble of limit) */
-
-	.word	0xFFFF		/* 64kb */
-	.word	0		/* base address = 0 */
-	.word	0x9b00		/* data read/write */
-	.word	0x0010		/* granularity = 1  (+5th nibble of limit) */
-
-	.word	0xFFFF		/* 64kb */
-	.word	0		/* base address = 0 */
-	.word	0x9300		/* data read/write */
-	.word	0x0010		/* granularity = 1 (+5th nibble of limit) */