mirror of
https://github.com/Fennix-Project/Kernel.git
synced 2025-05-25 22:14:37 +00:00
1023 lines
30 KiB
C++
1023 lines
30 KiB
C++
/*
|
|
This file is part of Fennix Kernel.
|
|
|
|
Fennix Kernel is free software: you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation, either version 3 of
|
|
the License, or (at your option) any later version.
|
|
|
|
Fennix Kernel is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <display.hpp>
|
|
#include <bitmap.hpp>
|
|
#include <convert.h>
|
|
#include <printf.h>
|
|
#include <lock.hpp>
|
|
#include <rand.hpp>
|
|
#include <uart.hpp>
|
|
#include <debug.h>
|
|
#include <smp.hpp>
|
|
#include <cpu.hpp>
|
|
#include <io.h>
|
|
|
|
#if defined(a64)
|
|
#include "../../arch/amd64/cpu/gdt.hpp"
|
|
#include "../arch/amd64/cpu/apic.hpp"
|
|
#elif defined(a32)
|
|
#include "../../arch/i386/cpu/gdt.hpp"
|
|
#include "../arch/i386/cpu/apic.hpp"
|
|
#elif defined(aa64)
|
|
#endif
|
|
|
|
#include "../../kernel.h"
|
|
#include "keyboard.hpp"
|
|
|
|
extern void ExPrint(const char *Format, ...);
|
|
extern void DiagnosticDataCollection();
|
|
extern void InitFont();
|
|
|
|
extern Video::Font *CrashFont;
|
|
extern void *FbBeforePanic;
|
|
|
|
struct StackFrame
|
|
{
|
|
struct StackFrame *bp;
|
|
uintptr_t ip;
|
|
};
|
|
|
|
struct x86ExceptionName
|
|
{
|
|
const char *Mnemonic;
|
|
const char *Name;
|
|
const char *Cause;
|
|
};
|
|
|
|
/* AMD64 Programmer's Manual Volume 2: 8.2 Vectors */
|
|
x86ExceptionName x86Exceptions[] = {
|
|
/* 0*/ {"#DE", "Divide-by-Zero-Error", "DIV, IDIV, AAM instructions"},
|
|
/* 1*/ {"#DB", "Debug", "Instruction accesses and data accesses"},
|
|
/* 2*/ {"NMI", "Non-Maskable-Interrupt", "Non-maskable interrupt"},
|
|
/* 3*/ {"#BP", "Breakpoint", "INT3 instruction"},
|
|
/* 4*/ {"#OF", "Overflow", "INTO instruction"},
|
|
/* 5*/ {"#BR", "Bound-Range", "BOUND instruction"},
|
|
/* 6*/ {"#UD", "Invalid-Opcode", "Invalid instructions"},
|
|
/* 7*/ {"#NM", "Device-Not-Available", "x87 instructions"},
|
|
/* 8*/ {"#DF", "Double-Fault", "Exception during the handling of another exception or interrupt"},
|
|
/* 9*/ {"#--", "Coprocessor-Segment-Overrun", "Unsupported (Reserved)"},
|
|
/*10*/ {"#TS", "Invalid-TSS", "Task-state segment access and task switch"},
|
|
/*11*/ {"#NP", "Segment-Not-Present", "Segment register loads"},
|
|
/*12*/ {"#SS", "Stack", "SS register loads and stack references"},
|
|
/*13*/ {"#GP", "General-Protection", "Memory accesses and protection checks"},
|
|
/*14*/ {"#PF", "Page-Fault", "Memory accesses when paging enabled"},
|
|
/*15*/ {"#r0", "Reserved", "Reserved"},
|
|
/*16*/ {"#MF", "x87 Floating-Point Exception-Pending", "x87 floating-point instructions"},
|
|
/*17*/ {"#AC", "Alignment-Check", "Misaligned memory accesses"},
|
|
/*18*/ {"#MC", "Machine-Check", "Model specific"},
|
|
/*19*/ {"#XF", "SIMD Floating-Point", "SSE floating-point instructions"},
|
|
/*20*/ {"#VE", "Virtualization Exception", "Virtualization event"}, /* AMD says this is reserved */
|
|
/*21*/ {"#CP", "Control-Protection Exception", "RET/IRET or other control transfer"},
|
|
/*22*/ {"#r1", "Reserved", "Reserved"},
|
|
/*23*/ {"#r2", "Reserved", "Reserved"},
|
|
/*24*/ {"#r3", "Reserved", "Reserved"},
|
|
/*25*/ {"#r4", "Reserved", "Reserved"},
|
|
/*26*/ {"#r5", "Reserved", "Reserved"},
|
|
/*27*/ {"#r6", "Reserved", "Reserved"},
|
|
/*28*/ {"#HV", "Hypervisor Injection Exception", "Event injection"},
|
|
/*29*/ {"#VC", "VMM Communication Exception", "Virtualization event"},
|
|
/*30*/ {"#SX", "Security Exception", "Security-sensitive event in host"},
|
|
/*31*/ {"#r7", "Reserved", "Reserved"},
|
|
};
|
|
|
|
static const char *x86PageFaultDescriptions[9] = {
|
|
"Supervisor tried to read a non-present page entry\n",
|
|
"Supervisor tried to read a page and caused a protection fault\n",
|
|
"Supervisor tried to write to a non-present page entry\n",
|
|
"Supervisor tried to write a page and caused a protection fault\n",
|
|
"User tried to read a non-present page entry\n",
|
|
"User tried to read a page and caused a protection fault\n",
|
|
"User tried to write to a non-present page entry\n",
|
|
"User tried to write a page and caused a protection fault\n",
|
|
"One or more page directory entries contain reserved bits which are set to 1.\n"};
|
|
|
|
int ActiveScreen = 0;
|
|
|
|
char __modSym[20];
|
|
nsa const char *ExGetKSymbolByAddress(uintptr_t Address)
|
|
{
|
|
if (Address < (uintptr_t)&_kernel_start &&
|
|
Address > (uintptr_t)&_kernel_end)
|
|
return "<OUTSIDE KERNEL>";
|
|
|
|
if (!KernelSymbolTable)
|
|
return "<UNKNOWN>";
|
|
|
|
const char *sym = KernelSymbolTable->GetSymbol(Address);
|
|
|
|
size_t symLen = strlen(sym);
|
|
|
|
if (symLen > 16)
|
|
{
|
|
strncpy(__modSym, sym, 16);
|
|
__modSym[16] = '.';
|
|
__modSym[17] = '.';
|
|
__modSym[18] = '.';
|
|
__modSym[19] = '\0';
|
|
sym = __modSym;
|
|
}
|
|
|
|
if (unlikely(symLen > 128))
|
|
warn("Symbol \"%s\" is too long! Memory corrupted?", sym);
|
|
return sym;
|
|
}
|
|
|
|
nsa const char *ExGetKSymbol(CPU::ExceptionFrame *Frame)
|
|
{
|
|
if (Frame->rip < (uintptr_t)&_kernel_start &&
|
|
Frame->rip > (uintptr_t)&_kernel_end)
|
|
return "<OUTSIDE KERNEL>";
|
|
|
|
#if defined(a64)
|
|
return ExGetKSymbolByAddress(Frame->rip);
|
|
#elif defined(a32)
|
|
return ExGetKSymbolByAddress(Frame->eip);
|
|
#elif defined(aa64)
|
|
return ExGetKSymbolByAddress(Frame->pc);
|
|
#endif
|
|
}
|
|
|
|
nsa char *TrimWhiteSpace(char *str)
|
|
{
|
|
char *end;
|
|
while (*str == ' ')
|
|
str++;
|
|
if (*str == 0)
|
|
return str;
|
|
end = str + strlen(str) - 1;
|
|
while (end > str && *end == ' ')
|
|
end--;
|
|
*(end + 1) = 0;
|
|
return str;
|
|
}
|
|
|
|
nsa void ExDumpData(void *Address, unsigned long Length)
|
|
{
|
|
ExPrint("\eAAAAAA-------------------------------------------------------------------------\n");
|
|
Display->UpdateBuffer();
|
|
unsigned char *AddressChar = (unsigned char *)Address;
|
|
unsigned char Buffer[17];
|
|
unsigned long Iterate;
|
|
for (Iterate = 0; Iterate < Length; Iterate++)
|
|
{
|
|
if ((Iterate % 16) == 0)
|
|
{
|
|
if (Iterate != 0)
|
|
ExPrint(" \e8A78FF%s\eAABBCC\n", Buffer);
|
|
ExPrint(" \e9E9E9E%04x\eAABBCC ", Iterate);
|
|
Display->UpdateBuffer();
|
|
}
|
|
ExPrint(" \e4287f5%02x\eAABBCC", AddressChar[Iterate]);
|
|
if ((AddressChar[Iterate] < 0x20) || (AddressChar[Iterate] > 0x7E))
|
|
Buffer[Iterate % 16] = '.';
|
|
else
|
|
Buffer[Iterate % 16] = AddressChar[Iterate];
|
|
Buffer[(Iterate % 16) + 1] = '\0';
|
|
}
|
|
|
|
while ((Iterate % 16) != 0)
|
|
{
|
|
ExPrint(" ");
|
|
Display->UpdateBuffer();
|
|
Iterate++;
|
|
}
|
|
|
|
ExPrint(" \e8A78FF%s\eAAAAAA\n", Buffer);
|
|
ExPrint("-------------------------------------------------------------------------\n\n.");
|
|
Display->UpdateBuffer();
|
|
}
|
|
|
|
nsa void DisplayTopOverlay()
|
|
{
|
|
Video::Font *f = Display->GetCurrentFont();
|
|
Video::FontInfo fi = f->GetInfo();
|
|
|
|
for (uint32_t i = 0; i < Display->GetWidth; i++)
|
|
{
|
|
for (uint32_t j = 0; j < fi.Height + 8; j++)
|
|
{
|
|
// uint32_t grayValue = 0x505050 - (j * 0x020202);
|
|
// Display->SetPixel(i, j, grayValue);
|
|
Display->SetPixel(i, j, 0x202020);
|
|
}
|
|
}
|
|
|
|
for (uint32_t i = 0; i < Display->GetWidth; i++)
|
|
Display->SetPixel(i, fi.Height + 8, 0x404040);
|
|
|
|
Display->SetBufferCursor(8, (fi.Height + 8) / 6);
|
|
|
|
/* This wouldn't have enough space for a 640x480 screen */
|
|
// ExPrint("%sMAIN %sDETAILS %sSTACK %sPROCESS \eAAAAAA| ",
|
|
// ActiveScreen == 0 ? "\e00AAFF" : "\eAAAAAA",
|
|
// ActiveScreen == 1 ? "\e00AAFF" : "\eAAAAAA",
|
|
// ActiveScreen == 2 ? "\e00AAFF" : "\eAAAAAA",
|
|
// ActiveScreen == 3 ? "\e00AAFF" : "\eAAAAAA");
|
|
|
|
ExPrint("%s %s %s %s \eAAAAAA| ",
|
|
ActiveScreen == 0 ? "\eAAAAAA"
|
|
"MAIN "
|
|
: "\e5A5A5A"
|
|
"M",
|
|
ActiveScreen == 1 ? "\eAAAAAA"
|
|
"DETAIL "
|
|
: "\e5A5A5A"
|
|
"D",
|
|
ActiveScreen == 2 ? "\eAAAAAA"
|
|
"STACK "
|
|
: "\e5A5A5A"
|
|
"S",
|
|
ActiveScreen == 3 ? "\eAAAAAA"
|
|
"PROCESS"
|
|
: "\e5A5A5A"
|
|
"P");
|
|
|
|
ExPrint("%s %s %s | ", KERNEL_NAME, KERNEL_ARCH, KERNEL_VERSION);
|
|
ExPrint("%ld | ", TO_MiB(KernelAllocator.GetFreeMemory()));
|
|
ExPrint("%s %s", CPU::Hypervisor(), CPU::Vendor());
|
|
|
|
Display->SetBufferCursor(0, fi.Height + 10);
|
|
|
|
/* https://imgflip.com/i/77slbl */
|
|
if ((Random::rand32() % 100) >= 98)
|
|
{
|
|
debug("Easter egg activated!");
|
|
int BaseXOffset = Display->GetWidth - 14;
|
|
int BaseYOffset = 8;
|
|
Display->SetPixel(BaseXOffset + 3, BaseYOffset + 0, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 4, BaseYOffset + 0, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 6, BaseYOffset + 0, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 7, BaseYOffset + 0, 0x21852E);
|
|
|
|
Display->SetPixel(BaseXOffset + 2, BaseYOffset + 1, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 3, BaseYOffset + 1, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 4, BaseYOffset + 1, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 5, BaseYOffset + 1, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 6, BaseYOffset + 1, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 7, BaseYOffset + 1, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 8, BaseYOffset + 1, 0x21852E);
|
|
|
|
Display->SetPixel(BaseXOffset + 1, BaseYOffset + 2, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 2, BaseYOffset + 2, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 3, BaseYOffset + 2, 0xFFFFFF);
|
|
Display->SetPixel(BaseXOffset + 4, BaseYOffset + 2, 0x000000);
|
|
Display->SetPixel(BaseXOffset + 5, BaseYOffset + 2, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 6, BaseYOffset + 2, 0xFFFFFF);
|
|
Display->SetPixel(BaseXOffset + 7, BaseYOffset + 2, 0x000000);
|
|
Display->SetPixel(BaseXOffset + 8, BaseYOffset + 2, 0x21852E);
|
|
|
|
Display->SetPixel(BaseXOffset + 1, BaseYOffset + 3, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 2, BaseYOffset + 3, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 3, BaseYOffset + 3, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 4, BaseYOffset + 3, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 5, BaseYOffset + 3, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 6, BaseYOffset + 3, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 7, BaseYOffset + 3, 0x21852E);
|
|
|
|
Display->SetPixel(BaseXOffset + 0, BaseYOffset + 4, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 1, BaseYOffset + 4, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 2, BaseYOffset + 4, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 3, BaseYOffset + 4, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 4, BaseYOffset + 4, 0xA84832);
|
|
Display->SetPixel(BaseXOffset + 5, BaseYOffset + 4, 0xA84832);
|
|
Display->SetPixel(BaseXOffset + 6, BaseYOffset + 4, 0xA84832);
|
|
Display->SetPixel(BaseXOffset + 7, BaseYOffset + 4, 0xA84832);
|
|
|
|
Display->SetPixel(BaseXOffset + 0, BaseYOffset + 5, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 1, BaseYOffset + 5, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 2, BaseYOffset + 5, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 3, BaseYOffset + 5, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 4, BaseYOffset + 5, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 5, BaseYOffset + 5, 0x21852E);
|
|
Display->SetPixel(BaseXOffset + 6, BaseYOffset + 5, 0x21852E);
|
|
|
|
Display->SetPixel(BaseXOffset + 0, BaseYOffset + 6, 0x1216FF);
|
|
Display->SetPixel(BaseXOffset + 1, BaseYOffset + 6, 0x1216FF);
|
|
Display->SetPixel(BaseXOffset + 2, BaseYOffset + 6, 0x1216FF);
|
|
Display->SetPixel(BaseXOffset + 3, BaseYOffset + 6, 0x1216FF);
|
|
Display->SetPixel(BaseXOffset + 4, BaseYOffset + 6, 0x1216FF);
|
|
Display->SetPixel(BaseXOffset + 5, BaseYOffset + 6, 0x1216FF);
|
|
Display->SetPixel(BaseXOffset + 6, BaseYOffset + 6, 0x1216FF);
|
|
|
|
Display->UpdateBuffer();
|
|
}
|
|
}
|
|
|
|
nsa void DisplayBottomOverlay()
|
|
{
|
|
Video::Font *f = Display->GetCurrentFont();
|
|
Video::FontInfo fi = f->GetInfo();
|
|
|
|
for (uint32_t i = 0; i < Display->GetWidth; i++)
|
|
for (uint32_t j = Display->GetHeight - fi.Height - 8; j < Display->GetHeight; j++)
|
|
Display->SetPixel(i, j, 0x202020);
|
|
|
|
for (uint32_t i = 0; i < Display->GetWidth; i++)
|
|
Display->SetPixel(i, Display->GetHeight - fi.Height - 8, 0x404040);
|
|
|
|
Display->SetBufferCursor(8, Display->GetHeight - fi.Height - 4);
|
|
ExPrint("\eAAAAAA> \eFAFAFA");
|
|
}
|
|
|
|
nsa void DisplayMainScreen(CPU::ExceptionFrame *Frame)
|
|
{
|
|
ExPrint("\n\eFAFAFAWe're sorry, but the system has encountered a critical error and needs to restart.\n");
|
|
|
|
ExPrint("\nError: %s (%s 0x%x)\n",
|
|
#if defined(a86)
|
|
x86Exceptions[Frame->InterruptNumber].Name,
|
|
x86Exceptions[Frame->InterruptNumber].Mnemonic,
|
|
#elif defined(aa64)
|
|
#error "AA64 not implemented"
|
|
#endif
|
|
Frame->InterruptNumber);
|
|
#if defined(a86)
|
|
ExPrint("Cause: %s\n", x86Exceptions[Frame->InterruptNumber].Cause);
|
|
#endif
|
|
ExPrint("Exception occurred in function %s (%#lx)\n",
|
|
ExGetKSymbol(Frame),
|
|
#if defined(a64)
|
|
Frame->rip);
|
|
#elif defined(a32)
|
|
Frame->eip);
|
|
#elif defined(aa64)
|
|
Frame->pc);
|
|
#endif
|
|
|
|
CPUData *core = GetCurrentCPU();
|
|
if (TaskManager)
|
|
{
|
|
ExPrint("Core: %d / pid: %d / tid: %d\n", core->ID,
|
|
core->CurrentProcess->ID, core->CurrentThread->ID);
|
|
}
|
|
else if (core->IsActive)
|
|
ExPrint("Core: %d\n", core->ID);
|
|
|
|
ExPrint("\nWhat to do:\n");
|
|
ExPrint(" 1. Restart your device and see if the problem persists.\n");
|
|
ExPrint(" 2. Remove any newly installed hardware.\n");
|
|
ExPrint(" 3. Check for any available updates for your operating system.\n");
|
|
ExPrint(" 4. Uninstall any recently installed drivers or software.\n");
|
|
ExPrint(" If none of the above steps resolve the issue, create a new issue on \e00AAFFhttps://github.com/Fennix-Project/Fennix\eFAFAFA for further assistance.\n");
|
|
|
|
ExPrint("\nUse command 'diag' to create a diagnostic report.\n");
|
|
}
|
|
|
|
nsa void DisplayDetailsScreen(CPU::ExceptionFrame *Frame)
|
|
{
|
|
ExPrint("\n\eFAFAFAException Frame:\n");
|
|
ExPrint(" General Purpose Registers:\n\eAAAAAA");
|
|
ExPrint(" RAX: %#lx RBX: %#lx RCX: %#lx RDX: %#lx\n",
|
|
Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx);
|
|
ExPrint(" RSI: %#lx RDI: %#lx R8: %#lx R9: %#lx\n",
|
|
Frame->rsi, Frame->rdi, Frame->r8, Frame->r9);
|
|
ExPrint(" R10: %#lx R11: %#lx R12: %#lx R13: %#lx\n",
|
|
Frame->r10, Frame->r11, Frame->r12, Frame->r13);
|
|
ExPrint(" R14: %#lx R15: %#lx\n", Frame->r14, Frame->r15);
|
|
|
|
ExPrint("\eFAFAFA Control Registers:\n\eAAAAAA");
|
|
ExPrint(" CR0: %#lx CR2: %#lx CR3: %#lx CR4: %#lx\n",
|
|
Frame->cr0, Frame->cr2, Frame->cr3, Frame->cr4);
|
|
ExPrint(" CR8: %#lx\n", Frame->cr8);
|
|
|
|
ExPrint("\eFAFAFA Segment Registers:\n\eAAAAAA");
|
|
ExPrint(" CS: %#lx SS: %#lx DS: %#lx ES: %#lx FS: %#lx GS: %#lx\n",
|
|
Frame->cs, Frame->ss, Frame->ds, Frame->es, Frame->fs, Frame->gs);
|
|
|
|
ExPrint("\eFAFAFA Debug Registers:\n\eAAAAAA");
|
|
ExPrint(" DR0: %#lx DR1: %#lx DR2: %#lx DR3: %#lx\n",
|
|
Frame->dr0, Frame->dr1, Frame->dr2, Frame->dr3);
|
|
ExPrint(" DR6: %#lx DR7: %#lx\n", Frame->dr6, Frame->dr7);
|
|
|
|
ExPrint("\eFAFAFA Other:\n\eAAAAAA");
|
|
ExPrint(" INT: %#lx ERR: %#lx RIP: %#lx RFLAGS: %#lx\n",
|
|
Frame->InterruptNumber, Frame->ErrorCode,
|
|
Frame->rip, Frame->rflags.raw);
|
|
ExPrint(" RSP: %#lx RBP: %#lx\n",
|
|
Frame->rsp, Frame->rbp);
|
|
|
|
ExPrint("\eFAFAFAException Details:\n");
|
|
switch (Frame->InterruptNumber)
|
|
{
|
|
case CPU::x86::PageFault:
|
|
{
|
|
CPU::x64::PageFaultErrorCode pfCode = {.raw = (uint32_t)Frame->ErrorCode};
|
|
ExPrint("PFEC: P:%d W:%d U:%d R:%d I:%d PK:%d SS:%d SGX:%d\n",
|
|
pfCode.P, pfCode.W, pfCode.U, pfCode.R,
|
|
pfCode.I, pfCode.PK, pfCode.SS, pfCode.SGX);
|
|
|
|
{
|
|
Memory::Virtual vmm((Memory::PageTable *)Frame->cr3);
|
|
if (vmm.GetMapType((void *)Frame->cr2) != Memory::Virtual::FourKiB)
|
|
ExPrint("Can't display page %#lx\n", Frame->cr2);
|
|
else
|
|
{
|
|
Memory::PageTableEntry *pte = vmm.GetPTE((void *)Frame->cr2);
|
|
ExPrint("Page %#lx: P:%d W:%d U:%d G:%d CoW:%d KRsv:%d NX:%d\n",
|
|
ALIGN_DOWN(Frame->cr2, 0x1000), pte->Present, pte->ReadWrite,
|
|
pte->UserSupervisor, pte->Global, pte->CopyOnWrite,
|
|
pte->KernelReserve, pte->ExecuteDisable);
|
|
}
|
|
}
|
|
|
|
ExPrint("%s", x86PageFaultDescriptions[Frame->ErrorCode & 0b111]);
|
|
if (Frame->ErrorCode & 0x8)
|
|
ExPrint("%s", x86PageFaultDescriptions[8]);
|
|
break;
|
|
}
|
|
case CPU::x86::StackSegmentFault:
|
|
case CPU::x86::GeneralProtectionFault:
|
|
{
|
|
CPU::x64::SelectorErrorCode sCode = {.raw = Frame->ErrorCode};
|
|
ExPrint("Kernel performed an illegal operation.\n");
|
|
ExPrint("External: %d\n", sCode.External);
|
|
ExPrint("Table: %d\n", sCode.Table);
|
|
ExPrint("Index: %#x\n", sCode.Idx);
|
|
break;
|
|
}
|
|
default:
|
|
ExPrint("No additional information available for this exception.\n");
|
|
break;
|
|
}
|
|
|
|
if (!TaskManager)
|
|
return;
|
|
|
|
CPUData *core = GetCurrentCPU();
|
|
Tasking::PCB *proc = core->CurrentProcess.load();
|
|
Tasking::TCB *thread = core->CurrentThread.load();
|
|
ExPrint("Exception in %s process %s(%d) thread %s(%d)\n",
|
|
proc->Security.ExecutionMode == Tasking::User ? "user" : "kernel",
|
|
proc->Name, proc->ID,
|
|
thread->Name, thread->ID);
|
|
}
|
|
|
|
nsa void DisplayStackScreen(CPU::ExceptionFrame *Frame)
|
|
{
|
|
Memory::Virtual vmm;
|
|
struct StackFrame *sf;
|
|
#if defined(a64)
|
|
sf = (struct StackFrame *)Frame->rbp;
|
|
#elif defined(a32)
|
|
sf = (struct StackFrame *)Frame->ebp;
|
|
#endif
|
|
|
|
ExPrint("\n\eFAFAFAStack trace (%#lx):\n", sf);
|
|
|
|
if (!vmm.Check(sf))
|
|
{
|
|
void *ptr = ((Memory::PageTable *)Frame->cr3)->Get(sf);
|
|
debug("Virtual pointer %#lx -> %#lx", sf, ptr);
|
|
if (vmm.Check(ptr))
|
|
sf = (struct StackFrame *)ptr;
|
|
else
|
|
{
|
|
ExPrint("\eFF0000< No stack trace available. >\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* FIXME: Get symbol offset more efficiently */
|
|
|
|
uintptr_t fIP;
|
|
#if defined(a64)
|
|
fIP = Frame->rip;
|
|
#elif defined(a32)
|
|
fIP = Frame->eip;
|
|
#elif defined(aa64)
|
|
fIP = Frame->pc;
|
|
#endif
|
|
|
|
ExPrint("\eAAAAAA%p", (void *)fIP);
|
|
ExPrint("\e5A5A5A in ");
|
|
if ((fIP >= (uintptr_t)&_kernel_start &&
|
|
fIP <= (uintptr_t)&_kernel_end))
|
|
{
|
|
const char *sym = KernelSymbolTable->GetSymbol(fIP);
|
|
ssize_t offset = fIP - KernelSymbolTable->GetSymbol(sym);
|
|
if (offset < 0)
|
|
offset = -offset;
|
|
|
|
ExPrint("\eFAFAFA%s\e5A5A5A+%#lx \eFFAAAA<- Exception\n",
|
|
sym, offset);
|
|
}
|
|
else
|
|
ExPrint("\eFF5555??? \eFFAAAA<- Exception\n");
|
|
|
|
if (!sf || !sf->ip || !sf->bp)
|
|
{
|
|
ExPrint("\n\eFF0000< No stack trace available. >\n");
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < 16; ++i)
|
|
{
|
|
if (!sf->ip)
|
|
break;
|
|
|
|
ExPrint("\eAAAAAA%p", (void *)sf->ip);
|
|
ExPrint("\e5A5A5A in ");
|
|
if ((sf->ip >= (uintptr_t)&_kernel_start &&
|
|
sf->ip <= (uintptr_t)&_kernel_end))
|
|
{
|
|
const char *sym = KernelSymbolTable->GetSymbol(sf->ip);
|
|
ssize_t offset = sf->ip - KernelSymbolTable->GetSymbol(sym);
|
|
if (offset < 0)
|
|
offset = -offset;
|
|
|
|
ExPrint("\eFAFAFA%s\e5A5A5A+%#lx\n", sym, offset);
|
|
}
|
|
else
|
|
ExPrint("\eFF5555???\n");
|
|
|
|
if (!vmm.Check(sf->bp))
|
|
return;
|
|
sf = sf->bp;
|
|
}
|
|
}
|
|
|
|
nsa void DisplayProcessScreen(CPU::ExceptionFrame *Frame, bool IgnoreReady = true)
|
|
{
|
|
const char *StatusColor[] = {
|
|
"FF0000", // Unknown
|
|
"AAFF00", // Ready
|
|
"00AA00", // Running
|
|
"FFAA00", // Sleeping
|
|
"FFAA00", // Blocked
|
|
"FFAA00", // Stopped
|
|
"FFAA00", // Waiting
|
|
|
|
"FF00FF", // Core dump
|
|
"FF0088", // Zombie
|
|
"FF0000", // Terminated
|
|
};
|
|
|
|
const char *StatusString[] = {
|
|
"UNK", // Unknown
|
|
"RDY", // Ready
|
|
"RUN", // Running
|
|
"SLP", // Sleeping
|
|
"BLK", // Blocked
|
|
"STP", // Stopped
|
|
"WTG", // Waiting
|
|
|
|
"CRD", // Core dump
|
|
"ZMB", // Zombie
|
|
"TRM", // Terminated
|
|
};
|
|
|
|
if (!TaskManager)
|
|
{
|
|
ExPrint("\eFF5555Tasking is not initialized\n");
|
|
return;
|
|
}
|
|
|
|
size_t textLimit = 32;
|
|
if (Display->GetWidth > 800 && Display->GetHeight > 600)
|
|
textLimit = 128;
|
|
|
|
std::vector<Tasking::PCB *> Plist = TaskManager->GetProcessList();
|
|
|
|
ExPrint("\n\eFAFAFAProcess list (%ld):\n", Plist.size());
|
|
bool pRdy = false;
|
|
bool showNote = false;
|
|
/* FIXME: This is slow */
|
|
foreach (auto Process in Plist)
|
|
{
|
|
bool ignore = true;
|
|
if (Process->State == Tasking::Ready && IgnoreReady)
|
|
{
|
|
foreach (auto Thread in Process->Threads)
|
|
{
|
|
if (Thread->State == Tasking::Ready)
|
|
continue;
|
|
ignore = false;
|
|
break;
|
|
}
|
|
|
|
if (ignore)
|
|
{
|
|
pRdy = true;
|
|
debug("Ignoring ready process %s(%d)",
|
|
Process->Name, Process->ID);
|
|
showNote = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ExPrint("\eAAAAAA-> \eFAFAFA%.*s%s\e8A8A8A(%ld) \e%s%s \e8A8A8A[exe: %s]\n",
|
|
textLimit, Process->Name,
|
|
strlen(Process->Name) > textLimit ? "\e8A8A8A..." : "",
|
|
Process->ID, StatusColor[Process->State.load()],
|
|
StatusString[Process->State.load()],
|
|
Process->Executable
|
|
? Process->Executable->Name.c_str()
|
|
: "none");
|
|
|
|
bool tRdy = false;
|
|
foreach (auto Thread in Process->Threads)
|
|
{
|
|
if (Thread->State == Tasking::Ready && IgnoreReady)
|
|
{
|
|
tRdy = true;
|
|
debug("Ignoring ready thread %s(%d)",
|
|
Thread->Name, Thread->ID);
|
|
showNote = true;
|
|
continue;
|
|
}
|
|
|
|
ExPrint("\eAAAAAA -> \eFAFAFA%.*s%s\e8A8A8A(%ld) \e%s%s\n",
|
|
textLimit, Thread->Name,
|
|
strlen(Thread->Name) > textLimit ? "\e8A8A8A..." : "",
|
|
Thread->ID, StatusColor[Thread->State.load()],
|
|
StatusString[Thread->State.load()]);
|
|
}
|
|
if (tRdy)
|
|
ExPrint("\eAAAAAA%s -> \e8A8A8A...\n");
|
|
}
|
|
if (pRdy)
|
|
ExPrint("\eAAAAAA-> \e8A8A8A...\n");
|
|
|
|
if (showNote)
|
|
ExPrint("\e5A5A5ANote: Some processes may not be displayed.\n");
|
|
}
|
|
|
|
CPU::ExceptionFrame *ExFrame;
|
|
nsa void DisplayCrashScreen(CPU::ExceptionFrame *Frame)
|
|
{
|
|
ExFrame = Frame;
|
|
|
|
/* TODO */
|
|
// void *BufferBeforeUpdate = KernelAllocator.RequestPages(TO_PAGES(Display->GetSize));
|
|
// if (BufferBeforeUpdate)
|
|
// memcpy(BufferBeforeUpdate, Display->GetBuffer, Display->GetSize);
|
|
|
|
Display->ClearBuffer();
|
|
if (Config.InterruptsOnCrash == false)
|
|
{
|
|
Display->SetBufferCursor(0, 0);
|
|
DisplayMainScreen(Frame);
|
|
Display->UpdateBuffer();
|
|
CPU::Stop();
|
|
}
|
|
|
|
DisplayTopOverlay();
|
|
|
|
DisplayMainScreen(Frame);
|
|
|
|
Display->UpdateBuffer();
|
|
new CrashKeyboardDriver;
|
|
|
|
DisplayBottomOverlay();
|
|
Display->UpdateBuffer();
|
|
CPU::Halt(true);
|
|
}
|
|
|
|
nsa void DisplayStackSmashing()
|
|
{
|
|
InitFont();
|
|
Display->ClearBuffer();
|
|
DisplayTopOverlay();
|
|
|
|
ExPrint("\n\eFAFAFAWe're sorry, but the system has encountered a critical error and needs to restart.\n");
|
|
ExPrint("\nError: Stack Smashing In Kernel Mode\n");
|
|
|
|
CPUData *core = GetCurrentCPU();
|
|
if (TaskManager)
|
|
{
|
|
ExPrint("Core: %d / pid: %d / tid: %d\n", core->ID,
|
|
core->CurrentProcess->ID, core->CurrentThread->ID);
|
|
}
|
|
else if (core->IsActive)
|
|
ExPrint("Core: %d\n", core->ID);
|
|
|
|
ExPrint("\nWhat to do:\n");
|
|
ExPrint(" This is a kernel bug.\n");
|
|
ExPrint(" Please create a new issue on \e00AAFFhttps://github.com/Fennix-Project/Fennix\eFAFAFA for further assistance.\n");
|
|
|
|
Display->UpdateBuffer();
|
|
|
|
/* TODO: Add additional info */
|
|
}
|
|
|
|
nsa void DisplayBufferOverflow()
|
|
{
|
|
InitFont();
|
|
Display->ClearBuffer();
|
|
DisplayTopOverlay();
|
|
|
|
ExPrint("\n\eFAFAFAWe're sorry, but the system has encountered a critical error and needs to restart.\n");
|
|
ExPrint("\nError: Buffer Overflow In Kernel Mode\n");
|
|
|
|
CPUData *core = GetCurrentCPU();
|
|
if (TaskManager)
|
|
{
|
|
ExPrint("Core: %d / pid: %d / tid: %d\n", core->ID,
|
|
core->CurrentProcess->ID, core->CurrentThread->ID);
|
|
}
|
|
else if (core->IsActive)
|
|
ExPrint("Core: %d\n", core->ID);
|
|
|
|
ExPrint("\nWhat to do:\n");
|
|
ExPrint(" This is a kernel bug.\n");
|
|
ExPrint(" Please create a new issue on \e00AAFFhttps://github.com/Fennix-Project/Fennix\eFAFAFA for further assistance.\n");
|
|
|
|
Display->UpdateBuffer();
|
|
|
|
/* TODO: Add additional info */
|
|
}
|
|
|
|
nsa void DisplayAssertionFailed(const char *File, int Line, const char *Expression)
|
|
{
|
|
InitFont();
|
|
Display->ClearBuffer();
|
|
DisplayTopOverlay();
|
|
|
|
ExPrint("\n\eFAFAFAWe're sorry, but the system has encountered a critical error and needs to restart.\n");
|
|
ExPrint("\nError: Assertion Failed\n");
|
|
ExPrint("In file \"%s\" at line %d, \"%s\" failed\n",
|
|
File, Line, Expression);
|
|
|
|
CPUData *core = GetCurrentCPU();
|
|
if (TaskManager)
|
|
{
|
|
ExPrint("Core: %d / pid: %d / tid: %d\n", core->ID,
|
|
core->CurrentProcess->ID, core->CurrentThread->ID);
|
|
}
|
|
else if (core->IsActive)
|
|
ExPrint("Core: %d\n", core->ID);
|
|
|
|
ExPrint("\nWhat to do:\n");
|
|
ExPrint(" This is a kernel bug.\n");
|
|
ExPrint(" Please create a new issue on \e00AAFFhttps://github.com/Fennix-Project/Fennix\eFAFAFA for further assistance.\n");
|
|
|
|
CPU::ExceptionFrame ef;
|
|
// Fill only the necessary fields
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wframe-address"
|
|
|
|
/* Jump over HandleAssertionFailed, and ip will be the function where it failed */
|
|
void *fun = __builtin_return_address(1);
|
|
/* Jump over this, HandleAssertionFailed & ip */
|
|
void *stk = __builtin_frame_address(2);
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
#ifdef __x86_64__
|
|
asmv("movq %%cr3, %0" : "=r"(ef.cr3));
|
|
ef.rip = (uint64_t)fun;
|
|
ef.rbp = ef.rsp = (uint64_t)stk;
|
|
#elif defined(__i386__)
|
|
asmv("movl %%cr3, %0" : "=r"(ef.cr3));
|
|
ef.eip = (uint32_t)fun;
|
|
ef.ebp = ef.esp = (uint32_t)stk;
|
|
#endif
|
|
DisplayStackScreen(&ef);
|
|
Display->UpdateBuffer();
|
|
|
|
/* TODO: Add additional info */
|
|
}
|
|
|
|
nsa void ArrowInput(uint8_t key)
|
|
{
|
|
switch (key)
|
|
{
|
|
case KEY_D_RIGHT:
|
|
if (ActiveScreen < 3)
|
|
ActiveScreen++;
|
|
else
|
|
return;
|
|
break;
|
|
case KEY_D_LEFT:
|
|
if (ActiveScreen > 0)
|
|
ActiveScreen--;
|
|
else
|
|
return;
|
|
break;
|
|
case KEY_D_DOWN:
|
|
case KEY_D_UP:
|
|
default:
|
|
return;
|
|
}
|
|
|
|
Display->ClearBuffer();
|
|
DisplayTopOverlay();
|
|
|
|
switch (ActiveScreen)
|
|
{
|
|
case 0:
|
|
DisplayMainScreen(ExFrame);
|
|
break;
|
|
case 1:
|
|
DisplayDetailsScreen(ExFrame);
|
|
break;
|
|
case 2:
|
|
DisplayStackScreen(ExFrame);
|
|
break;
|
|
case 3:
|
|
DisplayProcessScreen(ExFrame);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
DisplayBottomOverlay();
|
|
Display->UpdateBuffer();
|
|
}
|
|
|
|
nsa void UserInput(char *Input)
|
|
{
|
|
debug("User input: %s", Input);
|
|
Display->ClearBuffer();
|
|
DisplayTopOverlay();
|
|
|
|
if (strcmp(Input, "help") == 0)
|
|
{
|
|
ExPrint("Commands:\n");
|
|
ExPrint("\eAAAAAA help - Display this help message.\n");
|
|
ExPrint("\eCACACA clear - Clear the screen.\n");
|
|
ExPrint("\eAAAAAA exit - Shutdown the device.\n");
|
|
ExPrint("\eCACACA reboot - Reboot the device.\n");
|
|
ExPrint("\eAAAAAA bitmap - Display the kernel's bitmap.\n");
|
|
ExPrint("\eCACACA mem - Display memory information.\n");
|
|
ExPrint("\eAAAAAA dump [addr] [len] - Dump [len] bytes from [addr].\n");
|
|
ExPrint("\eCACACA diag - Collect diagnostic information.\n");
|
|
ExPrint("\eAAAAAA screen - Display the final output prior to system panic.\n");
|
|
}
|
|
else if (strcmp(Input, "clear") == 0)
|
|
{
|
|
Display->ClearBuffer();
|
|
DisplayTopOverlay();
|
|
}
|
|
else if (strcmp(Input, "exit") == 0)
|
|
{
|
|
Display->ClearBuffer();
|
|
|
|
const char msg[] = "Shutting down...";
|
|
size_t msgLen = strlen(msg);
|
|
size_t msgPixels = msgLen * CrashFont->GetInfo().Width;
|
|
uint32_t x = uint32_t((Display->GetWidth - msgPixels) / 2);
|
|
uint32_t y = uint32_t((Display->GetHeight - CrashFont->GetInfo().Height) / 2);
|
|
Display->SetBufferCursor(x, y);
|
|
Display->PrintString("\eAAAAAA");
|
|
Display->PrintString(msg, CrashFont);
|
|
|
|
Display->UpdateBuffer();
|
|
PowerManager->Shutdown();
|
|
CPU::Stop();
|
|
}
|
|
else if (strcmp(Input, "reboot") == 0)
|
|
{
|
|
Display->ClearBuffer();
|
|
|
|
const char msg[] = "Rebooting...";
|
|
size_t msgLen = strlen(msg);
|
|
size_t msgPixels = msgLen * CrashFont->GetInfo().Width;
|
|
uint32_t x = uint32_t((Display->GetWidth - msgPixels) / 2);
|
|
uint32_t y = uint32_t((Display->GetHeight - CrashFont->GetInfo().Height) / 2);
|
|
Display->SetBufferCursor(x, y);
|
|
Display->PrintString("\eAAAAAA");
|
|
Display->PrintString(msg, CrashFont);
|
|
|
|
Display->UpdateBuffer();
|
|
PowerManager->Reboot();
|
|
CPU::Stop();
|
|
}
|
|
else if (strncmp(Input, "bitmap", 6) == 0)
|
|
{
|
|
Bitmap bm = KernelAllocator.GetPageBitmap();
|
|
Video::Font *oldFont = CrashFont;
|
|
CrashFont = Display->GetDefaultFont();
|
|
|
|
ExPrint("\n\eAAAAAA[0%%] %08ld: ", 0);
|
|
for (size_t i = 0; i < bm.Size; i++)
|
|
{
|
|
if (bm.Get(i))
|
|
Display->PrintString("\eFF0000");
|
|
else
|
|
Display->PrintString("\e00FF00");
|
|
Display->Print('0');
|
|
if (i % 128 == 127)
|
|
{
|
|
short Percentage = s_cst(short, (i * 100) / bm.Size);
|
|
ExPrint("\n\eAAAAAA[%03ld%%] %08ld: ", Percentage, i);
|
|
Display->UpdateBuffer();
|
|
}
|
|
}
|
|
ExPrint("\n\eAAAAAA--- END OF BITMAP ---\nBitmap size: %ld\n\n.", bm.Size);
|
|
DisplayTopOverlay();
|
|
Display->UpdateBuffer();
|
|
CrashFont = oldFont;
|
|
}
|
|
else if (strcmp(Input, "mem") == 0)
|
|
{
|
|
uint64_t Total = KernelAllocator.GetTotalMemory();
|
|
uint64_t Used = KernelAllocator.GetUsedMemory();
|
|
uint64_t Free = KernelAllocator.GetFreeMemory();
|
|
uint64_t Reserved = KernelAllocator.GetReservedMemory();
|
|
|
|
ExPrint("\e22AA44Total: %ld bytes\n\eFF0000Used: %ld bytes\n\e00FF00Free: %ld bytes\n\eFF00FFReserved: %ld bytes\n", Total, Used, Free, Reserved);
|
|
int Progress = s_cst(int, (Used * 100) / Total);
|
|
int ReservedProgress = s_cst(int, (Reserved * 100) / Total);
|
|
ExPrint("\e22AA44%3d%% \eCCCCCC[", Progress);
|
|
for (int i = 0; i < Progress; i++)
|
|
ExPrint("\eFF0000|");
|
|
for (int i = 0; i < 100 - Progress; i++)
|
|
ExPrint("\e00FF00|");
|
|
for (int i = 0; i < ReservedProgress; i++)
|
|
ExPrint("\eFF00FF|");
|
|
ExPrint("\eCCCCCC]\eAAAAAA\n");
|
|
}
|
|
else if (strncmp(Input, "dump", 4) == 0)
|
|
{
|
|
char *arg = TrimWhiteSpace(Input + 4);
|
|
char *addr = strtok(arg, " ");
|
|
char *len = strtok(NULL, " ");
|
|
if (addr == NULL || len == NULL)
|
|
{
|
|
ExPrint("\eFF0000Invalid arguments\n");
|
|
goto End;
|
|
}
|
|
|
|
uintptr_t Address = strtoul(addr, NULL, 16);
|
|
size_t Length = strtoul(len, NULL, 10);
|
|
|
|
uintptr_t AlignedAddress = ROUND_DOWN(Address, PAGE_SIZE);
|
|
bool IsRangeValid = true;
|
|
{
|
|
Memory::Virtual vmm;
|
|
for (uintptr_t adr = AlignedAddress;
|
|
adr < Address + Length;
|
|
adr += PAGE_SIZE)
|
|
{
|
|
if (!vmm.Check((void *)adr))
|
|
{
|
|
ExPrint("\eFFA500Address %#lx is not mapped\n", adr);
|
|
IsRangeValid = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IsRangeValid)
|
|
{
|
|
debug("Dumping %ld bytes from %#lx\n", Length, Address);
|
|
|
|
Video::Font *oldFont = CrashFont;
|
|
CrashFont = Display->GetDefaultFont();
|
|
ExDumpData((void *)Address, (unsigned long)Length);
|
|
CrashFont = oldFont;
|
|
}
|
|
}
|
|
else if (strcmp(Input, "diag") == 0)
|
|
{
|
|
DiagnosticDataCollection();
|
|
}
|
|
else if (strcmp(Input, "screen") == 0)
|
|
{
|
|
if (unlikely(FbBeforePanic == nullptr))
|
|
{
|
|
ExPrint("\eFF0000No screen data available\n");
|
|
goto End;
|
|
}
|
|
memcpy(Display->GetBuffer, FbBeforePanic, Display->GetSize);
|
|
Display->UpdateBuffer();
|
|
return;
|
|
}
|
|
#ifdef DEBUG
|
|
else if (strcmp(Input, "pt") == 0)
|
|
{
|
|
/* Helpful for qemu "info tlb" command */
|
|
CPU::PageTable((void *)ExFrame->cr3);
|
|
ExPrint("Here be dragons\n");
|
|
}
|
|
else if (strcmp(Input, "ps") == 0)
|
|
{
|
|
DisplayProcessScreen(ExFrame, false);
|
|
}
|
|
#endif // DEBUG
|
|
else if (strlen(Input) > 0)
|
|
ExPrint("Unknown command: %s", Input);
|
|
else
|
|
ExPrint("Use the 'help' command to display a list of available commands.");
|
|
|
|
End:
|
|
DisplayBottomOverlay();
|
|
Display->UpdateBuffer();
|
|
}
|