/*
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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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"
using namespace KernelConsole;
extern const char *x86ExceptionMnemonics[];
extern void DisplayCrashScreen(CPU::ExceptionFrame *);
extern bool UserModeExceptionHandler(CPU::ExceptionFrame *);
extern void DisplayStackSmashing();
extern void DisplayBufferOverflow();
extern void DisplayAssertionFailed(const char *, int, const char *);
void *FbBeforePanic = nullptr;
size_t FbPagesBeforePanic = 0;
FontRenderer CrashFontRenderer;
int ExTermColors[] = {
[TerminalColor::BLACK] = 0x000000,
[TerminalColor::RED] = 0xAA0000,
[TerminalColor::GREEN] = 0x00AA00,
[TerminalColor::YELLOW] = 0xAA5500,
[TerminalColor::BLUE] = 0x0000AA,
[TerminalColor::MAGENTA] = 0xAA00AA,
[TerminalColor::CYAN] = 0x00AAAA,
[TerminalColor::GREY] = 0xAAAAAA,
};
int ExTermBrightColors[] = {
[TerminalColor::BLACK] = 0x858585,
[TerminalColor::RED] = 0xFF5555,
[TerminalColor::GREEN] = 0x55FF55,
[TerminalColor::YELLOW] = 0xFFFF55,
[TerminalColor::BLUE] = 0x5555FF,
[TerminalColor::MAGENTA] = 0xFF55FF,
[TerminalColor::CYAN] = 0x55FFFF,
[TerminalColor::GREY] = 0xFFFFFF,
};
void paint_callback(TerminalCell *cell, long x, long y)
{
if (cell->attr.Bright)
CrashFontRenderer.Paint(x, y, cell->c, ExTermBrightColors[cell->attr.Foreground], ExTermColors[cell->attr.Background]);
else
CrashFontRenderer.Paint(x, y, cell->c, ExTermColors[cell->attr.Foreground], ExTermColors[cell->attr.Background]);
}
nsa void __printfWrapper(char c, void *)
{
KernelConsole::Terminals[15]->Process(c);
}
nsa void ExPrint(const char *Format, ...)
{
va_list args;
va_start(args, Format);
vfctprintf(__printfWrapper, NULL, Format, args);
va_end(args);
}
nsa void HaltAllCores()
{
if (SMP::CPUCores <= 1)
return;
#if defined(a86)
if (Interrupts::apic[0] == nullptr)
return;
APIC::InterruptCommandRegister icr{};
bool x2APIC = ((APIC::APIC *)Interrupts::apic[0])->x2APIC;
if (likely(x2APIC))
{
icr.x2.VEC = s_cst(uint8_t, CPU::x86::IRQ31);
icr.x2.MT = APIC::Fixed;
icr.x2.L = APIC::Assert;
for (int i = 1; i < SMP::CPUCores; i++)
{
icr.x2.DES = uint8_t(i);
((APIC::APIC *)Interrupts::apic[i])->ICR(icr);
}
}
else
{
icr.VEC = s_cst(uint8_t, CPU::x86::IRQ31);
icr.MT = APIC::Fixed;
icr.L = APIC::Assert;
for (int i = 1; i < SMP::CPUCores; i++)
{
icr.DES = uint8_t(i);
((APIC::APIC *)Interrupts::apic[i])->ICR(icr);
}
}
#elif defined(aa64)
#endif
}
nsa void InitFont()
{
/* Hope we won't crash here */
if (Display == nullptr)
{
error("Can't initialize font without display initalized");
CPU::Stop();
}
if (FbBeforePanic == nullptr)
{
FbPagesBeforePanic = TO_PAGES(Display->GetSize);
FbBeforePanic = KernelAllocator.RequestPages(FbPagesBeforePanic);
debug("Created before panic framebuffer at %#lx", FbBeforePanic);
}
memcpy(FbBeforePanic, Display->GetBuffer, Display->GetSize);
if (CrashFontRenderer.CurrentFont == nullptr)
CrashFontRenderer.CurrentFont = new Video::Font(&_binary_files_tamsyn_font_1_11_Tamsyn8x16b_psf_start,
&_binary_files_tamsyn_font_1_11_Tamsyn8x16b_psf_end,
Video::FontType::PCScreenFont2);
if (Terminals[15] == nullptr)
{
size_t Cols = Display->GetWidth / CrashFontRenderer.CurrentFont->GetInfo().Width;
size_t Rows = Display->GetHeight / CrashFontRenderer.CurrentFont->GetInfo().Height;
Terminals[15] = new VirtualTerminal(Cols, Rows, Display->GetWidth, Display->GetHeight, paint_callback, nullptr);
}
}
std::atomic UnrecoverableLock = false;
nsa __noreturn void HandleUnrecoverableException(CPU::ExceptionFrame *Frame)
{
static int setOnce = 0;
if (!setOnce++) /* FIXME: SMP */
{
for (uint32_t x = 0; x < Display->GetWidth; x++)
for (uint32_t y = 0; y < Display->GetHeight; y++)
Display->SetPixel(x, y, 0xFF250500);
ExPrint("\x1b[H");
}
CPUData *core = GetCurrentCPU();
while (UnrecoverableLock.exchange(true, std::memory_order_acquire))
CPU::Pause();
ExPrint("\x1b[0m-----------------------------------------------\n");
ExPrint("\x1b[30;41mUnrecoverable exception %#lx on CPU %d\n",
Frame->InterruptNumber, core->ID);
#if defined(a86)
ExPrint("CR0=%#lx CR2=%#lx CR3=%#lx CR4=%#lx CR8=%#lx\n",
Frame->cr0, Frame->cr2, Frame->cr3, Frame->cr4, Frame->cr8);
ExPrint("DR0=%#lx DR1=%#lx DR2=%#lx DR3=%#lx DR6=%#lx DR7=%#lx\n",
Frame->dr0, Frame->dr1, Frame->dr2, Frame->dr3, Frame->dr6, Frame->dr7);
ExPrint("GS=%#lx FS=%#lx ES=%#lx DS=%#lx SS=%#lx CS=%#lx\n",
Frame->gs, Frame->fs, Frame->es, Frame->ds, Frame->ss, Frame->cs);
#endif
#if defined(a64)
ExPrint("R8=%#lx R9=%#lx R10=%#lx R11=%#lx R12=%#lx R13=%#lx R14=%#lx R15=%#lx\n",
Frame->r8, Frame->r9, Frame->r10, Frame->r11, Frame->r12, Frame->r13,
Frame->r14, Frame->r15);
#endif
#if defined(a86)
ExPrint("AX=%#lx BX=%#lx CX=%#lx DX=%#lx SI=%#lx DI=%#lx BP=%#lx SP=%#lx\n",
#ifdef a64
Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx, Frame->rsi, Frame->rdi,
Frame->rbp, Frame->rsp);
#else
Frame->eax, Frame->ebx, Frame->ecx, Frame->edx, Frame->esi, Frame->edi,
Frame->ebp, Frame->esp);
#endif /* a64 */
ExPrint("IP=%#lx FL=%#lx INT=%#lx ERR=%#lx\n",
#ifdef a64
Frame->rip, Frame->rflags.raw,
#else
Frame->eip, Frame->eflags.raw,
#endif /* a64 */
Frame->InterruptNumber, Frame->ErrorCode);
#endif /* a86 */
Display->UpdateBuffer();
error("Unrecoverable Exception: %#lx", Frame->InterruptNumber);
UnrecoverableLock.store(false, std::memory_order_release);
CPU::Stop();
}
nsa __noreturn void HandleExceptionInsideException(CPU::ExceptionFrame *Frame)
{
ExPrint("\x1b[0m-----------------------------------------------\n");
ExPrint("Exception inside exception: %#lx at %#lx\n",
Frame->InterruptNumber,
#if defined(a64)
Frame->rip);
#elif defined(a32)
Frame->eip);
#elif defined(aa64)
Frame->pc);
#endif
#if defined(a86)
ExPrint("CR0=%#lx CR2=%#lx CR3=%#lx CR4=%#lx CR8=%#lx\n",
Frame->cr0, Frame->cr2, Frame->cr3, Frame->cr4, Frame->cr8);
ExPrint("DR0=%#lx DR1=%#lx DR2=%#lx DR3=%#lx DR6=%#lx DR7=%#lx\n",
Frame->dr0, Frame->dr1, Frame->dr2, Frame->dr3, Frame->dr6, Frame->dr7);
ExPrint("GS=%#lx FS=%#lx ES=%#lx DS=%#lx SS=%#lx CS=%#lx\n",
Frame->gs, Frame->fs, Frame->es, Frame->ds, Frame->ss, Frame->cs);
#endif
#if defined(a64)
ExPrint("R8=%#lx R9=%#lx R10=%#lx R11=%#lx R12=%#lx R13=%#lx R14=%#lx R15=%#lx\n",
Frame->r8, Frame->r9, Frame->r10, Frame->r11, Frame->r12, Frame->r13,
Frame->r14, Frame->r15);
#endif
#if defined(a86)
ExPrint("AX=%#lx BX=%#lx CX=%#lx DX=%#lx SI=%#lx DI=%#lx BP=%#lx SP=%#lx\n",
#ifdef a64
Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx, Frame->rsi, Frame->rdi,
Frame->rbp, Frame->rsp);
#else
Frame->eax, Frame->ebx, Frame->ecx, Frame->edx, Frame->esi, Frame->edi,
Frame->ebp, Frame->esp);
#endif /* a64 */
ExPrint("IP=%#lx FL=%#lx INT=%#lx ERR=%#lx\n",
#ifdef a64
Frame->rip, Frame->rflags.raw,
#else
Frame->eip, Frame->eflags.raw,
#endif /* a64 */
Frame->InterruptNumber, Frame->ErrorCode);
#endif /* a86 */
Display->UpdateBuffer();
CPU::Stop();
}
std::atomic ExceptionLock = false;
nsa void HandleException(CPU::ExceptionFrame *Frame)
{
/* We don't need to restore the page table
because the ExceptionHandlerStub will
do it for us if we return. */
CPU::PageTable(KernelPageTable);
InitFont();
/* First check the exception */
if (unlikely(Frame->InterruptNumber == CPU::x86::DoubleFault))
{
ExceptionLock.store(true, std::memory_order_release);
HandleUnrecoverableException(Frame);
}
if (Frame->cs == GDT_USER_CODE && Frame->ss == GDT_USER_DATA)
{
if (UserModeExceptionHandler(Frame))
goto ExceptionExit;
CPUData *core = GetCurrentCPU();
if (likely(core->CurrentThread->Security.IsCritical == false))
TaskManager->Yield();
error("Critical thread \"%s\"(%d) died",
core->CurrentThread->Name,
core->CurrentThread->ID);
}
debug("-----------------------------------------------------------------------------------");
error("Exception: %#x", Frame->InterruptNumber);
debug("%ld MiB / %ld MiB (%ld MiB Reserved)",
TO_MiB(KernelAllocator.GetUsedMemory()),
TO_MiB(KernelAllocator.GetTotalMemory()),
TO_MiB(KernelAllocator.GetReservedMemory()));
if (ExceptionLock.load(std::memory_order_acquire))
HandleExceptionInsideException(Frame);
ExceptionLock.store(true, std::memory_order_release);
{
const char msg[] = "Entering in panic mode...";
size_t msgLen = strlen(msg);
size_t msgPixels = msgLen * CrashFontRenderer.CurrentFont->GetInfo().Width;
uint32_t x = uint32_t((Display->GetWidth - msgPixels) / 2);
uint32_t y = uint32_t((Display->GetHeight - CrashFontRenderer.CurrentFont->GetInfo().Height) / 2);
x /= CrashFontRenderer.CurrentFont->GetInfo().Width;
y /= CrashFontRenderer.CurrentFont->GetInfo().Height;
printf("\x1b[2J\x1b[%d;%dH", y, x);
printf("\x1b[30;41m%s\x1b[0m\x1b[H", msg);
}
if (DriverManager)
DriverManager->Panic();
if (TaskManager)
TaskManager->Panic();
Interrupts::RemoveAll();
HaltAllCores();
ForceUnlock = true;
DisplayCrashScreen(Frame);
CPU::Stop();
ExceptionExit:
ExceptionLock.store(false, std::memory_order_release);
}
nsa void BaseBufferStackError(bool Stack)
{
/* We don't need to restore the page table
because the ExceptionHandlerStub will
do it for us if we return. */
CPU::PageTable(KernelPageTable);
if (CrashFontRenderer.CurrentFont == nullptr)
CrashFontRenderer.CurrentFont = new Video::Font(&_binary_files_tamsyn_font_1_11_Tamsyn8x16b_psf_start,
&_binary_files_tamsyn_font_1_11_Tamsyn8x16b_psf_end,
Video::FontType::PCScreenFont2);
if (Terminals[15] == nullptr)
{
size_t Cols = Display->GetWidth / CrashFontRenderer.CurrentFont->GetInfo().Width;
size_t Rows = Display->GetHeight / CrashFontRenderer.CurrentFont->GetInfo().Height;
Terminals[15] = new VirtualTerminal(Cols, Rows, Display->GetWidth, Display->GetHeight, paint_callback, nullptr);
}
ExceptionLock.store(true, std::memory_order_release);
if (DriverManager)
DriverManager->Panic();
if (TaskManager)
TaskManager->Panic();
HaltAllCores();
ForceUnlock = true;
debug("-----------------------------------------------------------------------------------");
error("%s", Stack ? "Stack smashing detected" : "Buffer overflow detected");
debug("%ld MiB / %ld MiB (%ld MiB Reserved)",
TO_MiB(KernelAllocator.GetUsedMemory()),
TO_MiB(KernelAllocator.GetTotalMemory()),
TO_MiB(KernelAllocator.GetReservedMemory()));
}
nsa __noreturn void HandleStackSmashing()
{
BaseBufferStackError(true);
DisplayStackSmashing();
CPU::Stop();
}
nsa __noreturn void HandleBufferOverflow()
{
BaseBufferStackError(false);
DisplayBufferOverflow();
CPU::Stop();
}
EXTERNC nsa __noreturn void HandleAssertionFailed(const char *File, int Line,
const char *Expression)
{
DisplayAssertionFailed(File, Line, Expression);
CPU::Stop();
}