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:
Andreas Kling 2018-11-05 13:48:07 +01:00
parent e85c22fe58
commit 2d045d2a64
Notes: sideshowbarker 2024-07-19 18:33:38 +09:00
11 changed files with 271 additions and 97 deletions

View file

@ -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)

View file

@ -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))
{
}

View file

@ -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) { }

View file

@ -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;

View file

@ -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());

View file

@ -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();

View file

@ -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*);

View file

@ -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();
}

View file

@ -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;

View file

@ -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) {

View file

@ -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());
}