Browse Source

Implement COW pages! :^)

sys$fork() now clones all writable regions with per-page COW bits.
The pages are then mapped read-only and we handle a PF by COWing the pages.

This is quite delightful. Obviously there's lots of work to do still,
and it needs better data structures, but the general concept works.
Andreas Kling 6 years ago
parent
commit
2d045d2a64
11 changed files with 271 additions and 97 deletions
  1. 7 4
      AK/Bitmap.h
  2. 128 19
      Kernel/MemoryManager.cpp
  3. 23 1
      Kernel/MemoryManager.h
  4. 6 20
      Kernel/ProcFileSystem.cpp
  5. 17 1
      Kernel/Process.cpp
  6. 18 0
      Kernel/Process.h
  7. 0 10
      Kernel/StdLib.h
  8. 24 34
      Kernel/i386.cpp
  9. 24 0
      Kernel/i386.h
  10. 19 8
      Kernel/init.cpp
  11. 5 0
      Kernel/types.h

+ 7 - 4
AK/Bitmap.h

@@ -3,6 +3,7 @@
 #include "StdLib.h"
 #include "Types.h"
 #include "kmalloc.h"
+#include "Assertions.h"
 
 namespace AK {
 
@@ -14,9 +15,9 @@ public:
         return Bitmap(data, size);
     }
 
-    static Bitmap create(unsigned size)
+    static Bitmap create(unsigned size, bool default_value = 0)
     {
-        return Bitmap(size);
+        return Bitmap(size, default_value);
     }
 
     ~Bitmap()
@@ -45,12 +46,14 @@ public:
     const byte* data() const { return m_data; }
 
 private:
-    explicit Bitmap(unsigned size)
+    explicit Bitmap(unsigned size, bool default_value)
         : m_size(size)
         , m_owned(true)
     {
         ASSERT(m_size != 0);
-        m_data = reinterpret_cast<byte*>(kmalloc(ceilDiv(size, 8u)));
+        size_t size_to_allocate = ceilDiv(size, 8u);
+        m_data = reinterpret_cast<byte*>(kmalloc(size_to_allocate));
+        memset(m_data, default_value ? 0xff : 0x00, size_to_allocate);
     }
 
     Bitmap(byte* data, unsigned size)

+ 128 - 19
Kernel/MemoryManager.cpp

@@ -7,6 +7,7 @@
 #include "Process.h"
 
 //#define MM_DEBUG
+//#define PAGE_FAULT_DEBUG
 #define SCRUB_DEALLOCATED_PAGE_TABLES
 
 static MemoryManager* s_the;
@@ -67,7 +68,7 @@ void MemoryManager::initializePaging()
     memset(m_kernel_page_directory, 0, sizeof(PageDirectory));
 
 #ifdef MM_DEBUG
-    kprintf("MM: Kernel page directory @ %p\n", m_kernel_page_directory);
+    dbgprintf("MM: Kernel page directory @ %p\n", m_kernel_page_directory);
 #endif
 
     // Make null dereferences crash.
@@ -208,15 +209,66 @@ void MemoryManager::initialize()
     s_the = new MemoryManager;
 }
 
