feat: integrate gdb support over serial port for bare-metal debugging.
authorMinep <zelong56@gmail.com>
Tue, 18 Oct 2022 23:11:28 +0000 (00:11 +0100)
committerMinep <zelong56@gmail.com>
Tue, 18 Oct 2022 23:11:28 +0000 (00:11 +0100)
15 files changed:
lunaix-os/config/make-debug-tool
lunaix-os/debug/bg_lsdbg.c [new file with mode: 0644]
lunaix-os/debug/gdbstub.c [new file with mode: 0644]
lunaix-os/debug/sdbg.c [new file with mode: 0644]
lunaix-os/includes/arch/x86/vectors.h
lunaix-os/includes/hal/ioapic.h
lunaix-os/includes/lunaix/peripheral/serial.h [new file with mode: 0644]
lunaix-os/includes/lunaix/syslog.h
lunaix-os/includes/sdbg/gdbstub.h [new file with mode: 0644]
lunaix-os/includes/sdbg/lsdbg.h [new file with mode: 0644]
lunaix-os/includes/sdbg/protocol.h [new file with mode: 0644]
lunaix-os/kernel/kprintf.c
lunaix-os/kernel/peripheral/serial.c [new file with mode: 0644]
lunaix-os/kernel/proc0.c
lunaix-os/makefile

index 061cbc05cd3f3d2c01bfc0878dc23cb272c0fc92..25c99c03cc439bad133226cefe5dda9d3eaa1edb 100644 (file)
@@ -8,6 +8,7 @@ QEMU_OPTIONS := -s -S -m 1G \
                                -no-shutdown \
                                -d cpu_reset \
                                -d trace:ide_dma_cb \
+                               -serial tcp::12345,server,nowait\
                                -drive id=disk,file="machine/disk0.vdi",if=none \
                                -drive id=cdrom,file="$(BUILD_DIR)/$(OS_ISO)",readonly=on,if=none,format=raw \
                                -device ahci,id=ahci \
