feat: overhaul userspace heap and virtual memory
This commit is contained in:
1 parent
4ecec32c5c
commit
9b23d99082
21 files changed
+905
-451
No files matched your search
@@ -493,6 +493,8 @@ namespace Sched {
|
||||
proc.kernelStackTop = kernelStackTop;
|
||||
proc.userStackTop = UserStackTop - 8;
|
||||
proc.heapNext = UserHeapBase;
|
||||
for (uint64_t i = 0; i < UserHeapBitmapWords; i++)
|
||||
g_userHeapPageMap[slot][i] = 0;
|
||||
proc.fsBase = tls.fsBase;
|
||||
proc.tlsTemplateVaddr = tls.templateVaddr;
|
||||
proc.tlsFileSize = tls.fileSize;
|
||||
@@ -651,6 +653,7 @@ namespace Sched {
|
||||
}
|
||||
if (!ok) {
|
||||
Ipc::UnmapAndFreeUserRange(sharedPml4, base, mappedPages);
|
||||
ReleaseUserHeapRange(primarySlot_, base, numPages * 0x1000ULL);
|
||||
Kt::KernelLogStream(Kt::ERROR, "Sched")
|
||||
<< "Thread TLS allocation failed";
|
||||
return -1;
|
||||
@@ -675,8 +678,11 @@ namespace Sched {
|
||||
// Allocate kernel stack.
|
||||
void* stackMem = Memory::g_pfa->ReallocConsecutive(nullptr, StackPages);
|
||||
if (stackMem == nullptr) {
|
||||
if (threadTlsPages != 0)
|
||||
if (threadTlsPages != 0) {
|
||||
Ipc::UnmapAndFreeUserRange(sharedPml4, threadTlsBase, threadTlsPages);
|
||||
ReleaseUserHeapRange(primarySlot_, threadTlsBase,
|
||||
threadTlsPages * 0x1000ULL);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
memset(stackMem, 0, StackSize);
|
||||
@@ -698,8 +704,11 @@ namespace Sched {
|
||||
if (slot < 0) {
|
||||
schedLock.Release();
|
||||
Memory::g_pfa->Free(stackMem, StackPages);
|
||||
if (threadTlsPages != 0)
|
||||
if (threadTlsPages != 0) {
|
||||
Ipc::UnmapAndFreeUserRange(sharedPml4, threadTlsBase, threadTlsPages);
|
||||
ReleaseUserHeapRange(primarySlot_, threadTlsBase,
|
||||
threadTlsPages * 0x1000ULL);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -774,20 +783,54 @@ namespace Sched {
|
||||
|
||||
schedLock.Acquire();
|
||||
Process& primary = processTable[primarySlot_];
|
||||
uint64_t base = primary.heapNext;
|
||||
bool validOwner = primary.primarySlot == primarySlot_ &&
|
||||
primary.state != ProcessState::Free &&
|
||||
primary.state != ProcessState::Terminated;
|
||||
bool fits = validOwner && base >= UserHeapBase &&
|
||||
base <= UserHeapLimit && size <= UserHeapLimit - base;
|
||||
if (fits) {
|
||||
primary.heapNext = base + size;
|
||||
outVa = base;
|
||||
bool fits = false;
|
||||
uint64_t pages = size / 0x1000ULL;
|
||||
uint64_t runStart = 0;
|
||||
uint64_t runLength = 0;
|
||||
|
||||
if (validOwner && pages <= UserHeapPages) {
|
||||
for (uint64_t page = 0; page < UserHeapPages; page++) {
|
||||
bool used = (g_userHeapPageMap[primarySlot_][page / 64]
|
||||
& (1ULL << (page % 64))) != 0;
|
||||
if (used) {
|
||||
runLength = 0;
|
||||
continue;
|
||||
}
|
||||
if (runLength == 0) runStart = page;
|
||||
runLength++;
|
||||
if (runLength == pages) {
|
||||
for (uint64_t p = runStart; p < runStart + pages; p++)
|
||||
g_userHeapPageMap[primarySlot_][p / 64] |= 1ULL << (p % 64);
|
||||
outVa = UserHeapBase + runStart * 0x1000ULL;
|
||||
uint64_t end = outVa + size;
|
||||
if (end > primary.heapNext) primary.heapNext = end;
|
||||
fits = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
schedLock.Release();
|
||||
return fits;
|
||||
}
|
||||
|
||||
void ReleaseUserHeapRange(int primarySlot_, uint64_t va, uint64_t size) {
|
||||
if (primarySlot_ < 0 || primarySlot_ >= MaxProcesses || size == 0 ||
|
||||
(va & 0xFFFULL) != 0 || (size & 0xFFFULL) != 0 ||
|
||||
va < UserHeapBase || va > UserHeapLimit || size > UserHeapLimit - va) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t first = (va - UserHeapBase) / 0x1000ULL;
|
||||
uint64_t pages = size / 0x1000ULL;
|
||||
schedLock.Acquire();
|
||||
for (uint64_t p = first; p < first + pages; p++)
|
||||
g_userHeapPageMap[primarySlot_][p / 64] &= ~(1ULL << (p % 64));
|
||||
schedLock.Release();
|
||||
}
|
||||
|
||||
static void FreeSiblingThreadTls(Process& thr) {
|
||||
int primarySlot_ = thr.primarySlot;
|
||||
if (primarySlot_ < 0 || primarySlot_ >= MaxProcesses ||
|
||||
@@ -811,6 +854,7 @@ namespace Sched {
|
||||
uint64_t pages = (blockSize + 16 + 0xFFF) / 0x1000;
|
||||
thr.fsBase = 0;
|
||||
Ipc::UnmapAndFreeUserRange(primary.pml4Phys, base, pages);
|
||||
ReleaseUserHeapRange(primarySlot_, base, pages * 0x1000ULL);
|
||||
}
|
||||
|
||||
// Switch away from a slot we have just marked non-runnable while holding
|
||||
|
||||
@@ -33,6 +33,9 @@ namespace Sched {
|
||||
// failed bound check is recoverable (SYS_ALLOC returns null); allowing the
|
||||
// heap to cross this boundary would silently replace library PTEs.
|
||||
static constexpr uint64_t UserHeapLimit = 0x60000000ULL;
|
||||
static constexpr uint64_t UserHeapPages =
|
||||
(UserHeapLimit - UserHeapBase) / 0x1000ULL;
|
||||
static constexpr uint64_t UserHeapBitmapWords = (UserHeapPages + 63) / 64;
|
||||
// Surface mappings use reusable, fixed-size per-process slots instead of
|
||||
// consuming heap VA forever. 64 * 32 MiB occupies [0x7000000000,
|
||||
// 0x7080000000), comfortably below the main user stack.
|
||||
@@ -191,6 +194,7 @@ namespace Sched {
|
||||
// reservation is serialized across sibling threads and bounded below the
|
||||
// fixed shared-library mappings.
|
||||
bool ReserveUserHeapRange(int primarySlot, uint64_t size, uint64_t& outVa);
|
||||
void ReleaseUserHeapRange(int primarySlot, uint64_t va, uint64_t size);
|
||||
|
||||
// Terminate the currently executing thread. If this is the main thread,
|
||||
// the entire process exits (equivalent to ExitProcess).
|
||||
@@ -258,5 +262,8 @@ namespace Sched {
|
||||
|
||||
// Per-process allocated page count (tracked by Heap syscalls, separate from Process struct)
|
||||
inline uint64_t g_allocatedPages[MaxProcesses] = {};
|
||||
// One bit per page in the bounded userspace heap. Unlike heapNext, this
|
||||
// makes virtual ranges reusable after unmap and failed reservations.
|
||||
inline uint64_t g_userHeapPageMap[MaxProcesses][UserHeapBitmapWords] = {};
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user