fix: continue fix page fault heap regression
This commit is contained in:
+3
-15
@@ -61,22 +61,10 @@ namespace Zenith {
|
|||||||
return userVa;
|
return userVa;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free all heap allocations for a process slot (called on process exit)
|
// Reset heap allocation tracking for a process slot.
|
||||||
static void CleanupHeapForSlot(int slot, uint64_t pml4Phys) {
|
// The actual physical pages are freed by Paging::FreeUserHalf() during process cleanup.
|
||||||
|
static void CleanupHeapForSlot(int slot, uint64_t /*pml4Phys*/) {
|
||||||
if (slot < 0 || slot >= Sched::MaxProcesses) return;
|
if (slot < 0 || slot >= Sched::MaxProcesses) return;
|
||||||
|
|
||||||
for (int i = 0; i < g_heapAllocCount[slot]; i++) {
|
|
||||||
uint64_t va = g_heapAllocs[slot][i].va;
|
|
||||||
uint64_t numPages = g_heapAllocs[slot][i].numPages;
|
|
||||||
for (uint64_t p = 0; p < numPages; p++) {
|
|
||||||
uint64_t pageVa = va + p * 0x1000;
|
|
||||||
uint64_t physAddr = Memory::VMM::Paging::GetPhysAddr(pml4Phys, pageVa);
|
|
||||||
if (physAddr != 0) {
|
|
||||||
Memory::g_pfa->Free((void*)Memory::HHDM(physAddr));
|
|
||||||
}
|
|
||||||
Memory::VMM::Paging::UnmapUserIn(pml4Phys, pageVa);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_heapAllocCount[slot] = 0;
|
g_heapAllocCount[slot] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Sched/Scheduler.hpp>
|
#include <Sched/Scheduler.hpp>
|
||||||
#include <Timekeeping/ApicTimer.hpp>
|
#include <Timekeeping/ApicTimer.hpp>
|
||||||
|
#include <Memory/Paging.hpp>
|
||||||
|
#include <Memory/PageFrameAllocator.hpp>
|
||||||
|
#include <Memory/HHDM.hpp>
|
||||||
|
|
||||||
#include "Syscall.hpp"
|
#include "Syscall.hpp"
|
||||||
#include "WinServer.hpp"
|
#include "WinServer.hpp"
|
||||||
@@ -105,9 +108,34 @@ namespace Zenith {
|
|||||||
auto* proc = Sched::GetProcessByPid(pid);
|
auto* proc = Sched::GetProcessByPid(pid);
|
||||||
if (!proc) return -1;
|
if (!proc) return -1;
|
||||||
|
|
||||||
// Clean up any windows owned by this process
|
// Clean up any windows owned by this process (unmaps pixel pages from desktop)
|
||||||
WinServer::CleanupProcess(pid);
|
WinServer::CleanupProcess(pid);
|
||||||
|
|
||||||
|
// Free I/O redirect buffers
|
||||||
|
if (proc->outBuf) {
|
||||||
|
Memory::g_pfa->Free(proc->outBuf);
|
||||||
|
proc->outBuf = nullptr;
|
||||||
|
}
|
||||||
|
if (proc->inBuf) {
|
||||||
|
Memory::g_pfa->Free(proc->inBuf);
|
||||||
|
proc->inBuf = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free all user-space pages and page table structures
|
||||||
|
Memory::VMM::Paging::FreeUserHalf(proc->pml4Phys);
|
||||||
|
|
||||||
|
// Free kernel stack (safe — killed process isn't running on single-core)
|
||||||
|
if (proc->stackBase != 0) {
|
||||||
|
Memory::g_pfa->Free((void*)proc->stackBase, Sched::StackPages);
|
||||||
|
proc->stackBase = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the PML4 page
|
||||||
|
if (proc->pml4Phys != 0) {
|
||||||
|
Memory::g_pfa->Free((void*)Memory::HHDM(proc->pml4Phys));
|
||||||
|
proc->pml4Phys = 0;
|
||||||
|
}
|
||||||
|
|
||||||
proc->state = Sched::ProcessState::Terminated;
|
proc->state = Sched::ProcessState::Terminated;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <Memory/HHDM.hpp>
|
#include <Memory/HHDM.hpp>
|
||||||
#include <Libraries/Memory.hpp>
|
#include <Libraries/Memory.hpp>
|
||||||
#include <Terminal/Terminal.hpp>
|
#include <Terminal/Terminal.hpp>
|
||||||
|
#include <Sched/Scheduler.hpp>
|
||||||
|
|
||||||
namespace WinServer {
|
namespace WinServer {
|
||||||
|
|
||||||
@@ -229,6 +230,19 @@ namespace WinServer {
|
|||||||
if (g_slots[i].used && g_slots[i].ownerPid == pid) {
|
if (g_slots[i].used && g_slots[i].ownerPid == pid) {
|
||||||
Kt::KernelLogStream(Kt::INFO, "WinServer") << "Cleaning up window "
|
Kt::KernelLogStream(Kt::INFO, "WinServer") << "Cleaning up window "
|
||||||
<< i << " for exited PID " << pid;
|
<< i << " for exited PID " << pid;
|
||||||
|
|
||||||
|
// Unmap pixel pages from desktop's address space to prevent stale access
|
||||||
|
if (g_slots[i].desktopVa != 0 && g_slots[i].desktopPid != 0) {
|
||||||
|
auto* desktopProc = Sched::GetProcessByPid(g_slots[i].desktopPid);
|
||||||
|
if (desktopProc) {
|
||||||
|
for (int p = 0; p < g_slots[i].pixelNumPages; p++) {
|
||||||
|
Memory::VMM::Paging::UnmapUserIn(
|
||||||
|
desktopProc->pml4Phys,
|
||||||
|
g_slots[i].desktopVa + (uint64_t)p * 0x1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g_slots[i].used = false;
|
g_slots[i].used = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,26 +69,44 @@ namespace Memory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void* PageFrameAllocator::ReallocConsecutive(void* ptr, int n) {
|
void* PageFrameAllocator::ReallocConsecutive(void* ptr, int n) {
|
||||||
auto first = Allocate();
|
Lock.Acquire();
|
||||||
|
|
||||||
for (int i = 0; i < n - 1; i++) {
|
// Search the free list for a single contiguous region >= n pages.
|
||||||
if (Allocate() == nullptr) {
|
// The old implementation assumed N consecutive Allocate() calls gave
|
||||||
Panic("PageFrameAllocator Reallocation failed", nullptr);
|
// adjacent pages, which breaks when individual pages are freed back.
|
||||||
};
|
size_t needed = (size_t)n * 0x1000;
|
||||||
|
Page* current = head.next;
|
||||||
|
Page* prev = &head;
|
||||||
|
|
||||||
|
while (current != nullptr) {
|
||||||
|
if (current->size >= needed) {
|
||||||
|
// Carve from the top of this contiguous region
|
||||||
|
current->size -= needed;
|
||||||
|
void* base;
|
||||||
|
if (current->size == 0) {
|
||||||
|
base = (void*)current;
|
||||||
|
prev->next = current->next;
|
||||||
|
} else {
|
||||||
|
base = (void*)((uint64_t)current + current->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate() returns pages from the top of a free region in descending
|
Lock.Release();
|
||||||
// order, so 'first' is the highest address. The contiguous block
|
|
||||||
// actually starts (n-1) pages below 'first'.
|
|
||||||
void* base = (void*)((uint64_t)first - (uint64_t)(n - 1) * 0x1000);
|
|
||||||
|
|
||||||
if (ptr != nullptr) {
|
if (ptr != nullptr) {
|
||||||
memcpy(base, ptr, (uint64_t)n * 0x1000);
|
memcpy(base, ptr, 0x1000); // copy one page (ptr is always a single page)
|
||||||
Free(ptr);
|
Free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
prev = current;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lock.Release();
|
||||||
|
Panic("PageFrameAllocator: no contiguous region available", nullptr);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void PageFrameAllocator::Free(void* ptr) {
|
void PageFrameAllocator::Free(void* ptr) {
|
||||||
Lock.Acquire();
|
Lock.Acquire();
|
||||||
@@ -102,7 +120,7 @@ namespace Memory {
|
|||||||
|
|
||||||
void PageFrameAllocator::Free(void* ptr, int n) {
|
void PageFrameAllocator::Free(void* ptr, int n) {
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
Free((void*)(uint64_t)ptr + (0x1000 * n));
|
Free((void*)((uint64_t)ptr + 0x1000 * i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -268,6 +268,60 @@ namespace Memory::VMM {
|
|||||||
asm volatile("invlpg (%0)" :: "r"(virtualAddress) : "memory");
|
asm volatile("invlpg (%0)" :: "r"(virtualAddress) : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Paging::FreeUserHalf(std::uint64_t pml4Phys) {
|
||||||
|
// PageTable* pointers store PHYSICAL addresses (same convention as MapUserIn/UnmapUserIn).
|
||||||
|
// Each individual entry access goes through HHDM() to get a valid virtual pointer.
|
||||||
|
PageTable* pml4 = (PageTable*)pml4Phys;
|
||||||
|
|
||||||
|
// Walk user-half entries (0-255); kernel-half (256-511) is shared and must not be touched.
|
||||||
|
for (int i4 = 0; i4 < 256; i4++) {
|
||||||
|
PageTableEntry* pml4e = (PageTableEntry*)Memory::HHDM(&pml4->entries[i4]);
|
||||||
|
if (!pml4e->Present) continue;
|
||||||
|
|
||||||
|
uint64_t pdptPhys = (uint64_t)pml4e->Address << 12;
|
||||||
|
PageTable* pdpt = (PageTable*)pdptPhys;
|
||||||
|
|
||||||
|
for (int i3 = 0; i3 < 512; i3++) {
|
||||||
|
PageTableEntry* pdpte = (PageTableEntry*)Memory::HHDM(&pdpt->entries[i3]);
|
||||||
|
if (!pdpte->Present) continue;
|
||||||
|
|
||||||
|
uint64_t pdPhys = (uint64_t)pdpte->Address << 12;
|
||||||
|
PageTable* pd = (PageTable*)pdPhys;
|
||||||
|
|
||||||
|
for (int i2 = 0; i2 < 512; i2++) {
|
||||||
|
PageTableEntry* pde = (PageTableEntry*)Memory::HHDM(&pd->entries[i2]);
|
||||||
|
if (!pde->Present) continue;
|
||||||
|
|
||||||
|
uint64_t ptPhys = (uint64_t)pde->Address << 12;
|
||||||
|
PageTable* pt = (PageTable*)ptPhys;
|
||||||
|
|
||||||
|
// Free all leaf physical pages
|
||||||
|
for (int i1 = 0; i1 < 512; i1++) {
|
||||||
|
PageTableEntry* pte = (PageTableEntry*)Memory::HHDM(&pt->entries[i1]);
|
||||||
|
if (!pte->Present) continue;
|
||||||
|
|
||||||
|
// Skip MMIO/WC pages (not PFA-managed)
|
||||||
|
if (pte->WriteThrough || pte->CacheDisabled) continue;
|
||||||
|
|
||||||
|
uint64_t pagePhys = (uint64_t)pte->Address << 12;
|
||||||
|
if (pagePhys != 0) {
|
||||||
|
Memory::g_pfa->Free((void*)Memory::HHDM(pagePhys));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the PT page itself
|
||||||
|
Memory::g_pfa->Free((void*)Memory::HHDM(ptPhys));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the PD page
|
||||||
|
Memory::g_pfa->Free((void*)Memory::HHDM(pdPhys));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the PDPT page
|
||||||
|
Memory::g_pfa->Free((void*)Memory::HHDM(pdptPhys));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::uint64_t Paging::GetPhysAddr(std::uint64_t pml4, std::uint64_t virtualAddress, bool use40BitL1) {
|
std::uint64_t Paging::GetPhysAddr(std::uint64_t pml4, std::uint64_t virtualAddress, bool use40BitL1) {
|
||||||
VirtualAddress virtualAddressObj(virtualAddress);
|
VirtualAddress virtualAddressObj(virtualAddress);
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,11 @@ public:
|
|||||||
// Unmap a single page from an arbitrary PML4 (clears PTE + invalidates TLB).
|
// Unmap a single page from an arbitrary PML4 (clears PTE + invalidates TLB).
|
||||||
static void UnmapUserIn(std::uint64_t pml4Phys, std::uint64_t virtualAddress);
|
static void UnmapUserIn(std::uint64_t pml4Phys, std::uint64_t virtualAddress);
|
||||||
|
|
||||||
|
// Free all user-half page table structures and physical pages (PML4 entries 0-255).
|
||||||
|
// Does NOT free the PML4 page itself (caller handles that).
|
||||||
|
// Skips MMIO/WC pages (WriteThrough or CacheDisabled set in PTE).
|
||||||
|
static void FreeUserHalf(std::uint64_t pml4Phys);
|
||||||
|
|
||||||
// Identity-map EFI runtime service regions so firmware code can
|
// Identity-map EFI runtime service regions so firmware code can
|
||||||
// reference its own data at physical addresses.
|
// reference its own data at physical addresses.
|
||||||
void MapEfiRuntime(limine_efi_memmap_response* efiMemmap);
|
void MapEfiRuntime(limine_efi_memmap_response* efiMemmap);
|
||||||
|
|||||||
@@ -239,9 +239,19 @@ namespace Sched {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Schedule() {
|
void Schedule() {
|
||||||
// Reclaim terminated process slots so they can be reused
|
// Reclaim terminated process slots — free deferred resources first
|
||||||
for (int i = 0; i < MaxProcesses; i++) {
|
for (int i = 0; i < MaxProcesses; i++) {
|
||||||
if (processTable[i].state == ProcessState::Terminated) {
|
if (processTable[i].state == ProcessState::Terminated) {
|
||||||
|
// Free kernel stack (deferred from ExitProcess — can't free while running on it)
|
||||||
|
if (processTable[i].stackBase != 0) {
|
||||||
|
Memory::g_pfa->Free((void*)processTable[i].stackBase, StackPages);
|
||||||
|
processTable[i].stackBase = 0;
|
||||||
|
}
|
||||||
|
// Free the PML4 page (deferred from ExitProcess — can't free while it's CR3)
|
||||||
|
if (processTable[i].pml4Phys != 0) {
|
||||||
|
Memory::g_pfa->Free((void*)Memory::HHDM(processTable[i].pml4Phys));
|
||||||
|
processTable[i].pml4Phys = 0;
|
||||||
|
}
|
||||||
processTable[i].state = ProcessState::Free;
|
processTable[i].state = ProcessState::Free;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,10 +332,27 @@ namespace Sched {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up any windows owned by this process
|
Process& proc = processTable[currentPid];
|
||||||
WinServer::CleanupProcess(processTable[currentPid].pid);
|
|
||||||
|
|
||||||
processTable[currentPid].state = ProcessState::Terminated;
|
// Clean up any windows owned by this process (unmaps pixel pages from desktop)
|
||||||
|
WinServer::CleanupProcess(proc.pid);
|
||||||
|
|
||||||
|
// Free I/O redirect buffers (kernel-allocated pages)
|
||||||
|
if (proc.outBuf) {
|
||||||
|
Memory::g_pfa->Free(proc.outBuf);
|
||||||
|
proc.outBuf = nullptr;
|
||||||
|
}
|
||||||
|
if (proc.inBuf) {
|
||||||
|
Memory::g_pfa->Free(proc.inBuf);
|
||||||
|
proc.inBuf = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free all user-space physical pages and page table structures (entries 0-255).
|
||||||
|
// This covers ELF code/data, user stack, exit stub, heap, and window pixel buffers.
|
||||||
|
// The PML4 page itself is freed later in Schedule() (can't free while it's CR3).
|
||||||
|
Memory::VMM::Paging::FreeUserHalf(proc.pml4Phys);
|
||||||
|
|
||||||
|
proc.state = ProcessState::Terminated;
|
||||||
|
|
||||||
int next = -1;
|
int next = -1;
|
||||||
for (int i = 0; i < MaxProcesses; i++) {
|
for (int i = 0; i < MaxProcesses; i++) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* main.cpp
|
* main.cpp
|
||||||
* ZenithOS Font Preview - standalone Window Server process
|
* ZenithOS Font Preview app
|
||||||
* Displays TTF font samples at multiple sizes with vertical scrolling
|
* Displays TTF font samples at multiple sizes with vertical scrolling
|
||||||
* Copyright (c) 2026 Daniel Hammer
|
* Copyright (c) 2026 Daniel Hammer
|
||||||
*/
|
*/
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
using namespace gui;
|
using namespace gui;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Constants — light theme matching ZenithOS desktop conventions
|
// Constants
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
static constexpr int INIT_W = 800;
|
static constexpr int INIT_W = 800;
|
||||||
@@ -40,7 +40,7 @@ static const char* UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|||||||
static const char* LOWER_NUM = "abcdefghijklmnopqrstuvwxyz 0123456789";
|
static const char* LOWER_NUM = "abcdefghijklmnopqrstuvwxyz 0123456789";
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Pre-rendered glyph cache — rasterised once at startup for all 7 sizes
|
// Pre-rendered glyph cache
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
struct PreviewGlyph {
|
struct PreviewGlyph {
|
||||||
|
|||||||
BIN
Binary file not shown.
Reference in New Issue
Block a user