diff --git a/sortix/Makefile b/sortix/Makefile
index f89ba975..3707280d 100644
--- a/sortix/Makefile
+++ b/sortix/Makefile
@@ -83,6 +83,7 @@ copy.o \
$(CPU)/calltrace.o \
$(CPU)/kthread.o \
crc32.o \
+debugger.o \
descriptor.o \
dispmsg.o \
dtable.o \
diff --git a/sortix/debugger.cpp b/sortix/debugger.cpp
new file mode 100644
index 00000000..aeea8451
--- /dev/null
+++ b/sortix/debugger.cpp
@@ -0,0 +1,645 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2013.
+
+ This file is part of Sortix.
+
+ Sortix 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.
+
+ Sortix 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
+ Sortix. If not, see .
+
+ debugger.cpp
+ Internal kernel debugger.
+
+*******************************************************************************/
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "kb/layout/us.h"
+
+namespace Sortix {
+namespace Debugger {
+
+uint16_t* const VIDEO_MEMORY = (uint16_t*) 0xB8000;
+
+bool first_f10;
+static int column;
+static int row;
+static Thread* current_thread;
+#define current_process (current_thread->process)
+
+// Changes the position of the hardware cursor.
+void SetCursor(int x, int y)
+{
+ unsigned value = x + y * 80;
+
+ // This sends a command to indicies 14 and 15 in the
+ // CRT Control Register of the VGA controller. These
+ // are the high and low bytes of the index that show
+ // where the hardware cursor is to be 'blinking'.
+ CPU::OutPortB(0x3D4, 14);
+ CPU::OutPortB(0x3D5, (value >> 8) & 0xFF);
+ CPU::OutPortB(0x3D4, 15);
+ CPU::OutPortB(0x3D5, (value >> 0) & 0xFF);
+}
+
+void GetCursor(int* x, int* y)
+{
+ CPU::OutPortB(0x3D4, 14);
+ uint8_t high = CPU::InPortB(0x3D5);
+ CPU::OutPortB(0x3D4, 15);
+ uint8_t low = CPU::InPortB(0x3D5);
+ unsigned value = high << 8 | low;
+ *x = value % 80;
+ *y = value / 80;
+}
+
+uint16_t* Character(int x, int y)
+{
+ return &VIDEO_MEMORY[y * 80 + x];
+}
+
+void Scroll()
+{
+ for ( int y = 0; y < 25-1; y++ )
+ for ( int x = 0; x < 80; x++ )
+ *Character(x, y) = *Character(x, y+1);
+ for ( int x = 0; x < 80; x++ )
+ *Character(x, 25-1) = 0x700 | ' ';
+}
+
+void Newline()
+{
+ if ( row + 1 == 25 )
+ Scroll();
+ else
+ row++;
+ column = 0;
+}
+
+void PrintChar(char c)
+{
+ if ( c == '\n' )
+ Newline();
+ else if ( c == '\b' )
+ {
+ if ( column )
+ column--;
+ else if ( row )
+ column = 80,
+ row--;
+ *Character(column, row) = 0x700 | ' ';
+ }
+ else if ( c == '\r' )
+ column = 0;
+ else if ( c == '\t' )
+ {
+ do PrintChar(' ');
+ while ( column % 8 != 0 );
+ }
+ else
+ {
+ if ( column == 80 )
+ Newline();
+ *Character(column++, row) = 0x700 | (uint16_t) c;
+ }
+
+ SetCursor(column, row);
+}
+
+void PrintString(const char* str)
+{
+ while ( *str )
+ PrintChar(*str++);
+}
+
+size_t PrintCallback(void* /*user*/, const char* str, size_t len)
+{
+ for ( size_t i = 0; i < len; i++ )
+ PrintChar(str[i]);
+ return len;
+}
+
+void Print(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vprintf_callback(PrintCallback, NULL, format, ap);
+ va_end(ap);
+}
+
+void PrintSymbol(const char* symbol)
+{
+ if ( !symbol )
+ {
+ Print("");
+ return;
+ }
+ if ( !(symbol[0] == '_' && symbol[1] == 'Z') )
+ {
+ Print("%s(...)", symbol);
+ return;
+ }
+ symbol += 2;
+ while ( *symbol )
+ {
+ if ( *symbol == 'N' )
+ symbol++;
+ else if ( '0' <= *symbol && *symbol <= '9' )
+ {
+ size_t len = strtoul(symbol, (char**) &symbol, 10);
+ for ( size_t i = 0; i < len; i++ )
+ PrintChar(*symbol++);
+ if ( *symbol == 'L' )
+ symbol++; // TODO: What is this?
+ if ( '0' <= *symbol && *symbol <= '9' )
+ Print("::");
+ }
+ else if ( *symbol == 'v' )
+ {
+ symbol++;
+ Print("()");
+ break;
+ }
+ else if ( *symbol == 'i' )
+ {
+ symbol++;
+ Print("(...)");
+ break;
+ }
+ else if ( *symbol == 'E' || *symbol == 'P' )
+ {
+ symbol++;
+ if ( *symbol == 'v' )
+ Print("()");
+ else
+ Print("(...)");
+ break;
+ }
+ else
+ PrintChar(*symbol++);
+ }
+}
+
+static bool MatchesSymbol(const Symbol* symbol, uintptr_t address)
+{
+ return symbol->address <= address &&
+ address <= symbol->address + symbol->size;
+}
+
+const char* GetSymbolName(uintptr_t address)
+{
+ if ( const char* symbol_name = GetKernelSymbolName(address) )
+ return symbol_name;
+ for ( size_t i = 0; i < current_process->symbol_table_length; i++ )
+ {
+ const Symbol* symbol = current_process->symbol_table + i;
+ if ( MatchesSymbol(symbol, address) )
+ return symbol->name;
+ }
+ return NULL;
+}
+
+void ReadCommand(char* buffer, size_t buffer_length)
+{
+ KBLayoutUS kblayout;
+ bool scancode_escaped = false;
+
+ size_t written = 0;
+ while ( true )
+ {
+ // Get a scancode from the keyboard.
+ uint16_t iobase = 0x60;
+ const uint16_t DATA = 0x0;
+ //const uint16_t COMMAND = 0x0;
+ const uint16_t STATUS = 0x4;
+ while ( (CPU::InPortB(iobase + STATUS) & (1<<0)) == 0 );
+ uint8_t scancode = CPU::InPortB(iobase + DATA);
+
+ // Handle escaped scancodes.
+ const uint8_t SCANCODE_ESCAPE = 0xE0;
+ if ( (scancode_escaped = scancode == SCANCODE_ESCAPE) )
+ continue;
+
+ // Produce a format integer.
+ int offset = (scancode_escaped) ? 0x80 : 0;
+ int kbkey = scancode & 0x7F;
+ if ( scancode & 0x80 ) { kbkey = -kbkey - offset; }
+ else { kbkey = kbkey + offset; }
+
+ if ( !written && kbkey == -KBKEY_F10 )
+ {
+ if ( !first_f10 )
+ {
+ strncpy(buffer, "exit", buffer_length);
+ break;
+ }
+ first_f10 = false;
+ }
+
+ // Translate the keystroke into unicode.
+ uint32_t unicode = kblayout.Translate(kbkey);
+
+ if ( !unicode )
+ continue;
+
+ // Ignore depressed keys.
+ if ( kbkey < 0 )
+ continue;
+
+ // Discard anything but ascii.
+ if ( 128 <= unicode )
+ continue;
+
+ char c = (char) unicode;
+
+ // Discard tabs.
+ if ( c == '\t' )
+ continue;
+
+ // Handle backspace.
+ if ( c == '\b' )
+ {
+ if ( !written )
+ continue;
+ PrintChar(c);
+ written--;
+ continue;
+ }
+
+ // Truncate the user input if it is too long.
+ if ( written == buffer_length && c != '\n' )
+ continue;
+
+ PrintChar(c);
+
+ // Finish reading the line.
+ if ( c == '\n' )
+ {
+ buffer[written] = '\0';
+ break;
+ }
+
+ buffer[written++] = c;
+ }
+}
+
+Process* FindProcess(pid_t least_pid, pid_t max_pid)
+{
+ Process* best = NULL;
+ if ( least_pid <= current_process->pid && current_process->pid <= max_pid )
+ best = current_process;
+ Thread* first_thread = current_thread;
+ for ( Thread* iter = first_thread->schedulerlistnext;
+ iter != first_thread; iter = iter->schedulerlistnext )
+ if ( least_pid <= iter->process->pid && iter->process->pid <= max_pid &&
+ (!best || iter->process->pid < best->pid) )
+ best = iter->process;
+ return best;
+}
+
+int ThreadId(Thread* thread)
+{
+ int ret = 0;
+ while ( thread->prevsibling )
+ ret++, thread = thread->prevsibling;
+ return ret;
+}
+
+int main_bt(int /*argc*/, char* /*argv*/[])
+{
+ CPU::InterruptRegisters regs;
+ current_thread->LoadRegisters(®s);
+#if defined(__x86_64__)
+ unsigned long ip = regs.rip;
+ unsigned long bp = regs.rbp;
+#elif defined(__i386__)
+ unsigned long ip = regs.eip;
+ unsigned long bp = regs.ebp;
+#endif
+
+ bool userspace = false;
+ unsigned long depth = 0;
+ do
+ {
+ if ( 4*1024*1024 <= ip && !userspace )
+ Print(" -- userspace --\n"), userspace = true;
+ const char* symbol = GetSymbolName(ip);
+ Print("%-4lu 0x%zx ", depth, ip);
+ PrintSymbol(symbol);
+ Print("\n");
+ if ( !bp )
+ break;
+ ip = ((unsigned long*) bp)[1];
+ bp = ((unsigned long*) bp)[0];
+ depth++;
+ } while ( ip );
+ return 0;
+}
+
+int main_dump(int argc, char* argv[])
+{
+ unsigned long word_size = 0;
+ if ( !strcmp(argv[0], "dump8") ) word_size = 1;
+ if ( !strcmp(argv[0], "dump16") ) word_size = 2;
+ if ( !strcmp(argv[0], "dump32") ) word_size = 4;
+ if ( !strcmp(argv[0], "dump64") ) word_size = 8;
+ if ( argc < 2 )
+ return 0;
+
+ unsigned long start = strtoul(argv[1], NULL, 0);
+ unsigned long length = 1;
+ if ( 3 <= argc )
+ length = strtoul(argv[2], NULL, 0);
+
+ uint8_t* data = (uint8_t*) start;
+ for ( size_t i = 0; i < length; i++ )
+ {
+ size_t elemlen = (word_size ? word_size : 1) ;
+ size_t outlen = elemlen * 2;
+ if ( 80 - column < (int) outlen )
+ Print("\n");
+ if ( !column )
+#if __WORDSIZE == 64
+ Print("%016lX: ", start + i * elemlen);
+#else
+ Print("%08lX: ", start + i * elemlen);
+#endif
+ else if ( word_size )
+ Print(" ");
+ // TODO: Endianness!
+ for ( size_t n = 0; n < (word_size ? word_size : 1); n++ )
+ Print("%02X", data[i * elemlen + n]);
+ }
+
+ if ( column )
+ Print("\n");
+
+ return 0;
+}
+
+int main_echo(int argc, char* argv[])
+{
+ const char* prefix = "";
+ for ( int i = 1; i < argc; i++ )
+ Print("%s%s", prefix, argv[i]),
+ prefix = " ";
+ Print("\n");
+ return 0;
+}
+
+int main_exit(int /*argc*/, char* /*argv*/[])
+{
+ return -1;
+}
+
+int main_pid(int argc, char* argv[])
+{
+ if ( 2 <= argc )
+ {
+ int pid = atoi(argv[1]);
+ Process* process = FindProcess(pid, pid);
+ if ( !process )
+ {
+ Print("pid: %i: No such process\n", pid);
+ return 1;
+ }
+ current_thread = process->firstthread;
+ Memory::SwitchAddressSpace(current_thread->addrspace);
+ }
+ Print("%c %i\t`%s'\n", '*', (int) current_process->pid,
+ current_process->program_image_path);
+ return 0;
+}
+
+int main_ps(int /*argc*/, char* /*argv*/[])
+{
+ pid_t least = 0;
+ while ( Process* process = FindProcess(least, INT_MAX) )
+ {
+ Print("%c %i\t`%s'\n", process == current_process ? '*' : ' ',
+ (int) process->pid, process->program_image_path);
+ least = process->pid + 1;
+ }
+ return 0;
+}
+
+int main_rs(int /*argc*/, char* /*argv*/[])
+{
+ CPU::InterruptRegisters regs;
+ current_thread->LoadRegisters(®s);
+#if defined(__x86_64__)
+ Print("rax=0x%lx, ", regs.rax);
+ Print("rbx=0x%lx, ", regs.rbx);
+ Print("rcx=0x%lx, ", regs.rcx);
+ Print("rdx=0x%lx, ", regs.rdx);
+ Print("rdi=0x%lx, ", regs.rdi);
+ Print("rsi=0x%lx, ", regs.rsi);
+ Print("rsp=0x%lx, ", regs.userrsp);
+ Print("rbp=0x%lx, ", regs.rbp);
+ Print("r8=0x%lx, ", regs.r8);
+ Print("r9=0x%lx, ", regs.r9);
+ Print("r10=0x%lx, ", regs.r10);
+ Print("r11=0x%lx, ", regs.r11);
+ Print("r12=0x%lx, ", regs.r12);
+ Print("r13=0x%lx, ", regs.r13);
+ Print("r14=0x%lx, ", regs.r14);
+ Print("r15=0x%lx, ", regs.r15);
+ Print("rip=0x%lx, ", regs.rip);
+ Print("rflags=0x%lx, ", regs.rflags);
+ Print("int_no=%lu, ", regs.int_no);
+ Print("err_code=0x%lx, ", regs.err_code);
+ Print("cs=0x%lx, ", regs.cs);
+ Print("ds=0x%lx, ", regs.ds);
+ Print("ss=0x%lx, ", regs.ss);
+ Print("kerrno=%lu, ", regs.kerrno);
+ Print("cr2=%lx, ", regs.cr2);
+ Print("signal_pending=%lu.", regs.signal_pending);
+#elif defined(__i386__)
+ Print("eax=0x%lx, ", regs.eax);
+ Print("ebx=0x%lx, ", regs.ebx);
+ Print("ecx=0x%lx, ", regs.ecx);
+ Print("edx=0x%lx, ", regs.edx);
+ Print("edi=0x%lx, ", regs.edi);
+ Print("esi=0x%lx, ", regs.esi);
+ Print("esp=0x%lx, ", regs.useresp);
+ Print("ebp=0x%lx, ", regs.ebp);
+ Print("eip=0x%lx, ", regs.eip);
+ Print("eflags=0x%lx, ", regs.eflags);
+ Print("int_no=%lu, ", regs.int_no);
+ Print("err_code=0x%lx, ", regs.err_code);
+ Print("cs=0x%lx, ", regs.cs);
+ Print("ds=0x%lx, ", regs.ds);
+ Print("ss=0x%lx, ", regs.ss);
+ Print("kerrno=%lu, ", regs.kerrno);
+ Print("cr2=%lx, ", regs.cr2);
+ Print("signal_pending=%lu.", regs.signal_pending);
+#endif
+ Print("\n");
+ return 0;
+}
+
+static void DescribeThread(int tid, Thread* thread)
+{
+ CPU::InterruptRegisters regs;
+ thread->LoadRegisters(®s);
+#if defined(__x86_64__)
+ unsigned long ip = regs.rip;
+#elif defined(__i386__)
+ unsigned long ip = regs.eip;
+#endif
+
+ Print("%c ", thread == current_thread ? '*' : ' ');
+ Print("%i", tid);
+ Print("\tip=0x%lx", ip);
+ Print("\n");
+}
+
+int main_tid(int argc, char* argv[])
+{
+ if ( 2 <= argc )
+ {
+ int tid = atoi(argv[1]);
+ Thread* thread = current_process->firstthread;
+ for ( int i = 0; i < tid && thread; i++ )
+ thread = thread->nextsibling;
+ if ( !thread )
+ {
+ Print("tid: %i: No such thread\n", tid);
+ return 1;
+ }
+ current_thread = thread;
+ Memory::SwitchAddressSpace(current_thread->addrspace);
+ }
+ DescribeThread(ThreadId(current_thread), current_thread);
+ return 0;
+}
+
+int main_ts(int /*argc*/, char* /*argv*/[])
+{
+ int tid = 0;
+ for ( Thread* thread = current_process->firstthread; thread; thread = thread->nextsibling )
+ DescribeThread(tid++, thread);
+ return 0;
+}
+
+struct command_registration
+{
+ const char* command;
+ int (*function)(int, char*[]);
+ const char* help;
+};
+
+
+static const struct command_registration commands[] =
+{
+ "bt", main_bt, "bt Stack trace",
+ "dump", main_dump, "dump START [LEN] Dump continuous memory",
+ "dump8", main_dump, "dump8 START [LEN] Dump 8-bit memory words",
+ "dump16", main_dump, "dump16 START [LEN] Dump 16-bit memory words",
+ "dump32", main_dump, "dump32 START [LEN] Dump 32-bit memory words",
+ "dump64", main_dump, "dump16 START [LEN] Dump 64-bit memory words",
+ "echo", main_echo, "echo [ARG...] Echo string",
+ "exit", main_exit, "exit Quit debugger",
+ "pid", main_pid, "pid [NEWPID] Change current process",
+ "ps", main_ps, "ps List processes",
+ "rs", main_rs, "rs Print registers",
+ "tid", main_tid, "tid [NEWTID] Change current thread",
+ "ts", main_ts, "ts List threads in current process",
+ NULL, NULL, NULL,
+};
+
+bool RunCommand()
+{
+ const size_t BUFFER_LENGTH = 256;
+ static char buffer[BUFFER_LENGTH];
+ Print("> ");
+ ReadCommand(buffer, BUFFER_LENGTH-1);
+
+ static char* argv[256];
+
+ int argc = 0;
+ char* input = buffer;
+ char* saved = NULL;
+ while ( (argv[argc] = strtok_r(input, " \t\n", &saved)) )
+ argc++, input = NULL;
+
+ if ( !argc )
+ return true;
+
+ if ( !strcmp(argv[0], "help") )
+ {
+ Print("You can use the following kernel debugger commands:\n");
+ for ( size_t i = 0; commands[i].command; i++ )
+ Print("%s\n", commands[i].help);
+ Print("\n");
+ return true;
+ }
+
+ for ( size_t i = 0; commands[i].command; i++ )
+ if ( !strcmp(argv[0], commands[i].command) )
+ return commands[i].function(argc, argv) != -1;
+
+ Print("%s: No such kernel debugger command\n", argv[0]);
+
+ return true;
+}
+
+void Run()
+{
+ static uint16_t saved_video_memory[80*25];
+
+ current_thread = CurrentThread();
+
+ bool was_enabled = Interrupt::SetEnabled(false);
+
+ first_f10 = true;
+
+ addr_t saved_addrspace = current_thread->addrspace;
+
+ memcpy(saved_video_memory, VIDEO_MEMORY, sizeof(saved_video_memory));
+ int saved_x, saved_y;
+ GetCursor(&saved_x, &saved_y);
+
+ column = saved_x, row = saved_y;
+ Print("\nSortix kernel debugger - type `help' for help.\n");
+
+ while ( RunCommand() );
+ SetCursor(saved_x, saved_y);
+ memcpy(VIDEO_MEMORY, saved_video_memory, sizeof(saved_video_memory));
+
+ Memory::SwitchAddressSpace(saved_addrspace);
+
+ Interrupt::SetEnabled(was_enabled);
+}
+
+} // namespace Debugger
+} // namespace Sortix
diff --git a/sortix/include/sortix/kernel/debugger.h b/sortix/include/sortix/kernel/debugger.h
new file mode 100644
index 00000000..cb272936
--- /dev/null
+++ b/sortix/include/sortix/kernel/debugger.h
@@ -0,0 +1,36 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2013.
+
+ This file is part of Sortix.
+
+ Sortix 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.
+
+ Sortix 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
+ Sortix. If not, see .
+
+ sortix/kernel/debugger.h
+ Internal kernel debugger.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_SORTIX_KERNEL_DEBUGGER_H
+#define INCLUDE_SORTIX_KERNEL_DEBUGGER_H
+
+namespace Sortix {
+namespace Debugger {
+
+void Run();
+
+} // namespace Debugger
+} // namespace Sortix
+
+#endif
diff --git a/sortix/kb/ps2.cpp b/sortix/kb/ps2.cpp
index 9b2b7068..ff5651fa 100644
--- a/sortix/kb/ps2.cpp
+++ b/sortix/kb/ps2.cpp
@@ -23,8 +23,10 @@
*******************************************************************************/
#include
+#include
#include
#include
+#include
#include
#include
@@ -88,9 +90,14 @@ namespace Sortix
work->kb->InterruptWork(work->scancode);
}
- void PS2Keyboard::OnInterrupt(CPU::InterruptRegisters* /*regs*/)
+ void PS2Keyboard::OnInterrupt(CPU::InterruptRegisters* regs)
{
uint8_t scancode = PopScancode();
+ if ( scancode == KBKEY_F10 )
+ {
+ CurrentThread()->SaveRegisters(regs);
+ Debugger::Run();
+ }
PS2KeyboardWork work;
work.kb = this;
work.scancode = scancode;