[SeaBIOS] [PATCH 14/15] Experimental support for PAE paging

Kevin O'Connor kevin at koconnor.net
Thu Oct 1 04:04:17 CET 2015


Signed-off-by: Kevin O'Connor <kevin at koconnor.net>
---
 src/Kconfig     |   7 +++
 src/malloc.c    |   3 ++
 src/memmap.c    | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/memmap.h    |  17 ++++++--
 src/romlayout.S |  35 ++++++++++++---
 src/stacks.c    |  15 ++++++-
 src/x86.h       |  23 ++++++++++
 7 files changed, 217 insertions(+), 12 deletions(-)

diff --git a/src/Kconfig b/src/Kconfig
index b873cd3..c28f671 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -122,6 +122,13 @@ endchoice
             selected, the memory is instead allocated from the
             "9-segment" (0x90000-0xa0000).
 
+    config PAGING
+        bool "PAE paging support"
+        default y
+        help
+            Enable paging in 32bit mode on processors that support PAE
+            paging.
+
     config ROM_SIZE
         int "ROM size (in KB)"
         default 0
diff --git a/src/malloc.c b/src/malloc.c
index ec7a21f..c6b460e 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -418,6 +418,9 @@ malloc_preinit(void)
     // Mark known areas as reserved.
     e820_add(BUILD_BIOS_ADDR, BUILD_BIOS_SIZE, E820_RESERVED);
 
+    char memmap_early_data[MEMMAP_EARLY_DATA_SIZE];
+    memmap_early_preinit(memmap_early_data);
+
     // Populate temp high ram
     u32 highram = 0;
     int i;
diff --git a/src/memmap.c b/src/memmap.c
index b08313a..4e83112 100644
--- a/src/memmap.c
+++ b/src/memmap.c
@@ -64,8 +64,8 @@ handle_pf(struct x86_exception_stack *data)
 }
 
 // Setup a 32bit General Protection (GP) and Page Fault (PF) handler
-void
-memmap_preinit(void)
+static void
+idt_preinit(void)
 {
     u32 ilen = sizeof(u64)*(IDT_VECTOR_PF+1);
     u64 *pidt = malloc_high(ilen);
@@ -85,3 +85,128 @@ memmap_preinit(void)
     dprintf(1, "Installing pmode exception handler\n");
     lidt(&pmode_IDT_info);
 }