+Region* MemoryManager::region_from_laddr(Process& process, LinearAddress laddr)
+{
+    ASSERT_INTERRUPTS_DISABLED();
+
+    // FIXME: Use a binary search tree (maybe red/black?) or some other more appropriate data structure!
+    for (auto& region : process.m_regions) {
+        if (region->contains(laddr))
+            return region.ptr();
+    }
+    ASSERT_NOT_REACHED();
+}
+
+bool MemoryManager::copy_on_write(Process& process, Region& region, unsigned page_index_in_region)
+{
+#ifdef PAGE_FAULT_DEBUG
+    dbgprintf("    >> It's a COW page and it's time to COW!\n");
+#endif
+    auto physical_page_to_copy = move(region.physical_pages[page_index_in_region]);
+    auto ppages = allocate_physical_pages(1);
+    ASSERT(ppages.size() == 1);
+    byte* dest_ptr = quickmap_page(*ppages[0]);
+    const byte* src_ptr = region.linearAddress.offset(page_index_in_region * PAGE_SIZE).asPtr();
+#ifdef PAGE_FAULT_DEBUG
+    dbgprintf("      >> COW P%x <- P%x\n", ppages[0]->paddr().get(), physical_page_to_copy->paddr().get());
+#endif
+    memcpy(dest_ptr, src_ptr, PAGE_SIZE);
+    region.physical_pages[page_index_in_region] = move(ppages[0]);
+    unquickmap_page();
+    region.cow_map.set(page_index_in_region, false);
+    remap_region_page(process.m_page_directory, region, page_index_in_region, true);
+    return true;
+}
+
 PageFaultResponse MemoryManager::handle_page_fault(const PageFault& fault)
 {
     ASSERT_INTERRUPTS_DISABLED();
-    kprintf("MM: handle_page_fault(%w) at L%x\n", fault.code(), fault.laddr().get());
+#ifdef PAGE_FAULT_DEBUG
+    dbgprintf("MM: handle_page_fault(%w) at L%x\n", fault.code(), fault.laddr().get());
+#endif
+    auto* region = region_from_laddr(*current, fault.laddr());
+    ASSERT(region);
+    auto page_index_in_region = region->page_index_from_address(fault.laddr());
     if (fault.is_not_present()) {
-        kprintf("  >> NP fault!\n");
+        kprintf("  >> NP fault in Region{%p}[%u]\n", region, page_index_in_region);
     } else if (fault.is_protection_violation()) {
-        kprintf("  >> PV fault!\n");
+        if (region->cow_map.get(page_index_in_region)) {
+#ifdef PAGE_FAULT_DEBUG
+            dbgprintf("  >> PV (COW) fault in Region{%p}[%u]\n", region, page_index_in_region);
+#endif
+            bool success = copy_on_write(*current, *region, page_index_in_region);
+            ASSERT(success);
+            return PageFaultResponse::Continue;
+        }
+        kprintf("  >> PV fault in Region{%p}[%u]\n", region, page_index_in_region);
+    } else {
+        ASSERT_NOT_REACHED();
     }
+
+
+
     return PageFaultResponse::ShouldCrash;
 }
 
@@ -264,6 +316,66 @@ void MemoryManager::flushTLB(LinearAddress laddr)
     asm volatile("invlpg %0": :"m" (*(char*)laddr.get()) : "memory");
 }
 
