feat: add word processor, spreadsheet, kernel and userspace memory improvements

This commit is contained in:
2026-03-05 19:18:11 +01:00
parent fcb6f8e247
commit ead7d296f7
28 changed files with 4146 additions and 56 deletions
+20 -7
View File
@@ -97,6 +97,19 @@ namespace WinServer {
}
}
// Unmap pixel pages from the owner's address space so that
// FreeUserHalf() won't double-free them when the process exits.
{
auto* ownerProc = Sched::GetProcessByPid(slot.ownerPid);
if (ownerProc) {
for (int p = 0; p < slot.pixelNumPages; p++) {
Memory::VMM::Paging::UnmapUserIn(
ownerProc->pml4Phys,
slot.ownerVa + (uint64_t)p * 0x1000);
}
}
}
// Free physical pixel pages
for (int i = 0; i < slot.pixelNumPages; i++) {
if (slot.pixelPhysPages[i] != 0) {
@@ -198,9 +211,11 @@ namespace WinServer {
int numPages = (int)((bufSize + 0xFFF) / 0x1000);
if (numPages > MaxPixelPages) return -1;
// Free old pixel pages before allocating new ones
// Unmap old pixel pages from owner's address space, then free them
int oldNumPages = slot.pixelNumPages;
for (int i = 0; i < oldNumPages; i++) {
Memory::VMM::Paging::UnmapUserIn(
ownerPml4, slot.ownerVa + (uint64_t)i * 0x1000);
if (slot.pixelPhysPages[i] != 0) {
Memory::g_pfa->Free((void*)Memory::HHDM(slot.pixelPhysPages[i]));
slot.pixelPhysPages[i] = 0;
@@ -271,12 +286,10 @@ namespace WinServer {
}
}
// Free physical pixel pages
for (int p = 0; p < g_slots[i].pixelNumPages; p++) {
if (g_slots[i].pixelPhysPages[p] != 0) {
Memory::g_pfa->Free((void*)Memory::HHDM(g_slots[i].pixelPhysPages[p]));
}
}
// Do NOT free physical pixel pages here — they are still mapped
// in the owner's page tables and FreeUserHalf() (called right
// after CleanupProcess) will free them. Freeing here would
// cause a double-free, creating a cycle in the PFA free list.
g_slots[i].used = false;
}
+74 -10
View File
@@ -108,20 +108,84 @@ namespace Memory {
return nullptr;
}
void PageFrameAllocator::Free(void* ptr) {
Lock.Acquire();
auto prev_next = head.next;
head.next = (Page*)ptr;
// Core free implementation: sorted-insert with coalescing.
// The free list is kept sorted by address so adjacent blocks can be merged,
// preventing fragmentation from accumulating over time.
void PageFrameAllocator::FreeRange(void* ptr, size_t size) {
if (ptr == nullptr || size == 0) return;
Lock.Acquire();
uint64_t addr = (uint64_t)ptr;
// Walk to find the sorted insertion point: prev < addr < current
Page* prev = &head;
Page* current = head.next;
while (current != nullptr && (uint64_t)current < addr) {
// Double-free check: addr falls within an existing free block
if (addr < (uint64_t)current + current->size) {
Kt::KernelLogStream(Kt::WARNING, "PFA")
<< "Double-free detected at " << addr << ", ignoring";
Lock.Release();
return;
}
prev = current;
current = current->next;
}
// Double-free check: exact match with next block
if (current != nullptr && (uint64_t)current == addr) {
Kt::KernelLogStream(Kt::WARNING, "PFA")
<< "Double-free detected at " << addr << ", ignoring";
Lock.Release();
return;
}
// Try to coalesce with previous block (if prev ends where new block starts)
bool merged_prev = false;
if (prev != &head) {
uint64_t prev_end = (uint64_t)prev + prev->size;
if (prev_end == addr) {
prev->size += size;
merged_prev = true;
}
}
// Try to coalesce with next block (if new block ends where next starts)
if (current != nullptr && addr + size == (uint64_t)current) {
if (merged_prev) {
// Three-way merge: prev absorbs new block and current
prev->size += current->size;
prev->next = current->next;
} else {
// Forward merge: new block absorbs current
Page* new_page = (Page*)addr;
new_page->size = size + current->size;
new_page->next = current->next;
prev->next = new_page;
}
} else if (!merged_prev) {
// No merging possible: insert new block between prev and current
Page* new_page = (Page*)addr;
new_page->size = size;
new_page->next = current;
prev->next = new_page;
}
head.next->next = prev_next;
head.next->size = 0x1000;
Lock.Release();
}
void PageFrameAllocator::Free(void* ptr) {
FreeRange(ptr, 0x1000);
}
void PageFrameAllocator::Free(void* ptr, int n) {
for (int i = 0; i < n; i++) {
Free((void*)((uint64_t)ptr + 0x1000 * i));
}
if (ptr == nullptr || n <= 0) return;
// Free the entire contiguous range as a single block.
// This is more efficient than freeing one page at a time and
// guarantees the range stays coalesced.
FreeRange(ptr, (size_t)n * 0x1000);
}
void PageFrameAllocator::GetStats(Montauk::MemStats* out) {
@@ -140,4 +204,4 @@ namespace Memory {
out->usedBytes = g_section.size > freeBytes ? g_section.size - freeBytes : 0;
out->pageSize = 0x1000;
}
};
};
+2
View File
@@ -20,6 +20,8 @@ namespace Memory {
Page head{};
kcp::Spinlock Lock{};
LargestSection g_section;
void FreeRange(void* ptr, std::size_t size);
public:
PageFrameAllocator(LargestSection section);