+
+
+/****************************************************************
+ * PAE based paging
+ ****************************************************************/
+
+u32 PageDirPtr VARFSEG;
+u64 *PageDir;
+
+#define PAE_MAP_SIZE (2*1024*1024)
+#define PAE_MAP_COUNT 2048
+
+#define PG_P  (1<<0)
+#define PG_RW (1<<1)
+#define PG_A  (1<<5)
+#define PG_D  (1<<6)
+#define PG_PS (1<<7)
+#define PG_FLAGS (PG_P | PG_RW | PG_A | PG_D | PG_PS)
+
+u64
+memmap_virt_to_phys(void *v)
+{
+    if (!CONFIG_PAGING || !PageDirPtr)
+        return (u32)v;
+    int idx = (u32)v / PAE_MAP_SIZE;
+    int rem = (u32)v % PAE_MAP_SIZE;
+    return (PageDir[idx] & ~(PAE_MAP_SIZE-1)) + rem;
+}
+
+void *
+memmap_vmap(u64 addr, u32 len)
+{
+    if (!CONFIG_PAGING || !PageDirPtr)
+        return (void*)(u32)addr;
+    u64 newmap = (addr & ~(PAE_MAP_SIZE-1)) | PG_FLAGS;
+    u32 rem = addr % PAE_MAP_SIZE;
+    u64 key = addr / PAE_MAP_SIZE;
+    int pages = DIV_ROUND_UP(rem+len, PAE_MAP_SIZE);
+    int max;
+    for (max=64; max; max--, key*=33) {
+        u32 pos = key % PAE_MAP_COUNT;
+        if (pos + pages > PAE_MAP_COUNT)
+            continue;
+        int j;
+        for (j=0; j<pages; j++)
+            if (PageDir[pos+j] && PageDir[pos+j] != newmap + j*PAE_MAP_SIZE)
+                break;
+        if (j != pages)
+            continue;
+        for (j=0; j<pages; j++)
+            if (!PageDir[pos+j])
+                PageDir[pos+j] = newmap + j*PAE_MAP_SIZE;
+        return (void*)(pos*PAE_MAP_SIZE + rem);
+    }
+    // It's not worth updating all callers to handle a failed mapping
+    panic("Unable to map address %llx with size %d\n", addr, len);
+}
+
+// Detect PAE support and enable it if present
+void
+memmap_early_preinit(char *data)
+{
+    if (!CONFIG_PAGING)
+        return;
+    // Check if CPU supports PAE
+    u32 eax, ebx, ecx, edx, cpuid_features = 0;
+    cpuid(0, &eax, &ebx, &ecx, &edx);
+    if (!eax)
+        return;
+    cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
+    if (!(cpuid_features & (1<<6)))
+        return;
+
+    // Create page table in temporary ram
+    dprintf(1, "PAE support found\n");
+    char *aligned_data = (void*)ALIGN((u32)data, PAGE_SIZE);
+    u64 *dir = (void*)aligned_data;
+    u64 *pdp = (void*)aligned_data + sizeof(u64) * PAE_MAP_COUNT;
+    if (!pdp || !dir) {
+        warn_noalloc();
+        free(pdp);
+        free(dir);
+        return;
+    }
+    memset(dir, 0, sizeof(u64) * PAE_MAP_COUNT);
+    dir[0] = 0x0 | PG_FLAGS;
+    int i;
+    for (i=0; i<4; i++)
+        pdp[i] = ((u32)dir + i*4096) | PG_P;
+
+    // Enable PAE
+    PageDirPtr = (u32)pdp;
+    PageDir = dir;
+    cr3_write(PageDirPtr);
+    cr4_mask(0, CR4_PAE);
+    cr0_mask(0, CR0_PG);
+}
+
+void
+memmap_preinit(void)
+{
+    if (!CONFIG_PAGING || !PageDirPtr)
+        return;
+    // Move temporary page tables to permanent storage
+    u64 *dir = memalign_high(PAGE_SIZE, sizeof(u64) * PAE_MAP_COUNT);
+    u64 *pdp = malloc_high(sizeof(u64)*4);
+    if (!pdp || !dir) {
+        warn_noalloc();
+        free(pdp);
+        free(dir);
+        // XXX - may not be able to turn off paging at this point
+        PageDirPtr = 0;
+        PageDir = NULL;
+        return;
+    }
+    memcpy(dir, PageDir, sizeof(u64) * PAE_MAP_COUNT);
+    int i;
+    for (i=0; i<4; i++)
+        pdp[i] = ((u32)dir + i*4096) | PG_P;
+    PageDirPtr = (u32)pdp;
+    PageDir = dir;
+    cr3_write(PageDirPtr);
+
+    idt_preinit();
+}
diff --git a/src/memmap.h b/src/memmap.h
index d4f150a..850d726 100644
--- a/src/memmap.h
+++ b/src/memmap.h
@@ -7,13 +7,22 @@
 #define PAGE_SIZE 4096
 #define PAGE_SHIFT 12
 
+extern u32 PageDirPtr;
+u64 memmap_virt_to_phys(void *v);
+void *memmap_vmap(u64 addr, u32 len);
+#define MEMMAP_EARLY_DATA_SIZE (5*PAGE_SIZE + 4*sizeof(u64) - 1)
+void memmap_early_preinit(char *data);
 void memmap_preinit(void);
 
