[SeaBIOS] [PATCH] shadow: set code segment to high rom region when enabling RAM

Evgeny Yakovlev wrfsh at yandex-team.ru
Wed Dec 12 14:45:08 CET 2018


Currently make_bios_writable_intel will call __make_bios_writeable_intel
from high rom memory by manually correcting its offset to make sure that
we safely execute it while overriding memory mapping through PAMs

However we still may call code from low memory, when
__make_bios_writeable_intel itself calls other code without manual
pointer adjustments. Right now it calls pci_config_readl and
pci_config_writel.

Consider this scenario:
0. Linker puts pci_config_writel in F-segment.
1. first pci_config_writel is called to reprogram PAM0-3, which means
remap regions 0xF0000-0xFFFFF and 0xD0000 - 0xC7FFF.
2. second pci_config_writel is called to reprogram PAM4-7 but code in
F-segment is no longer valid, including pci_config_writel.

However we don't crash due to instruction cache being hot between two
calls. Adding manual i-cache flush (by reloading the same CS segment for
example) between two calls finally crashes the firmware.

This change wraps a call to __make_bios_writable_bios by setting code
segment at base 0xFFF00000. This way simple nested function calls work
as long as no data fetches happen during execution through CS segment
(i.e. something like dprintf is still not safe to call).

Signed-off-by: Evgeny Yakovlev <wrfsh at yandex-team.ru>
---
 src/config.h    | 16 ++++++++++------
 src/fw/shadow.c | 37 ++++++++++++++++++++++++++++---------
 src/misc.c      |  8 ++++++++
 3 files changed, 46 insertions(+), 15 deletions(-)

diff --git a/src/config.h b/src/config.h
index 93c8dbc..87c4118 100644
--- a/src/config.h
+++ b/src/config.h
@@ -60,13 +60,17 @@
 #define SEG_BDA      0x0040
 #define SEG_BIOS     0xf000
 
+// On the emulators, the bios at 0xf0000 is also at 0xffff0000
+#define BIOS_SRC_OFFSET 0xfff00000
+
 // Segment definitions in protected mode (see rombios32_gdt in misc.c)
-#define SEG32_MODE32_CS    (1 << 3)
-#define SEG32_MODE32_DS    (2 << 3)
-#define SEG32_MODE16_CS    (3 << 3)
-#define SEG32_MODE16_DS    (4 << 3)
-#define SEG32_MODE16BIG_CS (5 << 3)
-#define SEG32_MODE16BIG_DS (6 << 3)
+#define SEG32_MODE32_CS         (1 << 3)
+#define SEG32_MODE32_DS         (2 << 3)
+#define SEG32_MODE16_CS         (3 << 3)
+#define SEG32_MODE16_DS         (4 << 3)
+#define SEG32_MODE16BIG_CS      (5 << 3)
+#define SEG32_MODE16BIG_DS      (6 << 3)
+#define SEG32_MODE32_HIGH_CS    (7 << 3)
 
 // Debugging levels.  If non-zero and CONFIG_DEBUG_LEVEL is greater
 // than the specified value, then the corresponding irq handler will
diff --git a/src/fw/shadow.c b/src/fw/shadow.c
index 4c627a8..80d0889 100644
--- a/src/fw/shadow.c
+++ b/src/fw/shadow.c
@@ -18,9 +18,6 @@
 #include "util.h" // make_bios_writable
 #include "x86.h" // wbinvd
 
-// On the emulators, the bios at 0xf0000 is also at 0xffff0000
-#define BIOS_SRC_OFFSET 0xfff00000
-
 union pamdata_u {
     u8 data8[8];
     u32 data32[2];
@@ -56,6 +53,30 @@ __make_bios_writable_intel(u16 bdf, u32 pam0)
                , SYMBOL(code32flat_end) - SYMBOL(code32flat_start));
 }
 
+// A wrapper for executing __make_bios_writable_intel in high memory code segment
+static void
+__make_bios_writable_intel_highmem(u16 bdf, u32 pam0)
+{
+    // Save whatever CS segment we've had before entering here
+    // and switch to highmem code
+    u16 prev_cs;
+    __asm__ __volatile__ ("mov  %%cs, %0":"=rm"(prev_cs));
+    __asm__ __volatile__ ("ljmp %0, $__make_bios_writable_intel_highmem__enter\n"
+                          "__make_bios_writable_intel_highmem__enter:\n"
+                          :
+                          : "n"(SEG32_MODE32_HIGH_CS));
+
+    __make_bios_writable_intel(bdf, pam0);
+
+    // Far jump to return to previous segment
+    __asm__ __volatile__ ("push %0\n"
+                          "push $__make_bios_writable_intel_highmem__exit\n"
+                          "retf\n"
+                          "__make_bios_writable_intel_highmem__exit:\n"
+                          :
+                          : "rm"((u32)prev_cs));
+}
+
 static void
 make_bios_writable_intel(u16 bdf, u32 pam0)
 {
@@ -65,13 +86,11 @@ make_bios_writable_intel(u16 bdf, u32 pam0)
         // if ram isn't backing the bios segment when shadowing is
         // disabled, the code itself won't be in memory.  So, run the
         // code from the high-memory flash location.
-        u32 pos = (u32)__make_bios_writable_intel + BIOS_SRC_OFFSET;
-        void (*func)(u16 bdf, u32 pam0) = (void*)pos;
-        func(bdf, pam0);
-        return;
+        __make_bios_writable_intel_highmem(bdf, pam0);
+    } else {
+        // Ram already present - just enable writes
+        __make_bios_writable_intel(bdf, pam0);
     }
-    // Ram already present - just enable writes
-    __make_bios_writable_intel(bdf, pam0);
 }
 
 static void
diff --git a/src/misc.c b/src/misc.c
index b511730..cb931a5 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -160,6 +160,14 @@ u64 rombios32_gdt[] VARFSEG __aligned(8) = {
     GDT_GRANLIMIT(0xffffffff) | GDT_CODE | GDT_BASE(BUILD_BIOS_ADDR),
     // 16 bit data segment base=0 limit=0xffffffff (SEG32_MODE16BIG_DS)
     GDT_GRANLIMIT(0xffffffff) | GDT_DATA,
+
+    //
+    // Following segments are used when enabling shadow memory
+    // where we need to execute code strictly from base 0xFFF00000
+    //
+
+    // 32 bit code segment in high memory
+    GDT_GRANLIMIT(0xfffff) | GDT_CODE | GDT_B | GDT_BASE(BIOS_SRC_OFFSET),
 };
 
 // GDT descriptor
-- 
2.7.4




More information about the SeaBIOS mailing list