diff --git a/Architecture/amd64/cpu/AdvancedProgrammableInterruptController.cpp b/Architecture/amd64/cpu/AdvancedProgrammableInterruptController.cpp index 6cd8026..724c431 100644 --- a/Architecture/amd64/cpu/AdvancedProgrammableInterruptController.cpp +++ b/Architecture/amd64/cpu/AdvancedProgrammableInterruptController.cpp @@ -1,6 +1,7 @@ #include "apic.hpp" #include +#include #include #include #include @@ -49,7 +50,10 @@ namespace APIC void APIC::Write(uint32_t Register, uint32_t Value) { - if (Register != APIC_EOI) + if (Register != APIC_EOI && + Register != APIC_TDCR && + Register != APIC_TIMER && + Register != APIC_TICR) debug("APIC::Write(%#lx, %#lx) [x2=%d]", Register, Value, x2APICSupported ? 1 : 0); if (x2APICSupported) { @@ -209,6 +213,9 @@ namespace APIC void Timer::OnInterruptReceived(CPU::x64::TrapFrame *Frame) { // fixme("APIC IRQ0 INTERRUPT RECEIVED ON CPU %d", CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE)); + // UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM1).Write('\n'); + // UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM1).Write('H'); + // UniversalAsynchronousReceiverTransmitter::UART(UniversalAsynchronousReceiverTransmitter::COM1).Write('\n'); } void Timer::OneShot(uint32_t Vector, uint64_t Miliseconds) @@ -231,9 +238,9 @@ namespace APIC IOIn = (IOIn & 0xFD) | 1; outb(0x61, IOIn); outb(0x43, 178); - outb(0x42, Ticks & 0xff); + outb(0x40, Ticks & 0xff); inb(0x60); - outb(0x42, Ticks >> 8); + outb(0x40, Ticks >> 8); this->lapic->Write(APIC_TICR, 0xFFFFFFFF); diff --git a/Architecture/amd64/cpu/SymmetricMultiprocessing.cpp b/Architecture/amd64/cpu/SymmetricMultiprocessing.cpp index 721d376..7cbcba2 100644 --- a/Architecture/amd64/cpu/SymmetricMultiprocessing.cpp +++ b/Architecture/amd64/cpu/SymmetricMultiprocessing.cpp @@ -47,7 +47,8 @@ extern "C" void StartCPU() CPU::Interrupts(CPU::Enable); KPrint("\e058C19CPU \e8888FF%d \e058C19is online", CoreID); CPUEnabled = true; - CPU::Stop(); // Stop and surpress interrupts. + while (1) + CPU::Halt(); } namespace SMP @@ -100,7 +101,7 @@ namespace SMP ((APIC::APIC *)Interrupts::apic[0])->Write(APIC::APIC_ICRLO, 0x600 | ((uint32_t)TRAMPOLINE_START / PAGE_SIZE)); while (!CPUEnabled) - ; + CPU::Pause(); trace("CPU %d loaded.", ((ACPI::MADT *)madt)->lapic[i]->APICId); KernelAllocator.FreePages((void *)*reinterpret_cast(STACK), TO_PAGES(STACK_SIZE)); diff --git a/Core/Interrupts/IntManager.cpp b/Core/Interrupts/IntManager.cpp index 8968d75..b347e08 100644 --- a/Core/Interrupts/IntManager.cpp +++ b/Core/Interrupts/IntManager.cpp @@ -118,6 +118,7 @@ namespace Interrupts if (apic[Core]) { ((APIC::APIC *)Interrupts::apic[Core])->EOI(); + // apic->IPI(CurrentCPU->ID, SchedulerInterrupt); ?????? return; } // TODO: PIC diff --git a/KThread.cpp b/KThread.cpp index 333b8fb..550444a 100644 --- a/KThread.cpp +++ b/KThread.cpp @@ -6,7 +6,8 @@ void KernelMainThread() { KPrint("Kernel main thread started!"); // asm("int $0x1"); - CPU::Stop(); + while (1) + CPU::Halt(); } void KernelShutdownThread(bool Reboot) diff --git a/Kernel.cpp b/Kernel.cpp index fa2b604..7d22d20 100644 --- a/Kernel.cpp +++ b/Kernel.cpp @@ -57,11 +57,11 @@ EXTERNC void Entry(BootInfo *Info) KPrint("CPU: \e8822AA%s \e8888FF%s (\e058C19%s\e8888FF)", CPU::Vendor(), CPU::Name(), CPU::Hypervisor()); KPrint("Initializing GDT and IDT"); Interrupts::Initialize(0); - KPrint("Initializing CPU features"); + KPrint("Initializing CPU Features"); CPU::InitializeFeatures(); - KPrint("Loading kernel symbols"); + KPrint("Loading Kernel Symbols"); KernelSymbolTable = new SymbolResolver::Symbols((uint64_t)Info->Kernel.FileBase); - KPrint("Reading kernel parameters"); + KPrint("Reading Kernel Parameters"); Config = ParseConfig((char *)bInfo->Kernel.CommandLine); KPrint("Initializing Power Manager"); PowerManager = new Power::Power; @@ -76,14 +76,14 @@ EXTERNC void Entry(BootInfo *Info) PCI::Descriptors::GetSubclassName(Device->Class, Device->Subclass), PCI::Descriptors::GetProgIFName(Device->Class, Device->Subclass, Device->ProgIF)); } - KPrint("Enabling interrupts"); + KPrint("Enabling Interrupts on Bootstrap Processor"); Interrupts::Enable(0); - KPrint("Initializing timer"); + KPrint("Initializing Bootstrap Processor Timer"); Interrupts::InitializeTimer(0); KPrint("Initializing SMP"); SMP::Initialize(PowerManager->GetMADT()); - CPU::Interrupts(CPU::Enable); TaskManager = new Tasking::Task((Tasking::IP)KernelMainThread); KPrint("\e058C19######## \eE85230END \e058C19########"); - CPU::Stop(); + while (1) + CPU::Halt(); } diff --git a/Tasking/Task.cpp b/Tasking/Task.cpp index 5e81a5c..3020252 100644 --- a/Tasking/Task.cpp +++ b/Tasking/Task.cpp @@ -13,7 +13,7 @@ #elif defined(__aarch64__) #endif -#define DEBUG_SCHEDULER 1 +// #define DEBUG_SCHEDULER 1 #ifdef DEBUG_SCHEDULER #define schedbg(m, ...) debug(m, ##__VA_ARGS__) @@ -50,15 +50,266 @@ namespace Tasking #endif } - Vector ListProcess; - PCB *IdleProcess = nullptr; - TCB *IdleThread = nullptr; - #if defined(__amd64__) __attribute__((no_stack_protector)) void Task::OnInterruptReceived(CPU::x64::TrapFrame *Frame) { - fixme("unimplemented"); - OneShot(500); // test + SmartCriticalSection(TaskingLock); + CPUData *CurrentCPU = GetCurrentCPU(); + debug("Scheduler called on CPU %d.", CurrentCPU->ID); + + schedbg("Status: 0-ukn | 1-rdy | 2-run | 3-wait | 4-term"); + schedbg("Technical Informations on regs %#lx", Frame->InterruptNumber); + schedbg("FS=%#lx GS=%#lx SS=%#lx CS=%#lx DS=%#lx", + CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE), + Frame->ss, Frame->cs, Frame->ds); + schedbg("R8=%#lx R9=%#lx R10=%#lx R11=%#lx", + Frame->r8, Frame->r9, Frame->r10, Frame->r11); + schedbg("R12=%#lx R13=%#lx R14=%#lx R15=%#lx", + Frame->r12, Frame->r13, Frame->r14, Frame->r15); + schedbg("RAX=%#lx RBX=%#lx RCX=%#lx RDX=%#lx", + Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx); + schedbg("RSI=%#lx RDI=%#lx RBP=%#lx RSP=%#lx", + Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp); + schedbg("RIP=%#lx RFL=%#lx INT=%#lx ERR=%#lx", + Frame->rip, Frame->rflags, Frame->InterruptNumber, Frame->ErrorCode); + + // Null or invalid process/thread? Let's find a new one to execute. + if (InvalidPCB(CurrentCPU->CurrentProcess) || InvalidTCB(CurrentCPU->CurrentThread)) + { + schedbg("%d processes", ListProcess.size()); +#ifdef DEBUG_SCHEDULER + foreach (auto var in ListProcess) + { + schedbg("Process %d %s", var->ID, var->Name); + } +#endif + // Find a new process to execute. + foreach (PCB *pcb in ListProcess) + { + if (InvalidPCB(pcb)) + continue; + + // Check process status. + switch (pcb->Status) + { + case TaskStatus::Ready: + schedbg("Ready process (%s)%d", pcb->Name, pcb->ID); + break; + default: + schedbg("Process %s(%d) status %d", pcb->Name, pcb->ID, pcb->Status); + // RemoveProcess(pcb); // ignore for now + continue; + } + + // Get first available thread from the list. + foreach (TCB *tcb in pcb->Threads) + { + if (InvalidTCB(tcb)) + continue; + + if (tcb->Status != TaskStatus::Ready) + continue; + + // Set process and thread as the current one's. + CurrentCPU->CurrentProcess = pcb; + CurrentCPU->CurrentThread = tcb; + // Success! + goto Success; + } + } + schedbg("No process to run."); + // No process found. Idling... + goto Idle; + } + else + { + // Save current process and thread registries, gs, fs, fpu, etc... + CurrentCPU->CurrentThread->Registers = *Frame; + // _fxsave(CurrentCPU->CurrentThread->FXRegion); + + // Set the process & thread as ready if it's running. + if (CurrentCPU->CurrentProcess->Status == TaskStatus::Running) + CurrentCPU->CurrentProcess->Status = TaskStatus::Ready; + if (CurrentCPU->CurrentThread->Status == TaskStatus::Running) + CurrentCPU->CurrentThread->Status = TaskStatus::Ready; + + // Get next available thread from the list. + for (uint64_t i = 0; i < CurrentCPU->CurrentProcess->Threads.size(); i++) + { + // Loop until we find the current thread from the process thread list. + if (CurrentCPU->CurrentProcess->Threads[i] == CurrentCPU->CurrentThread) + { + // Check if the next thread is valid. If not, we search until we find, but if we reach the end of the list, we go to the next process. + uint64_t tmpidx = i; + RetryAnotherThread: + TCB *thread = CurrentCPU->CurrentProcess->Threads[tmpidx + 1]; + if (InvalidTCB(thread)) + { + if (tmpidx > CurrentCPU->CurrentProcess->Threads.size()) + break; + tmpidx++; + goto RetryAnotherThread; + } + + schedbg("%s(%d) and next thread is %s(%d)", CurrentCPU->CurrentProcess->Threads[i]->Name, CurrentCPU->CurrentProcess->Threads[i]->ID, thread->Name, thread->ID); + + // Check if the thread is ready to be executed. + if (thread->Status != TaskStatus::Ready) + { + schedbg("Thread %d is not ready", thread->ID); + goto RetryAnotherThread; + } + + // Everything is fine, we can set the new thread as the current one. + CurrentCPU->CurrentThread = thread; + schedbg("[thd 0 -> end] Scheduling thread %d parent of %s->%d Procs %d", thread->ID, thread->Parent->Name, CurrentCPU->CurrentProcess->Threads.size(), ListProcess.size()); + // Yay! We found a new thread to execute. + goto Success; + } + } + + // If the last process didn't find a thread to execute, we search for a new process. + for (uint64_t i = 0; i < ListProcess.size(); i++) + { + // Loop until we find the current process from the process list. + if (ListProcess[i] == CurrentCPU->CurrentProcess) + { + // Check if the next process is valid. If not, we search until we find. + uint64_t tmpidx = i; + RetryAnotherProcess: + PCB *pcb = ListProcess[tmpidx + 1]; + if (InvalidPCB(pcb)) + { + if (tmpidx > ListProcess.size()) + break; + tmpidx++; + goto RetryAnotherProcess; + } + + if (pcb->Status != TaskStatus::Ready) + goto RetryAnotherProcess; + + // Everything good, now search for a thread. + for (uint64_t j = 0; j < pcb->Threads.size(); j++) + { + TCB *tcb = pcb->Threads[j]; + if (InvalidTCB(tcb)) + continue; + if (tcb->Status != TaskStatus::Ready) + continue; + // Success! We set as the current one and restore the stuff. + CurrentCPU->CurrentProcess = pcb; + CurrentCPU->CurrentThread = tcb; + schedbg("[cur proc+1 -> first thd] Scheduling thread %d %s->%d (Total Procs %d)", tcb->ID, tcb->Name, pcb->Threads.size(), ListProcess.size()); + goto Success; + } + } + } + + // Before checking from the beginning, we remove everything that is terminated. + foreach (PCB *pcb in ListProcess) + { + if (InvalidPCB(pcb)) + continue; + // RemoveProcess(pcb); // comment this until i will find a way to handle properly vectors, the memory need to be 0ed after removing. + } + + // If we didn't find anything, we check from the start of the list. This is the last chance to find something or we go to idle. + foreach (PCB *pcb in ListProcess) + { + if (InvalidPCB(pcb)) + continue; + if (pcb->Status != TaskStatus::Ready) + continue; + + // Now do the thread search! + foreach (TCB *tcb in pcb->Threads) + { + if (InvalidTCB(tcb)) + continue; + if (tcb->Status != TaskStatus::Ready) + continue; + // \o/ We found a new thread to execute. + CurrentCPU->CurrentProcess = pcb; + CurrentCPU->CurrentThread = tcb; + schedbg("[proc 0 -> end -> first thd] Scheduling thread %d parent of %s->%d (Procs %d)", tcb->ID, tcb->Parent->Name, pcb->Threads.size(), ListProcess.size()); + goto Success; + } + } + } + + Idle: + { + // I should remove processes that are no longer having any threads? remove only from userspace? + if (IdleProcess == nullptr) + { + schedbg("Idle process created"); + IdleProcess = CreateProcess(nullptr, (char *)"idle", TaskTrustLevel::Idle); + IdleThread = CreateThread(IdleProcess, reinterpret_cast(IdleProcessLoop), 0); + } + CurrentCPU->CurrentProcess = IdleProcess; + CurrentCPU->CurrentThread = IdleThread; + + *Frame = CurrentCPU->CurrentThread->Registers; + // UpdatePageTable(CurrentCPU->CurrentProcess->PageTable); // kernel one + // _fxrstor(CurrentCPU->CurrentThread->FXRegion); + goto End; + } + + Success: + { + schedbg("Success Prc:%s(%d) Thd:%s(%d)->RIP:%#lx-RSP:%#lx(STACK: %#lx)", + CurrentCPU->CurrentProcess->Name, CurrentCPU->CurrentProcess->ID, + CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID, + CurrentCPU->CurrentThread->Registers.rip, CurrentCPU->CurrentThread->Registers.rsp, CurrentCPU->CurrentThread->Stack); + + CurrentCPU->CurrentProcess->Status = TaskStatus::Running; + CurrentCPU->CurrentThread->Status = TaskStatus::Running; + + *Frame = CurrentCPU->CurrentThread->Registers; + // UpdatePageTable(CurrentCPU->CurrentProcess->PageTable); + + switch (CurrentCPU->CurrentProcess->Security.TrustLevel) + { + case TaskTrustLevel::System: + case TaskTrustLevel::Idle: + case TaskTrustLevel::Kernel: + // wrmsr(MSR_SHADOW_GS_BASE, (uint64_t)CurrentCPU->CurrentThread); + break; + case TaskTrustLevel::User: + // wrmsr(MSR_SHADOW_GS_BASE, CurrentCPU->CurrentThread->gs); + break; + default: + error("Unknown trust level %d.", CurrentCPU->CurrentProcess->Security.TrustLevel); + break; + } + // _fxrstor(CurrentCPU->CurrentThread->FXRegion); + } + End: + { + // UpdateTimeUsed(&CurrentCPU->CurrentProcess->Info); + // UpdateTimeUsed(&CurrentCPU->CurrentThread->Info); + // UpdateCPUUsage(&CurrentCPU->CurrentProcess->Info); + // UpdateCPUUsage(&CurrentCPU->CurrentThread->Info); + OneShot(CurrentCPU->CurrentThread->Info.Priority); + schedbg("Scheduler end"); + } + schedbg("Technical Informations on Thread %s[%ld]:", CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID); + schedbg("FS=%#lx GS=%#lx SS=%#lx CS=%#lx DS=%#lx", + CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE), + Frame->ss, Frame->cs, Frame->ds); + schedbg("R8=%#lx R9=%#lx R10=%#lx R11=%#lx", + Frame->r8, Frame->r9, Frame->r10, Frame->r11); + schedbg("R12=%#lx R13=%#lx R14=%#lx R15=%#lx", + Frame->r12, Frame->r13, Frame->r14, Frame->r15); + schedbg("RAX=%#lx RBX=%#lx RCX=%#lx RDX=%#lx", + Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx); + schedbg("RSI=%#lx RDI=%#lx RBP=%#lx RSP=%#lx", + Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp); + schedbg("RIP=%#lx RFL=%#lx INT=%#lx ERR=%#lx", + Frame->rip, Frame->rflags, Frame->InterruptNumber, Frame->ErrorCode); + + schedbg("SCHEDULER FUNCTION END"); } #elif defined(__i386__) __attribute__((no_stack_protector)) void Task::OnInterruptReceived(void *Frame) @@ -112,7 +363,12 @@ namespace Tasking Thread->Stack = (void *)((uint64_t)KernelAllocator.RequestPages(TO_PAGES(STACK_SIZE)) + STACK_SIZE); Thread->Status = TaskStatus::Ready; - memset(&Thread->Registers, 0, sizeof(ThreadFrame)); // Just in case +#if defined(__amd64__) + memset(&Thread->Registers, 0, sizeof(CPU::x64::TrapFrame)); // Just in case + Thread->Registers.rip = (EntryPoint + Offset); +#elif defined(__i386__) +#elif defined(__aarch64__) +#endif switch (Parent->Security.TrustLevel) { case TaskTrustLevel::System: @@ -265,6 +521,7 @@ namespace Tasking Process->Info.Priority = 0; Parent->Children.push_back(Process); + ListProcess.push_back(Process); return Process; } @@ -290,12 +547,21 @@ namespace Tasking kthrd->Rename("Main Thread"); debug("Created Kernel Process: %s and Thread: %s", kproc->Name, kthrd->Name); TaskingLock.Lock(); + #if defined(__amd64__) || defined(__i386__) + uint32_t rax, rbx, rcx, rdx; + CPU::x64::cpuid(0x1, &rax, &rbx, &rcx, &rdx); + if (rcx & CPU::x64::CPUID_FEAT_RCX_MONITOR) + { + trace("CPU has MONITOR/MWAIT support."); + } + for (int i = 0; i < SMP::CPUCores; i++) { /* do stuff i guess */ - ((APIC::Timer *)Interrupts::apicTimer[i])->OneShot(CPU::x64::IRQ16, 100); + ((APIC::Timer *)Interrupts::apicTimer[i])->OneShot(CPU::x64::IRQ16, 1000); } + // ((APIC::Timer *)Interrupts::apicTimer[0])->OneShot(CPU::x64::IRQ16, 100); #endif debug("Tasking Started"); } diff --git a/include/cpu.hpp b/include/cpu.hpp index eee5efd..f9eda31 100644 --- a/include/cpu.hpp +++ b/include/cpu.hpp @@ -136,7 +136,7 @@ namespace CPU /** * @brief Pause the CPU */ - static inline void Pause() + __attribute__((no_stack_protector)) static inline void Pause() { #if defined(__amd64__) || defined(__i386__) asmv("pause"); @@ -148,7 +148,7 @@ namespace CPU /** * @brief Stop the CPU (infinite loop) */ - static inline void Stop() + __attribute__((no_stack_protector)) static inline void Stop() { while (1) { @@ -158,11 +158,8 @@ namespace CPU "hlt\n" "jmp CPUStopLoop"); #elif defined(__aarch64__) - while (1) - { - asmv("msr daifset, #2"); - asmv("wfe"); - } + asmv("msr daifset, #2"); + asmv("wfe"); #endif } } @@ -170,7 +167,7 @@ namespace CPU /** * @brief Halt the CPU */ - static inline void Halt() + __attribute__((no_stack_protector)) static inline void Halt() { #if defined(__amd64__) || defined(__i386__) asmv("hlt"); diff --git a/include/task.hpp b/include/task.hpp index a0fc804..039cef0 100644 --- a/include/task.hpp +++ b/include/task.hpp @@ -15,90 +15,6 @@ namespace Tasking typedef unsigned long UTID; typedef unsigned long Token; - struct ThreadFrame - { -#if defined(__amd64__) - // uint64_t gs; // General-purpose Segment - // uint64_t fs; // General-purpose Segment - // uint64_t es; // Extra Segment (used for string operations) - uint64_t ds; // Data Segment - uint64_t r15; // General purpose - uint64_t r14; // General purpose - uint64_t r13; // General purpose - uint64_t r12; // General purpose - uint64_t r11; // General purpose - uint64_t r10; // General purpose - uint64_t r9; // General purpose - uint64_t r8; // General purpose - uint64_t rbp; // Base Pointer (meant for stack frames) - uint64_t rdi; // First Argument - uint64_t rsi; // Second Argument - uint64_t rdx; // Data (commonly extends the A register) - uint64_t rcx; // Counter - uint64_t rbx; // Base - uint64_t rax; // Accumulator - uint64_t int_num; // Interrupt Number - uint64_t error_code; // Error code - uint64_t rip; // Instruction Pointer - uint64_t cs; // Code Segment - union - { - struct - { - /** @brief Carry Flag */ - uint64_t CF : 1; - /** @brief Reserved */ - uint64_t AlwaysOne : 1; - /** @brief Parity Flag */ - uint64_t PF : 1; - /** @brief Reserved */ - uint64_t Reserved0 : 1; - /** @brief Auxiliary Carry Flag */ - uint64_t AF : 1; - /** @brief Reserved */ - uint64_t Reserved1 : 1; - /** @brief Zero Flag */ - uint64_t ZF : 1; - /** @brief Sign Flag */ - uint64_t SF : 1; - /** @brief Trap Flag */ - uint64_t TF : 1; - /** @brief Interrupt Enable Flag */ - uint64_t IF : 1; - /** @brief Direction Flag */ - uint64_t DF : 1; - /** @brief Overflow Flag */ - uint64_t OF : 1; - /** @brief I/O Privilege Level */ - uint64_t IOPL : 2; - /** @brief Nested Task */ - uint64_t NT : 1; - /** @brief Reserved */ - uint64_t Reserved2 : 1; - /** @brief Resume Flag */ - uint64_t RF : 1; - /** @brief Virtual 8086 Mode */ - uint64_t VM : 1; - /** @brief Alignment Check */ - uint64_t AC : 1; - /** @brief Virtual Interrupt Flag */ - uint64_t VIF : 1; - /** @brief Virtual Interrupt Pending */ - uint64_t VIP : 1; - /** @brief ID Flag */ - uint64_t ID : 1; - /** @brief Reserved */ - uint64_t Reserved3 : 10; - }; - uint64_t raw; - } rflags; // Register Flags - uint64_t rsp; // Stack Pointer - uint64_t ss; // Stack Segment / Data Segment -#elif defined(__i386__) -#elif defined(__aarch64__) -#endif - }; - enum TaskArchitecture { UnknownArchitecture, @@ -164,7 +80,13 @@ namespace Tasking int ExitCode; void *Stack; TaskStatus Status; - ThreadFrame Registers; +#if defined(__amd64__) + CPU::x64::TrapFrame Registers; +#elif defined(__i386__) + uint32_t Registers; // TODO +#elif defined(__aarch64__) + uint64_t Registers; // TODO +#endif TaskSecurity Security; TaskInfo Info; @@ -220,6 +142,20 @@ namespace Tasking UPID NextPID = 0; UTID NextTID = 0; + Vector ListProcess; + PCB *IdleProcess = nullptr; + TCB *IdleThread = nullptr; + + bool InvalidPCB(PCB *pcb) + { + return false; + } + + bool InvalidTCB(TCB *tcb) + { + return false; + } + #if defined(__amd64__) void OnInterruptReceived(CPU::x64::TrapFrame *Frame); #elif defined(__i386__)