mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-04 05:20:30 +00:00
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.
This commit is contained in:
parent
e85c22fe58
commit
2d045d2a64
Notes:
sideshowbarker
2024-07-19 18:33:38 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/2d045d2a647
11 changed files with 271 additions and 97 deletions
11
AK/Bitmap.h
11
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)
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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) { }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue