File: [local] / sys / kern / kgdb_stub.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:14:58 2008 UTC (16 years, 3 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: kgdb_stub.c,v 1.8 2005/11/17 19:23:01 fgsch Exp $ */
/* $NetBSD: kgdb_stub.c,v 1.6 1998/08/30 20:30:57 scottr Exp $ */
/*
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Lawrence Berkeley Laboratories.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kgdb_stub.c 8.4 (Berkeley) 1/12/94
*/
/*
* "Stub" to allow remote cpu to debug over a serial line using gdb.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kgdb.h>
/* #define DEBUG_KGDB XXX */
/* XXX: Maybe these should be in the MD files? */
#ifndef KGDBDEV
#define KGDBDEV -1
#endif
#ifndef KGDBRATE
#define KGDBRATE 19200
#endif
int kgdb_dev = KGDBDEV; /* remote debugging device (-1 if none) */
int kgdb_rate = KGDBRATE; /* remote debugging baud rate */
int kgdb_active = 0; /* remote debugging active if != 0 */
int kgdb_debug_init = 0; /* != 0 waits for remote at system init */
int kgdb_debug_panic = 0; /* != 0 waits for remote on panic */
label_t *kgdb_recover = 0;
static void kgdb_copy(void *, void *, int);
/* static void kgdb_zero(void *, int); */
static void kgdb_send(u_char *);
static int kgdb_recv(u_char *, int);
static int digit2i(u_char);
static u_char i2digit(int);
static void mem2hex(void *, void *, int);
static u_char *hex2mem(void *, u_char *, int);
static vaddr_t hex2i(u_char **);
static int (*kgdb_getc)(void *);
static void (*kgdb_putc)(void *, int);
static void *kgdb_ioarg;
static u_char buffer[KGDB_BUFLEN];
static kgdb_reg_t gdb_regs[KGDB_NUMREGS];
#define GETC() ((*kgdb_getc)(kgdb_ioarg))
#define PUTC(c) ((*kgdb_putc)(kgdb_ioarg, c))
/*
* This little routine exists simply so that bcopy() can be debugged.
*/
static void
kgdb_copy(void *vsrc, void *vdst, int len)
{
char *src = vsrc;
char *dst = vdst;
while (--len >= 0)
*dst++ = *src++;
}
#if 0
/* ditto for bzero */
static void
kgdb_zero(void *vptr, int len)
{
char *ptr = vptr;
while (--len >= 0)
*ptr++ = (char) 0;
}
#endif
/*
* Convert a hex digit into an integer.
* This returns -1 if the argument passed is no
* valid hex digit.
*/
static int
digit2i(u_char c)
{
if (c >= '0' && c <= '9')
return (c - '0');
else if (c >= 'a' && c <= 'f')
return (c - 'a' + 10);
else if (c >= 'A' && c <= 'F')
return (c - 'A' + 10);
else
return (-1);
}
/*
* Convert the low 4 bits of an integer into
* an hex digit.
*/
static u_char
i2digit(int n)
{
return ("0123456789abcdef"[n & 0x0f]);
}
/*
* Convert a byte array into an hex string.
*/
static void
mem2hex(void *vdst, void *vsrc, int len)
{
u_char *dst = vdst;
u_char *src = vsrc;
while (len--) {
*dst++ = i2digit(*src >> 4);
*dst++ = i2digit(*src++);
}
*dst = '\0';
}
/*
* Convert an hex string into a byte array.
* This returns a pointer to the character following
* the last valid hex digit. If the string ends in
* the middle of a byte, NULL is returned.
*/
static u_char *
hex2mem(void *vdst, u_char *src, int maxlen)
{
u_char *dst = vdst;
int msb, lsb;
while (*src && maxlen--) {
msb = digit2i(*src++);
if (msb < 0)
return (src - 1);
lsb = digit2i(*src++);
if (lsb < 0)
return (NULL);
*dst++ = (msb << 4) | lsb;
}
return (src);
}
/*
* Convert an hex string into an integer.
* This returns a pointer to the character following
* the last valid hex digit.
*/
static vaddr_t
hex2i(u_char **srcp)
{
char *src = *srcp;
vaddr_t r = 0;
int nibble;
while ((nibble = digit2i(*src)) >= 0) {
r *= 16;
r += nibble;
src++;
}
*srcp = src;
return (r);
}
/*
* Send a packet.
*/
static void
kgdb_send(u_char *bp)
{
u_char *p;
u_char csum, c;
#ifdef DEBUG_KGDB
printf("kgdb_send: %s\n", bp);
#endif
do {
p = bp;
PUTC(KGDB_START);
for (csum = 0; (c = *p); p++) {
PUTC(c);
csum += c;
}
PUTC(KGDB_END);
PUTC(i2digit(csum >> 4));
PUTC(i2digit(csum));
} while ((c = GETC() & 0x7f) == KGDB_BADP);
}
/*
* Receive a packet.
*/
static int
kgdb_recv(u_char *bp, int maxlen)
{
u_char *p;
int c, csum;
int len;
do {
p = bp;
csum = len = 0;
while ((c = GETC()) != KGDB_START)
;
while ((c = GETC()) != KGDB_END && len < maxlen) {
c &= 0x7f;
csum += c;
*p++ = c;
len++;
}
csum &= 0xff;
*p = '\0';
if (len >= maxlen) {
PUTC(KGDB_BADP);
continue;
}
csum -= digit2i(GETC()) * 16;
csum -= digit2i(GETC());
if (csum == 0) {
PUTC(KGDB_GOODP);
/* Sequence present? */
if (bp[2] == ':') {
PUTC(bp[0]);
PUTC(bp[1]);
len -= 3;
kgdb_copy(bp + 3, bp, len);
}
break;
}
PUTC(KGDB_BADP);
} while (1);
#ifdef DEBUG_KGDB
printf("kgdb_recv: %s\n", bp);
#endif
return (len);
}
/*
* This is called by the appropriate tty driver.
*/
void
kgdb_attach(int (*getfn)(void *), void (*putfn)(void *, int), void *ioarg)
{
kgdb_getc = getfn;
kgdb_putc = putfn;
kgdb_ioarg = ioarg;
}
/*
* This function does all command processing for interfacing to
* a remote gdb. Note that the error codes are ignored by gdb
* at present, but might eventually become meaningful. (XXX)
* It might makes sense to use POSIX errno values, because
* that is what the gdb/remote.c functions want to return.
*/
int
kgdb_trap(int type, db_regs_t *regs)
{
label_t jmpbuf;
vaddr_t addr;
size_t len;
u_char *p;
if (kgdb_dev < 0 || kgdb_getc == NULL) {
/* not debugging */
return (0);
}
/* Detect and recover from unexpected traps. */
if (kgdb_recover != 0) {
printf("kgdb: caught trap 0x%x at %p\n",
type, (void *)PC_REGS(regs));
kgdb_send("E0E"); /* 14==EFAULT */
longjmp(kgdb_recover);
}
/*
* The first entry to this function is normally through
* a breakpoint trap in kgdb_connect(), in which case we
* must advance past the breakpoint because gdb will not.
*
* Machines vary as to where they leave the PC after a
* breakpoint trap. Those that leave the PC set to the
* address of the trap instruction (i.e. pc532) will not
* define FIXUP_PC_AFTER_BREAK(), and therefore will just
* advance the PC. On machines that leave the PC set to
* the instruction after the trap, FIXUP_PC_AFTER_BREAK
* will be defined to back-up the PC, so that after the
* "first-time" part of the if statement below has run,
* the PC will be the same as it was on entry.
*
* On the first entry here, we expect that gdb is not yet
* listening to us, so just enter the interaction loop.
* After the debugger is "active" (connected) it will be
* waiting for a "signaled" message from us.
*/
if (kgdb_active == 0) {
if (!IS_BREAKPOINT_TRAP(type, 0)) {
/* No debugger active -- let trap handle this. */
return (0);
}
/* Make the PC point at the breakpoint... */
#ifdef FIXUP_PC_AFTER_BREAK
FIXUP_PC_AFTER_BREAK(regs);
#endif
/* ... and then advance past it. */
#ifdef PC_ADVANCE
PC_ADVANCE(regs);
#else
PC_REGS(regs) += BKPT_SIZE;
#endif
kgdb_active = 1;
} else {
/* Tell remote host that an exception has occurred. */
snprintf(buffer, sizeof buffer, "S%02x", kgdb_signal(type));
kgdb_send(buffer);
}
/* Stick frame regs into our reg cache. */
kgdb_getregs(regs, gdb_regs);
/*
* Interact with gdb until it lets us go.
* If we cause a trap, resume here.
*/
(void)setjmp((kgdb_recover = &jmpbuf));
for (;;) {
kgdb_recv(buffer, sizeof(buffer));
switch (buffer[0]) {
default:
/* Unknown command. */
kgdb_send("");
continue;
case KGDB_SIGNAL:
/*
* if this command came from a running gdb,
* answer it -- the other guy has no way of
* knowing if we're in or out of this loop
* when he issues a "remote-signal".
*/
snprintf(buffer, sizeof buffer, "S%02x",
kgdb_signal(type));
kgdb_send(buffer);
continue;
case KGDB_REG_R:
mem2hex(buffer, gdb_regs, sizeof(gdb_regs));
kgdb_send(buffer);
continue;
case KGDB_REG_W:
p = hex2mem(gdb_regs, buffer + 1, sizeof(gdb_regs));
if (p == NULL || *p != '\0')
kgdb_send("E01");
else {
kgdb_setregs(regs, gdb_regs);
kgdb_send("OK");
}
continue;
case KGDB_MEM_R:
p = buffer + 1;
addr = hex2i(&p);
if (*p++ != ',') {
kgdb_send("E02");
continue;
}
len = hex2i(&p);
if (*p != '\0') {
kgdb_send("E03");
continue;
}
if (len > sizeof(buffer) / 2) {
kgdb_send("E04");
continue;
}
if (kgdb_acc(addr, len) == 0) {
kgdb_send("E05");
continue;
}
db_read_bytes(addr, (size_t)len,
(char *)buffer + sizeof(buffer) / 2);
mem2hex(buffer, buffer + sizeof(buffer) / 2, len);
kgdb_send(buffer);
continue;
case KGDB_MEM_W:
p = buffer + 1;
addr = hex2i(&p);
if (*p++ != ',') {
kgdb_send("E06");
continue;
}
len = hex2i(&p);
if (*p++ != ':') {
kgdb_send("E07");
continue;
}
if (len > (sizeof(buffer) - (p - buffer))) {
kgdb_send("E08");
continue;
}
p = hex2mem(buffer, p, sizeof(buffer));
if (p == NULL) {
kgdb_send("E09");
continue;
}
if (kgdb_acc(addr, len) == 0) {
kgdb_send("E0A");
continue;
}
db_write_bytes(addr, (size_t)len, (char *)buffer);
kgdb_send("OK");
continue;
case KGDB_DETACH:
kgdb_active = 0;
printf("kgdb detached\n");
db_clear_single_step(regs);
kgdb_send("OK");
goto out;
case KGDB_KILL:
kgdb_active = 0;
printf("kgdb detached\n");
db_clear_single_step(regs);
goto out;
case KGDB_CONT:
if (buffer[1]) {
p = buffer + 1;
addr = hex2i(&p);
if (*p) {
kgdb_send("E0B");
continue;
}
PC_REGS(regs) = addr;
}
db_clear_single_step(regs);
goto out;
case KGDB_STEP:
if (buffer[1]) {
p = buffer + 1;
addr = hex2i(&p);
if (*p) {
kgdb_send("E0B");
continue;
}
PC_REGS(regs) = addr;
}
db_set_single_step(regs);
goto out;
}
}
out:
kgdb_recover = 0;
return (1);
}
/*
* Trap into kgdb to wait for debugger to connect,
* noting on the console why nothing else is going on.
*/
void
kgdb_connect(int verbose)
{
if (kgdb_dev < 0)
return;
KGDB_PREPARE;
if (verbose)
printf("kgdb waiting...");
KGDB_ENTER;
if (verbose)
printf("connected.\n");
kgdb_debug_panic = 1;
}
/*
* Decide what to do on panic.
* (This is called by panic, like Debugger())
*/
void
kgdb_panic()
{
if (kgdb_dev >= 0 && kgdb_debug_panic) {
printf("entering kgdb\n");
kgdb_connect(kgdb_active == 0);
}
}