mirror of https://gitlab.com/nakst/essence
467 lines
12 KiB
C++
467 lines
12 KiB
C++
// This file is part of the Essence operating system.
|
|
// It is released under the terms of the MIT license -- see LICENSE.md.
|
|
// Written by: nakst.
|
|
|
|
// TODO Everything in this file is a hack just so I can debug the kernel.
|
|
// Replace all of it!!!
|
|
|
|
#ifdef IMPLEMENTATION
|
|
|
|
#include <shared/vga_font.cpp>
|
|
|
|
#define TERMINAL_ADDRESS ((uint16_t *) (LOW_MEMORY_MAP_START + 0xB8000))
|
|
|
|
#if 1
|
|
KSpinlock terminalLock;
|
|
KSpinlock printLock;
|
|
#else
|
|
KMutex terminalLock;
|
|
KMutex printLock;
|
|
#endif
|
|
|
|
void DebugWriteCharacter(uintptr_t character);
|
|
int KWaitKey();
|
|
|
|
#if defined(ES_ARCH_X86_32) || defined(ES_ARCH_X86_64)
|
|
bool printToDebugger = false;
|
|
uintptr_t terminalPosition = 80;
|
|
|
|
#define KERNEL_LOG_SIZE (262144)
|
|
char kernelLog[KERNEL_LOG_SIZE];
|
|
uintptr_t kernelLogPosition;
|
|
|
|
static void TerminalCallback(int character, void *) {
|
|
if (!character) return;
|
|
|
|
KSpinlockAcquire(&terminalLock);
|
|
EsDefer(KSpinlockRelease(&terminalLock));
|
|
|
|
if (sizeof(kernelLog)) {
|
|
kernelLog[kernelLogPosition] = character;
|
|
kernelLogPosition++;
|
|
if (kernelLogPosition == sizeof(kernelLog)) kernelLogPosition = 0;
|
|
}
|
|
|
|
#ifdef VGA_TEXT_MODE
|
|
{
|
|
if (character == '\n') {
|
|
terminalPosition = terminalPosition - (terminalPosition % 80) + 80;
|
|
} else {
|
|
TERMINAL_ADDRESS[terminalPosition] = (uint16_t) character | 0x0700;
|
|
terminalPosition++;
|
|
}
|
|
|
|
if (terminalPosition >= 80 * 25) {
|
|
for (int i = 80; i < 80 * 25; i++) {
|
|
TERMINAL_ADDRESS[i - 80] = TERMINAL_ADDRESS[i];
|
|
}
|
|
|
|
for (int i = 80 * 24; i < 80 * 25; i++) {
|
|
TERMINAL_ADDRESS[i] = 0x700;
|
|
}
|
|
|
|
terminalPosition -= 80;
|
|
|
|
// uint64_t start = ProcessorReadTimeStamp();
|
|
// uint64_t end = start + 250 * KGetTimeStampTicksPerMs();
|
|
// while (ProcessorReadTimeStamp() < end);
|
|
}
|
|
|
|
{
|
|
ProcessorOut8(0x3D4, 0x0F);
|
|
ProcessorOut8(0x3D5, terminalPosition);
|
|
ProcessorOut8(0x3D4, 0x0E);
|
|
ProcessorOut8(0x3D5, terminalPosition >> 8);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
{
|
|
ProcessorDebugOutputByte((uint8_t) character);
|
|
|
|
if (character == '\n') {
|
|
ProcessorDebugOutputByte((uint8_t) 13);
|
|
}
|
|
}
|
|
|
|
if (printToDebugger) {
|
|
DebugWriteCharacter(character);
|
|
if (character == '\t') DebugWriteCharacter(' ');
|
|
}
|
|
}
|
|
#endif
|
|
|
|
size_t debugRows, debugColumns, debugCurrentRow, debugCurrentColumn;
|
|
|
|
void DebugWriteCharacter(uintptr_t character) {
|
|
if (!graphics.target || !graphics.target->debugPutBlock) return;
|
|
|
|
if (debugCurrentRow == debugRows) {
|
|
#if 0
|
|
debugCurrentRow = 0;
|
|
|
|
// uint64_t start = ProcessorReadTimeStamp();
|
|
// uint64_t end = start + 3000 * KGetTimeStampTicksPerMs();
|
|
// while (ProcessorReadTimeStamp() < end);
|
|
|
|
graphics.target->debugClearScreen();
|
|
#else
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
uintptr_t row = debugCurrentRow;
|
|
uintptr_t column = debugCurrentColumn;
|
|
|
|
if (character == '\n') {
|
|
debugCurrentRow++;
|
|
debugCurrentColumn = 0;
|
|
return;
|
|
}
|
|
|
|
if (character > 127) character = ' ';
|
|
if (row >= debugRows) return;
|
|
if (column >= debugColumns) return;
|
|
|
|
for (int j = 0; j < VGA_FONT_HEIGHT; j++) {
|
|
uint8_t byte = ((uint8_t *) vgaFont)[character * 16 + j];
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
uint8_t bit = byte & (1 << i);
|
|
if (bit) graphics.target->debugPutBlock((column + 1) * 9 + i, row * 16 + j, false);
|
|
}
|
|
}
|
|
|
|
debugCurrentColumn++;
|
|
|
|
if (debugCurrentColumn == debugColumns) {
|
|
debugCurrentRow++;
|
|
debugCurrentColumn = 4;
|
|
}
|
|
}
|
|
|
|
void StartDebugOutput() {
|
|
if (graphics.target && graphics.target->debugClearScreen && graphics.target->debugPutBlock && !printToDebugger) {
|
|
graphics.target->debugClearScreen();
|
|
|
|
int widthUsed = 0;
|
|
|
|
if (graphics.target->debugPutData) {
|
|
widthUsed = graphics.target->debugPutData((const uint8_t *) kernelLog, KERNEL_LOG_SIZE);
|
|
}
|
|
|
|
debugRows = (graphics.height - 1) / VGA_FONT_HEIGHT;
|
|
debugColumns = (graphics.width - 1 - widthUsed) / VGA_FONT_WIDTH - 2;
|
|
debugCurrentRow = debugCurrentColumn = 0;
|
|
printToDebugger = true;
|
|
}
|
|
}
|
|
|
|
bool debugKeyPressed;
|
|
|
|
void KDebugKeyPressed() {
|
|
if (debugKeyPressed) return;
|
|
debugKeyPressed = true;
|
|
KernelPanic("Debug key pressed.\n");
|
|
}
|
|
|
|
#ifdef POST_PANIC_DEBUGGING
|
|
uintptr_t DebugReadNumber() {
|
|
uintptr_t value = 0;
|
|
|
|
for (uintptr_t i = 0; i < 2 * sizeof(uintptr_t); i++) {
|
|
value <<= 4;
|
|
|
|
while (true) {
|
|
int key = KWaitKey();
|
|
if (key == ES_SCANCODE_0) { EsPrint("0"); value |= 0; }
|
|
else if (key == ES_SCANCODE_1) { EsPrint("1"); value |= 1; }
|
|
else if (key == ES_SCANCODE_2) { EsPrint("2"); value |= 2; }
|
|
else if (key == ES_SCANCODE_3) { EsPrint("3"); value |= 3; }
|
|
else if (key == ES_SCANCODE_4) { EsPrint("4"); value |= 4; }
|
|
else if (key == ES_SCANCODE_5) { EsPrint("5"); value |= 5; }
|
|
else if (key == ES_SCANCODE_6) { EsPrint("6"); value |= 6; }
|
|
else if (key == ES_SCANCODE_7) { EsPrint("7"); value |= 7; }
|
|
else if (key == ES_SCANCODE_8) { EsPrint("8"); value |= 8; }
|
|
else if (key == ES_SCANCODE_9) { EsPrint("9"); value |= 9; }
|
|
else if (key == ES_SCANCODE_A) { EsPrint("A"); value |= 10; }
|
|
else if (key == ES_SCANCODE_B) { EsPrint("B"); value |= 11; }
|
|
else if (key == ES_SCANCODE_C) { EsPrint("C"); value |= 12; }
|
|
else if (key == ES_SCANCODE_D) { EsPrint("D"); value |= 13; }
|
|
else if (key == ES_SCANCODE_E) { EsPrint("E"); value |= 14; }
|
|
else if (key == ES_SCANCODE_F) { EsPrint("F"); value |= 15; }
|
|
else if (key == ES_SCANCODE_ENTER) { value >>= 4; return value; }
|
|
else continue;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
#endif
|
|
|
|
void KernelPanic(const char *format, ...) {
|
|
ProcessorDisableInterrupts();
|
|
ProcessorSendIPI(KERNEL_PANIC_IPI, true);
|
|
|
|
// Disable synchronisation objects. The panic IPI must be sent before this,
|
|
// so other processors don't start getting "mutex not correctly acquired" panics.
|
|
scheduler.panic = true;
|
|
|
|
if (debugKeyPressed) {
|
|
DriversDumpState();
|
|
}
|
|
|
|
StartDebugOutput();
|
|
|
|
EsPrint("\n--- System Error ---\n");
|
|
EsPrint("\n========================================================================================================\n");
|
|
EsPrint("| If you are using an emulator, please capture a screenshot of the entire window and report the error. |\n");
|
|
EsPrint("| If you are using Qemu, press Ctrl+Alt+2, then type \"cpu %d\", then \"dump-guest-memory -p mem.dat\". |\n",
|
|
GetLocalStorage() ? (GetLocalStorage()->archCPU ? GetLocalStorage()->archCPU->apicID : 0) : 0);
|
|
EsPrint("========================================================================================================\n");
|
|
EsPrint("\n>> ");
|
|
|
|
va_list arguments;
|
|
va_start(arguments, format);
|
|
_StringFormat(TerminalCallback, (void *) 0x4F00, format, arguments);
|
|
va_end(arguments);
|
|
|
|
EsPrint("Current thread = %x\n", GetCurrentThread());
|
|
EsPrint("Trace: %x\n", __builtin_return_address(0));
|
|
#ifdef ES_ARCH_X86_64
|
|
EsPrint("RSP: %x; RBP: %x\n", ProcessorGetRSP(), ProcessorGetRBP());
|
|
#endif
|
|
// EsPrint("Memory: %x/%x\n", pmm.pagesAllocated, pmm.startPageCount);
|
|
|
|
{
|
|
EsPrint("Threads:\n");
|
|
|
|
LinkedItem<Thread> *item = scheduler.allThreads.firstItem;
|
|
|
|
while (item) {
|
|
Thread *thread = item->thisItem;
|
|
|
|
#ifdef ES_ARCH_X86_64
|
|
EsPrint("%z %d %x @%x:%x ", (GetCurrentThread() == thread) ? "=>" : " ",
|
|
thread->id, thread, thread->interruptContext ? thread->interruptContext->rip : 0,
|
|
thread->interruptContext ? thread->interruptContext->rbp : 0);
|
|
#endif
|
|
|
|
if (thread->state == THREAD_WAITING_EVENT) {
|
|
EsPrint("WaitEvent(Count:%d, %x) ", thread->blocking.eventCount, thread->blocking.events[0]);
|
|
} else if (thread->state == THREAD_WAITING_MUTEX) {
|
|
EsPrint("WaitMutex(%x, Owner:%d) ", thread->blocking.mutex, thread->blocking.mutex->owner->id);
|
|
} else if (thread->state == THREAD_WAITING_WRITER_LOCK) {
|
|
EsPrint("WaitWriterLock(%x, %d) ", thread->blocking.writerLock, thread->blocking.writerLockType);
|
|
}
|
|
|
|
Process *process = thread->process;
|
|
EsPrint("%z:%z\n", process->cExecutableName, thread->cName);
|
|
|
|
item = item->nextItem;
|
|
}
|
|
}
|
|
|
|
for (uintptr_t i = 0; i < KGetCPUCount(); i++) {
|
|
CPULocalStorage *local = KGetCPULocal(i);
|
|
|
|
if (local && local->panicContext) {
|
|
#ifdef ES_ARCH_X86_64
|
|
EsPrint("CPU %d LS %x RIP/RBP %x:%x TID %d\n", local->processorID, local,
|
|
local->panicContext->rip, local->panicContext->rbp,
|
|
local->currentThread ? local->currentThread->id : 0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef POST_PANIC_DEBUGGING
|
|
uintptr_t kernelLogEnd = kernelLogPosition;
|
|
EsPrint("Press 'D' to enter debugger.\n");
|
|
|
|
while (true) {
|
|
int key = KWaitKey();
|
|
if (key == ES_SCANCODE_D) break;
|
|
if (key == -1) ProcessorHalt();
|
|
}
|
|
|
|
graphics.debuggerActive = true;
|
|
|
|
while (true) {
|
|
#ifdef VGA_TEXT_MODE
|
|
for (uintptr_t i = 0; i < 80 * 25; i++) {
|
|
TERMINAL_ADDRESS[i] = 0x0700;
|
|
}
|
|
|
|
terminalPosition = 80;
|
|
#else
|
|
graphics.target->debugClearScreen();
|
|
|
|
debugCurrentRow = debugCurrentColumn = 0;
|
|
#endif
|
|
|
|
|
|
EsPrint("0 - view log\n1 - reset\n2 - view pmem\n3 - view vmem\n4 - stack trace\n");
|
|
|
|
int key = KWaitKey();
|
|
|
|
if (key == ES_SCANCODE_0) {
|
|
uintptr_t position = 0, nextPosition = 0;
|
|
uintptr_t x = 0, y = 0;
|
|
|
|
#ifdef VGA_TEXT_MODE
|
|
for (uintptr_t i = 0; i < 80 * 25; i++) {
|
|
TERMINAL_ADDRESS[i] = 0x0700;
|
|
}
|
|
#else
|
|
graphics.target->debugClearScreen();
|
|
#endif
|
|
|
|
while (position < kernelLogEnd) {
|
|
char c = kernelLog[position];
|
|
|
|
if (c != '\n') {
|
|
#ifdef VGA_TEXT_MODE
|
|
TERMINAL_ADDRESS[x + y * 80] = c | 0x0700;
|
|
#else
|
|
debugCurrentRow = y, debugCurrentColumn = x;
|
|
DebugWriteCharacter(c);
|
|
#endif
|
|
}
|
|
|
|
x++;
|
|
|
|
if (x ==
|
|
#ifdef VGA_TEXT_MODE
|
|
80
|
|
#else
|
|
debugColumns
|
|
#endif
|
|
|| c == '\n') {
|
|
x = 0;
|
|
y++;
|
|
|
|
if (y == 1) {
|
|
nextPosition = position;
|
|
}
|
|
}
|
|
|
|
if (y ==
|
|
#ifdef VGA_TEXT_MODE
|
|
25
|
|
#else
|
|
debugRows
|
|
#endif
|
|
) {
|
|
while (true) {
|
|
int key = KWaitKey();
|
|
|
|
if (key == ES_SCANCODE_SPACE || key == ES_SCANCODE_DOWN_ARROW) {
|
|
position = nextPosition;
|
|
break;
|
|
} else if (key == ES_SCANCODE_UP_ARROW) {
|
|
position = nextPosition;
|
|
if (position < 240) position = 0;
|
|
else position -= 240;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef VGA_TEXT_MODE
|
|
for (uintptr_t i = 0; i < 80 * 25; i++) {
|
|
TERMINAL_ADDRESS[i] = 0x0700;
|
|
}
|
|
#else
|
|
graphics.target->debugClearScreen();
|
|
#endif
|
|
|
|
y = 0;
|
|
}
|
|
|
|
position++;
|
|
}
|
|
|
|
KWaitKey();
|
|
} else if (key == ES_SCANCODE_1) {
|
|
ProcessorReset();
|
|
} else if (key == ES_SCANCODE_2) {
|
|
EsPrint("Enter address: ");
|
|
uintptr_t address = DebugReadNumber();
|
|
uintptr_t offset = address & (K_PAGE_SIZE - 1);
|
|
MMRemapPhysical(kernelMMSpace, pmm.pmManipulationRegion, address - offset);
|
|
uintptr_t *data = (uintptr_t *) ((uint8_t *) pmm.pmManipulationRegion + offset);
|
|
|
|
for (uintptr_t i = 0; i < 8 && (offset + 8 * sizeof(uintptr_t) < K_PAGE_SIZE); i++) {
|
|
EsPrint("\n%x - %x\n", address + 8 * sizeof(uintptr_t), data[i]);
|
|
}
|
|
|
|
while (KWaitKey() != ES_SCANCODE_ENTER);
|
|
} else if (key == ES_SCANCODE_3) {
|
|
EsPrint("Enter address: ");
|
|
uintptr_t address = DebugReadNumber();
|
|
uintptr_t offset = address & (K_PAGE_SIZE - 1);
|
|
uintptr_t *data = (uintptr_t *) address;
|
|
|
|
for (uintptr_t i = 0; i < 8 && (offset + i * sizeof(uintptr_t) < K_PAGE_SIZE); i++) {
|
|
EsPrint("\n%x - %x", address + i * sizeof(uintptr_t), data[i]);
|
|
}
|
|
|
|
while (KWaitKey() != ES_SCANCODE_ENTER);
|
|
} else if (key == ES_SCANCODE_4) {
|
|
EsPrint("Enter RBP: ");
|
|
uintptr_t address = DebugReadNumber();
|
|
|
|
while (address) {
|
|
EsPrint("\n%x", ((uintptr_t *) address)[1]);
|
|
address = ((uintptr_t *) address)[0];
|
|
}
|
|
|
|
while (KWaitKey() != ES_SCANCODE_ENTER);
|
|
}
|
|
}
|
|
#else
|
|
EsPrint("End of report.\n");
|
|
|
|
if (graphics.target->debugPutData) {
|
|
// We put the log data on the screen before and after the panic report in case it gets stuck.
|
|
graphics.target->debugPutData((const uint8_t *) kernelLog, KERNEL_LOG_SIZE);
|
|
}
|
|
#endif
|
|
|
|
ProcessorHalt();
|
|
}
|
|
|
|
void EsPrint(const char *format, ...) {
|
|
KSpinlockAcquire(&printLock);
|
|
EsDefer(KSpinlockRelease(&printLock));
|
|
|
|
va_list arguments;
|
|
va_start(arguments, format);
|
|
_StringFormat(TerminalCallback, (void *) 0x0700, format, arguments);
|
|
va_end(arguments);
|
|
}
|
|
|
|
void __KernelLog(const char *format, ...) {
|
|
va_list arguments;
|
|
va_start(arguments, format);
|
|
_StringFormat(TerminalCallback, nullptr, format, arguments);
|
|
va_end(arguments);
|
|
}
|
|
|
|
void KernelLog(KLogLevel level, const char *subsystem, const char *event, const char *format, ...) {
|
|
if (level == LOG_VERBOSE) return;
|
|
(void) event;
|
|
|
|
KSpinlockAcquire(&printLock);
|
|
EsDefer(KSpinlockRelease(&printLock));
|
|
|
|
__KernelLog("[%z:%z] ", level == LOG_INFO ? "Info" : level == LOG_ERROR ? "**Error**" : level == LOG_VERBOSE ? "Verbose" : "", subsystem);
|
|
|
|
va_list arguments;
|
|
va_start(arguments, format);
|
|
_StringFormat(TerminalCallback, nullptr, format, arguments);
|
|
va_end(arguments);
|
|
}
|
|
|
|
#endif
|