feat: expand user mode, add DOOM game, add manpages

This commit is contained in:
2026-02-18 15:13:53 +01:00
parent f384d4cf75
commit fb8d16ee7b
50 changed files with 4484 additions and 43 deletions
+4 -9
View File
@@ -52,8 +52,6 @@ namespace Sched {
}
uint64_t ElfLoad(const char* vfsPath, uint64_t pml4Phys) {
Kt::KernelLogStream(Kt::INFO, "ELF") << "Loading " << vfsPath;
int handle = Fs::Vfs::VfsOpen(vfsPath);
if (handle < 0) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to open " << vfsPath;
@@ -78,6 +76,10 @@ namespace Sched {
Fs::Vfs::VfsRead(handle, fileData, 0, fileSize);
Fs::Vfs::VfsClose(handle);
// Prevent the optimizer from reordering the VfsRead store past the
// header validation reads that follow.
asm volatile("" ::: "memory");
// Validate ELF header
Elf64Header* hdr = (Elf64Header*)fileData;
if (!ValidateElfHeader(hdr)) {
@@ -85,9 +87,6 @@ namespace Sched {
return 0;
}
Kt::KernelLogStream(Kt::OK, "ELF") << "Entry point: " << kcp::hex << hdr->e_entry << kcp::dec
<< ", " << (uint64_t)hdr->e_phnum << " program header(s)";
// Process program headers
for (uint16_t i = 0; i < hdr->e_phnum; i++) {
Elf64ProgramHeader* phdr = (Elf64ProgramHeader*)(fileData + hdr->e_phoff + i * hdr->e_phentsize);
@@ -100,9 +99,6 @@ namespace Sched {
continue;
}
Kt::KernelLogStream(Kt::INFO, "ELF") << "PT_LOAD: vaddr=" << kcp::hex << phdr->p_vaddr
<< " filesz=" << phdr->p_filesz << " memsz=" << phdr->p_memsz << kcp::dec;
// Allocate pages and map them in the process PML4 with User bit
uint64_t segBase = phdr->p_vaddr & ~0xFFFULL;
uint64_t segEnd = (phdr->p_vaddr + phdr->p_memsz + 0xFFF) & ~0xFFFULL;
@@ -147,7 +143,6 @@ namespace Sched {
uint64_t entryPoint = hdr->e_entry;
Memory::g_heap->Free(fileData);
Kt::KernelLogStream(Kt::OK, "ELF") << "Loaded successfully, entry=" << kcp::hex << entryPoint << kcp::dec;
return entryPoint;
}
+25 -14
View File
@@ -75,6 +75,7 @@ namespace Sched {
processTable[i].kernelStackTop = 0;
processTable[i].userStackTop = 0;
processTable[i].heapNext = 0;
processTable[i].args[0] = '\0';
}
currentPid = -1;
@@ -85,7 +86,7 @@ namespace Sched {
<< " process slots, " << (uint64_t)TimeSliceMs << " ms time slice)";
}
void Spawn(const char* vfsPath) {
int Spawn(const char* vfsPath, const char* args) {
int slot = -1;
for (int i = 0; i < MaxProcesses; i++) {
if (processTable[i].state == ProcessState::Free) {
@@ -96,7 +97,7 @@ namespace Sched {
if (slot < 0) {
Kt::KernelLogStream(Kt::ERROR, "Sched") << "No free process slots";
return;
return -1;
}
// Create per-process PML4 with kernel-half copied
@@ -106,20 +107,20 @@ namespace Sched {
uint64_t entry = ElfLoad(vfsPath, pml4Phys);
if (entry == 0) {
Kt::KernelLogStream(Kt::ERROR, "Sched") << "Failed to load ELF: " << vfsPath;
return;
return -1;
}
// Allocate kernel stack (used during syscalls and interrupts)
void* firstPage = Memory::g_pfa->AllocateZeroed();
if (firstPage == nullptr) {
Kt::KernelLogStream(Kt::ERROR, "Sched") << "Out of memory for kernel stack";
return;
return -1;
}
void* stackMem = Memory::g_pfa->ReallocConsecutive(firstPage, StackPages);
if (stackMem == nullptr) {
Kt::KernelLogStream(Kt::ERROR, "Sched") << "Failed to allocate contiguous kernel stack";
Memory::g_pfa->Free(firstPage);
return;
return -1;
}
uint8_t* kernelStackBase = (uint8_t*)stackMem;
@@ -132,7 +133,7 @@ namespace Sched {
void* page = Memory::g_pfa->AllocateZeroed();
if (page == nullptr) {
Kt::KernelLogStream(Kt::ERROR, "Sched") << "Out of memory for user stack";
return;
return -1;
}
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
Memory::VMM::Paging::MapUserIn(pml4Phys, physAddr, userStackBase + i * 0x1000);
@@ -145,7 +146,7 @@ namespace Sched {
void* stubPage = Memory::g_pfa->AllocateZeroed();
if (stubPage == nullptr) {
Kt::KernelLogStream(Kt::ERROR, "Sched") << "Out of memory for exit stub";
return;
return -1;
}
uint64_t stubPhys = Memory::SubHHDM((uint64_t)stubPage);
Memory::VMM::Paging::MapUserIn(pml4Phys, stubPhys, ExitStubAddr);
@@ -189,11 +190,17 @@ namespace Sched {
proc.userStackTop = UserStackTop - 8; // account for pushed exit stub return address
proc.heapNext = UserHeapBase;
Kt::KernelLogStream(Kt::OK, "Sched") << "Spawned process " << (uint64_t)proc.pid
<< " (" << vfsPath << ") entry=" << kcp::hex << entry
<< " kstack=" << (uint64_t)kernelStackBase << "-" << kernelStackTop
<< " ustack=" << userStackBase << "-" << UserStackTop
<< " pml4=" << pml4Phys << kcp::dec;
// Copy arguments string into process
proc.args[0] = '\0';
if (args != nullptr) {
int i = 0;
for (; i < 255 && args[i]; i++) {
proc.args[i] = args[i];
}
proc.args[i] = '\0';
}
return proc.pid;
}
void Schedule() {
@@ -271,8 +278,6 @@ namespace Sched {
return;
}
Kt::KernelLogStream(Kt::OK, "Sched") << "Process " << (uint64_t)processTable[currentPid].pid << " terminated";
processTable[currentPid].state = ProcessState::Terminated;
int next = -1;
@@ -305,4 +310,10 @@ namespace Sched {
}
}
bool IsAlive(int pid) {
if (pid < 0 || pid >= MaxProcesses) return false;
return processTable[pid].state == ProcessState::Ready
|| processTable[pid].state == ProcessState::Running;
}
}
+5 -1
View File
@@ -38,10 +38,11 @@ namespace Sched {
uint64_t kernelStackTop; // Top of kernel stack (for TSS RSP0 / SYSCALL)
uint64_t userStackTop; // User-space stack top
uint64_t heapNext; // Simple bump allocator for user heap
char args[256]; // Command-line arguments (set by parent via Spawn)
};
void Initialize();
void Spawn(const char* vfsPath);
int Spawn(const char* vfsPath, const char* args = nullptr);
void Schedule();
// Called from the APIC timer handler on every tick.
@@ -56,4 +57,7 @@ namespace Sched {
// Called by terminated processes to mark themselves done
void ExitProcess();
// Check if a process is still alive (Ready or Running)
bool IsAlive(int pid);
}