x86: fsp: Load GDT before calling FspInitEntry
Currently the FSP execution environment GDT is setup by U-Boot in
arch/x86/cpu/start16.S, which works pretty well. But if we try to
move the FspInitEntry call a little bit later to better fit into
U-Boot's initialization sequence, FSP will fail to bring up the AP
due to #GP fault as AP's GDT is duplicated from BSP whose GDT is
now moved into CAR, and unfortunately FSP calls AP initialization
after it disables the CAR. So basically the BSP's GDT still refers
to the one in the CAR, whose content is no longer available, so
when AP starts up and loads its segment register, it blows up.
To resolve this, we load GDT before calling into FspInitEntry.
The GDT is the same one used in arch/x86/cpu/start16.S, which is
in the ROM and exists forever.
Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Tested-by: Andrew Bradford <andrew.bradford@kodakalaris.com>
Tested-by: Simon Glass <sjg@chromium.org>
Acked-by: Simon Glass <sjg@chromium.org>
diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c
index bb4a110..b6c585a 100644
--- a/arch/x86/cpu/cpu.c
+++ b/arch/x86/cpu/cpu.c
@@ -164,6 +164,26 @@
load_fs(X86_GDT_ENTRY_32BIT_FS);
}
+#ifdef CONFIG_HAVE_FSP
+/*
+ * Setup FSP execution environment GDT
+ *
+ * Per Intel FSP external architecture specification, before calling any FSP
+ * APIs, we need make sure the system is in flat 32-bit mode and both the code
+ * and data selectors should have full 4GB access range. Here we reuse the one
+ * we used in arch/x86/cpu/start16.S, and reload the segement registers.
+ */
+void setup_fsp_gdt(void)
+{
+ load_gdt((const u64 *)(gdt_rom + CONFIG_RESET_SEG_START), 4);
+ load_ds(X86_GDT_ENTRY_32BIT_DS);
+ load_ss(X86_GDT_ENTRY_32BIT_DS);
+ load_es(X86_GDT_ENTRY_32BIT_DS);
+ load_fs(X86_GDT_ENTRY_32BIT_DS);
+ load_gs(X86_GDT_ENTRY_32BIT_DS);
+}
+#endif
+
int __weak x86_cleanup_before_linux(void)
{
#ifdef CONFIG_BOOTSTAGE_STASH