diff --git a/lunaix-os/debug/bg_lsdbg.c b/lunaix-os/debug/bg_lsdbg.c
new file mode 100644 (file)
index 0000000..0f0496d
--- /dev/null
@@ -0,0 +1,77 @@
+#include <klibc/stdio.h>
+#include <lunaix/peripheral/serial.h>
+#include <lunaix/syslog.h>
+#include <sdbg/lsdbg.h>
+
+#define COM SERIAL_COM1
+
+LOG_MODULE("DBG");
+
+static volatile sdbg_state = 0;
+
+void
+sdbg_printf(char* fmt, ...)
+{
+    char buf[256];
+    va_list l;
+
+    va_start(l, fmt);
+
+    size_t len = __ksprintf_internal(buf, fmt, 256, l);
+    serial_tx_buffer(COM, buf, len);
+
+    va_end(l);
+}
+
+void
+lunaix_sdbg_loop(isr_param* param)
+{
+    char c;
+
+    if (sdbg_state == SDBG_STATE_WAIT_BRK) {
+        (param)->eflags &= ~(1 << 8);
+        sdbg_state = SDBG_STATE_INSESSION;
+        sdbg_printf("[%p:%p] Break point reached.\n", param->cs, param->eip);
+    }
+
+    while (1) {
+        c = serial_rx_byte(SERIAL_COM1);
+        if (c == SDBG_CLNT_QUIT) {
+            sdbg_state = SDBG_STATE_START;
+            break;
+        }
+
+        switch (c) {
+            case SDBG_CLNT_HI:
+                if (sdbg_state == SDBG_STATE_START) {
+                    sdbg_printf(
+                      "[%p:%p] Session started.\n", param->cs, param->eip);
+                    sdbg_state = SDBG_STATE_INSESSION;
+                } else {
+                    sdbg_printf(
+                      "[%p:%p] Session resumed.\n", param->cs, param->eip);
+                }
+                break;
+            case SDBG_CLNT_RREG:
+
+                serial_tx_buffer(SERIAL_COM1, (char*)param, sizeof(isr_param));
+                break;
+            case SDBG_CLNT_STEP:
+                ((isr_param*)param)->eflags |= (1 << 8); // set TF flags
+                sdbg_state = SDBG_STATE_WAIT_BRK;
+                return;
+            case SDBG_CLNT_BRKP:
+                // the break point address
+                // serial_rx_buffer(SERIAL_COM1, buffer, sizeof(uintptr_t));
+
+                // asm("movl %0, %%dr0" ::"r"(*((uintptr_t*)buffer)));
+
+                sdbg_state = SDBG_STATE_WAIT_BRK;
+                return;
+            case SDBG_CLNT_CONT:
+                return;
+            default:
+                break;
+        }
+    }
+}
\ No newline at end of file
diff --git a/lunaix-os/debug/gdbstub.c b/lunaix-os/debug/gdbstub.c
new file mode 100644 (file)
index 0000000..9546b85
--- /dev/null
@@ -0,0 +1,1325 @@
+/**
+ * @file gdbstub.h
+ * @author Lunaixsky ()
+ * @brief GDB Stub implementation (https://github.com/mborgerson/gdbstub).
+ *          Ported for LunaixOS
+ * @version 0.1
+ * @date 2022-10-18
+ *
+ * @copyright Copyright (c) 2022 Lunaixsky & (See license below)
+ *
+ */
+
+/*
+ * Copyright (c) 2016-2022 Matt Borgerson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <hal/io.h>
+#include <klibc/string.h>
+#include <lunaix/peripheral/serial.h>
+#include <sdbg/gdbstub.h>
+
+/*****************************************************************************
+ * Types
+ ****************************************************************************/
+
+#ifndef GDBSTUB_DONT_DEFINE_STDINT_TYPES
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned long uint32_t;
+#endif
+
+typedef unsigned int address;
+typedef unsigned int reg;
+
+enum GDB_REGISTER
+{
+    GDB_CPU_I386_REG_EAX = 0,
+    GDB_CPU_I386_REG_ECX = 1,
+    GDB_CPU_I386_REG_EDX = 2,
+    GDB_CPU_I386_REG_EBX = 3,
+    GDB_CPU_I386_REG_ESP = 4,
+    GDB_CPU_I386_REG_EBP = 5,
+    GDB_CPU_I386_REG_ESI = 6,
+    GDB_CPU_I386_REG_EDI = 7,
+    GDB_CPU_I386_REG_PC = 8,
+    GDB_CPU_I386_REG_PS = 9,
+    GDB_CPU_I386_REG_CS = 10,
+    GDB_CPU_I386_REG_SS = 11,
+    GDB_CPU_I386_REG_DS = 12,
+    GDB_CPU_I386_REG_ES = 13,
+    GDB_CPU_I386_REG_FS = 14,
+    GDB_CPU_I386_REG_GS = 15,
+    GDB_CPU_NUM_REGISTERS = 16
+};
+
+struct gdb_state
+{
+    int signum;
+    reg registers[GDB_CPU_NUM_REGISTERS];
+};
+
+/*****************************************************************************
+ *
+ *  GDB Remote Serial Protocol
+ *
+ ****************************************************************************/
+
+/*****************************************************************************
+ * Macros
+ ****************************************************************************/
+
+#define GDB_PRINT(...)
+
+#define COM_PORT SERIAL_COM1
+
+#define GDB_EOF (-1)
+
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+
+#ifndef GDB_ASSERT
+#define GDB_ASSERT(x)                                                          \
+    do {                                                                       \
+    } while (0)
+#endif
+
+/*****************************************************************************
+ * Prototypes
+ ****************************************************************************/
+
+int
+gdb_main(struct gdb_state* state);
+
+/* System functions, supported by all stubs */
+void
+gdb_sys_init(void);
+int
+gdb_sys_getc(struct gdb_state* state);
+int
+gdb_sys_putchar(struct gdb_state* state, int ch);
+int
+gdb_sys_mem_readb(struct gdb_state* state, address addr, char* val);
+int
+gdb_sys_mem_writeb(struct gdb_state* state, address addr, char val);
+int
+gdb_sys_continue(struct gdb_state* state);
+int
+gdb_sys_step(struct gdb_state* state);
+
+/*****************************************************************************
+ * Types
+ ****************************************************************************/
+
+typedef int (*gdb_enc_func)(char* buf,
+                            unsigned int buf_len,
+                            const char* data,
+                            unsigned int data_len);
+typedef int (*gdb_dec_func)(const char* buf,
+                            unsigned int buf_len,
+                            char* data,
+                            unsigned int data_len);
+
+/*****************************************************************************
+ * Const Data
+ ****************************************************************************/
+
+static const char digits[] = "0123456789abcdef";
+
+/*****************************************************************************
+ * Prototypes
+ ****************************************************************************/
+
+/* Communication functions */
+static int
+gdb_write(struct gdb_state* state, const char* buf, unsigned int len);
+static int
+gdb_read(struct gdb_state* state,
+         char* buf,
+         unsigned int buf_len,
+         unsigned int len);
+
+/* String processing helper functions */
+static int
+gdb_strlen(const char* ch);
+static char
+gdb_get_digit(int val);
+static int
+gdb_get_val(char digit, int base);
+static int
+gdb_strtol(const char* str, unsigned int len, int base, const char** endptr);
+
+/* Packet functions */
+static int
+gdb_send_packet(struct gdb_state* state, const char* pkt, unsigned int pkt_len);
+static int
+gdb_recv_packet(struct gdb_state* state,
+                char* pkt_buf,
+                unsigned int pkt_buf_len,
+                unsigned int* pkt_len);
+static int
+gdb_checksum(const char* buf, unsigned int len);
+static int
+gdb_recv_ack(struct gdb_state* state);
+
+/* Data encoding/decoding */
+static int
+gdb_enc_hex(char* buf,
+            unsigned int buf_len,
+            const char* data,
+            unsigned int data_len);
+static int
+gdb_dec_hex(const char* buf,
+            unsigned int buf_len,
+            char* data,
+            unsigned int data_len);
+static int
+gdb_enc_bin(char* buf,
+            unsigned int buf_len,
+            const char* data,
+            unsigned int data_len);
+static int
+gdb_dec_bin(const char* buf,
+            unsigned int buf_len,
+            char* data,
+            unsigned int data_len);
+
+/* Packet creation helpers */
+static int
+gdb_send_ok_packet(struct gdb_state* state, char* buf, unsigned int buf_len);
+static int
+gdb_send_conmsg_packet(struct gdb_state* state,
+                       char* buf,
+                       unsigned int buf_len,
+                       const char* msg);
+static int
+gdb_send_signal_packet(struct gdb_state* state,
+                       char* buf,
+                       unsigned int buf_len,
+                       char signal);
+static int
+gdb_send_error_packet(struct gdb_state* state,
+                      char* buf,
+                      unsigned int buf_len,
+                      char error);
+
+/* Command functions */
+static int
+gdb_mem_read(struct gdb_state* state,
+             char* buf,
+             unsigned int buf_len,
+             address addr,
+             unsigned int len,
+             gdb_enc_func enc);
+static int
+gdb_mem_write(struct gdb_state* state,
+              const char* buf,
+              unsigned int buf_len,
+              address addr,
+              unsigned int len,
+              gdb_dec_func dec);
+static int
+gdb_continue(struct gdb_state* state);
+static int
+gdb_step(struct gdb_state* state);
+
+/*****************************************************************************
+ * String Processing Helper Functions
+ ****************************************************************************/
+
+/*
+ * Get null-terminated string length.
+ */
+static int
+gdb_strlen(const char* ch)
+{
+    int len;
+
+    len = 0;
+    while (*ch++) {
+        len += 1;
+    }
+
+    return len;
+}
+
+/*
+ * Get integer value for a string representation.
+ *
+ * If the string starts with + or -, it will be signed accordingly.
+ *
+ * If base == 0, the base will be determined:
+ *   base 16 if the string starts with 0x or 0X,
+ *   base 10 otherwise
+ *
+ * If endptr is specified, it will point to the last non-digit in the
+ * string. If there are no digits in the string, it will be set to NULL.
+ */
+static int
+gdb_strtol(const char* str, unsigned int len, int base, const char** endptr)
+{
+    unsigned int pos;
+    int sign, tmp, value, valid;
+
+    value = 0;
+    pos = 0;
+    sign = 1;
+    valid = 0;
+
+    if (endptr) {
+        *(endptr) = 0;
+    }
+
+    if (len < 1) {
+        return 0;
+    }
+
+    /* Detect negative numbers */
+    if (str[pos] == '-') {
+        sign = -1;
+        pos += 1;
+    } else if (str[pos] == '+') {
+        sign = 1;
+        pos += 1;
+    }
+
+    /* Detect '0x' hex prefix */
+    if ((pos + 2 < len) && (str[pos] == '0') &&
+        ((str[pos + 1] == 'x') || (str[pos + 1] == 'X'))) {
+        base = 16;
+        pos += 2;
+    }
+
+    if (base == 0) {
+        base = 10;
+    }
+
+    for (; (pos < len) && (str[pos] != '\x00'); pos++) {
+        tmp = gdb_get_val(str[pos], base);
+        if (tmp == GDB_EOF) {
+            break;
+        }
+
+        value = value * base + tmp;
+        valid = 1; /* At least one digit is valid */
+    }
+
+    if (!valid) {
+        return 0;
+    }
+
+    if (endptr) {
+        *endptr = str + pos;
+    }
+
+    value *= sign;
+
+    return value;
+}
+
+/*
+ * Get the corresponding ASCII hex digit character for a value.
+ */
+static char
+gdb_get_digit(int val)
+{
+    if ((val >= 0) && (val <= 0xf)) {
+        return digits[val];
+    } else {
+        return GDB_EOF;
+    }
+}
+
+/*
+ * Get the corresponding value for a ASCII digit character.
+ *
+ * Supports bases 2-16.
+ */
+static int
+gdb_get_val(char digit, int base)
+{
+    int value;
+
+    if ((digit >= '0') && (digit <= '9')) {
+        value = digit - '0';
+    } else if ((digit >= 'a') && (digit <= 'f')) {
+        value = digit - 'a' + 0xa;
+    } else if ((digit >= 'A') && (digit <= 'F')) {
+        value = digit - 'A' + 0xa;
+    } else {
+        return GDB_EOF;
+    }
+
+    return (value < base) ? value : GDB_EOF;
+}
+
+/*****************************************************************************
+ * Packet Functions
+ ****************************************************************************/
+
+/*
+ * Receive a packet acknowledgment
+ *
+ * Returns:
+ *    0   if an ACK (+) was received
+ *    1   if a NACK (-) was received
+ *    GDB_EOF otherwise
+ */
+static int
+gdb_recv_ack(struct gdb_state* state)
+{
+    int response;
+
+    /* Wait for packet ack */
+    switch (response = gdb_sys_getc(state)) {
+        case '+':
+            /* Packet acknowledged */
+            return 0;
+        case '-':
+            /* Packet negative acknowledged */
+            return 1;
+        default:
+            /* Bad response! */
+            GDB_PRINT("received bad packet response: 0x%2x\n", response);
+            return GDB_EOF;
+    }
+}
+
+/*
+ * Calculate 8-bit checksum of a buffer.
+ *
+ * Returns:
+ *    8-bit checksum.
+ */
+static int
+gdb_checksum(const char* buf, unsigned int len)
+{
+    unsigned char csum;
+
+    csum = 0;
+
+    while (len--) {
+        csum += *buf++;
+    }
+
+    return csum;
+}
+
+/*
+ * Transmits a packet of data.
+ * Packets are of the form: $<packet-data>#<checksum>
+ *
+ * Returns:
+ *    0   if the packet was transmitted and acknowledged
+ *    1   if the packet was transmitted but not acknowledged
+ *    GDB_EOF otherwise
+ */
+static int
+gdb_send_packet(struct gdb_state* state,
+                const char* pkt_data,
+                unsigned int pkt_len)
+{
+    char buf[3];
+    char csum;
+
+    /* Send packet start */
+    if (gdb_sys_putchar(state, '$') == GDB_EOF) {
+        return GDB_EOF;
+    }
+
+    /* Send packet data */
+    if (gdb_write(state, pkt_data, pkt_len) == GDB_EOF) {
+        return GDB_EOF;
+    }
+
+    /* Send the checksum */
+    buf[0] = '#';
+    csum = gdb_checksum(pkt_data, pkt_len);
+    if ((gdb_enc_hex(buf + 1, sizeof(buf) - 1, &csum, 1) == GDB_EOF) ||
+        (gdb_write(state, buf, sizeof(buf)) == GDB_EOF)) {
+        return GDB_EOF;
+    }
+
+    return gdb_recv_ack(state);
+}
+
+/*
+ * Receives a packet of data, assuming a 7-bit clean connection.
+ *
+ * Returns:
+ *    0   if the packet was received
+ *    GDB_EOF otherwise
+ */
+static int
+gdb_recv_packet(struct gdb_state* state,
+                char* pkt_buf,
+                unsigned int pkt_buf_len,
+                unsigned int* pkt_len)
+{
+    int data;
+    char expected_csum, actual_csum;
+    char buf[2];
+
+    /* Wait for packet start */
+    actual_csum = 0;
+
+    while (1) {
+        data = gdb_sys_getc(state);
+        if (data == GDB_EOF) {
+            return GDB_EOF;
+        } else if (data == '$') {
+            /* Detected start of packet. */
+            break;
+        }
+    }
+
+    /* Read until checksum */
+    *pkt_len = 0;
+    while (1) {
+        data = gdb_sys_getc(state);
+
+        if (data == GDB_EOF) {
+            /* Error receiving character */
+            return GDB_EOF;
+        } else if (data == '#') {
+            /* End of packet */
+            break;
+        } else {
+            /* Check for space */
+            if (*pkt_len >= pkt_buf_len) {
+                GDB_PRINT("packet buffer overflow\n");
+                return GDB_EOF;
+            }
+
+            /* Store character and update checksum */
+            pkt_buf[(*pkt_len)++] = (char)data;
+        }
+    }
+
+    /* Receive the checksum */
+    if ((gdb_read(state, buf, sizeof(buf), 2) == GDB_EOF) ||
+        (gdb_dec_hex(buf, 2, &expected_csum, 1) == GDB_EOF)) {
+        return GDB_EOF;
+    }
+
+    /* Verify checksum */
+    actual_csum = gdb_checksum(pkt_buf, *pkt_len);
+    if (actual_csum != expected_csum) {
+        /* Send packet nack */
+        GDB_PRINT("received packet with bad checksum\n");
+        gdb_sys_putchar(state, '-');
+        return GDB_EOF;
+    }
+
+    /* Send packet ack */
+    gdb_sys_putchar(state, '+');
+    return 0;
+}
+
+/*****************************************************************************
+ * Data Encoding/Decoding
+ ****************************************************************************/
+
+/*
+ * Encode data to its hex-value representation in a buffer.
+ *
+ * Returns:
+ *    0+  number of bytes written to buf
+ *    GDB_EOF if the buffer is too small
+ */
+static int
+gdb_enc_hex(char* buf,
+            unsigned int buf_len,
+            const char* data,
+            unsigned int data_len)
+{
+    unsigned int pos;
+
+    if (buf_len < data_len * 2) {
+        /* Buffer too small */
+        return GDB_EOF;
+    }
+
+    for (pos = 0; pos < data_len; pos++) {
+        *buf++ = gdb_get_digit((data[pos] >> 4) & 0xf);
+        *buf++ = gdb_get_digit((data[pos]) & 0xf);
+    }
+
+    return data_len * 2;
+}
+
+/*
+ * Decode data from its hex-value representation to a buffer.
+ *
+ * Returns:
+ *    0   if successful
+ *    GDB_EOF if the buffer is too small
+ */
+static int
+gdb_dec_hex(const char* buf,
+            unsigned int buf_len,
+            char* data,
+            unsigned int data_len)
+{
+    unsigned int pos;
+    int tmp;
+
+    if (buf_len != data_len * 2) {
+        /* Buffer too small */
+        return GDB_EOF;
+    }
+
+    for (pos = 0; pos < data_len; pos++) {
+        /* Decode high nibble */
+        tmp = gdb_get_val(*buf++, 16);
+        if (tmp == GDB_EOF) {
+            /* Buffer contained junk. */
+            GDB_ASSERT(0);
+            return GDB_EOF;
+        }
+
+        data[pos] = tmp << 4;
+
+        /* Decode low nibble */
+        tmp = gdb_get_val(*buf++, 16);
+        if (tmp == GDB_EOF) {
+            /* Buffer contained junk. */
+            GDB_ASSERT(0);
+            return GDB_EOF;
+        }
+        data[pos] |= tmp;
+    }
+
+    return 0;
+}
+
+/*
+ * Encode data to its binary representation in a buffer.
+ *
+ * Returns:
+ *    0+  number of bytes written to buf
+ *    GDB_EOF if the buffer is too small
+ */
+static int
+gdb_enc_bin(char* buf,
+            unsigned int buf_len,
+            const char* data,
+            unsigned int data_len)
+{
+    unsigned int buf_pos, data_pos;
+
+    for (buf_pos = 0, data_pos = 0; data_pos < data_len; data_pos++) {
+        if (data[data_pos] == '$' || data[data_pos] == '#' ||
+            data[data_pos] == '}' || data[data_pos] == '*') {
+            if (buf_pos + 1 >= buf_len) {
+                GDB_ASSERT(0);
+                return GDB_EOF;
+            }
+            buf[buf_pos++] = '}';
+            buf[buf_pos++] = data[data_pos] ^ 0x20;
+        } else {
+            if (buf_pos >= buf_len) {
+                GDB_ASSERT(0);
+                return GDB_EOF;
+            }
+            buf[buf_pos++] = data[data_pos];
+        }
+    }
+
+    return buf_pos;
+}
+
+/*
+ * Decode data from its bin-value representation to a buffer.
+ *
+ * Returns:
+ *    0+  if successful, number of bytes decoded
+ *    GDB_EOF if the buffer is too small
+ */
+static int
+gdb_dec_bin(const char* buf,
+            unsigned int buf_len,
+            char* data,
+            unsigned int data_len)
+{
+    unsigned int buf_pos, data_pos;
+
+    for (buf_pos = 0, data_pos = 0; buf_pos < buf_len; buf_pos++) {
+        if (data_pos >= data_len) {
+            /* Output buffer overflow */
+            GDB_ASSERT(0);
+            return GDB_EOF;
+        }
+        if (buf[buf_pos] == '}') {
+            /* The next byte is escaped! */
+            if (buf_pos + 1 >= buf_len) {
+                /* There's an escape character, but no escaped character
+                 * following the escape character. */
+                GDB_ASSERT(0);
+                return GDB_EOF;
+            }
+            buf_pos += 1;
+            data[data_pos++] = buf[buf_pos] ^ 0x20;
+        } else {
+            data[data_pos++] = buf[buf_pos];
+        }
+    }
+
+    return data_pos;
+}
+
+/*****************************************************************************
+ * Command Functions
+ ****************************************************************************/
+
+/*
+ * Read from memory and encode into buf.
+ *
+ * Returns:
+ *    0+  number of bytes written to buf
+ *    GDB_EOF if the buffer is too small
+ */
+static int
+gdb_mem_read(struct gdb_state* state,
+             char* buf,
+             unsigned int buf_len,
+             address addr,
+             unsigned int len,
+             gdb_enc_func enc)
+{
+    char data[64];
+    unsigned int pos;
+
+    if (len > sizeof(data)) {
+        return GDB_EOF;
+    }
+
+    /* Read from system memory */
+    for (pos = 0; pos < len; pos++) {
+        if (gdb_sys_mem_readb(state, addr + pos, &data[pos])) {
+            /* Failed to read */
+            return GDB_EOF;
+        }
+    }
+
+    /* Encode data */
+    return enc(buf, buf_len, data, len);
+}
+
+/*
+ * Write to memory from encoded buf.
+ */
+static int
+gdb_mem_write(struct gdb_state* state,
+              const char* buf,
+              unsigned int buf_len,
+              address addr,
+              unsigned int len,
+              gdb_dec_func dec)
+{
+    char data[64];
+    unsigned int pos;
+
+    if (len > sizeof(data)) {
+        return GDB_EOF;
+    }
+
+    /* Decode data */
+    if (dec(buf, buf_len, data, len) == GDB_EOF) {
+        return GDB_EOF;
+    }
+
+    /* Write to system memory */
+    for (pos = 0; pos < len; pos++) {
+        if (gdb_sys_mem_writeb(state, addr + pos, data[pos])) {
+            /* Failed to write */
+            return GDB_EOF;
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Continue program execution at PC.
+ */
+int
+gdb_continue(struct gdb_state* state)
+{
+    gdb_sys_continue(state);
+    return 0;
+}
+
+/*
+ * Step one instruction.
+ */
+int
+gdb_step(struct gdb_state* state)
+{
+    gdb_sys_step(state);
+    return 0;
+}
+
+/*****************************************************************************
+ * Packet Creation Helpers
+ ****************************************************************************/
+
+/*
+ * Send OK packet
+ */
+static int
+gdb_send_ok_packet(struct gdb_state* state, char* buf, unsigned int buf_len)
+{
+    return gdb_send_packet(state, "OK", 2);
+}
+
+/*
+ * Send a message to the debugging console (via O XX... packet)
+ */
+static int
+gdb_send_conmsg_packet(struct gdb_state* state,
+                       char* buf,
+                       unsigned int buf_len,
+                       const char* msg)
+{
+    unsigned int size;
+    int status;
+
+    if (buf_len < 2) {
+        /* Buffer too small */
+        return GDB_EOF;
+    }
+
+    buf[0] = 'O';
+    status = gdb_enc_hex(&buf[1], buf_len - 1, msg, gdb_strlen(msg));
+    if (status == GDB_EOF) {
+        return GDB_EOF;
+    }
+    size = 1 + status;
+    return gdb_send_packet(state, buf, size);
+}
+
+/*
+ * Send a signal packet (S AA).
+ */
+static int
+gdb_send_signal_packet(struct gdb_state* state,
+                       char* buf,
+                       unsigned int buf_len,
+                       char signal)
+{
+    unsigned int size;
+    int status;
+
+    if (buf_len < 4) {
+        /* Buffer too small */
+        return GDB_EOF;
+    }
+
+    buf[0] = 'S';
+    status = gdb_enc_hex(&buf[1], buf_len - 1, &signal, 1);
+    if (status == GDB_EOF) {
+        return GDB_EOF;
+    }
+    size = 1 + status;
+    return gdb_send_packet(state, buf, size);
+}
+
+/*
+ * Send a error packet (E AA).
+ */
+static int
+gdb_send_error_packet(struct gdb_state* state,
+                      char* buf,
+                      unsigned int buf_len,
+                      char error)
+{
+    unsigned int size;
+    int status;
+
+    if (buf_len < 4) {
+        /* Buffer too small */
+        return GDB_EOF;
+    }
+
+    buf[0] = 'E';
+    status = gdb_enc_hex(&buf[1], buf_len - 1, &error, 1);
+    if (status == GDB_EOF) {
+        return GDB_EOF;
+    }
+    size = 1 + status;
+    return gdb_send_packet(state, buf, size);
+}
+
+/*****************************************************************************
+ * Communication Functions
+ ****************************************************************************/
+
+/*
+ * Write a sequence of bytes.
+ *
+ * Returns:
+ *    0   if successful
+ *    GDB_EOF if failed to write all bytes
+ */
+static int
+gdb_write(struct gdb_state* state, const char* buf, unsigned int len)
+{
+    while (len--) {
+        if (gdb_sys_putchar(state, *buf++) == GDB_EOF) {
+            return GDB_EOF;
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Read a sequence of bytes.
+ *
+ * Returns:
+ *    0   if successfully read len bytes
+ *    GDB_EOF if failed to read all bytes
+ */
+static int
+gdb_read(struct gdb_state* state,
+         char* buf,
+         unsigned int buf_len,
+         unsigned int len)
+{
+    char c;
+
+    if (buf_len < len) {
+        /* Buffer too small */
+        return GDB_EOF;
+    }
+
+    while (len--) {
+        if ((c = gdb_sys_getc(state)) == GDB_EOF) {
+            return GDB_EOF;
+        }
+        *buf++ = c;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * Main Loop
+ ****************************************************************************/
+
+/*
+ * Main debug loop. Handles commands.
+ */
+int
+gdb_main(struct gdb_state* state)
+{
+    address addr;
+    char pkt_buf[256];
+    int status;
+    unsigned int length;
+    unsigned int pkt_len;
+    const char* ptr_next;
+
+    gdb_send_signal_packet(state, pkt_buf, sizeof(pkt_buf), state->signum);
+
+    while (1) {
+        /* Receive the next packet */
+        status = gdb_recv_packet(state, pkt_buf, sizeof(pkt_buf), &pkt_len);
+        if (status == GDB_EOF) {
+            break;
+        }
+
+        if (pkt_len == 0) {
+            /* Received empty packet.. */
+            continue;
+        }
+
+        ptr_next = pkt_buf;
+
+        /*
+         * Handle one letter commands
+         */
+        switch (pkt_buf[0]) {
+
+/* Calculate remaining space in packet from ptr_next position. */
+#define token_remaining_buf (pkt_len - (ptr_next - pkt_buf))
+
+/* Expecting a seperator. If not present, go to error */
+#define token_expect_seperator(c)                                              \
+    {                                                                          \
+        if (!ptr_next || *ptr_next != c) {                                     \
+            goto error;                                                        \
+        } else {                                                               \
+            ptr_next += 1;                                                     \
+        }                                                                      \
+    }
+
+/* Expecting an integer argument. If not present, go to error */
+#define token_expect_integer_arg(arg)                                          \
+    {                                                                          \
+        arg = gdb_strtol(ptr_next, token_remaining_buf, 16, &ptr_next);        \
+        if (!ptr_next) {                                                       \
+            goto error;                                                        \
+        }                                                                      \
+    }
+
+            /*
+             * Read Registers
+             * Command Format: g
+             */
+            case 'g':
+                /* Encode registers */
+                status = gdb_enc_hex(pkt_buf,
+                                     sizeof(pkt_buf),
+                                     (char*)&(state->registers),
+                                     sizeof(state->registers));
+                if (status == GDB_EOF) {
+                    goto error;
+                }
+                pkt_len = status;
+                gdb_send_packet(state, pkt_buf, pkt_len);
+                break;
+
+            /*
+             * Write Registers
+             * Command Format: G XX...
+             */
+            case 'G':
+                status = gdb_dec_hex(pkt_buf + 1,
+                                     pkt_len - 1,
+                                     (char*)&(state->registers),
+                                     sizeof(state->registers));
+                if (status == GDB_EOF) {
+                    goto error;
+                }
+                gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf));
+                break;
+
+            /*
+             * Read a Register
+             * Command Format: p n
+             */
+            case 'p':
+                ptr_next += 1;
+                token_expect_integer_arg(addr);
+
+                if (addr >= GDB_CPU_NUM_REGISTERS) {
+                    goto error;
+                }
+
+                /* Read Register */
+                status = gdb_enc_hex(pkt_buf,
+                                     sizeof(pkt_buf),
+                                     (char*)&(state->registers[addr]),
+                                     sizeof(state->registers[addr]));
+                if (status == GDB_EOF) {
+                    goto error;
+                }
+                gdb_send_packet(state, pkt_buf, status);
+                break;
+
+            /*
+             * Write a Register
+             * Command Format: P n...=r...
+             */
+            case 'P':
+                ptr_next += 1;
+                token_expect_integer_arg(addr);
+                token_expect_seperator('=');
+
+                if (addr < GDB_CPU_NUM_REGISTERS) {
+                    status = gdb_dec_hex(ptr_next,
+                                         token_remaining_buf,
+                                         (char*)&(state->registers[addr]),
+                                         sizeof(state->registers[addr]));
+                    if (status == GDB_EOF) {
+                        goto error;
+                    }
+                }
+                gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf));
+                break;
+
+            /*
+             * Read Memory
+             * Command Format: m addr,length
+             */
+            case 'm':
+                ptr_next += 1;
+                token_expect_integer_arg(addr);
+                token_expect_seperator(',');
+                token_expect_integer_arg(length);
+
+                /* Read Memory */
+                status = gdb_mem_read(
+                  state, pkt_buf, sizeof(pkt_buf), addr, length, gdb_enc_hex);
+                if (status == GDB_EOF) {
+                    goto error;
+                }
+                gdb_send_packet(state, pkt_buf, status);
+                break;
+
+            /*
+             * Write Memory
+             * Command Format: M addr,length:XX..
+             */
+            case 'M':
+                ptr_next += 1;
+                token_expect_integer_arg(addr);
+                token_expect_seperator(',');
+                token_expect_integer_arg(length);
+                token_expect_seperator(':');
+
+                /* Write Memory */
+                status = gdb_mem_write(state,
+                                       ptr_next,
+                                       token_remaining_buf,
+                                       addr,
+                                       length,
+                                       gdb_dec_hex);
+                if (status == GDB_EOF) {
+                    goto error;
+                }
+                gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf));
+                break;
+
+            /*
+             * Write Memory (Binary)
+             * Command Format: X addr,length:XX..
+             */
+            case 'X':
+                ptr_next += 1;
+                token_expect_integer_arg(addr);
+                token_expect_seperator(',');
+                token_expect_integer_arg(length);
+                token_expect_seperator(':');
+
+                /* Write Memory */
+                status = gdb_mem_write(state,
+                                       ptr_next,
+                                       token_remaining_buf,
+                                       addr,
+                                       length,
+                                       gdb_dec_bin);
+                if (status == GDB_EOF) {
+                    goto error;
+                }
+                gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf));
+                break;
+
+            /*
+             * Continue, Kill (also treated as continue!)
+             * Command Format: c [addr]
+             */
+            case 'c':
+            case 'C':
+            case 'k':
+                gdb_continue(state);
+                return 0;
+
+            /*
+             * Single-step
+             * Command Format: s [addr]
+             */
+            case 's':
+            case 'S':
+                gdb_step(state);
+                return 0;
+
+            case '?':
+                gdb_send_signal_packet(
+                  state, pkt_buf, sizeof(pkt_buf), state->signum);
+                break;
+
+            /*
+             * Unsupported Command
+             */
+            default:
+                gdb_send_packet(state, 0, 0);
+        }
+
+        continue;
+
+    error:
+        gdb_send_error_packet(state, pkt_buf, sizeof(pkt_buf), 0x00);
+
+#undef token_remaining_buf
+#undef token_expect_seperator
+#undef token_expect_integer_arg
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * Types
+ ****************************************************************************/
+
+struct gdb_idtr
+{
+    uint16_t len;
+    uint32_t offset;
+} __attribute__((packed));
+
+struct gdb_idt_gate
+{
+    uint16_t offset_low;
+    uint16_t segment;
+    uint16_t flags;
+    uint16_t offset_high;
+} __attribute__((packed));
+
+/*****************************************************************************
+ * Prototypes
+ ****************************************************************************/
+#define gdb_x86_io_write_8(port, val) io_outb(port, val)
+#define gdb_x86_io_read_8(port) io_inb(port)
+
+#define gdb_x86_serial_getc() serial_rx_byte(COM_PORT)
+#define gdb_x86_serial_putchar(ch) serial_tx_byte(COM_PORT, ch)
+
+#ifdef __STRICT_ANSI__
+#define asm __asm__
+#endif
+
+static struct gdb_state gdb_state;
+static volatile int start_debugging = 0;
+
+/*
+ * Debug interrupt handler.
+ */
+void
+gdbstub_loop(isr_param* param)
+{
+    /* Translate vector to signal */
+    switch (param->vector) {
+        case 1:
+            gdb_state.signum = 5;
+            break;
+        case 3:
+            gdb_state.signum = 5;
+            break;
+        default:
+            gdb_state.signum = 7;
+    }
+
+    /* Load Registers */
+    gdb_state.registers[GDB_CPU_I386_REG_EAX] = param->registers.eax;
+    gdb_state.registers[GDB_CPU_I386_REG_ECX] = param->registers.ecx;
+    gdb_state.registers[GDB_CPU_I386_REG_EDX] = param->registers.edx;
+    gdb_state.registers[GDB_CPU_I386_REG_EBX] = param->registers.ebx;
+    gdb_state.registers[GDB_CPU_I386_REG_ESP] = param->registers.esp;
+    gdb_state.registers[GDB_CPU_I386_REG_EBP] = param->registers.ebp;
+    gdb_state.registers[GDB_CPU_I386_REG_ESI] = param->registers.esi;
+    gdb_state.registers[GDB_CPU_I386_REG_EDI] = param->registers.edi;
+    gdb_state.registers[GDB_CPU_I386_REG_PC] = param->eip;
+    gdb_state.registers[GDB_CPU_I386_REG_CS] = param->cs;
+    gdb_state.registers[GDB_CPU_I386_REG_PS] = param->eflags;
+    gdb_state.registers[GDB_CPU_I386_REG_SS] = param->ss;
+    gdb_state.registers[GDB_CPU_I386_REG_DS] = param->registers.ds;
+    gdb_state.registers[GDB_CPU_I386_REG_ES] = param->registers.es;
+    gdb_state.registers[GDB_CPU_I386_REG_FS] = param->registers.fs;
+    gdb_state.registers[GDB_CPU_I386_REG_GS] = param->registers.gs;
+
+    gdb_main(&gdb_state);
+
+    /* Restore Registers */
+    param->registers.eax = gdb_state.registers[GDB_CPU_I386_REG_EAX];
+    param->registers.ecx = gdb_state.registers[GDB_CPU_I386_REG_ECX];
+    param->registers.edx = gdb_state.registers[GDB_CPU_I386_REG_EDX];
+    param->registers.ebx = gdb_state.registers[GDB_CPU_I386_REG_EBX];
+    param->registers.esp = gdb_state.registers[GDB_CPU_I386_REG_ESP];
+    param->registers.ebp = gdb_state.registers[GDB_CPU_I386_REG_EBP];
+    param->registers.esi = gdb_state.registers[GDB_CPU_I386_REG_ESI];
+    param->registers.edi = gdb_state.registers[GDB_CPU_I386_REG_EDI];
+    param->eip = gdb_state.registers[GDB_CPU_I386_REG_PC];
+    param->cs = gdb_state.registers[GDB_CPU_I386_REG_CS];
+    param->eflags = gdb_state.registers[GDB_CPU_I386_REG_PS];
+    param->ss = gdb_state.registers[GDB_CPU_I386_REG_SS];
+    param->registers.ds = gdb_state.registers[GDB_CPU_I386_REG_DS];
+    param->registers.es = gdb_state.registers[GDB_CPU_I386_REG_ES];
+    param->registers.fs = gdb_state.registers[GDB_CPU_I386_REG_FS];
+    param->registers.gs = gdb_state.registers[GDB_CPU_I386_REG_GS];
+}
+
+/*****************************************************************************
+ * Debugging System Functions
+ ****************************************************************************/
+
+/*
+ * Write one character to the debugging stream.
+ */
+int
+gdb_sys_putchar(struct gdb_state* state, int ch)
+{
+    gdb_x86_serial_putchar(ch);
+    return ch;
+}
+
+/*
+ * Read one character from the debugging stream.
+ */
+int
+gdb_sys_getc(struct gdb_state* state)
+{
+    return gdb_x86_serial_getc() & 0xff;
+}
+
+/*
+ * Read one byte from memory.
+ */
+int
+gdb_sys_mem_readb(struct gdb_state* state, address addr, char* val)
+{
+    *val = *(volatile char*)addr;
+    return 0;
+}
+
+/*
+ * Write one byte to memory.
+ */
+int
+gdb_sys_mem_writeb(struct gdb_state* state, address addr, char val)
+{
+    *(volatile char*)addr = val;
+    return 0;
+}
+
+/*
+ * Continue program execution.
+ */
+int
+gdb_sys_continue(struct gdb_state* state)
+{
+    gdb_state.registers[GDB_CPU_I386_REG_PS] &= ~(1 << 8);
+    return 0;
+}
+
+/*
+ * Single step the next instruction.
+ */
+int
+gdb_sys_step(struct gdb_state* state)
+{
+    gdb_state.registers[GDB_CPU_I386_REG_PS] |= 1 << 8;
+    return 0;
+}
\ No newline at end of file
diff --git a/lunaix-os/debug/sdbg.c b/lunaix-os/debug/sdbg.c
new file mode 100644 (file)
index 0000000..537e9cd
--- /dev/null
@@ -0,0 +1,83 @@
+#include <hal/acpi/acpi.h>
+#include <hal/ioapic.h>
+#include <klibc/stdio.h>
+#include <lunaix/lxconsole.h>
+#include <lunaix/peripheral/serial.h>
+#include <lunaix/syslog.h>
+#include <sdbg/gdbstub.h>
+#include <sdbg/lsdbg.h>
+#include <sdbg/protocol.h>
+
+// #define USE_LSDBG_BACKEND
+
+volatile int debug_mode = 0;
+
+void
+sdbg_loop(const isr_param* param)
+{
+    // This is importnat, because we will handle any subsequent RX/TX in a
+    // synchronized way. And we don't want these irq queue up at our APIC and
+    // confuse the CPU after ACK with APIC.
+    serial_disable_irq(SERIAL_COM1);
+    if (param->vector == 1 || param->vector == 3) {
+        goto cont;
+    }
+
+    if (!debug_mode) {
+        // Oh... C.M.C. is about to help the debugging!
+        if (serial_rx_byte(SERIAL_COM1) != '@') {
+            goto done;
+        }
+        if (serial_rx_byte(SERIAL_COM1) != 'c') {
+            goto done;
+        }
+        if (serial_rx_byte(SERIAL_COM1) != 'm') {
+            goto done;
+        }
+        if (serial_rx_byte(SERIAL_COM1) != 'c') {
+            goto done;
+        }
+        debug_mode = 1;
+    } else {
+        if (serial_rx_byte(SERIAL_COM1) != '@') {
+            goto cont;
+        }
+        if (serial_rx_byte(SERIAL_COM1) != 'y') {
+            goto cont;
+        }
+        if (serial_rx_byte(SERIAL_COM1) != 'a') {
+            goto cont;
+        }
+        if (serial_rx_byte(SERIAL_COM1) != 'y') {
+            goto cont;
+        }
+        debug_mode = 0;
+        goto done;
+    }
+
+cont:
+    kprint_dbg(" DEBUG");
+#ifdef USE_LSDBG_BACKEND
+    lunaix_sdbg_loop(param);
+#else
+    gdbstub_loop(param);
+#endif
+    console_flush();
+
+done:
+    serial_enable_irq(SERIAL_COM1);
+}
+
+extern uint8_t
+ioapic_get_irq(acpi_context* acpi_ctx, uint8_t old_irq);
+
+void
+sdbg_init()
+{
+    intr_subscribe(UART_COM1, sdbg_loop);
+    intr_subscribe(INSTR_DEBUG, sdbg_loop); // #DB
+
+    acpi_context* acpi_ctx = acpi_get_context();
+    uint8_t irq = ioapic_get_irq(acpi_ctx, COM1_IRQ);
+    ioapic_redirect(irq, UART_COM1, 0, IOAPIC_DELMOD_FIXED);
+}
\ No newline at end of file
index 9873db6eb9586b5c205b6f02b34e72347548f58b..acf28b4ac02cdddcf831bcce876c63a0bdfdf871 100644 (file)
@@ -4,7 +4,7 @@
 // clang-format off
 
 #define FAULT_DIVISION_ERROR            0
-#define FAULT_TRAP_DEBUG_EXCEPTION      1
+#define INSTR_DEBUG                     1
 #define INT_NMI                         2
 #define TRAP_BREAKPOINT                 3
 #define TRAP_OVERFLOW                   4
@@ -36,6 +36,7 @@
 // Keyboard
 #define PC_KBD_IV                       201
 #define AHCI_HBA_IV                     202
+#define UART_COM1                       203
 
 #define RTC_TIMER_IV                    210
 
index a6974c28bb9b6af7f4bf5f6dd398c96881ac6cfc..76e515269d691c9b3c7403863a1365b5952da7d5 100644 (file)
@@ -3,24 +3,24 @@
 
 #include <stdint.h>
 
-#define IOAPIC_IOREGSEL             0x00
-#define IOAPIC_IOWIN                0x10
-#define IOAPIC_IOREDTBL_BASE        0x10
+#define IOAPIC_IOREGSEL 0x00
+#define IOAPIC_IOWIN 0x10
+#define IOAPIC_IOREDTBL_BASE 0x10
 
-#define IOAPIC_REG_ID               0x00
-#define IOAPIC_REG_VER              0x01
-#define IOAPIC_REG_ARB              0x02
+#define IOAPIC_REG_ID 0x00
+#define IOAPIC_REG_VER 0x01
+#define IOAPIC_REG_ARB 0x02
 
-#define IOAPIC_DELMOD_FIXED         0b000
-#define IOAPIC_DELMOD_LPRIO         0b001
-#define IOAPIC_DELMOD_NMI           0b100
+#define IOAPIC_DELMOD_FIXED 0b000
+#define IOAPIC_DELMOD_LPRIO 0b001
+#define IOAPIC_DELMOD_NMI 0b100
 
-#define IOAPIC_MASKED               (1 << 16)
-#define IOAPIC_TRIG_LEVEL           (1 << 15)
-#define IOAPIC_INTPOL_L             (1 << 13)
-#define IOAPIC_DESTMOD_LOGIC        (1 << 11)
+#define IOAPIC_MASKED (1 << 16)
+#define IOAPIC_TRIG_LEVEL (1 << 15)
+#define IOAPIC_INTPOL_L (1 << 13)
+#define IOAPIC_DESTMOD_LOGIC (1 << 11)
 
-#define IOAPIC_BASE_VADDR   0x2000
+#define IOAPIC_BASE_VADDR 0x2000
 
 void
 ioapic_init();
diff --git a/lunaix-os/includes/lunaix/peripheral/serial.h b/lunaix-os/includes/lunaix/peripheral/serial.h
new file mode 100644 (file)
index 0000000..a8cbed2
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef __LUNAIX_SERIAL_H
+#define __LUNAIX_SERIAL_H
+
+#include <lunaix/types.h>
+
+#define SERIAL_COM1 0x3f8
+#define SERIAL_COM2 0x2f8
+
+#define COM1_IRQ 4
+#define COM2_IRQ 3
+
+#define BAUD_115200 1
+#define BAUD_57600 2
+#define BAUD_38400 3
+#define BAUD_9600 12
+
+#define COM_RRXTX(port) (port)
+#define COM_RIE(port) (port + 1)
+#define COM_RCFIFO(port) (port + 2)
+#define COM_RCLINE(port) (port + 3)
+#define COM_RCMODEM(port) (port + 4)
+#define COM_RSLINE(port) (port + 5)
+#define COM_RSMODEM(port) (port + 6)
+
+void
+serial_init();
+
+char
+serial_rx_byte(uintptr_t port);
+
+void
+serial_rx_buffer(uintptr_t port, char* data, size_t len);
+
+void
+serial_tx_byte(uintptr_t port, char data);
+
+void
+serial_tx_buffer(uintptr_t port, char* data, size_t len);
+
+void
+serial_clear_fifo(uintptr_t port);
+
+void
+serial_disable_irq(uintptr_t port);
+
+void
+serial_enable_irq(uintptr_t port);
+
+#endif /* __LUNAIX_SERIAL_H */
index f5fc8dba760e2f547e926b8c0c48a6d3af870947..464a133f889b89f90119f02e24f0e085eb50b545 100644 (file)
@@ -31,4 +31,7 @@ kprint_hex(const void* buffer, unsigned int size);
 void
 kprint_panic(const char* fmt, ...);
 
+void
+kprint_dbg(const char* fmt, ...);
+
 #endif /* __LUNAIX_SYSLOG_H */
diff --git a/lunaix-os/includes/sdbg/gdbstub.h b/lunaix-os/includes/sdbg/gdbstub.h
new file mode 100644 (file)
index 0000000..58e48ab
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __LUNAIX_GDBSTUB_H
+#define __LUNAIX_GDBSTUB_H
+
+#include <arch/x86/interrupts.h>
+
+void
+gdbstub_loop(isr_param* param);
+
+#endif /* __LUNAIX_GDBSTUB_H */
diff --git a/lunaix-os/includes/sdbg/lsdbg.h b/lunaix-os/includes/sdbg/lsdbg.h
new file mode 100644 (file)
index 0000000..1667f31
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef __LUNAIX_LSDBG_H
+#define __LUNAIX_LSDBG_H
+
+#include <arch/x86/interrupts.h>
+
+#define SDBG_CLNT_HI 0x10
+#define SDBG_CLNT_QUIT 0xff
+#define SDBG_CLNT_RREG 0x11
+#define SDBG_CLNT_STEP 0x12
+#define SDBG_CLNT_CONT 0x13
+#define SDBG_CLNT_BRKP 0x14
+
+#define SDBG_SVER_MSG 0xa1
+#define SDBG_SVER_WHATNEXT 0xa2
+
+#define SDBG_STATE_START 0
+#define SDBG_STATE_INSESSION 1
+#define SDBG_STATE_WAIT_BRK 2
+
+void
+lunaix_sdbg_loop(isr_param* param);
+
+#endif /* __LUNAIX_LSDBG_H */
diff --git a/lunaix-os/includes/sdbg/protocol.h b/lunaix-os/includes/sdbg/protocol.h
new file mode 100644 (file)
index 0000000..940194e
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __LUNAIX_SDBG_PROTOCOL_H
+#define __LUNAIX_SDBG_PROTOCOL_H
+
+/**
+ * @brief Initialize Serial Debugger
+ *
+ */
+void
+sdbg_init();
+
+#endif /* __LUNAIX_SDBG_H */
index fd005c09b9295f23b33ea737df3dc4612af118fc..0da00ff6bd873d421e3c2371e623bd071f92c525 100644 (file)
@@ -86,6 +86,24 @@ kprint_panic(const char* fmt, ...)
     va_end(args);
 }
 
+void
+kprint_dbg(const char* fmt, ...)
+{
+    char buf[MAX_KPRINTF_BUF_SIZE];
+    va_list args;
+    va_start(args, fmt);
+
+    tty_set_theme(VGA_COLOR_WHITE, VGA_COLOR_MAGENTA);
+    tty_clear_line(24);
+
+    __ksprintf_internal(buf, fmt, MAX_KPRINTF_BUF_SIZE, args);
+    tty_put_str_at(buf, 0, 24);
+
+    va_end(args);
+
+    tty_set_theme(VGA_COLOR_WHITE, VGA_COLOR_BLACK);
+}
+
 void
 kprint_hex(const void* buffer, unsigned int size)
 {
diff --git a/lunaix-os/kernel/peripheral/serial.c b/lunaix-os/kernel/peripheral/serial.c
new file mode 100644 (file)
index 0000000..c7145b8
--- /dev/null
@@ -0,0 +1,102 @@
+#include <hal/cpu.h>
+#include <hal/io.h>
+#include <lunaix/peripheral/serial.h>
+#include <lunaix/syslog.h>
+
+LOG_MODULE("COM")
+
+void
+serial_init_port(uintptr_t port)
+{
+    // disable interrupt, use irq instead
+    io_outb(COM_RIE(port), 0);
+
+    // baud rate config (DLAB = 1)
+    io_outb(COM_RCLINE(port), 0x80);
+    io_outb(COM_RRXTX(port), BAUD_9600);
+    io_outb(COM_RIE(port), 0);
+
+    // transmission size = 7bits, no parity, 1 stop bits
+    io_outb(COM_RCLINE(port), 0x2);
+
+    // rx trigger level = 14, clear rx/tx buffer, enable buffer
+    io_outb(COM_RCFIFO(port), 0xcf);
+
+    io_outb(COM_RCMODEM(port), 0x1e);
+
+    io_outb(COM_RRXTX(port), 0xaa);
+
+    if (io_inb(COM_RRXTX(port)) != 0xaa) {
+        kprintf(KWARN "Port (%p) is faulty, use other instead.\n", port);
+        return;
+    }
+
+    io_outb(COM_RCMODEM(port), 0xf);
+    io_outb(COM_RIE(port), 0x1);
+    kprintf("Port (%p) ok\n", port);
+}
+
+void
+serial_init()
+{
+    cpu_disable_interrupt();
+    serial_init_port(SERIAL_COM1);
+    serial_init_port(SERIAL_COM2);
+    cpu_enable_interrupt();
+}
+
+char
+serial_rx_byte(uintptr_t port)
+{
+    while (!(io_inb(COM_RSLINE(port)) & 0x01))
+        ;
+
+    return io_inb(COM_RRXTX(port));
+}
+
+void
+serial_rx_buffer(uintptr_t port, char* data, size_t len)
+{
+    for (size_t i = 0; i < len; i++) {
+        data[i] = serial_rx_byte(port);
+    }
+}
+
+void
+serial_tx_byte(uintptr_t port, char data)
+{
+    while (!(io_inb(COM_RSLINE(port)) & 0x20))
+        ;
+
+    io_outb(COM_RRXTX(port), data);
+}
+
+void
+serial_tx_buffer(uintptr_t port, char* data, size_t len)
+{
+    for (size_t i = 0; i < len; i++) {
+        serial_tx_byte(port, data[i]);
+    }
+}
+
+void
+serial_clear_fifo(uintptr_t port)
+{
+    io_outb(COM_RIE(port), 0x0);
+    io_outb(COM_RCFIFO(port), 0x00);
+
+    io_outb(COM_RCFIFO(port), 0xcf);
+    io_outb(COM_RIE(port), 0x1);
+}
+
+void
+serial_disable_irq(uintptr_t port)
+{
+    io_outb(COM_RIE(port), 0x0);
+}
+
+void
+serial_enable_irq(uintptr_t port)
+{
+    io_outb(COM_RIE(port), 0x1);
+}
\ No newline at end of file
index e242196f0b969255bbf848b3b723baf2d3ceb599..2a410322345706414a4d2d4719c548e63b8e7c4a 100644 (file)
 #include <lunaix/mm/valloc.h>
 #include <lunaix/mm/vmm.h>
 #include <lunaix/peripheral/ps2kbd.h>
+#include <lunaix/peripheral/serial.h>
 #include <lunaix/proc.h>
 #include <lunaix/spike.h>
 #include <lunaix/syscall.h>
 #include <lunaix/syslog.h>
 #include <lunaix/types.h>
+#include <sdbg/protocol.h>
 
 #include <hal/acpi/acpi.h>
 #include <hal/ahci/ahci.h>
@@ -161,6 +163,8 @@ init_platform()
     acpi_init(_k_init_mb_info);
     apic_init();
     ioapic_init();
+    serial_init();
+    sdbg_init();
     timer_init(SYS_TIMER_FREQUENCY_HZ);
     clock_init();
     ps2_kbd_init();
index 20de158c0bf57ad9cf353d21a00a71796870ba0d..27ce9b7bdd9d86fe35d373881b10db56e28cbb9a 100644 (file)
@@ -70,3 +70,7 @@ debug-qemu-vscode: all-debug
 
 debug-bochs: all-debug
        @bochs -q -f bochs.cfg
+
+debug-metal: 
+       @printf "@cmc" > $(PORT)
+       @gdb -s $(BUILD_DIR)/kernel.dbg -ex "target remote $(PORT)"
\ No newline at end of file