-/* i386-nlmstub.c -- NLM debugging stub for the i386.
+/* gdbserve.c -- NLM debugging stub for Novell NetWare.
This is originally based on an m68k software stub written by Glenn
Engel at HP, but has changed quite a bit. It was modified for the
NetWare by Ian Lance Taylor, Cygnus Support.
This code is intended to produce an NLM (a NetWare Loadable Module)
- to run under NetWare on an i386 platform. To create the NLM,
- compile this code into an object file using the NLM SDK on any i386
- host, and use the nlmconv program (available in the GNU binutils)
- to transform the resulting object file into an NLM. */
+ to run under Novell NetWare. To create the NLM, compile this code
+ into an object file using the NLM SDK on any i386 host, and use the
+ nlmconv program (available in the GNU binutils) to transform the
+ resulting object file into an NLM. */
/****************************************************************************
*
****************************************************************************/
-#include <nwdfs.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
-/*#include <ctype.h>*/
+#include <ctype.h>
+#include <errno.h>
#include <time.h>
-/*#include <aio.h>*/
+
+#ifdef __i386__
+#include <dfs.h>
+#include <conio.h>
+#include <advanced.h>
+#include <debugapi.h>
+#include <process.h>
+#else
+#include <nwtypes.h>
+#include <nwdfs.h>
#include <nwconio.h>
#include <nwadv.h>
#include <nwdbgapi.h>
-/*#include <process.h>*/
-#include <errno.h>
#include <nwthread.h>
-#include "alpha-patch.h"
+#endif
+
+#include <aio.h>
+#include "cpu.h"
+
/****************************************************/
/* This information is from Novell. It is not in any of the standard
#define LO_AUTO_LOAD 0x0008
/* Loader returned error codes */
-#define LOAD_COULD_NOT_FIND_FILE 1
-#define LOAD_ERROR_READING_FILE 2
-#define LOAD_NOT_NLM_FILE_FORMAT 3
-#define LOAD_WRONG_NLM_FILE_VERSION 4
+#define LOAD_COULD_NOT_FIND_FILE 1
+#define LOAD_ERROR_READING_FILE 2
+#define LOAD_NOT_NLM_FILE_FORMAT 3
+#define LOAD_WRONG_NLM_FILE_VERSION 4
#define LOAD_REENTRANT_INITIALIZE_FAILURE 5
#define LOAD_CAN_NOT_LOAD_MULTIPLE_COPIES 6
-#define LOAD_ALREADY_IN_PROGRESS 7
-#define LOAD_NOT_ENOUGH_MEMORY 8
-#define LOAD_INITIALIZE_FAILURE 9
+#define LOAD_ALREADY_IN_PROGRESS 7
+#define LOAD_NOT_ENOUGH_MEMORY 8
+#define LOAD_INITIALIZE_FAILURE 9
#define LOAD_INCONSISTENT_FILE_FORMAT 10
#define LOAD_CAN_NOT_LOAD_AT_STARTUP 11
#define LOAD_AUTO_LOAD_MODULES_NOT_LOADED 12
-#define LOAD_UNRESOLVED_EXTERNAL 13
-#define LOAD_PUBLIC_ALREADY_DEFINED 14
+#define LOAD_UNRESOLVED_EXTERNAL 13
+#define LOAD_PUBLIC_ALREADY_DEFINED 14
/****************************************************/
/* The main thread ID. */
static const char hexchars[] = "0123456789abcdef";
-/* Register values. All of these values *MUST* agree with tm.h */
-#define RA_REGNUM 26 /* Contains return address value */
-#define SP_REGNUM 30 /* Contains address of top of stack */
-#define PC_REGNUM 64 /* Contains program counter */
-#define FP_REGNUM 65 /* Virtual frame pointer */
-#define V0_REGNUM 0 /* Function integer return value */
-#define NUM_REGS 66 /* Number of machine registers */
-#define REGISTER_BYTES (NUM_REGS * 8) /* Total size of registers array */
+unsigned char breakpoint_insn[] = BREAKPOINT;
-/*#define flush_i_cache() asm("call_pal 0x86")*/
+char *mem2hex (void *mem, char *buf, int count, int may_fault);
+char *hex2mem (char *buf, void *mem, int count, int may_fault);
+extern void set_step_traps (struct StackFrame *);
+extern void clear_step_traps (struct StackFrame *);
-static char *mem2hex (void *mem, char *buf, int count, int may_fault);
-static char *hex2mem (char *buf, void *mem, int count, int may_fault);
-
-#if 0
-__main() {};
-#endif
+static int __main() {};
/* Read a character from the serial port. This must busy wait, but
that's OK because we will be the only thread running anyhow. */
return 1;
}
-/* Get the registers out of the frame information. */
-
-static void
-frame_to_registers (frame, regs)
- struct StackFrame *frame;
- char *regs;
-{
- mem2hex (&frame->ExceptionRegs[SF_REG_PC], ®s[PC_REGNUM * 8 * 2], 8 * 1, 0);
-
- mem2hex (&frame->ExceptionRegs[SF_IREG_OFFSET], ®s[V0_REGNUM * 8 * 2], 8 * 64, 0);
-}
-
-/* Put the registers back into the frame information. */
-
-static void
-registers_to_frame (regs, frame)
- char *regs;
- struct StackFrame *frame;
-{
- hex2mem (®s[PC_REGNUM * 8 * 2], &frame->ExceptionRegs[SF_REG_PC], 8 * 1, 0);
-
- hex2mem (®s[V0_REGNUM * 8 * 2], &frame->ExceptionRegs[SF_IREG_OFFSET], 8 * 64, 0);
-}
-
/* Turn a hex character into a number. */
static int
/* Indicate to caller of mem2hex or hex2mem that there has been an
error. */
-static volatile int mem_err = 0;
+volatile int mem_err = 0;
+#ifndef ALTERNATE_MEM_FUNCS
/* These are separate functions so that they are so short and sweet
that the compiler won't save any registers (if there is a fault
to mem_fault, they won't get restored, so there better not be any
saved). */
-static int
+int
get_char (addr)
char *addr;
{
return *addr;
}
-static void
+void
set_char (addr, val)
char *addr;
int val;
{
*addr = val;
}
-
-/* This bit of assembly language just returns from a function. If a
- memory error occurs within get_char or set_char, the debugger
- handler points EIP at these instructions to get out. */
-
-extern void just_return ();
-#if 0
-asm (".globl just_return");
-asm (".globl _just_return");
-asm ("just_return:");
-asm ("_just_return:");
-asm ("leave");
-asm ("ret");
-#endif
+#endif /* ALTERNATE_MEM_FUNCS */
/* convert the memory pointed to by mem into hex, placing result in buf */
/* return a pointer to the last char put in buf (null) */
/* If MAY_FAULT is non-zero, then we should set mem_err in response to
a fault; if zero treat a fault like any other fault in the stub. */
-static char *
+char *
mem2hex (mem, buf, count, may_fault)
void *mem;
char *buf;
/* convert the hex array pointed to by buf into binary to be placed in mem */
/* return a pointer to the character AFTER the last byte written */
-static char *
+char *
hex2mem (buf, mem, count, may_fault)
char *buf;
void *mem;
/* This function takes the 386 exception vector and attempts to
translate this number into a unix compatible signal value. */
-static int
+int
computeSignal (exceptionVector)
int exceptionVector;
{
return (numChars);
}
-union inst
-{
- LONG l;
-
- struct
- {
- union
- {
- struct
- {
- unsigned hint : 16;
- unsigned rb : 5;
- unsigned ra : 5;
- unsigned opcode : 6;
- } jump;
- struct
- {
- signed disp : 21;
- unsigned ra : 5;
- unsigned opcode : 6;
- } branch;
- } variant;
- } inst;
-};
-
-static LONG saved_inst;
-static LONG *saved_inst_pc = 0;
-static LONG saved_target_inst;
-static LONG *saved_target_inst_pc = 0;
-
-static void
-set_step_breakpoint (pc, frame)
- LONG *pc;
- struct StackFrame *frame;
-{
- union inst inst;
- LONG *target;
- int opcode;
- int ra, rb;
-
- inst.l = *pc++;
-
- opcode = inst.inst.variant.branch.opcode;
-
- if ((opcode & 0x30) == 0x30) /* A branch of some sort */
- target = inst.inst.variant.branch.disp + pc;
- else if (opcode == 0x1a) /* jmp, ret, etc... */
- target = (LONG *)(frame->ExceptionRegs[SF_IREG_OFFSET
- + inst.inst.variant.jump.rb].lo
- & ~3);
- else
- target = pc;
-
- saved_inst = *pc;
- *pc = 0x80; /* call_pal bpt */
- saved_inst_pc = pc;
-
- if (target != pc)
- {
- saved_target_inst = *target;
- *target = 0x80; /* call_pal bpt */
- saved_target_inst_pc = target;
- }
-}
-
-/* Remove step breakpoints. Returns non-zero if pc was at a step breakpoint,
- zero otherwise. This routine works even if there were no step breakpoints
- set. */
-
-static int
-clear_step_breakpoint (pc)
- LONG *pc;
-{
- int retcode;
-
- if (saved_inst_pc == pc || saved_target_inst_pc == pc)
- retcode = 1;
- else
- retcode = 0;
-
- if (saved_inst_pc)
- {
- *saved_inst_pc = saved_inst;
- saved_inst_pc = 0;
- }
-
- if (saved_target_inst_pc)
- {
- *saved_target_inst_pc = saved_target_inst;
- saved_target_inst_pc = 0;
- }
-
- return retcode;
-}
-
-static void
-do_status (ptr, frame)
- char *ptr;
- struct StackFrame *frame;
-{
- int sigval;
-
- sigval = computeSignal (frame->ExceptionNumber);
-
- sprintf (ptr, "T%02x", sigval);
- ptr += 3;
-
- sprintf (ptr, "%02x:", PC_REGNUM);
- ptr = mem2hex (&frame->ExceptionRegs[SF_REG_PC], ptr + 3, 8, 0);
- *ptr++ = ';';
-
- sprintf (ptr, "%02x:", SP_REGNUM);
- ptr = mem2hex (&frame->ExceptionRegs[SF_IREG_OFFSET + SP_REGNUM], ptr + 3, 8, 0);
- *ptr++ = ';';
-
- sprintf (ptr, "%02x:", RA_REGNUM);
- ptr = mem2hex (&frame->ExceptionRegs[SF_IREG_OFFSET + RA_REGNUM], ptr + 3, 8, 0);
- *ptr++ = ';';
-
- sprintf (ptr, "%02x:", FP_REGNUM);
- ptr = mem2hex (&frame->ExceptionRegs[SF_IREG_OFFSET + FP_REGNUM], ptr + 3, 8, 0);
- *ptr++ = ';';
-
- *ptr = '\000';
-}
-
/* This function does all command processing for interfacing to gdb.
It is called whenever an exception occurs in the module being
debugged. */
int addr, length;
char *ptr;
static struct DBG_LoadDefinitionStructure *ldinfo = 0;
- static LONG first_insn; /* The first instruction in the program. */
+ static unsigned char first_insn[BREAKPOINT_SIZE]; /* The first instruction in the program. */
+
+#if 0
+ /* According to some documentation from Novell, the bell sometimes
+ may be ringing at this point. This can be stopped on Netware 4
+ systems by calling the undocumented StopBell() function. */
- /* Apparently the bell can sometimes be ringing at this point, and
- should be stopped. */
StopBell ();
+#endif
if (remote_debug)
{
ConsolePrintf ("vector=%d: %s, pc=%08x, thread=%08x\r\n",
frame->ExceptionNumber,
frame->ExceptionDescription,
- frame->ExceptionRegs[SF_REG_PC].lo,
+ frame->ExceptionPC,
GetThreadID ());
}
ldinfo = ((struct DBG_LoadDefinitionStructure *)
frame->ExceptionErrorCode);
- first_insn = *(LONG *)ldinfo->LDInitializationProcedure;
- *(LONG *)ldinfo->LDInitializationProcedure = 0x80; /* call_pal bpt */
+ memcpy (first_insn, ldinfo->LDInitializationProcedure,
+ BREAKPOINT_SIZE);
+ memcpy (ldinfo->LDInitializationProcedure, breakpoint_insn,
+ BREAKPOINT_SIZE);
flush_i_cache ();
return RETURN_TO_PROGRAM;
case 3: /* Breakpoint */
/* After we've reached the initial breakpoint, reset it. */
- if (frame->ExceptionRegs[SF_REG_PC].lo == (LONG) ldinfo->LDInitializationProcedure
- && *(LONG *) ldinfo->LDInitializationProcedure == 0x80)
+ if (frame->ExceptionPC - DECR_PC_AFTER_BREAK == (LONG) ldinfo->LDInitializationProcedure
+ && memcmp (ldinfo->LDInitializationProcedure, breakpoint_insn,
+ BREAKPOINT_SIZE) == 0)
{
- *(LONG *) ldinfo->LDInitializationProcedure = first_insn;
+ memcpy (ldinfo->LDInitializationProcedure, first_insn,
+ BREAKPOINT_SIZE);
+ frame->ExceptionPC -= DECR_PC_AFTER_BREAK;
flush_i_cache ();
}
/* Normal breakpoints end up here */
instruction pointer is near set_char or get_char, then we caused
the fault ourselves accessing an illegal memory location. */
if (mem_may_fault
- && ((frame->ExceptionRegs[SF_REG_PC].lo >= (long) &set_char
- && frame->ExceptionRegs[SF_REG_PC].lo < (long) &set_char + 50)
- || (frame->ExceptionRegs[SF_REG_PC].lo >= (long) &get_char
- && frame->ExceptionRegs[SF_REG_PC].lo < (long) &get_char + 50)))
+ && ((frame->ExceptionPC >= (long) &set_char
+ && frame->ExceptionPC < (long) &set_char + 50)
+ || (frame->ExceptionPC >= (long) &get_char
+ && frame->ExceptionPC < (long) &get_char + 50)))
{
mem_err = 1;
/* Point the instruction pointer at an assembly language stub
which just returns from the function. */
- frame->ExceptionRegs[SF_REG_PC].lo += 4; /* Skip the load or store */
+ frame->ExceptionPC += 4; /* Skip the load or store */
/* Keep going. This will act as though it returned from
set_char or get_char. The calling routine will check
the range of the module we are debugging, but that doesn't help
much since an error could occur in a library routine. */
- clear_step_breakpoint (frame->ExceptionRegs[SF_REG_PC]);
+ clear_step_traps (frame);
if (! putpacket(remcomOutBuffer))
return RETURN_TO_NEXT_DEBUGGER;
}
if (remcomInBuffer[0] == 's')
- set_step_breakpoint (frame->ExceptionRegs[SF_REG_PC].lo);
+ set_step_traps (frame);
flush_i_cache ();
return RETURN_TO_PROGRAM;
}
}
-char *baudRates[] = { "50", "75", "110", "134.5", "150", "300", "600", "1200",
- "1800", "2000", "2400", "3600", "4800", "7200", "9600",
- "19200", "38400", "57600", "115200" };
+char *progname;
-char dataBits[] = "5678";
+struct bitRate {
+ BYTE bitRate;
+ const char *bitRateString;
+};
-char *stopBits[] = { "1", "1.5", "2" };
+struct bitRate bitRateTable[] =
+{
+ { AIO_BAUD_50 , "50" },
+ { AIO_BAUD_75 , "75" },
+ { AIO_BAUD_110 , "110" },
+ { AIO_BAUD_134p5 , "134.5" },
+ { AIO_BAUD_150 , "150" },
+ { AIO_BAUD_300 , "300" },
+ { AIO_BAUD_600 , "600" },
+ { AIO_BAUD_1200 , "1200" },
+ { AIO_BAUD_1800 , "1800" },
+ { AIO_BAUD_2000 , "2000" },
+ { AIO_BAUD_2400 , "2400" },
+ { AIO_BAUD_3600 , "3600" },
+ { AIO_BAUD_4800 , "4800" },
+ { AIO_BAUD_7200 , "7200" },
+ { AIO_BAUD_9600 , "9600" },
+ { AIO_BAUD_19200 , "19200" },
+ { AIO_BAUD_38400 , "38400" },
+ { AIO_BAUD_57600 , "57600" },
+ { AIO_BAUD_115200, "115200" },
+ { -1, NULL }
+};
+
+char dataBitsTable[] = "5678";
+
+char *stopBitsTable[] = { "1", "1.5", "2" };
char parity[] = "NOEMS";
char **argv;
{
int hardware, board, port;
+ BYTE bitRate;
+ BYTE dataBits;
+ BYTE stopBits;
+ BYTE parityMode;
LONG err;
struct debuggerStructure s;
+ int cmdindx;
char *cmdlin;
int i;
-/* Use the -B option to invoke the NID if you want to debug the stub. */
+ /* set progname */
+ progname = "gdbserve";
- if (argc > 1 && strcmp(argv[1], "-B") == 0)
+ /* set default serial line */
+ hardware = -1;
+ board = 0;
+ port = 0;
+
+ /* set default serial line characteristics */
+ bitRate = AIO_BAUD_9600;
+ dataBits = AIO_DATA_BITS_8;
+ stopBits = AIO_STOP_BITS_1;
+ parityMode = AIO_PARITY_NONE;
+
+ cmdindx = 0;
+ for (argc--, argv++; *argv; argc--, argv++)
{
- Breakpoint(argc);
- ++argv, --argc;
+ char *bp;
+ char *ep;
+
+ if (strnicmp(*argv, "BAUD=", 5) == 0)
+ {
+ struct bitRate *brp;
+
+ bp = *argv + 5;
+ for (brp = bitRateTable; brp->bitRate != (BYTE) -1; brp++)
+ {
+ if (strcmp(brp->bitRateString, bp) == 0)
+ {
+ bitRate = brp->bitRate;
+ break;
+ }
+ }
+
+ if (brp->bitRateString == NULL)
+ {
+ fprintf(stderr, "%s: %s: unknown or unsupported bit rate",
+ progname, bp);
+ exit (1);
+ }
+ }
+ else if (strnicmp(*argv, "BOARD=", 6) == 0)
+ {
+ bp = *argv + 6;
+ board = strtol (bp, &ep, 0);
+ if (ep == bp || *ep != '\0')
+ {
+ fprintf (stderr, "%s: %s: expected integer argument\n",
+ progname, bp);
+ exit(1);
+ }
+ }
+#if 1 /* FIXME: this option has been depricated */
+ else if (strnicmp(*argv, "NODE=", 5) == 0)
+ {
+ bp = *argv + 5;
+ board = strtol (bp, &ep, 0);
+ if (ep == bp || *ep != '\0')
+ {
+ fprintf (stderr, "%s: %s: expected integer argument\n",
+ progname, bp);
+ exit(1);
+ }
+ }
+#endif
+ else if (strnicmp(*argv, "PORT=", 5) == 0)
+ {
+ bp = *argv + 5;
+ port = strtol (bp, &ep, 0);
+ if (ep == bp || *ep != '\0')
+ {
+ fprintf (stderr, "%s: %s: expected integer argument\n",
+ progname, bp);
+ exit(1);
+ }
+ }
+ else
+ {
+ break;
+ }
+
+ cmdindx++;
}
- if (argc < 4)
+ if (argc == 0)
{
fprintf (stderr,
- "Usage: load gdbserve board port program [arguments]\n");
+ "Usage: load %s [options] program [arguments]\n", progname);
exit (1);
}
- hardware = -1;
- board = strtol (argv[1], (char **) NULL, 0);
- port = strtol (argv[2], (char **) NULL, 0);
-
err = AIOAcquirePort (&hardware, &board, &port, &AIOhandle);
if (err != AIO_SUCCESS)
{
exit (1);
}
- err = AIOConfigurePort (AIOhandle, AIO_BAUD_9600, AIO_DATA_BITS_8,
- AIO_STOP_BITS_1, AIO_PARITY_NONE,
+ err = AIOConfigurePort (AIOhandle, bitRate, dataBits, stopBits, parityMode,
AIO_HARDWARE_FLOW_CONTROL_OFF);
if (err == AIO_QUALIFIED_SUCCESS)
{
AIOPORTCONFIG portConfig;
- AIODVRCONFIG dvrConfig;
fprintf (stderr, "Port configuration changed!\n");
- AIOGetPortConfiguration (AIOhandle, &portConfig, &dvrConfig);
+
+ portConfig.returnLength = sizeof(portConfig);
+ AIOGetPortConfiguration (AIOhandle, &portConfig, NULL);
+
fprintf (stderr,
" Bit Rate: %s, Data Bits: %c, Stop Bits: %s, Parity: %c,\
Flow:%s\n",
- baudRates[portConfig.bitRate],
- dataBits[portConfig.dataBits],
- stopBits[portConfig.stopBits],
+ bitRateTable[portConfig.bitRate].bitRateString,
+ dataBitsTable[portConfig.dataBits],
+ stopBitsTable[portConfig.stopBits],
parity[portConfig.parityMode],
portConfig.flowCtrlMode ? "ON" : "OFF");
}
/* Get the command line we were invoked with, and advance it past
our name and the board and port arguments. */
cmdlin = getcmd ((char *) NULL);
- for (i = 0; i < 2; i++)
+ for (i = 0; i < cmdindx; i++)
{
while (! isspace (*cmdlin))
++cmdlin;