+byte* MemoryManager::quickmap_page(PhysicalPage& physical_page)
+{
+    ASSERT_INTERRUPTS_DISABLED();
+    auto page_laddr = LinearAddress(4 * MB);
+    auto pte = ensurePTE(m_kernel_page_directory, page_laddr);
+    pte.setPhysicalPageBase(physical_page.paddr().get());
+    pte.setPresent(true); // FIXME: Maybe we should use the is_readable flag here?
+    pte.setWritable(true);
+    pte.setUserAllowed(false);
+    flushTLB(page_laddr);
+#ifdef MM_DEBUG
+    dbgprintf("MM: >> quickmap_page L%x => P%x\n", page_laddr, physical_page.paddr().get());
+#endif
+    return page_laddr.asPtr();
+}
+
+void MemoryManager::unquickmap_page()
+{
+    ASSERT_INTERRUPTS_DISABLED();
+    auto page_laddr = LinearAddress(4 * MB);
+    auto pte = ensurePTE(m_kernel_page_directory, page_laddr);
+#ifdef MM_DEBUG
+    auto old_physical_address = pte.physicalPageBase();
+#endif
+    pte.setPhysicalPageBase(0);
+    pte.setPresent(false);
+    pte.setWritable(false);
+    pte.setUserAllowed(false);
+    flushTLB(page_laddr);
+#ifdef MM_DEBUG
+    dbgprintf("MM: >> unquickmap_page L%x =/> P%x\n", page_laddr, old_physical_address);
+#endif
+}
+
+void MemoryManager::remap_region_page(PageDirectory* page_directory, Region& region, unsigned page_index_in_region, bool user_allowed)
+{
+    InterruptDisabler disabler;
+    auto page_laddr = region.linearAddress.offset(page_index_in_region * PAGE_SIZE);
+    auto pte = ensurePTE(page_directory, page_laddr);
+    auto& physical_page = region.physical_pages[page_index_in_region];
+    ASSERT(physical_page);
+    pte.setPhysicalPageBase(physical_page->paddr().get());
+    pte.setPresent(true); // FIXME: Maybe we should use the is_readable flag here?
+    if (region.cow_map.get(page_index_in_region))
+        pte.setWritable(false);
+    else
+        pte.setWritable(region.is_writable);
+    pte.setUserAllowed(user_allowed);
+    flushTLB(page_laddr);
+#ifdef MM_DEBUG
+    dbgprintf("MM: >> remap_region_page (PD=%x) '%s' L%x => P%x (@%p)\n", page_directory, region.name.characters(), page_laddr.get(), physical_page->paddr().get(), physical_page.ptr());
+#endif
+}
+
+void MemoryManager::remap_region(Process& process, Region& region)
+{
+    InterruptDisabler disabler;
+    map_region_at_address(process.m_page_directory, region, region.linearAddress, true);
+}
+
 void MemoryManager::map_region_at_address(PageDirectory* page_directory, Region& region, LinearAddress laddr, bool user_allowed)
 {
     InterruptDisabler disabler;
@@ -274,11 +386,15 @@ void MemoryManager::map_region_at_address(PageDirectory* page_directory, Region&
         if (physical_page) {
             pte.setPhysicalPageBase(physical_page->paddr().get());
             pte.setPresent(true); // FIXME: Maybe we should use the is_readable flag here?
+            if (region.cow_map.get(i))
+                pte.setWritable(false);
+            else
+                pte.setWritable(region.is_writable);
         } else {
             pte.setPhysicalPageBase(0);
             pte.setPresent(false);
+            pte.setWritable(region.is_writable);
         }
-        pte.setWritable(region.is_writable);
         pte.setUserAllowed(user_allowed);
         flushTLB(page_laddr);
 #ifdef MM_DEBUG
@@ -399,34 +515,27 @@ bool MemoryManager::validate_user_write(const Process& process, LinearAddress la
 RetainPtr<Region> Region::clone()
 {
     InterruptDisabler disabler;
-    KernelPagingScope pagingScope;
 
     if (is_readable && !is_writable) {
         // Create a new region backed by the same physical pages.
         return adopt(*new Region(linearAddress, size, physical_pages, String(name), is_readable, is_writable));
     }
-    // FIXME: Implement COW regions.
-    auto clone_physical_pages = MM.allocate_physical_pages(physical_pages.size());
-    auto clone_region = adopt(*new Region(linearAddress, size, move(clone_physical_pages), String(name), is_readable, is_writable));
-
-    // FIXME: It would be cool to make the src_alias a read-only mapping.
-    byte* src_alias = MM.create_kernel_alias_for_region(*this);
-    byte* dest_alias = MM.create_kernel_alias_for_region(*clone_region);
-
-    memcpy(dest_alias, src_alias, size);
 
-    MM.remove_kernel_alias_for_region(*clone_region, dest_alias);
-    MM.remove_kernel_alias_for_region(*this, src_alias);
-    return clone_region;
+    // Set up a COW region. The parent (this) region becomes COW as well!
+    for (size_t i = 0; i < physical_pages.size(); ++i)
+        cow_map.set(i, true);
+    MM.remap_region(*current, *this);
+    return adopt(*new Region(linearAddress, size, physical_pages, String(name), is_readable, is_writable, true));
 }
 
-Region::Region(LinearAddress a, size_t s, Vector<RetainPtr<PhysicalPage>> pp, String&& n, bool r, bool w)
+Region::Region(LinearAddress a, size_t s, Vector<RetainPtr<PhysicalPage>> pp, String&& n, bool r, bool w, bool cow)
     : linearAddress(a)
     , size(s)
     , physical_pages(move(pp))
     , name(move(n))
     , is_readable(r)
     , is_writable(w)
+    , cow_map(Bitmap::create(physical_pages.size(), cow))
 {
 }
 

+ 23 - 1
Kernel/MemoryManager.h

@@ -2,6 +2,7 @@
 
 #include "types.h"
 #include "i386.h"
+#include <AK/Bitmap.h>
 #include <AK/ByteBuffer.h>
 #include <AK/Retainable.h>
 #include <AK/RetainPtr.h>
@@ -54,16 +55,27 @@ struct PageDirectory {
 };
 
 struct Region : public Retainable<Region> {
-    Region(LinearAddress, size_t, Vector<RetainPtr<PhysicalPage>>, String&&, bool r, bool w);
+    Region(LinearAddress, size_t, Vector<RetainPtr<PhysicalPage>>, String&&, bool r, bool w, bool cow = false);
     ~Region();
 
     RetainPtr<Region> clone();
+    bool contains(LinearAddress laddr) const
+    {
+        return laddr >= linearAddress && laddr < linearAddress.offset(size);
+    }
+
+    unsigned page_index_from_address(LinearAddress laddr) const
+    {
+        return (laddr - linearAddress).get() / PAGE_SIZE;
+    }
+
     LinearAddress linearAddress;
     size_t size { 0 };
     Vector<RetainPtr<PhysicalPage>> physical_pages;
     String name;
     bool is_readable { true };
     bool is_writable { true };
+    Bitmap cow_map;
 };
 
 #define MM MemoryManager::the()
@@ -98,6 +110,8 @@ public:
 
     Vector<RetainPtr<PhysicalPage>> allocate_physical_pages(size_t count);
 
+    void remap_region(Process&, Region&);
+
 private:
     MemoryManager();
     ~MemoryManager();
@@ -105,6 +119,7 @@ private:
     LinearAddress allocate_linear_address_range(size_t);
     void map_region_at_address(PageDirectory*, Region&, LinearAddress, bool user_accessible);
     void unmap_range(PageDirectory*, LinearAddress, size_t);
+    void remap_region_page(PageDirectory*, Region&, unsigned page_index_in_region, bool user_allowed);
 
     void initializePaging();
     void flushEntireTLB();
@@ -118,6 +133,13 @@ private:
     void create_identity_mapping(LinearAddress, size_t length);
     void remove_identity_mapping(LinearAddress, size_t);
 
+    static Region* region_from_laddr(Process&, LinearAddress);
+
+    bool copy_on_write(Process&, Region&, unsigned page_index_in_region);
+
+    byte* quickmap_page(PhysicalPage&);
+    void unquickmap_page();
+
     struct PageDirectoryEntry {
         explicit PageDirectoryEntry(dword* pde) : m_pde(pde) { }
 

+ 6 - 20
Kernel/ProcFileSystem.cpp

@@ -59,8 +59,12 @@ ByteBuffer procfs$pid_vm(Process& process)
             region->linearAddress.offset(region->size - 1).get(),
             region->size,
             region->name.characters());
-        for (auto& physical_page : region->physical_pages) {
-            ptr += ksprintf(ptr, "P%x ", physical_page ? physical_page->paddr().get() : 0);
+        for (size_t i = 0; i < region->physical_pages.size(); ++i) {
+            auto& physical_page = region->physical_pages[i];
+            ptr += ksprintf(ptr, "P%x%s ",
+                physical_page ? physical_page->paddr().get() : 0,
+                region->cow_map.get(i) ? "!" : ""
+            );
         }
         ptr += ksprintf(ptr, "\n");
     }
@@ -249,24 +253,6 @@ ByteBuffer procfs$kmalloc()
     return buffer;
 }
 
-static const char* toString(Process::State state)
-{
-    switch (state) {
-    case Process::Invalid: return "Invalid";
-    case Process::Runnable: return "Runnable";
-    case Process::Running: return "Running";
-    case Process::Terminated: return "Term";
-    case Process::Crashing: return "Crash";
-    case Process::Exiting: return "Exit";
-    case Process::BlockedSleep: return "Sleep";
-    case Process::BlockedWait: return "Wait";
-    case Process::BlockedRead: return "Read";
-    case Process::BeingInspected: return "Inspect";
-    }
-    ASSERT_NOT_REACHED();
-    return nullptr;
-}
-
 ByteBuffer procfs$summary()
 {
     InterruptDisabler disabler;

+ 17 - 1
Kernel/Process.cpp

@@ -19,6 +19,14 @@
 //#define TASK_DEBUG
 //#define FORK_DEBUG
 //#define SCHEDULER_DEBUG
+#define COOL_GLOBALS
+
+#ifdef COOL_GLOBALS
+struct CoolGlobals {
+    dword current_pid;
+};
+CoolGlobals* g_cool_globals;
+#endif
 
 // FIXME: Only do a single validation for accesses that don't span multiple pages.
 // FIXME: Some places pass strlen(arg1) as arg2. This doesn't seem entirely perfect..
@@ -96,6 +104,9 @@ static void hlt_loop()
 
 void Process::initialize()
 {
+#ifdef COOL_GLOBALS
+    g_cool_globals = (CoolGlobals*)0x1000;
+#endif
     current = nullptr;
     next_pid = 0;
     s_processes = new InlineLinkedList<Process>;
@@ -353,6 +364,7 @@ int Process::exec(const String& path, Vector<String>&& arguments, Vector<String>
     m_tss.ds = 0x23;
     m_tss.es = 0x23;
     m_tss.fs = 0x23;
+    m_tss.gs = 0x23;
     m_tss.ss = 0x23;
     m_tss.cr3 = (dword)m_page_directory;
     auto* stack_region = allocate_region(LinearAddress(), defaultStackSize, "stack");
@@ -845,7 +857,7 @@ bool scheduleNewProcess()
     for (auto* process = s_processes->head(); process; process = process->next()) {
         //if (process->state() == Process::BlockedWait || process->state() == Process::BlockedSleep)
 //            continue;
-        dbgprintf("%w %s(%u) @ %w:%x\n", process->state(), process->name().characters(), process->pid(), process->tss().cs, process->tss().eip);
+        dbgprintf("% 12s %s(%u) @ %w:%x\n", toString(process->state()), process->name().characters(), process->pid(), process->tss().cs, process->tss().eip);
     }
 #endif
 
@@ -915,6 +927,10 @@ static bool contextSwitch(Process* t)
     current = t;
     t->set_state(Process::Running);
 
+#ifdef COOL_GLOBALS
+    g_cool_globals->current_pid = t->pid();
+#endif
+
     if (!t->selector()) {
         t->setSelector(gdt_alloc_entry());
         auto& descriptor = getGDTEntry(t->selector());

+ 18 - 0
Kernel/Process.h

@@ -236,6 +236,24 @@ private:
     Process::State m_original_state { Process::Invalid };
 };
 
+static inline const char* toString(Process::State state)
+{
+    switch (state) {
+    case Process::Invalid: return "Invalid";
+    case Process::Runnable: return "Runnable";
+    case Process::Running: return "Running";
+    case Process::Terminated: return "Term";
+    case Process::Crashing: return "Crash";
+    case Process::Exiting: return "Exit";
+    case Process::BlockedSleep: return "Sleep";
+    case Process::BlockedWait: return "Wait";
+    case Process::BlockedRead: return "Read";
+    case Process::BeingInspected: return "Inspect";
+    }
+    ASSERT_NOT_REACHED();
+    return nullptr;
+}
+
 extern void yield();
 extern bool scheduleNewProcess();
 extern void switchNow();

+ 0 - 10
Kernel/StdLib.h

@@ -2,17 +2,7 @@
 
 #include "types.h"
 
-#if 0
-inline void memcpy(void *dest, const void *src, DWORD n)
-{
-    BYTE* bdest = (BYTE*)dest;
-    const BYTE* bsrc = (const BYTE*)src;
-    for (; n; --n)
-        *(bdest++) = *(bsrc++);
-}
-#else
 void memcpy(void*, const void*, DWORD);
-#endif
 void strcpy(char*, const char*);
 int strcmp(char const*, const char*);
 DWORD strlen(const char*);

+ 24 - 34
Kernel/i386.cpp

@@ -56,24 +56,12 @@ asm(
     "    iret\n"
 );
 
-extern volatile dword exception_state_dump;
-extern volatile word exception_code;
-asm(
-    ".globl exception_state_dump\n"
-    "exception_state_dump:\n"
-    ".long 0\n"
-    ".globl exception_code\n"
-    "exception_code:\n"
-    ".short 0\n"
-);
-
 #define EH_ENTRY(ec) \
-extern "C" void exception_ ## ec ## _handler(); \
+extern "C" void exception_ ## ec ## _handler(RegisterDumpWithExceptionCode&); \
 extern "C" void exception_ ## ec ## _entry(); \
 asm( \
     ".globl exception_" # ec "_entry\n" \
     "exception_" # ec "_entry: \n" \
-    "    pop exception_code\n" \
     "    pusha\n" \
     "    pushw %ds\n" \
     "    pushw %es\n" \
@@ -88,7 +76,7 @@ asm( \
     "    popw %es\n" \
     "    popw %fs\n" \
     "    popw %gs\n" \
-    "    mov %esp, exception_state_dump\n" \
+    "    mov %esp, %eax\n" \
     "    call exception_" # ec "_handler\n" \
     "    popw %gs\n" \
     "    popw %gs\n" \
@@ -96,11 +84,12 @@ asm( \
     "    popw %es\n" \
     "    popw %ds\n" \
     "    popa\n" \
+    "    add $0x4, %esp\n" \
     "    iret\n" \
 );
 
 #define EH_ENTRY_NO_CODE(ec) \
-extern "C" void exception_ ## ec ## _handler(); \
+extern "C" void exception_ ## ec ## _handler(RegisterDump&); \
 extern "C" void exception_ ## ec ## _entry(); \
 asm( \
     ".globl exception_" # ec "_entry\n" \
@@ -119,7 +108,7 @@ asm( \
     "    popw %es\n" \
     "    popw %fs\n" \
     "    popw %gs\n" \
-    "    mov %esp, exception_state_dump\n" \
+    "    mov %esp, %eax\n" \
     "    call exception_" # ec "_handler\n" \
     "    popw %gs\n" \
     "    popw %gs\n" \
@@ -132,9 +121,8 @@ asm( \
 
 // 6: Invalid Opcode
 EH_ENTRY_NO_CODE(6);
-void exception_6_handler()
+void exception_6_handler(RegisterDump& regs)
 {
-    auto& regs = *reinterpret_cast<RegisterDump*>(exception_state_dump);
     kprintf("%s invalid opcode: %u(%s)\n", current->isRing0() ? "Kernel" : "Process", current->pid(), current->name().characters());
 
     word ss;
@@ -147,7 +135,6 @@ void exception_6_handler()
         esp = regs.esp_if_crossRing;
     }
 
-    kprintf("exception code: %w\n", exception_code);
     kprintf("pc=%w:%x ds=%w es=%w fs=%w gs=%w\n", regs.cs, regs.eip, regs.ds, regs.es, regs.fs, regs.gs);
     kprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
     kprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi);
@@ -163,10 +150,9 @@ void exception_6_handler()
 
 // 13: General Protection Fault
 EH_ENTRY(13);
-void exception_13_handler()
+void exception_13_handler(RegisterDumpWithExceptionCode& regs)
 {
-    auto& regs = *reinterpret_cast<RegisterDump*>(exception_state_dump);
-    kprintf("%s crash: %u(%s)\n", current->isRing0() ? "Kernel" : "Process", current->pid(), current->name().characters());
+    kprintf("%s GPF: %u(%s)\n", current->isRing0() ? "Kernel" : "User", current->pid(), current->name().characters());
 
     word ss;
     dword esp;
@@ -178,7 +164,7 @@ void exception_13_handler()
         esp = regs.esp_if_crossRing;
     }
 
-    kprintf("exception code: %w\n", exception_code);
+    kprintf("exception code: %w\n", regs.exception_code);
     kprintf("pc=%w:%x ds=%w es=%w fs=%w gs=%w\n", regs.cs, regs.eip, regs.ds, regs.es, regs.fs, regs.gs);
     kprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
     kprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi);
@@ -193,19 +179,18 @@ void exception_13_handler()
 
 // 14: Page Fault
 EH_ENTRY(14);
-void exception_14_handler()
+void exception_14_handler(RegisterDumpWithExceptionCode& regs)
 {
     ASSERT(current);
 
     dword faultAddress;
     asm ("movl %%cr2, %%eax":"=a"(faultAddress));
 
-    auto& regs = *reinterpret_cast<RegisterDump*>(exception_state_dump);
-    kprintf("Ring%u page fault in %s(%u), %s laddr=%p\n",
+    dbgprintf("Ring%u page fault in %s(%u), %s laddr=%p\n",
         regs.cs & 3,
         current->name().characters(),
         current->pid(),
-        exception_code & 2 ? "write" : "read",
+        regs.exception_code & 2 ? "write" : "read",
         faultAddress);
 
     word ss;
@@ -218,13 +203,15 @@ void exception_14_handler()
         esp = regs.esp_if_crossRing;
     }
 
-    kprintf("exception code: %w\n", exception_code);
-    kprintf("pc=%w:%x ds=%w es=%w fs=%w gs=%w\n", regs.cs, regs.eip, regs.ds, regs.es, regs.fs, regs.gs);
-    kprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
-    kprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi);
+#ifdef PAGE_FAULT_DEBUG
+    dbgprintf("exception code: %w\n", regs.exception_code);
+    dbgprintf("pc=%w:%x ds=%w es=%w fs=%w gs=%w\n", regs.cs, regs.eip, regs.ds, regs.es, regs.fs, regs.gs);
+    dbgprintf("stk=%w:%x\n", ss, esp);
+    dbgprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
+    dbgprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi);
 
     byte* codeptr = (byte*)regs.eip;
-    kprintf("code: %b %b %b %b %b %b %b %b\n",
+    dbgprintf("code: %b %b %b %b %b %b %b %b\n",
             codeptr[0],
             codeptr[1],
             codeptr[2],
@@ -234,17 +221,20 @@ void exception_14_handler()
             codeptr[6],
             codeptr[7]
     );
+#endif
 
     if (current->isRing0())
         HANG;
 
-    auto response = MM.handle_page_fault(PageFault(exception_code, LinearAddress(faultAddress)));
+    auto response = MM.handle_page_fault(PageFault(regs.exception_code, LinearAddress(faultAddress)));
 
     if (response == PageFaultResponse::ShouldCrash) {
         kprintf("Crashing after unresolved page fault\n");
         Process::processDidCrash(current);
     } else if (response == PageFaultResponse::Continue) {
-        kprintf("Continuing after resolved page fault\n");
+#ifdef PAGE_FAULT_DEBUG
+        dbgprintf("Continuing after resolved page fault\n");
+#endif
     } else {
         ASSERT_NOT_REACHED();
     }

+ 24 - 0
Kernel/i386.h

@@ -170,6 +170,30 @@ struct RegisterDump {
     WORD ss_if_crossRing;
 } PACKED;
 
+struct RegisterDumpWithExceptionCode {
+    WORD ss;
+    WORD gs;
+    WORD fs;
+    WORD es;
+    WORD ds;
+    DWORD edi;
+    DWORD esi;
+    DWORD ebp;
+    DWORD esp;
+    DWORD ebx;
+    DWORD edx;
+    DWORD ecx;
+    DWORD eax;
+    WORD exception_code;
+    WORD __exception_code_padding;
+    DWORD eip;
+    WORD cs;
+    WORD __csPadding;
+    DWORD eflags;
+    DWORD esp_if_crossRing;
+    WORD ss_if_crossRing;
+} PACKED;
+
 inline constexpr dword pageBaseOf(dword address)
 {
     return address & 0xfffff000;

+ 19 - 8
Kernel/init.cpp

@@ -100,23 +100,34 @@ static void loadKsyms(const ByteBuffer& buffer)
     s_ksyms_ready = true;
 }
 
-void dump_backtrace()
+void dump_backtrace(bool use_ksyms)
 {
-    if (!current)
+    if (!current) {
+        HANG;
         return;
+    }
     extern volatile bool ksyms_ready();
-    if (!ksyms_ready())
+    if (use_ksyms && !ksyms_ready()) {
+        HANG;
         return;
-    dword stack_variable;
+    }
     struct RecognizedSymbol {
         dword address;
         const KSym* ksym;
     };
     Vector<RecognizedSymbol> recognizedSymbols;
-    for (dword* stackPtr = &stack_variable; current->isValidAddressForKernel(LinearAddress((dword)stackPtr)); stackPtr = (dword*)*stackPtr) {
-        dword retaddr = stackPtr[1];
-        if (auto* ksym = ksymbolicate(retaddr))
-            recognizedSymbols.append({ retaddr, ksym });
+    if (use_ksyms) {
+        for (dword* stackPtr = (dword*)&use_ksyms; current->isValidAddressForKernel(LinearAddress((dword)stackPtr)); stackPtr = (dword*)*stackPtr) {
+            dword retaddr = stackPtr[1];
+            if (auto* ksym = ksymbolicate(retaddr))
+                recognizedSymbols.append({ retaddr, ksym });
+        }
+    } else{
+        for (dword* stackPtr = (dword*)&use_ksyms; current->isValidAddressForKernel(LinearAddress((dword)stackPtr)); stackPtr = (dword*)*stackPtr) {
+            dword retaddr = stackPtr[1];
+            kprintf("%x (next: %x)\n", retaddr, stackPtr ? (dword*)*stackPtr : 0);
+        }
+        return;
     }
     size_t bytesNeeded = 0;
     for (auto& symbol : recognizedSymbols) {

+ 5 - 0
Kernel/types.h

@@ -105,3 +105,8 @@ public:
 private:
     dword m_address { 0 };
 };
+
+inline LinearAddress operator-(const LinearAddress& a, const LinearAddress& b)
+{
+    return LinearAddress(a.get() - b.get());
+}