-static inline u32 virt_to_phys(void *v) {
-    return (u32)v;
+static inline u64 virt_to_phys(void *v) {
+    if (MODESEGMENT)
+        return (u32)v;
+    return memmap_virt_to_phys(v);
 }
-static inline void *memremap(u32 addr, u32 len) {
-    return (void*)addr;
+static inline void *memremap(u64 addr, u32 len) {
+    if (MODESEGMENT)
+        return (void*)(u32)addr;
+    return memmap_vmap(addr, len);
 }
 static inline void *ioremap(u64 addr, u32 len) {
     return memremap(addr, len);
diff --git a/src/romlayout.S b/src/romlayout.S
index acf0f32..d931516 100644
--- a/src/romlayout.S
+++ b/src/romlayout.S
@@ -51,12 +51,26 @@ transition32_nmi_off:
         orl $CR0_PE, %ecx
         movl %ecx, %cr0
 
+#if CONFIG_PAGING
+        movl %cs:PageDirPtr, %ecx
+        cmpl $0, %ecx
+        jz 1f
+        // Enable PAE paging
+        movl %ecx, %cr3
+        movl %cr4, %ecx
+        orl $CR4_PAE, %ecx
+        movl %ecx, %cr4
+        movl %cr0, %ecx
+        orl $CR0_PG, %ecx
+        movl %ecx, %cr0
+#endif
+
         // start 32bit protected mode code
-        ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 1f)
+1:      ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 2f)
 
         .code32
         // init data segments
-1:      movl $SEG32_MODE32_DS, %ecx
+2:      movl $SEG32_MODE32_DS, %ecx
         movw %cx, %ds
         movw %cx, %es
         movw %cx, %ss
@@ -97,14 +111,25 @@ transition16big:
         .code16
         // Disable protected mode
 1:      movl %cr0, %ecx
-        andl $~CR0_PE, %ecx
+        andl $~(CR0_PE|CR0_PG), %ecx
         movl %ecx, %cr0
 
+#if CONFIG_PAGING
+        cmpl $0, %cs:PageDirPtr
+        jz 2f
+        // Disable PAE paging
+        movl %cr4, %ecx
+        andl $~CR4_PAE, %ecx
+        movl %ecx, %cr4
+        xorl %ecx, %ecx
+        movl %ecx, %cr3
+#endif
+
         // far jump to flush CPU queue after transition to real mode
-        ljmpw $SEG_BIOS, $2f
+2:      ljmpw $SEG_BIOS, $3f
 
         // restore IDT to normal real-mode defaults
-2:      lidtl %cs:rmode_IDT_info
+3:      lidtl %cs:rmode_IDT_info
 
         // Clear segment registers
         xorw %cx, %cx
diff --git a/src/stacks.c b/src/stacks.c
index fa9c7db..a3de397 100644
--- a/src/stacks.c
+++ b/src/stacks.c
@@ -10,6 +10,7 @@
 #include "hw/rtc.h" // rtc_use
 #include "list.h" // hlist_node
 #include "malloc.h" // free
+#include "memmap.h" // PageDirPtr
 #include "output.h" // dprintf
 #include "romfile.h" // romfile_loadint
 #include "stacks.h" // struct mutex_s
@@ -28,7 +29,7 @@ struct {
     u8 cmosindex;
     u8 a20;
     u16 ss, fs, gs;
-    u32 cr0;
+    u32 cr0, cr3, cr4;
     struct descloc_s gdt;
 } Call16Data VARLOW;
 
@@ -48,6 +49,10 @@ call32_prep(u8 method)
             // Called in 16bit protected mode?!
             return -1;
         SET_LOW(Call16Data.cr0, cr0);
+        if (CONFIG_PAGING && GET_GLOBAL(PageDirPtr)) {
+            SET_LOW(Call16Data.cr3, cr3_read());
+            SET_LOW(Call16Data.cr4, cr4_read());
+        }
 
         // Backup fs/gs and gdt
         SET_LOW(Call16Data.fs, GET_SEG(FS));
@@ -98,6 +103,14 @@ call32_post(void)
         u32 cr0_caching = GET_LOW(Call16Data.cr0) & (CR0_CD|CR0_NW);
         if (cr0_caching)
             cr0_mask(CR0_CD|CR0_NW, cr0_caching);
+        if (CONFIG_PAGING && GET_GLOBAL(PageDirPtr)) {
+            u32 cr4_pae = GET_LOW(Call16Data.cr4) & CR4_PAE;
+            if (cr4_pae)
+                cr4_mask(CR4_PAE, cr4_pae);
+            u32 cr3 = GET_LOW(Call16Data.cr3);
+            if (cr3)
+                cr3_write(cr3);
+        }
     }
 
     // Restore cmos index register
diff --git a/src/x86.h b/src/x86.h
index 5de149a..58cc20c 100644
--- a/src/x86.h
+++ b/src/x86.h
@@ -14,6 +14,8 @@
 #define CR0_NW (1<<29) // Not Write-through
 #define CR0_PE (1<<0)  // Protection enable
 
+#define CR4_PAE (1<<5)
+
 // PORT_A20 bitdefs
 #define PORT_A20 0x0092
 #define A20_ENABLE_BIT 0x02
@@ -98,6 +100,27 @@ static inline u32 cr2_read(void) {
     return cr2;
 }
 
+static inline u32 cr3_read(void) {
+    u32 cr3;
+    asm("movl %%cr3, %0" : "=r"(cr3));
+    return cr3;
+}
+static inline void cr3_write(u32 cr3) {
+    asm("movl %0, %%cr3" : : "r"(cr3));
+}
+
+static inline u32 cr4_read(void) {
+    u32 cr4;
+    asm("movl %%cr4, %0" : "=r"(cr4));
+    return cr4;
+}
+static inline void cr4_write(u32 cr4) {
+    asm("movl %0, %%cr4" : : "r"(cr4));
+}
+static inline void cr4_mask(u32 off, u32 on) {
+    cr4_write((cr4_read() & ~off) | on);
+}
+
 static inline u64 rdmsr(u32 index)
 {
     u64 ret;
-- 
2.4.3




More information about the SeaBIOS mailing list