fix: kernel concurrency, interrupt context, and user VA safety
This commit is contained in:
1 parent
f222bf1f04
commit
b1c55073c7
71 files changed
+2654
-1092
No files matched your search
@@ -9,6 +9,9 @@
|
||||
#include <Drivers/USB/Bluetooth/Bluetooth.hpp>
|
||||
#include <Drivers/USB/Bluetooth/Hci.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
|
||||
#include "Syscall.hpp"
|
||||
|
||||
@@ -53,6 +56,9 @@ namespace montauk::abi {
|
||||
if (!bdAddr) return -1;
|
||||
if (!Drivers::USB::Bluetooth::IsInitialized()) return -1;
|
||||
|
||||
Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec
|
||||
<< (uint64_t)Sched::GetCurrentPid()
|
||||
<< " entered Bluetooth disconnect stage";
|
||||
return (int64_t)Drivers::USB::Bluetooth::Disconnect(bdAddr);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MONTAUK_BUILD_NUMBER 33
|
||||
#define MONTAUK_BUILD_NUMBER 37
|
||||
+18
-29
@@ -14,59 +14,47 @@ namespace montauk::abi {
|
||||
}
|
||||
|
||||
static bool ResolveProcessHandle(Sched::Process* proc, int handle, Ipc::HandleType expectedType,
|
||||
Ipc::Object*& outObject, uint32_t* outRights = nullptr) {
|
||||
Ipc::HandleSnapshot& snapshot) {
|
||||
if (proc == nullptr || handle < 0) return false;
|
||||
|
||||
int slot = Ipc::SlotForPid(proc->pid);
|
||||
if (slot < 0) return false;
|
||||
|
||||
Ipc::HandleType type = Ipc::HandleType::None;
|
||||
Ipc::Object* object = nullptr;
|
||||
uint32_t rights = 0;
|
||||
if (!Ipc::SnapshotHandleForSlot(slot, handle, type, object, rights)) return false;
|
||||
if (type != expectedType || object == nullptr) return false;
|
||||
|
||||
outObject = object;
|
||||
if (outRights != nullptr) *outRights = rights;
|
||||
return true;
|
||||
if (!snapshot.Capture(slot, handle)) return false;
|
||||
return snapshot.type == expectedType && snapshot.object != nullptr;
|
||||
}
|
||||
|
||||
static Ipc::Stream* GetRedirOutStream(Sched::Process* proc) {
|
||||
static Ipc::Stream* GetRedirOutStream(Sched::Process* proc, Ipc::HandleSnapshot& snapshot) {
|
||||
proc = GetRedirTarget(proc);
|
||||
if (proc == nullptr) return nullptr;
|
||||
|
||||
Ipc::Object* object = nullptr;
|
||||
if (!ResolveProcessHandle(proc, proc->ioOutHandle, Ipc::HandleType::Stream, object)) return nullptr;
|
||||
return (Ipc::Stream*)object;
|
||||
if (!ResolveProcessHandle(proc, proc->ioOutHandle, Ipc::HandleType::Stream, snapshot)) return nullptr;
|
||||
return (Ipc::Stream*)snapshot.object;
|
||||
}
|
||||
|
||||
static Ipc::Stream* GetRedirInStream(Sched::Process* proc) {
|
||||
static Ipc::Stream* GetRedirInStream(Sched::Process* proc, Ipc::HandleSnapshot& snapshot) {
|
||||
proc = GetRedirTarget(proc);
|
||||
if (proc == nullptr) return nullptr;
|
||||
|
||||
Ipc::Object* object = nullptr;
|
||||
if (!ResolveProcessHandle(proc, proc->ioInHandle, Ipc::HandleType::Stream, object)) return nullptr;
|
||||
return (Ipc::Stream*)object;
|
||||
if (!ResolveProcessHandle(proc, proc->ioInHandle, Ipc::HandleType::Stream, snapshot)) return nullptr;
|
||||
return (Ipc::Stream*)snapshot.object;
|
||||
}
|
||||
|
||||
static Ipc::Mailbox* GetRedirKeyMailbox(Sched::Process* proc) {
|
||||
static Ipc::Mailbox* GetRedirKeyMailbox(Sched::Process* proc, Ipc::HandleSnapshot& snapshot) {
|
||||
proc = GetRedirTarget(proc);
|
||||
if (proc == nullptr) return nullptr;
|
||||
|
||||
Ipc::Object* object = nullptr;
|
||||
if (!ResolveProcessHandle(proc, proc->ioKeyHandle, Ipc::HandleType::Mailbox, object)) return nullptr;
|
||||
return (Ipc::Mailbox*)object;
|
||||
if (!ResolveProcessHandle(proc, proc->ioKeyHandle, Ipc::HandleType::Mailbox, snapshot)) return nullptr;
|
||||
return (Ipc::Mailbox*)snapshot.object;
|
||||
}
|
||||
|
||||
static int DuplicateHandleBetweenSlots(int srcSlot, int handle, int dstSlot) {
|
||||
if (srcSlot < 0 || dstSlot < 0 || handle < 0) return -1;
|
||||
|
||||
Ipc::HandleType type = Ipc::HandleType::None;
|
||||
Ipc::Object* object = nullptr;
|
||||
uint32_t rights = 0;
|
||||
if (!Ipc::SnapshotHandleForSlot(srcSlot, handle, type, object, rights)) return -1;
|
||||
if ((rights & Ipc::RightDup) == 0) return -1;
|
||||
return Ipc::InstallHandleForSlot(dstSlot, object, type, rights);
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
if (!snapshot.Capture(srcSlot, handle)) return -1;
|
||||
if ((snapshot.rights & Ipc::RightDup) == 0) return -1;
|
||||
return Ipc::InstallHandleForSlot(dstSlot, snapshot.object, snapshot.type, snapshot.rights);
|
||||
}
|
||||
|
||||
static bool ConfigureRedirWaitsetForSlot(int slot, Sched::Process* proc) {
|
||||
@@ -100,12 +88,13 @@ namespace montauk::abi {
|
||||
|
||||
int total = 0;
|
||||
while (total < len) {
|
||||
uint64_t observedWake = Sched::ObserveObjectWake(stream);
|
||||
int written = Ipc::StreamWrite(stream, data + total, len - total, false);
|
||||
if (written < 0) {
|
||||
return (total > 0) ? total : -1;
|
||||
}
|
||||
if (written == 0) {
|
||||
Sched::BlockOnObject(stream, 0);
|
||||
Sched::BlockOnObjectSince(stream, 0, observedWake);
|
||||
continue;
|
||||
}
|
||||
total += written;
|
||||
|
||||
@@ -36,8 +36,17 @@ namespace montauk::abi {
|
||||
if (fbBase == nullptr) return 0;
|
||||
|
||||
uint64_t fbPhys = Memory::SubHHDM((uint64_t)fbBase);
|
||||
uint64_t fbSize = Graphics::Framebuffer::GetHeight()
|
||||
* Graphics::Framebuffer::GetPitch();
|
||||
uint64_t width = Graphics::Framebuffer::GetWidth();
|
||||
uint64_t height = Graphics::Framebuffer::GetHeight();
|
||||
uint64_t pitch = Graphics::Framebuffer::GetPitch();
|
||||
if (width == 0 || height == 0 || pitch == 0
|
||||
|| width > (~0ULL / 4) || pitch < width * 4
|
||||
|| height > (~0ULL / pitch)) {
|
||||
Kt::KernelLogStream(Kt::ERROR, "FbMap")
|
||||
<< "Invalid or overflowing framebuffer geometry";
|
||||
return 0;
|
||||
}
|
||||
uint64_t fbSize = height * pitch;
|
||||
uint64_t numPages = (fbSize + 0xFFF) / 0x1000;
|
||||
|
||||
Kt::KernelLogStream(Kt::INFO, "FbMap") << "fbPhys=" << kcp::hex << fbPhys
|
||||
@@ -47,8 +56,28 @@ namespace montauk::abi {
|
||||
<< "x" << Graphics::Framebuffer::GetHeight()
|
||||
<< " pitch=" << Graphics::Framebuffer::GetPitch() << ")";
|
||||
|
||||
// Map at a fixed user VA
|
||||
constexpr uint64_t userVa = 0x50000000ULL;
|
||||
// Map in the dedicated framebuffer arena, away from the heap,
|
||||
// shared libraries, reusable IPC surfaces, and the user stack.
|
||||
constexpr uint64_t userVa = Sched::UserFramebufferBase;
|
||||
uint64_t mappedBytes = numPages * 0x1000ULL;
|
||||
bool flipSupported = Drivers::Graphics::IntelGPU::FlipSupported();
|
||||
uint64_t buffers = flipSupported ? 2 : 1;
|
||||
if (mappedBytes > (Sched::UserFramebufferLimit - userVa) / buffers) {
|
||||
Kt::KernelLogStream(Kt::ERROR, "FbMap")
|
||||
<< "Framebuffer mappings exceed reserved user VA range";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Never expose more of buffer 1 than the GPU driver allocated. This
|
||||
// independently contains a future geometry-contract regression before
|
||||
// any partial framebuffer mapping is installed in the process.
|
||||
if (flipSupported
|
||||
&& (Drivers::Graphics::IntelGPU::GetBufferPhys(1) == 0
|
||||
|| Drivers::Graphics::IntelGPU::GetBufferPageCount(1) < numPages)) {
|
||||
Kt::KernelLogStream(Kt::ERROR, "FbMap")
|
||||
<< "Page-flip buffer is smaller than firmware framebuffer";
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (uint64_t i = 0; i < numPages; i++) {
|
||||
if (!Memory::VMM::Paging::MapUserInWC(
|
||||
@@ -63,7 +92,7 @@ namespace montauk::abi {
|
||||
// When page flipping is available, map the second scanout buffer
|
||||
// immediately after the first so a double-buffered client can reach
|
||||
// both. Single-buffer clients never touch the extra pages.
|
||||
if (Drivers::Graphics::IntelGPU::FlipSupported()) {
|
||||
if (flipSupported) {
|
||||
uint64_t buf1Phys = Drivers::Graphics::IntelGPU::GetBufferPhys(1);
|
||||
for (uint64_t i = 0; i < numPages; i++) {
|
||||
if (!Memory::VMM::Paging::MapUserInWC(
|
||||
@@ -82,6 +111,7 @@ namespace montauk::abi {
|
||||
// SYS_FBFLIP: point scanout at buffer `index` (0 or 1). The hardware
|
||||
// latches the new surface at vblank, so the flip is tear-free.
|
||||
// index == -1 queries support without side effects.
|
||||
// index == -2 acquires flip ownership and returns the live front buffer.
|
||||
static int64_t Sys_FbFlip(uint64_t index, uint64_t flags) {
|
||||
namespace GPU = Drivers::Graphics::IntelGPU;
|
||||
|
||||
@@ -91,9 +121,11 @@ namespace montauk::abi {
|
||||
if (!GPU::FlipSupported()) return -1;
|
||||
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc != nullptr) GPU::SetFlipOwner(proc->pid);
|
||||
|
||||
return GPU::Flip((int)index, (flags & 1) != 0);
|
||||
if ((int64_t)index == -2) {
|
||||
return proc != nullptr ? GPU::AcquireFlip(proc->pid) : -1;
|
||||
}
|
||||
if (proc == nullptr) return -1;
|
||||
return GPU::Flip((int)index, (flags & 1) != 0, proc->pid);
|
||||
}
|
||||
|
||||
static int64_t Sys_DisplayInfo(DisplayInfo* out) {
|
||||
|
||||
+32
-35
@@ -9,6 +9,8 @@
|
||||
#include <Memory/Paging.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
#include <Memory/PageFrameAllocator.hpp>
|
||||
#include <CppLib/Spinlock.hpp>
|
||||
#include <Ipc/Ipc.hpp>
|
||||
|
||||
namespace montauk::abi {
|
||||
|
||||
@@ -22,6 +24,7 @@ namespace montauk::abi {
|
||||
|
||||
inline HeapAlloc g_heapAllocs[Sched::MaxProcesses][MaxHeapAllocs] = {};
|
||||
inline int g_heapAllocCount[Sched::MaxProcesses] = {};
|
||||
inline kcp::Mutex g_heapLocks[Sched::MaxProcesses];
|
||||
|
||||
// Get the process table slot index for the current process
|
||||
inline int GetCurrentSlot() {
|
||||
@@ -38,20 +41,15 @@ namespace montauk::abi {
|
||||
int slot = GetCurrentSlot();
|
||||
if (slot < 0) return 0;
|
||||
|
||||
// Guard against overflow before rounding
|
||||
static constexpr uint64_t USER_SPACE_END = 0x0000800000000000ULL;
|
||||
// Guard against overflow before rounding.
|
||||
if (size > 0xFFFFFFFFFFFF0000ULL) return 0;
|
||||
|
||||
// Round up to page boundary
|
||||
size = (size + 0xFFF) & ~0xFFFULL;
|
||||
if (size == 0) size = 0x1000;
|
||||
|
||||
uint64_t userVa = proc->heapNext;
|
||||
|
||||
// Ensure allocation stays within user address space
|
||||
if (userVa + size < userVa || userVa + size > USER_SPACE_END) return 0;
|
||||
|
||||
uint64_t numPages = size / 0x1000;
|
||||
g_heapLocks[slot].Acquire();
|
||||
if (g_heapAllocCount[slot] >= MaxHeapAllocs) {
|
||||
// Out of allocation records, not out of memory. Log it: a
|
||||
// silent 0 here surfaced as bogus downstream errors (BFD
|
||||
@@ -60,6 +58,16 @@ namespace montauk::abi {
|
||||
<< "pid " << proc->pid << " (" << proc->name
|
||||
<< ") hit MaxHeapAllocs (" << (uint64_t)MaxHeapAllocs
|
||||
<< "), SYS_ALLOC refused";
|
||||
g_heapLocks[slot].Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t userVa = 0;
|
||||
if (!Sched::ReserveUserHeapRange(slot, size, userVa)) {
|
||||
Kt::KernelLogStream(Kt::ERROR, "Heap")
|
||||
<< "pid " << proc->pid << " (" << proc->name
|
||||
<< ") exhausted bounded user heap";
|
||||
g_heapLocks[slot].Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -68,38 +76,25 @@ namespace montauk::abi {
|
||||
for (uint64_t i = 0; i < numPages; i++) {
|
||||
void* page = Memory::g_pfa->AllocateZeroed();
|
||||
if (page == nullptr) {
|
||||
for (uint64_t j = 0; j < mappedPages; j++) {
|
||||
uint64_t pageVa = userVa + j * 0x1000;
|
||||
uint64_t physAddr = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, pageVa);
|
||||
if (physAddr != 0) {
|
||||
Memory::g_pfa->Free((void*)Memory::HHDM(physAddr));
|
||||
}
|
||||
Memory::VMM::Paging::UnmapUserIn(proc->pml4Phys, pageVa);
|
||||
}
|
||||
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, userVa, mappedPages);
|
||||
g_heapLocks[slot].Release();
|
||||
return 0;
|
||||
}
|
||||
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
||||
if (!Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa + i * 0x1000)) {
|
||||
Memory::g_pfa->Free(page);
|
||||
for (uint64_t j = 0; j < mappedPages; j++) {
|
||||
uint64_t pageVa = userVa + j * 0x1000;
|
||||
uint64_t mappedPhys = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, pageVa);
|
||||
if (mappedPhys != 0) {
|
||||
Memory::g_pfa->Free((void*)Memory::HHDM(mappedPhys));
|
||||
}
|
||||
Memory::VMM::Paging::UnmapUserIn(proc->pml4Phys, pageVa);
|
||||
}
|
||||
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, userVa, mappedPages);
|
||||
g_heapLocks[slot].Release();
|
||||
return 0;
|
||||
}
|
||||
mappedPages++;
|
||||
}
|
||||
|
||||
proc->heapNext += size;
|
||||
|
||||
// Track the allocation so Sys_Free can release it
|
||||
Sched::g_allocatedPages[slot] += numPages;
|
||||
g_heapAllocs[slot][g_heapAllocCount[slot]++] = { userVa, numPages };
|
||||
|
||||
g_heapLocks[slot].Release();
|
||||
return userVa;
|
||||
}
|
||||
|
||||
@@ -107,8 +102,10 @@ namespace montauk::abi {
|
||||
// The actual physical pages are freed by Paging::FreeUserHalf() during process cleanup.
|
||||
inline void CleanupHeapForSlot(int slot, uint64_t /*pml4Phys*/) {
|
||||
if (slot < 0 || slot >= Sched::MaxProcesses) return;
|
||||
g_heapLocks[slot].Acquire();
|
||||
g_heapAllocCount[slot] = 0;
|
||||
Sched::g_allocatedPages[slot] = 0;
|
||||
g_heapLocks[slot].Release();
|
||||
}
|
||||
|
||||
inline void Sys_Free(uint64_t addr) {
|
||||
@@ -118,6 +115,8 @@ namespace montauk::abi {
|
||||
int slot = GetCurrentSlot();
|
||||
if (slot < 0) return;
|
||||
|
||||
g_heapLocks[slot].Acquire();
|
||||
|
||||
// Find the allocation record matching this address
|
||||
int idx = -1;
|
||||
for (int i = 0; i < g_heapAllocCount[slot]; i++) {
|
||||
@@ -126,25 +125,23 @@ namespace montauk::abi {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx < 0) return; // Unknown address — ignore
|
||||
if (idx < 0) {
|
||||
g_heapLocks[slot].Release();
|
||||
return; // Unknown address — ignore
|
||||
}
|
||||
|
||||
uint64_t va = g_heapAllocs[slot][idx].va;
|
||||
uint64_t numPages = g_heapAllocs[slot][idx].numPages;
|
||||
|
||||
// Free physical pages in bulk and unmap virtual addresses
|
||||
for (uint64_t i = 0; i < numPages; i++) {
|
||||
uint64_t pageVa = va + i * 0x1000;
|
||||
uint64_t physAddr = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, pageVa);
|
||||
if (physAddr != 0) {
|
||||
Memory::g_pfa->Free((void*)Memory::HHDM(physAddr));
|
||||
}
|
||||
Memory::VMM::Paging::UnmapUserIn(proc->pml4Phys, pageVa);
|
||||
}
|
||||
// Unmap and invalidate sibling CPUs before recycling the frames. A
|
||||
// stale user TLB entry can otherwise corrupt the frame's next owner.
|
||||
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, va, numPages);
|
||||
|
||||
Sched::g_allocatedPages[slot] -= numPages;
|
||||
|
||||
// Remove tracking entry by swapping with the last element
|
||||
g_heapAllocs[slot][idx] = g_heapAllocs[slot][g_heapAllocCount[slot] - 1];
|
||||
g_heapAllocCount[slot]--;
|
||||
g_heapLocks[slot].Release();
|
||||
}
|
||||
};
|
||||
@@ -85,14 +85,16 @@ namespace montauk::abi {
|
||||
|
||||
static int Sys_ChildIoRead(int childPid, char* buf, int maxLen) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
Ipc::Stream* stream = GetRedirOutStream(child);
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Stream* stream = GetRedirOutStream(child, snapshot);
|
||||
if (child == nullptr || !child->redirected || stream == nullptr) return -1;
|
||||
return Ipc::StreamRead(stream, (uint8_t*)buf, maxLen, true);
|
||||
}
|
||||
|
||||
static int Sys_ChildIoWrite(int childPid, const char* data, int len) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
Ipc::Stream* stream = GetRedirInStream(child);
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Stream* stream = GetRedirInStream(child, snapshot);
|
||||
if (child == nullptr || !child->redirected || stream == nullptr) return -1;
|
||||
return WriteAllToStream(stream, (const uint8_t*)data, len);
|
||||
}
|
||||
@@ -100,14 +102,16 @@ namespace montauk::abi {
|
||||
static int Sys_ChildIoWriteKey(int childPid, const KeyEvent* key) {
|
||||
if (key == nullptr) return -1;
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(child);
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(child, snapshot);
|
||||
if (child == nullptr || !child->redirected || mailbox == nullptr) return -1;
|
||||
|
||||
for (;;) {
|
||||
uint64_t observedWake = Sched::ObserveObjectWake(mailbox);
|
||||
int rc = Ipc::MailboxSend(mailbox, 0, key, sizeof(KeyEvent));
|
||||
if (rc < 0) return -1;
|
||||
if (rc == 0) {
|
||||
Sched::BlockOnObject(mailbox, 0);
|
||||
Sched::BlockOnObjectSince(mailbox, 0, observedWake);
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -15,7 +15,8 @@ namespace montauk::abi {
|
||||
static bool Sys_IsKeyAvailable() {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc);
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc, snapshot);
|
||||
if (mailbox != nullptr) return Ipc::MailboxHasMessage(mailbox);
|
||||
}
|
||||
return Drivers::PS2::Keyboard::IsKeyAvailable();
|
||||
@@ -25,9 +26,11 @@ namespace montauk::abi {
|
||||
if (outEvent == nullptr) return;
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc);
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc, snapshot);
|
||||
if (mailbox != nullptr) {
|
||||
for (;;) {
|
||||
uint64_t observedWake = Sched::ObserveObjectWake(mailbox);
|
||||
uint16_t len = sizeof(KeyEvent);
|
||||
int rc = Ipc::MailboxRecv(mailbox, nullptr, outEvent, &len, true);
|
||||
if (rc > 0) return;
|
||||
@@ -35,7 +38,7 @@ namespace montauk::abi {
|
||||
memset(outEvent, 0, sizeof(KeyEvent));
|
||||
return;
|
||||
}
|
||||
Sched::BlockOnObject(mailbox, 0);
|
||||
Sched::BlockOnObjectSince(mailbox, 0, observedWake);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,10 +54,14 @@ namespace montauk::abi {
|
||||
static char Sys_GetChar() {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
Ipc::Stream* input = GetRedirInStream(proc);
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc);
|
||||
Ipc::HandleSnapshot inputSnapshot;
|
||||
Ipc::HandleSnapshot mailboxSnapshot;
|
||||
Ipc::Stream* input = GetRedirInStream(proc, inputSnapshot);
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc, mailboxSnapshot);
|
||||
if (input != nullptr || mailbox != nullptr) {
|
||||
for (;;) {
|
||||
uint64_t inputWake = input ? Sched::ObserveObjectWake(input) : 0;
|
||||
uint64_t mailboxWake = mailbox ? Sched::ObserveObjectWake(mailbox) : 0;
|
||||
if (input != nullptr) {
|
||||
uint8_t c = 0;
|
||||
int rc = Ipc::StreamRead(input, &c, 1, true);
|
||||
@@ -79,9 +86,9 @@ namespace montauk::abi {
|
||||
return 0;
|
||||
}
|
||||
} else if (input != nullptr) {
|
||||
Sched::BlockOnObject(input, 0);
|
||||
Sched::BlockOnObjectSince(input, 0, inputWake);
|
||||
} else if (mailbox != nullptr) {
|
||||
Sched::BlockOnObject(mailbox, 0);
|
||||
Sched::BlockOnObjectSince(mailbox, 0, mailboxWake);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <Libraries/String.hpp>
|
||||
#include <CppLib/Spinlock.hpp>
|
||||
#include <Ipc/Ipc.hpp>
|
||||
|
||||
namespace montauk::abi {
|
||||
|
||||
@@ -34,16 +36,19 @@ namespace montauk::abi {
|
||||
|
||||
// Per-process library table
|
||||
inline LibEntry g_libTable[MaxProcesses][MaxLibsPerProcess] = {};
|
||||
inline kcp::Mutex g_libLocks[MaxProcesses];
|
||||
|
||||
// Initialize library table for a process slot
|
||||
inline void InitLibTable(int slot) {
|
||||
if (slot < 0 || slot >= MaxProcesses) return;
|
||||
g_libLocks[slot].Acquire();
|
||||
for (int i = 0; i < MaxLibsPerProcess; i++) {
|
||||
g_libTable[slot][i].inUse = false;
|
||||
g_libTable[slot][i].refcount = 0;
|
||||
g_libTable[slot][i].loadBias = 0;
|
||||
g_libTable[slot][i].path[0] = '\0';
|
||||
}
|
||||
g_libLocks[slot].Release();
|
||||
}
|
||||
|
||||
// Load a shared library into the current process's address space.
|
||||
@@ -61,6 +66,8 @@ namespace montauk::abi {
|
||||
size_t pathLen = Lib::strlen(path);
|
||||
if (pathLen == 0 || pathLen >= sizeof(LibEntry::path)) return 0;
|
||||
|
||||
g_libLocks[slot].Acquire();
|
||||
|
||||
// Find a free slot or reuse an existing slot with the same path
|
||||
int libSlot = -1;
|
||||
for (int i = 0; i < MaxLibsPerProcess; i++) {
|
||||
@@ -68,6 +75,7 @@ namespace montauk::abi {
|
||||
if (Lib::strncmp(g_libTable[slot][i].path, path, sizeof(LibEntry::path)) == 0) {
|
||||
// Same library already loaded - just increment refcount
|
||||
g_libTable[slot][i].refcount++;
|
||||
g_libLocks[slot].Release();
|
||||
return (uint64_t)(i + 1); // Handle = slot + 1 (0 = invalid)
|
||||
}
|
||||
} else if (libSlot < 0) {
|
||||
@@ -77,6 +85,7 @@ namespace montauk::abi {
|
||||
|
||||
if (libSlot < 0) {
|
||||
Kt::KernelLogStream(Kt::ERROR, "Lib") << "No free library slots";
|
||||
g_libLocks[slot].Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -84,6 +93,7 @@ namespace montauk::abi {
|
||||
uint64_t loadBias = Sched::ElfLoadLib(path, proc->pml4Phys, libSlot);
|
||||
if (loadBias == 0) {
|
||||
Kt::KernelLogStream(Kt::ERROR, "Lib") << "Failed to load library: " << path;
|
||||
g_libLocks[slot].Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -93,6 +103,8 @@ namespace montauk::abi {
|
||||
g_libTable[slot][libSlot].loadBias = loadBias;
|
||||
Lib::strncpy(g_libTable[slot][libSlot].path, path, sizeof(LibEntry::path));
|
||||
|
||||
g_libLocks[slot].Release();
|
||||
|
||||
Kt::KernelLogStream(Kt::OK, "Lib") << "Loaded library: " << path << " with load bias " << kcp::hex << loadBias << kcp::dec;
|
||||
|
||||
return (uint64_t)(libSlot + 1); // Handle = slot + 1 (0 = invalid)
|
||||
@@ -107,7 +119,11 @@ namespace montauk::abi {
|
||||
int libSlot = (int)handle - 1;
|
||||
if (libSlot < 0 || libSlot >= MaxLibsPerProcess) return -1;
|
||||
|
||||
if (!g_libTable[slot][libSlot].inUse) return -1;
|
||||
g_libLocks[slot].Acquire();
|
||||
if (!g_libTable[slot][libSlot].inUse) {
|
||||
g_libLocks[slot].Release();
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_libTable[slot][libSlot].refcount--;
|
||||
if (g_libTable[slot][libSlot].refcount == 0) {
|
||||
@@ -117,14 +133,8 @@ namespace montauk::abi {
|
||||
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc != nullptr) {
|
||||
// Unmap all pages in the library region
|
||||
for (uint64_t va = libBase; va < libEnd; va += 0x1000) {
|
||||
uint64_t physAddr = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, va);
|
||||
if (physAddr != 0) {
|
||||
Memory::g_pfa->Free((void*)Memory::HHDM(physAddr));
|
||||
Memory::VMM::Paging::UnmapUserIn(proc->pml4Phys, va);
|
||||
}
|
||||
}
|
||||
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, libBase,
|
||||
(libEnd - libBase) / 0x1000ULL);
|
||||
}
|
||||
|
||||
g_libTable[slot][libSlot].inUse = false;
|
||||
@@ -132,6 +142,7 @@ namespace montauk::abi {
|
||||
g_libTable[slot][libSlot].path[0] = '\0';
|
||||
}
|
||||
|
||||
g_libLocks[slot].Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -146,9 +157,14 @@ namespace montauk::abi {
|
||||
int libSlot = (int)handle - 1;
|
||||
if (libSlot < 0 || libSlot >= MaxLibsPerProcess) return 0;
|
||||
|
||||
if (!g_libTable[slot][libSlot].inUse) return 0;
|
||||
g_libLocks[slot].Acquire();
|
||||
if (!g_libTable[slot][libSlot].inUse) {
|
||||
g_libLocks[slot].Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t loadBias = g_libTable[slot][libSlot].loadBias;
|
||||
g_libLocks[slot].Release();
|
||||
return loadBias + symbolOffset;
|
||||
}
|
||||
|
||||
@@ -160,9 +176,15 @@ namespace montauk::abi {
|
||||
int libSlot = (int)handle - 1;
|
||||
if (libSlot < 0 || libSlot >= MaxLibsPerProcess) return 0;
|
||||
|
||||
if (!g_libTable[slot][libSlot].inUse) return 0;
|
||||
g_libLocks[slot].Acquire();
|
||||
if (!g_libTable[slot][libSlot].inUse) {
|
||||
g_libLocks[slot].Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return g_libTable[slot][libSlot].loadBias;
|
||||
uint64_t loadBias = g_libTable[slot][libSlot].loadBias;
|
||||
g_libLocks[slot].Release();
|
||||
return loadBias;
|
||||
}
|
||||
|
||||
// Cleanup library table for a process slot
|
||||
@@ -172,25 +194,23 @@ namespace montauk::abi {
|
||||
auto* proc = Sched::GetProcessSlot(slot);
|
||||
if (proc == nullptr) return;
|
||||
|
||||
g_libLocks[slot].Acquire();
|
||||
|
||||
// Unmap all libraries for this process
|
||||
for (int i = 0; i < MaxLibsPerProcess; i++) {
|
||||
if (g_libTable[slot][i].inUse) {
|
||||
uint64_t libBase = GetLibSlotBase(i);
|
||||
uint64_t libEnd = libBase + Sched::LIB_MAX_SIZE;
|
||||
|
||||
for (uint64_t va = libBase; va < libEnd; va += 0x1000) {
|
||||
uint64_t physAddr = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, va);
|
||||
if (physAddr != 0) {
|
||||
Memory::g_pfa->Free((void*)Memory::HHDM(physAddr));
|
||||
Memory::VMM::Paging::UnmapUserIn(proc->pml4Phys, va);
|
||||
}
|
||||
}
|
||||
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, libBase,
|
||||
(libEnd - libBase) / 0x1000ULL);
|
||||
|
||||
g_libTable[slot][i].inUse = false;
|
||||
g_libTable[slot][i].refcount = 0;
|
||||
g_libTable[slot][i].loadBias = 0;
|
||||
}
|
||||
}
|
||||
g_libLocks[slot].Release();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,9 @@
|
||||
#include <ACPI/AcpiSleep.hpp>
|
||||
#include <ACPI/CpuIdle.hpp>
|
||||
#include <Hal/CpuPower.hpp>
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
|
||||
#include "Syscall.hpp"
|
||||
|
||||
@@ -31,13 +34,21 @@ namespace montauk::abi {
|
||||
if (action == POWER_REQ_QUERY) {
|
||||
int pending = g_pendingPowerAction;
|
||||
g_pendingPowerAction = POWER_REQ_QUERY;
|
||||
Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec
|
||||
<< (uint64_t)Sched::GetCurrentPid()
|
||||
<< " consumed graceful power request " << (uint64_t)pending;
|
||||
return (int64_t)pending;
|
||||
}
|
||||
g_pendingPowerAction = action;
|
||||
Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec
|
||||
<< (uint64_t)Sched::GetCurrentPid()
|
||||
<< " posted graceful power request " << (uint64_t)action;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Sys_Reset() {
|
||||
Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec
|
||||
<< (uint64_t)Sched::GetCurrentPid() << " entered final reboot stage";
|
||||
if (Efi::g_ResetSystem) {
|
||||
/* Switch to kernel PML4 which has identity-mapped UEFI runtime regions */
|
||||
Memory::VMM::LoadCR3(Memory::VMM::g_paging->PML4);
|
||||
@@ -51,6 +62,8 @@ namespace montauk::abi {
|
||||
}
|
||||
|
||||
static void Sys_Shutdown() {
|
||||
Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec
|
||||
<< (uint64_t)Sched::GetCurrentPid() << " entered final shutdown stage";
|
||||
/* Primary: ACPI S5 shutdown via PM1 control registers */
|
||||
if (Hal::AcpiShutdown::IsAvailable()) {
|
||||
Hal::AcpiShutdown::Shutdown();
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#include <Fs/Fat32.hpp>
|
||||
#include <Fs/Ext2.hpp>
|
||||
#include <Api/UserMemory.hpp>
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
|
||||
#include "Syscall.hpp"
|
||||
|
||||
@@ -112,6 +115,9 @@ namespace montauk::abi {
|
||||
// Flush all block-device write caches and cleanly unmount disk-backed
|
||||
// volumes ahead of power-off. Returns the number of volumes unmounted.
|
||||
static int64_t Sys_FsSync() {
|
||||
Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec
|
||||
<< (uint64_t)Sched::GetCurrentPid()
|
||||
<< " entered filesystem sync stage";
|
||||
return (int64_t)Fs::FsProbe::SyncAndUnmountAll();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@ namespace montauk::abi {
|
||||
static void Sys_Print(const char* text) {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
Ipc::Stream* stream = GetRedirOutStream(proc);
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Stream* stream = GetRedirOutStream(proc, snapshot);
|
||||
if (stream != nullptr) {
|
||||
int len = 0;
|
||||
while (text[len]) len++;
|
||||
@@ -33,7 +34,8 @@ namespace montauk::abi {
|
||||
static void Sys_Putchar(char c) {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
Ipc::Stream* stream = GetRedirOutStream(proc);
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Stream* stream = GetRedirOutStream(proc, snapshot);
|
||||
if (stream != nullptr) {
|
||||
uint8_t byte = (uint8_t)c;
|
||||
WriteAllToStream(stream, &byte, 1);
|
||||
|
||||
Reference in new issue
Block a user