mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
bitkeeper revision 1.698.1.1 (4022a73eOGiwh6ZU9-EU4LZoLcVnuQ)
pervasive debugger
This commit is contained in:
1 parent
f21e4de207
commit
5a84d1bfb7
16 files changed
+2274
-26
No files matched your search
@@ -12,6 +12,7 @@
|
||||
3f9e7d60PWZJeVh5xdnk0nLUdxlqEA docs/eps/xenlogo.eps
|
||||
3f9e7d63lTwQbp2fnx7yY93epWS-eQ docs/figs/dummy
|
||||
3f9e7d564bWFB-Czjv1qdmE6o0GqNg docs/interface.tex
|
||||
4022a73cgxX1ryj1HgS-IwwB6NUi2A docs/pdb.txt
|
||||
3f9e7d58t7N6hjjBMxSn-NMxBphchA docs/style.tex
|
||||
3f9e7d5bz8BwYkNuwyiPVu7JJG441A docs/xenstyle.cls
|
||||
3f815144d1vI2777JI-dO4wk49Iw7g extras/mini-os/Makefile
|
||||
@@ -73,6 +74,8 @@
|
||||
3f13d81e6Z6806ihYYUw8GVKNkYnuw tools/misc/xen_nat_enable.README
|
||||
3f1668d4F29Jsw0aC0bJEIkOBiagiQ tools/misc/xen_read_console.c
|
||||
3f87ba90EUVPQLVOlFG0sW89BCwouQ tools/misc/xen_refresh_dev.c
|
||||
4022a73cEKvrYe_DVZW2JlAxobg9wg tools/nsplitd/Makefile
|
||||
4022a73cKms4Oq030x2JBzUB426lAQ tools/nsplitd/nsplitd.c
|
||||
3fbca441SjQr8vJwTQIgH1laysaWog tools/xc/Makefile
|
||||
3fbba6dbDfYvJSsw9500b4SZyUhxjQ tools/xc/lib/Makefile
|
||||
3fbba6dc1uU7U3IFeF6A-XEOYF2MkQ tools/xc/lib/rpm.spec
|
||||
@@ -118,6 +121,7 @@
|
||||
3ddb79bdIKgipvGoqExEQ7jawfVowA xen/arch/i386/pci-i386.h
|
||||
3ddb79bdHe6_Uij4-glW91vInNtBYQ xen/arch/i386/pci-irq.c
|
||||
3ddb79bcZ_2FxINljqNSkqa17ISyJw xen/arch/i386/pci-pc.c
|
||||
4022a73czgX7d-2zfF_cb33oVemApQ xen/arch/i386/pdb-stub.c
|
||||
3ddb79bc1_2bAt67x9MFCP4AZrQnvQ xen/arch/i386/process.c
|
||||
3ddb79bc7KxGCEJsgBnkDX7XjD_ZEQ xen/arch/i386/rwlock.c
|
||||
3ddb79bcrD6Z_rUvSDgrvjyb4846Eg xen/arch/i386/setup.c
|
||||
@@ -133,6 +137,8 @@
|
||||
3ddb79bddEYJbcURvqqcx99Yl2iAhQ xen/common/block.c
|
||||
3ddb79bdrqnW93GR9gZk1OJe1qK-iQ xen/common/brlock.c
|
||||
3fb10d07GscSWPKxBqpvNfU-dYfa0g xen/common/console.c
|
||||
4022a73c_BbDFd2YJ_NQYVvKX5Oz7w xen/common/debug-linux.c
|
||||
4022a73c_KPZ1VEbYOrpAhQffd01kA xen/common/debug-linux.h
|
||||
3fa152581E5KhrAtqZef2Sr5NKTz4w xen/common/debug.c
|
||||
3ddb79bdLX_P6iB7ILiblRLWvebapg xen/common/dom0_ops.c
|
||||
3e6377e4i0c9GtKN65e99OtRbw3AZw xen/common/dom_mem_ops.c
|
||||
@@ -394,6 +400,7 @@
|
||||
3ddb79c3xjYnrv5t3VqYlR4tNEOl4Q xen/include/asm-i386/page.h
|
||||
3e450943kzme29HPCtq5HNOVQkddfw xen/include/asm-i386/param.h
|
||||
3ddb79c3ysKUbxZuwKBRK3WXU2TlEg xen/include/asm-i386/pci.h
|
||||
4022a73diKn2Ax4-R4gzk59lm1YdDg xen/include/asm-i386/pdb.h
|
||||
3ddb79c3nm2zdzeO6Mj8g7ex3txgGw xen/include/asm-i386/pgalloc.h
|
||||
3ddb79c2QF5-pZGzuX4QukPCDAl59A xen/include/asm-i386/processor.h
|
||||
3ddb79c3mbqEM7QQr3zVq7NiBNhouA xen/include/asm-i386/ptrace.h
|
||||
|
||||
@@ -240,10 +240,16 @@ that may be able to help diagnose problems:
|
||||
|
||||
ifname=dummy Don't use any network interface.
|
||||
|
||||
ser_baud=xxx Enable serial I/O and set the baud rate.
|
||||
ser_baud=xxx Enable serial I/O and set the baud rate (COM1)
|
||||
|
||||
dom0_mem=xxx Set the initial amount of memory for domain0.
|
||||
|
||||
|
||||
pdb=xxx Enable the pervasive debugger. See docs/pdb.txt
|
||||
xxx defines how the gdb stub will communicate:
|
||||
com1 use com1
|
||||
com1H use com1 (with high bit set)
|
||||
com2 use on com2
|
||||
com2H use com2 (with high bit set)
|
||||
|
||||
It's probably a good idea to join the Xen developer's mailing list on
|
||||
Sourceforge: http://lists.sourceforge.net/lists/listinfo/xen-devel
|
||||
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
Pervasive Debugging
|
||||
===================
|
||||
|
||||
040205 Alex Ho (alex.ho@cl.cam.ac.uk)
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The pervasive debugging project is leveraging Xen to
|
||||
debug distributed systems. We have added a gdb stub
|
||||
to Xen to allow for remote debugging of both Xen and
|
||||
guest operating systems. More information about the
|
||||
pervasive debugger is available at: http://www.cl.cam.ac.uk/netos/pdb
|
||||
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
|
||||
The gdb stub communicates with gdb running over a serial line.
|
||||
The main entry point is pdb_handle_exception() which is invoked
|
||||
from: pdb_key_pressed() ('D' on the console)
|
||||
do_int3_exception() (interrupt 3: breakpoint exception)
|
||||
do_debug() (interrupt 1: debug exception)
|
||||
|
||||
This accepts characters from the serial port and passes gdb
|
||||
commands to pdb_process_command() which implements the gdb stub
|
||||
interface. This file draws heavily from the kgdb project and
|
||||
sample gdbstub provided with gdb.
|
||||
|
||||
The stub can examine registers, single step and continue, and
|
||||
read and write memory (in Xen, a domain, or a Linux process'
|
||||
address space). The debugger does not currently trace the
|
||||
current process, so all bets are off if context switch occurs
|
||||
in the domain.
|
||||
|
||||
|
||||
Setup
|
||||
-----
|
||||
|
||||
+-------+ telnet +-----------+ serial +-------+
|
||||
| GDB |--------| nsplitd |--------| Xen |
|
||||
+-------+ +-----------+ +-------+
|
||||
|
||||
To run pdb, Xen must be appropriately configured and
|
||||
a suitable serial interface attached to the target machine.
|
||||
GDB and nsplitd can run on the same machine.
|
||||
|
||||
Xen Configuration
|
||||
|
||||
Add the "pdb=xxx" option to your Xen boot command line
|
||||
where xxx is one of the following values:
|
||||
com1 gdb stub should communicate on com1
|
||||
com1H gdb stub should communicate on com1 (with high bit set)
|
||||
com2 gdb stub should communicate on com2
|
||||
com2H gdb stub should communicate on com2 (with high bit set)
|
||||
|
||||
Symbolic debugging infomration is quite helpful too:
|
||||
xeno.bk/xen/arch/i386/Rules.mk
|
||||
add -g to CFLAGS to compile Xen with symbols
|
||||
xeno.bk/xenolinux-2.4.24-sparse/arch/xeno/Makefile
|
||||
add -g to CFLAGS to compile XenoLinux with symbols
|
||||
|
||||
You may also want to consider dedicating a register to the
|
||||
frame pointer (disable the -fomit-frame-pointer compile flag).
|
||||
|
||||
When booting Xen and domain 0, look for the console text
|
||||
"Initializing pervasive debugger (PDB)" just before DOM0 starts up.
|
||||
|
||||
Serial Port Configuration
|
||||
|
||||
pdb expects to communicate with gdb using the serial port. Since
|
||||
this port is often shared with the machine's console output, pdb can
|
||||
discriminate its communication by setting the high bit of each char.
|
||||
|
||||
A new tool has been added to the source tree which splits
|
||||
the serial output from a remote machine into two streams:
|
||||
one stream (without the high bit) is the console and
|
||||
one stream (with the high bit stripped) is the pdb communication.
|
||||
|
||||
See: xeno.bk/tools/nsplitd
|
||||
|
||||
Note: nsplitd was originally written for the Nemesis project
|
||||
at Cambridge.
|
||||
|
||||
Usage:
|
||||
%telnet <hostname> <port>
|
||||
This is the console of the remote machine. You will probably
|
||||
want to set telnet in char mode (or create a .telnetrc file).
|
||||
<hostname> and <port> are for your instance of nsplitd.
|
||||
You should be able to press 'h' to display a list of keyboard
|
||||
handlers. In particular, you should see:
|
||||
key 'D' (ascii '44') => enter pervasive debugger
|
||||
|
||||
After nsplitd accepts a connection on <port>, it starts listening
|
||||
on port <port + 1>. Characters sent to the <port + 1> will have the
|
||||
high bit set and vice versa for characters received.
|
||||
|
||||
Note: if you are not using a serial console and can dedicate your
|
||||
serial line to pdb messages, then edit pdb_put_char and pdb_get_char
|
||||
in pdb-stub.c to remove the high bit tests.
|
||||
|
||||
GDB 6.0
|
||||
pdb has been tested with gdb 6.0. It should also work with
|
||||
earlier versions.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
1. Boot Xen and XenoLinux
|
||||
2. Interrupt Xen by pressing 'D' at the console
|
||||
You should see the console message:
|
||||
pdb_handle_exception [0x88][0xfc5c9d88]
|
||||
At this point Xen is waiting for gdb commands on the serial line.
|
||||
3. Attach with gdb
|
||||
(gdb) file xeno.bk/xen/xen
|
||||
Reading symbols from xeno.bk/xen/xen...done.
|
||||
(gdb) target remote <hostname>:<port + 1> /* contact nsplitd */
|
||||
Remote debugging using serial.srg:12131
|
||||
continue_cpu_idle_loop () at current.h:10
|
||||
warning: shared library handler failed to enable breakpoint
|
||||
(gdb) break __enter_scheduler
|
||||
Breakpoint 1 at 0xfc510a94: file schedule.c, line 330.
|
||||
(gdb) cont
|
||||
Continuing.
|
||||
|
||||
Program received signal SIGTRAP, Trace/breakpoint trap.
|
||||
__enter_scheduler () at schedule.c:330
|
||||
(gdb) step
|
||||
(gdb) step
|
||||
(gdb) print next /* the variable prev has been optimized away! */
|
||||
$1 = (struct task_struct *) 0x0
|
||||
(gdb) delete
|
||||
Delete all breakpoints? (y or n) y
|
||||
4. You can add additional symbols to gdb
|
||||
(gdb) add-sym xenolinux-2.4.24/vmlinux
|
||||
add symbol table from file "xenolinux-2.4.24/vmlinux" at
|
||||
(y or n) y
|
||||
Reading symbols from xenolinux-2.4.24/vmlinux...done.
|
||||
(gdb) x/s cpu_vendor_names[0]
|
||||
0xc01530d2 <cpdext+62898>: "Intel"
|
||||
(gdb) break free_uid
|
||||
Breakpoint 2 at 0xc0012250
|
||||
(gdb) cont
|
||||
Continuing. /* run a command in domain 0 */
|
||||
|
||||
Program received signal SIGTRAP, Trace/breakpoint trap.
|
||||
free_uid (up=0xbffff738) at user.c:77
|
||||
|
||||
(gdb) print *up
|
||||
$2 = {__count = {counter = 0}, processes = {counter = 135190120}, files = {
|
||||
counter = 0}, next = 0x395, pprev = 0xbffff878, uid = 134701041}
|
||||
(gdb) finish
|
||||
Run till exit from #0 free_uid (up=0xbffff738) at user.c:77
|
||||
|
||||
Program received signal SIGTRAP, Trace/breakpoint trap.
|
||||
release_task (p=0xc2da0000) at exit.c:51
|
||||
(gdb) print *p
|
||||
$3 = {state = 4, flags = 4, sigpending = 0, addr_limit = {seg = 3221225472},
|
||||
exec_domain = 0xc016a040, need_resched = 0, ptrace = 0, lock_depth = -1,
|
||||
counter = 1, nice = 0, policy = 0, mm = 0x0, processor = 0,
|
||||
cpus_runnable = 1, cpus_allowed = 4294967295, run_list = {next = 0x0,
|
||||
prev = 0x0}, sleep_time = 18995, next_task = 0xc017c000,
|
||||
prev_task = 0xc2f94000, active_mm = 0x0, local_pages = {next = 0xc2da0054,
|
||||
prev = 0xc2da0054}, allocation_order = 0, nr_local_pages = 0,
|
||||
binfmt = 0xc016c6a0, exit_code = 0, exit_signal = 17, pdeath_signal = 0,
|
||||
personality = 0, did_exec = -1, task_dumpable = 1, pid = 917, pgrp = 914,
|
||||
tty_old_pgrp = 0, session = 914, tgid = 917, leader = 0,
|
||||
p_opptr = 0xc2f94000, p_pptr = 0xc2f94000, p_cptr = 0x0, p_ysptr = 0x0,
|
||||
p_osptr = 0x0, thread_group = {next = 0xc2da00a8, prev = 0xc2da00a8},
|
||||
pidhash_next = 0x0, pidhash_pprev = 0xc01900b8, wait_chldexit = {
|
||||
lock = <incomplete type>, task_list = {next = 0xc2da00b8,
|
||||
prev = 0xc2da00b8}}, vfork_done = 0x0, rt_priority = 0,
|
||||
it_real_value = 0, it_prof_value = 0, it_virt_value = 0, it_real_incr = 0,
|
||||
it_prof_incr = 0, it_virt_incr = 0, real_timer = {list = {next = 0x0,
|
||||
prev = 0x0}, expires = 18950, data = 3269066752,
|
||||
function = 0xc000ce30 <it_real_fn>}, times = {tms_utime = 0,
|
||||
tms_stime = 0, tms_cutime = 0, tms_cstime = 0}, start_time = 18989,
|
||||
per_cpu_utime = {1}, per_cpu_stime = {310}, min_flt = 13, maj_flt = 104,
|
||||
nswap = 0, cmin_flt = 0, cmaj_flt = 0, cnswap = 0, swappable = -1, uid = 0,
|
||||
euid = 0, suid = 0, fsuid = 0, gid = 0, egid = 0, sgid = 0, fsgid = 0,
|
||||
ngroups = 7, groups = {0, 1, 2, 3, 4, 6, 10, 0 <repeats 25 times>},
|
||||
cap_effective = 4294967039, cap_inheritable = 0, cap_permitted = 4294967039,
|
||||
keep_capabilities = 0, user = 0xc016b18c, rlim = {{rlim_cur = 4294967295,
|
||||
rlim_max = 4294967295}, {rlim_cur = 4294967295, rlim_max = 4294967295}, {
|
||||
rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 8388608,
|
||||
rlim_max = 4294967295}, {rlim_cur = 0, rlim_max = 4294967295}, {
|
||||
rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 512,
|
||||
rlim_max = 512}, {rlim_cur = 1024, rlim_max = 1024}, {
|
||||
rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 4294967295,
|
||||
rlim_max = 4294967295}, {rlim_cur = 4294967295, rlim_max = 4294967295}},
|
||||
used_math = 0, comm = "id\000h\000og\000\000\000\000\000\000\000\000",
|
||||
link_count = 0, total_link_count = 1, tty = 0xc3ed1000, locks = 0,
|
||||
semundo = 0x0, semsleeping = 0x0, thread = {esp0 = 3269074944,
|
||||
eip = 3221249046, esp = 3269074792, fs = 0, gs = 0, io_pl = 3, debugreg = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0}, cr2 = 0, trap_no = 0, error_code = 0, i387 = {
|
||||
fsave = {cwd = 2098047, swd = 125632512, twd = 1073944696, fip = 2091,
|
||||
fcs = -1073745032, foo = 2099, fos = 8064, st_space = {
|
||||
0 <repeats 20 times>}, status = 0}, fxsave = {cwd = 895, swd = 32,
|
||||
twd = 0, fop = 1917, fip = 1073944696, fcs = 2091, foo = -1073745032,
|
||||
fos = 2099, mxcsr = 8064, reserved = 0, st_space = {
|
||||
0 <repeats 24 times>, 1449431204, -1774489361, 16383, 0, 1,
|
||||
-1891252224, 16404, 0}, xmm_space = {0 <repeats 32 times>},
|
||||
padding = {0 <repeats 56 times>}}, soft = {cwd = 2098047,
|
||||
swd = 125632512, twd = 1073944696, fip = 2091, fcs = -1073745032,
|
||||
foo = 2099, fos = 8064, st_space = {0 <repeats 20 times>},
|
||||
ftop = 0 '\0', changed = 0 '\0', lookahead = 0 '\0',
|
||||
no_update = 0 '\0', rm = 0 '\0', alimit = 0 '\0', info = 0x0,
|
||||
entry_eip = 0}}, vm86_info = 0x0, screen_bitmap = 0, v86flags = 0,
|
||||
v86mask = 0, saved_esp0 = 0}, fs = 0x0, files = 0x0, namespace = 0x0,
|
||||
sigmask_lock = <incomplete type>, sig = 0x0, blocked = {sig = {0, 0}},
|
||||
pending = {head = 0x0, tail = 0xc2da04f8, signal = {sig = {0, 0}}},
|
||||
sas_ss_sp = 0, sas_ss_size = 0, notifier = 0, notifier_data = 0x0,
|
||||
notifier_mask = 0x0, parent_exec_id = 7, self_exec_id = 8,
|
||||
alloc_lock = <incomplete type>, journal_info = 0x0}
|
||||
@@ -0,0 +1,11 @@
|
||||
ROOT = ..
|
||||
|
||||
CFILES = $(wildcard *.c)
|
||||
|
||||
INSTALL_BINS := nsplitd
|
||||
|
||||
include $(ROOT)/mk/rules.mk
|
||||
|
||||
nsplitd: $(OBJS)
|
||||
$(CC) $(CFLAGS) -o nsplitd $(OBJS)
|
||||
|
||||
@@ -0,0 +1,686 @@
|
||||
/*
|
||||
* nsplitd.c
|
||||
* ---------
|
||||
*
|
||||
* $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $
|
||||
*
|
||||
* Copyright (c) 1995, University of Cambridge Computer Laboratory,
|
||||
* Copyright (c) 1995, Richard Black, All Rights Reserved.
|
||||
*
|
||||
*
|
||||
* A complete re-implementation of DME's nsplitd for use from inetd
|
||||
*
|
||||
*/
|
||||
|
||||
/* The basic stream comes in (via inetd) and we then conenct to
|
||||
* somewhere else providing a loop-through service, except we offer
|
||||
* two other ports for connection - one of which gets a second channel
|
||||
* using the top bit to distinguish, and the other is a master control
|
||||
* port (normally used for gdb) which gets complete exclusive access
|
||||
* for its duration.
|
||||
*
|
||||
* Originally designed for multiplexing a xwcons/telnet with a gdb
|
||||
* post-mortem debugging session.
|
||||
*
|
||||
* Here is a picture:
|
||||
*
|
||||
* port0 (from inetd)
|
||||
* 8-bit connection /
|
||||
* made by us <----> nsplitd <-----gdbport (default port0+2)
|
||||
* to host:port/tcp |\
|
||||
* | port1 (default port0+1)
|
||||
* \
|
||||
* control (default port0+3)
|
||||
*
|
||||
* If port1 is explicitly disabled (through a command-line option) then
|
||||
* port0 becomes 8-bit clean.
|
||||
*/
|
||||
|
||||
/*
|
||||
* N.B.: We do NOT support 8 bit stdin/stdout usage on a
|
||||
* /dev/... because to do that right involves much messing with ioctl
|
||||
* and TIOC... etc. If you want to do that sort of thing then the
|
||||
* right way to do it is to chain this onto wconsd (which does know
|
||||
* about and understand all the ioctl and TIOC grief).
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DAEMON
|
||||
#define LOG_DAEMON 0
|
||||
#endif
|
||||
|
||||
#define DB(x) /* ((x), fflush(stderr)) */
|
||||
|
||||
extern char *optarg;
|
||||
|
||||
extern int optind, opterr, optopt;
|
||||
|
||||
static char *prog_name;
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "This program (%s) should be run via inetd (tcp)\n\n",
|
||||
prog_name);
|
||||
fprintf(stderr, "usage: %s [-h<highport>][-g<gdbport>]"
|
||||
"[-c<ctlport>][-8] host:service\n",
|
||||
prog_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void fault(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char logbuf[1024];
|
||||
|
||||
va_start(ap, format);
|
||||
fprintf(stderr, "%s: ", prog_name);
|
||||
vfprintf(stderr, format, ap);
|
||||
fflush(stderr);
|
||||
va_end(ap);
|
||||
|
||||
/* XXX This is a bit dubious, but there is no vsyslog */
|
||||
va_start(ap, format);
|
||||
vsprintf(logbuf, format, ap);
|
||||
syslog(LOG_ERR, logbuf);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int getservice(char *name, unsigned short *port)
|
||||
{
|
||||
struct servent *se;
|
||||
|
||||
if (!name) return -1;
|
||||
|
||||
if (isdigit(name[0]))
|
||||
*port = atoi(name);
|
||||
else
|
||||
{
|
||||
if (!(se = getservbyname(name, "tcp")))
|
||||
return -1;
|
||||
*port = ntohs(se->s_port);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* connect_host: connect to ("name", "port")
|
||||
*/
|
||||
static int connect_host (char *name, unsigned int port)
|
||||
{
|
||||
int fd;
|
||||
struct hostent *hostent;
|
||||
struct sockaddr_in sin;
|
||||
int on;
|
||||
|
||||
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
fault("socket");
|
||||
|
||||
if (!(hostent = gethostbyname(name)))
|
||||
fault("gethostbyname: %s: %s\n", name, strerror(errno));
|
||||
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons (port);
|
||||
memcpy(&sin.sin_addr.s_addr, hostent->h_addr, sizeof(struct in_addr));
|
||||
|
||||
if (connect(fd, (struct sockaddr *) &sin, sizeof (sin)) < 0)
|
||||
fault("connect: %s:%u: %s\n", name, port, strerror(errno));
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
|
||||
syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
|
||||
syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* open a tcp socket and start listening for connections on it
|
||||
*/
|
||||
static int startlistening(unsigned short port)
|
||||
{
|
||||
int fd, on;
|
||||
struct sockaddr_in sin;
|
||||
|
||||
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
fault("socket");
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
|
||||
syslog(LOG_WARNING, "setsockopt (SO_REUSEADDR): %m");
|
||||
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons (port);
|
||||
sin.sin_addr.s_addr = INADDR_ANY;
|
||||
if (bind(fd, &sin, sizeof(sin)) < 0)
|
||||
fault("bind: %u: %s\n", port, strerror(errno));
|
||||
|
||||
if (listen(fd, 1) < 0)
|
||||
fault("listen: %s\n", strerror(errno));
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void noblock(int fd)
|
||||
{
|
||||
int on=1;
|
||||
|
||||
if (ioctl(fd, FIONBIO, &on) < 0)
|
||||
fault("ioctl: FIONBIO: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
|
||||
/* You might not believe this, but fd_sets don't have to be a 32-bit
|
||||
* integer. In particular, in glibc2 it is an array of unsigned
|
||||
* longs. Hence, this hacked up FD_SET_rjb() that works out if it
|
||||
* would have been a nop. */
|
||||
#define FD_SET_rjb(fd, setp) \
|
||||
do { \
|
||||
if ((fd) != 32) \
|
||||
FD_SET((fd), (setp)); \
|
||||
} while(0)
|
||||
|
||||
#define FD_ISSET_rjb(fd, setp) (((fd) != 32)? FD_ISSET((fd), (setp)) : 0)
|
||||
|
||||
#define MAXSIZE 256
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* The main bit of the algorithm. Note we use 32 to mean not connected
|
||||
* because this gives us 1<<32 == 0. We could have done this one
|
||||
* character at a time, but that would have been very inefficient and
|
||||
* not the unix way. */
|
||||
static int debug;
|
||||
|
||||
static void doit(int actl, int acto, int lish, int lisg, int lisc)
|
||||
{
|
||||
int acth, actg, actc;
|
||||
int gdbmode = FALSE;
|
||||
char gibuf[MAXSIZE], oibuf[MAXSIZE];
|
||||
char libuf[MAXSIZE], lobuf[MAXSIZE];
|
||||
char hibuf[MAXSIZE], hobuf[MAXSIZE];
|
||||
char ctlbuf[MAXSIZE];
|
||||
fd_set rdfs, wrfs, exfs;
|
||||
int gicc, oicc, licc, locc, hicc, hocc, ctlcc;
|
||||
char *giptr, *oiptr, *liptr, *loptr, *hiptr, *hoptr;
|
||||
int rc, fromlen;
|
||||
struct sockaddr_in from;
|
||||
|
||||
gicc = oicc = licc = locc = hicc = hocc = ctlcc = 0;
|
||||
acth = actg = actc = 32; /* XXX yummy */
|
||||
|
||||
noblock(actl);
|
||||
noblock(acto);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
FD_ZERO(&rdfs);
|
||||
FD_ZERO(&wrfs);
|
||||
FD_ZERO(&exfs);
|
||||
|
||||
/* always take input from the control port (if it's connected) */
|
||||
FD_SET_rjb(actc, &rdfs);
|
||||
|
||||
if (gdbmode)
|
||||
{
|
||||
if (oicc)
|
||||
FD_SET_rjb(actg, &wrfs);
|
||||
else
|
||||
FD_SET_rjb(acto, &rdfs);
|
||||
|
||||
if (gicc)
|
||||
FD_SET_rjb(acto, &wrfs);
|
||||
else
|
||||
FD_SET_rjb(actg, &rdfs);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There is no such thing as oibuf because its been split into
|
||||
* lobuf and hobuf
|
||||
*/
|
||||
if (locc || hocc)
|
||||
{
|
||||
if (locc)
|
||||
FD_SET_rjb(actl, &wrfs);
|
||||
if (hocc)
|
||||
FD_SET_rjb(acth, &wrfs);
|
||||
}
|
||||
else
|
||||
FD_SET_rjb(acto, &rdfs);
|
||||
|
||||
if (licc)
|
||||
FD_SET_rjb(acto, &wrfs);
|
||||
else
|
||||
FD_SET_rjb(actl, &rdfs);
|
||||
|
||||
if (hicc)
|
||||
FD_SET_rjb(acto, &wrfs);
|
||||
else
|
||||
FD_SET_rjb(acth, &rdfs);
|
||||
}
|
||||
|
||||
if (acth == 32 && lish>=0) FD_SET_rjb(lish, &rdfs);
|
||||
if (actg == 32) FD_SET_rjb(lisg, &rdfs);
|
||||
if (actc == 32) FD_SET_rjb(lisc, &rdfs);
|
||||
|
||||
/* now make exfs the union of the read and write fd sets, plus
|
||||
* "actl" */
|
||||
{
|
||||
int i;
|
||||
exfs = rdfs;
|
||||
for(i=0; i<32; i++) /* XXX we only copy fd numbers up to 31 */
|
||||
if (FD_ISSET(i, &wrfs))
|
||||
FD_SET_rjb(i, &exfs);
|
||||
FD_SET_rjb(actl, &exfs);
|
||||
}
|
||||
|
||||
/* XXX AND: can't print something of type fd_set as %x - it
|
||||
* might be an array */
|
||||
DB(fprintf(stderr, "%s: before select: %08x %08x %08x\n",
|
||||
prog_name, rdfs, wrfs, exfs));
|
||||
|
||||
if (select(32, &rdfs, &wrfs, &exfs, NULL) < 0)
|
||||
fault("select: %s\n", strerror(errno));
|
||||
|
||||
DB(fprintf(stderr, "%s: after select: %08x %08x %08x\n",
|
||||
prog_name, rdfs, wrfs, exfs));
|
||||
|
||||
/* XXX it appears that a non-blocking socket may not show up
|
||||
* correctly in exfs but instead goes readable with no data in
|
||||
* it. Thus we check for zero and goto the appropriate close
|
||||
* method. */
|
||||
|
||||
/* Deal with exceptions */
|
||||
if (FD_ISSET_rjb(actg, &exfs))
|
||||
{
|
||||
exfs_actg:
|
||||
close(actg);
|
||||
gdbmode = FALSE;
|
||||
oicc = 0;
|
||||
oiptr = oibuf;
|
||||
actg = 32;
|
||||
continue; /* because assumptions changed */
|
||||
}
|
||||
if (FD_ISSET_rjb(acth, &exfs))
|
||||
{
|
||||
exfs_acth:
|
||||
close(acth);
|
||||
hicc = hocc = 0;
|
||||
hiptr = hibuf;
|
||||
hoptr = hibuf;
|
||||
acth = 32;
|
||||
continue; /* because assumptions changed */
|
||||
}
|
||||
if (FD_ISSET_rjb(actl, &exfs) ||
|
||||
FD_ISSET_rjb(acto, &exfs))
|
||||
{
|
||||
exfs_actl:
|
||||
exfs_acto:
|
||||
/* Thats all folks ... */
|
||||
break;
|
||||
}
|
||||
if (FD_ISSET_rjb(actc, &exfs))
|
||||
{
|
||||
exfs_ctl:
|
||||
close(actc);
|
||||
actc = 32;
|
||||
ctlcc = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Deal with reading */
|
||||
if (FD_ISSET_rjb(acto, &rdfs))
|
||||
{
|
||||
if ((oicc = read(acto, oiptr = oibuf, MAXSIZE)) < 0)
|
||||
fault("read acto: %d: %s\n", oicc, strerror(errno));
|
||||
if (!oicc) goto exfs_acto;
|
||||
|
||||
if (!gdbmode)
|
||||
{
|
||||
int t;
|
||||
|
||||
assert((locc == 0) && (hocc == 0));
|
||||
loptr = lobuf;
|
||||
hoptr = hobuf;
|
||||
|
||||
if (lish>=0) {
|
||||
for(t=0; t<oicc; t++)
|
||||
if (oibuf[t] & 0x80)
|
||||
hobuf[hocc++] = oibuf[t] & 0x7f;
|
||||
else
|
||||
lobuf[locc++] = oibuf[t];
|
||||
} else {
|
||||
for (t=0; t<oicc; t++)
|
||||
lobuf[locc++] = oibuf[t];
|
||||
}
|
||||
/* If no high connection scratch that */
|
||||
if (acth == 32)
|
||||
hocc=0;
|
||||
}
|
||||
}
|
||||
if (FD_ISSET_rjb(actl, &rdfs))
|
||||
{
|
||||
if ((licc = read(actl, liptr = libuf, MAXSIZE)) < 0)
|
||||
fault("read actl: %d: %s\n", licc, strerror(errno));
|
||||
if (!licc) goto exfs_actl;
|
||||
}
|
||||
if (FD_ISSET_rjb(acth, &rdfs))
|
||||
{
|
||||
int t;
|
||||
|
||||
if ((hicc = read(acth, hiptr = hibuf, MAXSIZE)) < 0)
|
||||
fault("read acth: %d: %s\n", hicc, strerror(errno));
|
||||
if (!hicc) goto exfs_acth;
|
||||
for(t=0; t<hicc; t++)
|
||||
hibuf[t] |= 0x80;
|
||||
}
|
||||
if (FD_ISSET_rjb(actg, &rdfs))
|
||||
{
|
||||
if ((gicc = read(actg, giptr = gibuf, MAXSIZE)) < 0)
|
||||
fault("read actg: %d: %s\n", gicc, strerror(errno));
|
||||
if (debug) write(1, giptr, gicc); /* XXX */
|
||||
if (!gicc) goto exfs_actg;
|
||||
}
|
||||
if (FD_ISSET_rjb(actc, &rdfs))
|
||||
{
|
||||
if ((ctlcc = read(actc, ctlbuf, MAXSIZE)) < 0)
|
||||
fault("read actc: %d: %s\n", ctlcc, strerror(errno));
|
||||
if (debug) write(1, ctlbuf, gicc);
|
||||
if (!ctlcc) goto exfs_ctl;
|
||||
if (ctlbuf[0] == 'r') /* reset command */
|
||||
{
|
||||
syslog(LOG_INFO, "reset command read, exiting");
|
||||
if (debug) write(1, "reseting\n", sizeof("reseting\n"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deal with writing */
|
||||
if (FD_ISSET_rjb(actg, &wrfs))
|
||||
{
|
||||
/* We must be in gdb mode so send oi buffer data */
|
||||
assert(gdbmode);
|
||||
if (debug) write(2, oiptr, oicc); /* XXX */
|
||||
if ((rc = write(actg, oiptr, oicc)) <= 0)
|
||||
fault("write actg: %d: %s\n", rc, strerror(errno));
|
||||
oiptr += rc;
|
||||
oicc -= rc;
|
||||
}
|
||||
if (FD_ISSET_rjb(actl, &wrfs))
|
||||
{
|
||||
if ((rc = write(actl, loptr, locc)) <= 0)
|
||||
fault("write actl: %d: %s\n", rc, strerror(errno));
|
||||
loptr += rc;
|
||||
locc -= rc;
|
||||
}
|
||||
if (FD_ISSET_rjb(acth, &wrfs))
|
||||
{
|
||||
if ((rc = write(acth, hoptr, hocc)) <= 0)
|
||||
fault("write acth: %d: %s\n", rc, strerror(errno));
|
||||
hoptr += rc;
|
||||
hocc -= rc;
|
||||
}
|
||||
if (FD_ISSET_rjb(acto, &wrfs))
|
||||
{
|
||||
/* If in gdb mode send gdb input, otherwise send low data
|
||||
preferentially */
|
||||
if (gdbmode)
|
||||
{
|
||||
assert(gicc);
|
||||
if ((rc = write(acto, giptr, gicc)) <= 0)
|
||||
fault("write acto: %d: %s\n", rc, strerror(errno));
|
||||
giptr += rc;
|
||||
gicc -= rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (licc)
|
||||
{
|
||||
if ((rc = write(acto, liptr, licc)) <= 0)
|
||||
fault("write acto: %d: %s\n", rc, strerror(errno));
|
||||
liptr += rc;
|
||||
licc -= rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(hicc);
|
||||
if ((rc = write(acto, hiptr, hicc)) <= 0)
|
||||
fault("write acto: %d: %s\n", rc, strerror(errno));
|
||||
hiptr += rc;
|
||||
hicc -= rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Deals with new connections */
|
||||
if ((acth == 32) && lish>=0 && (FD_ISSET_rjb(lish, &rdfs)))
|
||||
{
|
||||
fromlen = sizeof(from);
|
||||
if ((acth = accept(lish, &from, &fromlen)) < 0)
|
||||
{
|
||||
syslog(LOG_WARNING, "accept: %m");
|
||||
acth = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
noblock(acth);
|
||||
hicc = hocc = 0;
|
||||
syslog(LOG_INFO, "highbit client peer is %s:%u\n",
|
||||
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
|
||||
}
|
||||
}
|
||||
|
||||
if ((actg == 32) && (FD_ISSET_rjb(lisg, &rdfs)))
|
||||
{
|
||||
fromlen = sizeof(from);
|
||||
if ((actg = accept(lisg, &from, &fromlen)) < 0)
|
||||
{
|
||||
syslog(LOG_WARNING, "accept: %m");
|
||||
actg = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
noblock(actg);
|
||||
gicc = 0;
|
||||
gdbmode = TRUE;
|
||||
syslog(LOG_INFO, "gdb client peer is %s:%u\n",
|
||||
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
|
||||
}
|
||||
}
|
||||
|
||||
if ((actc == 32) && (FD_ISSET_rjb(lisc, &rdfs)))
|
||||
{
|
||||
fromlen = sizeof(from);
|
||||
if ((actc = accept(lisc, &from, &fromlen)) < 0)
|
||||
{
|
||||
syslog(LOG_WARNING, "accept (ctl): %m");
|
||||
actc = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
noblock(actc);
|
||||
syslog(LOG_INFO, "ctl client peer is %s:%u\n",
|
||||
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
|
||||
}
|
||||
}
|
||||
|
||||
/* Back to top of loop */
|
||||
}
|
||||
|
||||
/* We are bailing because one of the primary connections has gone
|
||||
* away. We close these all explicitly here because that way the
|
||||
* timeout on reusing the port numbers is smnaller. */
|
||||
|
||||
close(acth);
|
||||
close(actg);
|
||||
/* XXX AND: why are we closing all these "character counts" ?? */
|
||||
close(gicc);
|
||||
close(oicc);
|
||||
close(licc);
|
||||
close(locc);
|
||||
close(hicc);
|
||||
close(hocc);
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* In general, suffix "l" is low channel, "h" is high channel, "g"
|
||||
* is gdb channel, "c" is control channel and "o" is output channel.
|
||||
*/
|
||||
struct sockaddr_in from;
|
||||
int infd = 0, outfd;
|
||||
unsigned short portl, porth, portg, portc, porto;
|
||||
int on = 1, c;
|
||||
char *outname, *outservice;
|
||||
int fromlen;
|
||||
int lish, lisg, lisc;
|
||||
#if 0
|
||||
FILE *newerr;
|
||||
#endif /* 0 */
|
||||
|
||||
prog_name = argv[0];
|
||||
|
||||
if (isatty(infd))
|
||||
usage();
|
||||
|
||||
/* Here, then not just a simple idiot. */
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
openlog(prog_name, LOG_PID, LOG_DAEMON);
|
||||
|
||||
fromlen = sizeof(from);
|
||||
if (getsockname(infd, &from, &fromlen) < 0)
|
||||
fault("getsockname: %s", strerror(errno));
|
||||
if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET))
|
||||
fault("not an inet socket (family=%d)\n", from.sin_family);
|
||||
|
||||
portl = ntohs(from.sin_port);
|
||||
porth = portl+1;
|
||||
portg = porth+1;
|
||||
portc = portg+1;
|
||||
|
||||
fromlen = sizeof(from);
|
||||
if (getpeername(infd, &from, &fromlen) < 0)
|
||||
fault("getpeername: %s", strerror(errno));
|
||||
if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET))
|
||||
fault("not an inet socket (family=%d)\n", from.sin_family);
|
||||
|
||||
syslog(LOG_INFO, "on port %u peer is %s:%u\n", portl,
|
||||
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
|
||||
|
||||
if (setsockopt(infd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
|
||||
syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
|
||||
|
||||
/* from here on, we map stderr to output on the connection so we can
|
||||
* report errors to the remote user.
|
||||
*/
|
||||
#if 0
|
||||
if (!(newerr = fdopen(infd, "w")))
|
||||
syslog(LOG_WARNING, "fdopen: %m");
|
||||
else
|
||||
*stderr = *newerr;
|
||||
#endif
|
||||
|
||||
while((c = getopt(argc, argv, "d8h:g:c:")) != EOF)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
/* high bit port */
|
||||
if (getservice(optarg, &porth) < 0)
|
||||
fault("getservice failed (high port '%s')\n", optarg);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
/* gdb port */
|
||||
if (getservice(optarg, &portg) < 0)
|
||||
fault("getservice failed (gdb port '%s')\n", optarg);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
/* control port */
|
||||
if (getservice(optarg, &portc) < 0)
|
||||
fault("getservice failed (control port '%s')\n", optarg);
|
||||
break;
|
||||
|
||||
case '8':
|
||||
/* 8-bit clean; no high port */
|
||||
porth=0;
|
||||
break;
|
||||
|
||||
default:
|
||||
fault("bad argument list!\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (argc != optind + 1)
|
||||
fault("unparsed arguments (%d!=%d)\n", argc, optind+1);
|
||||
|
||||
outname = argv[optind];
|
||||
if (!(outservice = strchr(outname, ':')))
|
||||
fault("output arg '%s' doesn't contain ':'\n", outname);
|
||||
*outservice++ = 0;
|
||||
if (getservice(outservice, &porto) < 0)
|
||||
fault("getservice failed (output port '%s')\n", outservice);
|
||||
|
||||
/* Time to start the sockets */
|
||||
|
||||
if (porth) {
|
||||
lish = startlistening(porth);
|
||||
} else {
|
||||
lish = -1;
|
||||
}
|
||||
lisg = startlistening(portg);
|
||||
lisc = startlistening(portc);
|
||||
|
||||
outfd = connect_host(outname, porto);
|
||||
|
||||
doit(infd, outfd, lish, lisg, lisc);
|
||||
|
||||
syslog(LOG_INFO, "terminating normally\n");
|
||||
|
||||
fclose(stderr);
|
||||
|
||||
closelog();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* End $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ */
|
||||
@@ -0,0 +1,831 @@
|
||||
#include <xeno/lib.h>
|
||||
#include <xeno/sched.h>
|
||||
#include <asm-i386/ptrace.h>
|
||||
#include <xeno/keyhandler.h>
|
||||
#include <asm/pdb.h>
|
||||
#include <xeno/list.h>
|
||||
|
||||
#undef DEBUG_TRACE
|
||||
#ifdef DEBUG_TRACE
|
||||
#define TRC(_x) _x
|
||||
#else
|
||||
#define TRC(_x)
|
||||
#endif
|
||||
|
||||
#define BUFMAX 400
|
||||
|
||||
#define PDB_DOMAIN_OFFSET 2 /* all domains are positive numbers */
|
||||
|
||||
static const char hexchars[]="0123456789abcdef";
|
||||
|
||||
int remote_debug;
|
||||
|
||||
int pdb_foobar = 0x123456; /* testing */
|
||||
char *pdb_foobaz = "cambridge"; /* testing */
|
||||
|
||||
#define PDB_BUFMAX 1024
|
||||
static char pdb_in_buffer[PDB_BUFMAX];
|
||||
static char pdb_out_buffer[PDB_BUFMAX];
|
||||
static char pdb_buffer[PDB_BUFMAX];
|
||||
static int pdb_in_buffer_ptr;
|
||||
static unsigned char pdb_in_checksum;
|
||||
static unsigned char pdb_xmit_checksum;
|
||||
|
||||
int pdb_ctrl_thread = -1;
|
||||
int pdb_info_thread = -1;
|
||||
int pdb_stepping = 0;
|
||||
|
||||
int hex (char);
|
||||
char *mem2hex (char *, char *, int);
|
||||
char *hex2mem (char *, char *, int);
|
||||
int hexToInt (char **ptr, int *intValue);
|
||||
|
||||
void pdb_put_packet (unsigned char *buffer, int ack);
|
||||
void pdb_put_char (u_char c);
|
||||
u_char pdb_get_char ();
|
||||
|
||||
static volatile int mem_err = 0;
|
||||
void set_mem_err (void) /* NOT USED YET... */
|
||||
{
|
||||
mem_err = 1;
|
||||
}
|
||||
|
||||
/* 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). */
|
||||
int
|
||||
get_char (char *addr)
|
||||
{
|
||||
return *addr;
|
||||
}
|
||||
|
||||
void
|
||||
set_char (char *addr, int val)
|
||||
{
|
||||
*addr = val;
|
||||
}
|
||||
|
||||
void
|
||||
pdb_process_query (char *ptr)
|
||||
{
|
||||
if (strcmp(ptr, "C") == 0)
|
||||
{
|
||||
/* empty string */
|
||||
}
|
||||
else if (strcmp(ptr, "fThreadInfo") == 0)
|
||||
{
|
||||
struct task_struct *p = &idle0_task;
|
||||
u_long flags;
|
||||
int count = 0, buf_idx = 0;
|
||||
|
||||
read_lock_irqsave (&tasklist_lock, flags);
|
||||
|
||||
pdb_out_buffer[buf_idx++] = 'm';
|
||||
while ( (p = p->next_task) != &idle0_task )
|
||||
{
|
||||
int domain = p->domain + PDB_DOMAIN_OFFSET;
|
||||
|
||||
if (count > 0)
|
||||
pdb_out_buffer[buf_idx++] = ',';
|
||||
/*
|
||||
if (domain < 0)
|
||||
{ pdb_out_buffer[buf_idx++] = '-'; domain = domain * -1; }
|
||||
*/
|
||||
if (domain > 15)
|
||||
{
|
||||
pdb_out_buffer[buf_idx++] = hexchars[domain >> 4];
|
||||
}
|
||||
pdb_out_buffer[buf_idx++] = hexchars[domain % 16];
|
||||
count++;
|
||||
}
|
||||
pdb_out_buffer[buf_idx++] = 'l';
|
||||
pdb_out_buffer[buf_idx++] = 0;
|
||||
|
||||
read_unlock_irqrestore(&tasklist_lock, flags);
|
||||
}
|
||||
else if (strcmp(ptr, "sThreadInfo") == 0)
|
||||
{
|
||||
}
|
||||
else if (strncmp(ptr, "ThreadExtraInfo,", 16) == 0)
|
||||
{
|
||||
int thread = 0;
|
||||
char *message = "whatever!";
|
||||
|
||||
ptr += 16;
|
||||
if (hexToInt (&ptr, &thread))
|
||||
{
|
||||
mem2hex ((char *)message, pdb_out_buffer, strlen(message) + 1);
|
||||
}
|
||||
}
|
||||
else if (strcmp(ptr, "Offsets") == 0)
|
||||
{
|
||||
/* empty string */
|
||||
}
|
||||
else if (strncmp(ptr, "Symbol", 6) == 0)
|
||||
{
|
||||
strcpy (pdb_out_buffer, "OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
printk("pdb_process_query: unknown query [%s]\n", ptr);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
pdb_process_command (char *ptr, struct pt_regs *regs)
|
||||
{
|
||||
int sigval = 10;
|
||||
int length;
|
||||
unsigned long addr;
|
||||
int ack = 1; /* wait for ack in pdb_put_packet */
|
||||
int go = 0;
|
||||
|
||||
TRC(printk("pdb: [%s]\n", ptr));
|
||||
{
|
||||
pdb_out_buffer[0] = 0;
|
||||
|
||||
switch (*ptr++)
|
||||
{
|
||||
case '?':
|
||||
pdb_out_buffer[0] = 'S';
|
||||
pdb_out_buffer[1] = hexchars[sigval >> 4];
|
||||
pdb_out_buffer[2] = hexchars[sigval % 16];
|
||||
pdb_out_buffer[3] = 0;
|
||||
break;
|
||||
case 'S': /* step with signal */
|
||||
case 's': /* step */
|
||||
regs->eflags |= 0x100;
|
||||
pdb_stepping = 1;
|
||||
return 1;
|
||||
/* not reached */
|
||||
case 'C': /* continue with signal */
|
||||
case 'c': /* continue */
|
||||
regs->eflags &= ~0x100;
|
||||
/* jump out before replying to gdb */
|
||||
return 1;
|
||||
/* not reached */
|
||||
case 'd':
|
||||
remote_debug = !(remote_debug); /* toggle debug flag */
|
||||
break;
|
||||
case 'D': /* detach */
|
||||
return go;
|
||||
/* not reached */
|
||||
case 'g': /* return the value of the CPU registers */
|
||||
{
|
||||
int idx = 0;
|
||||
mem2hex ((char *)®s->eax, &pdb_out_buffer[idx], sizeof(regs->eax));
|
||||
idx += sizeof(regs->eax) * 2;
|
||||
mem2hex ((char *)®s->ecx, &pdb_out_buffer[idx], sizeof(regs->ecx));
|
||||
idx += sizeof(regs->ecx) * 2;
|
||||
mem2hex ((char *)®s->edx, &pdb_out_buffer[idx], sizeof(regs->edx));
|
||||
idx += sizeof(regs->edx) * 2;
|
||||
mem2hex ((char *)®s->ebx, &pdb_out_buffer[idx], sizeof(regs->ebx));
|
||||
idx += sizeof(regs->ebx) * 2;
|
||||
mem2hex ((char *)®s->esp, &pdb_out_buffer[idx], sizeof(regs->esp));
|
||||
idx += sizeof(regs->esp) * 2;
|
||||
mem2hex ((char *)®s->ebp, &pdb_out_buffer[idx], sizeof(regs->ebp));
|
||||
idx += sizeof(regs->ebp) * 2;
|
||||
mem2hex ((char *)®s->esi, &pdb_out_buffer[idx], sizeof(regs->esi));
|
||||
idx += sizeof(regs->esi) * 2;
|
||||
mem2hex ((char *)®s->edi, &pdb_out_buffer[idx], sizeof(regs->edi));
|
||||
idx += sizeof(regs->edi) * 2;
|
||||
mem2hex ((char *)®s->eip, &pdb_out_buffer[idx], sizeof(regs->eip));
|
||||
idx += sizeof(regs->eip) * 2;
|
||||
mem2hex ((char *)®s->eflags, &pdb_out_buffer[idx], sizeof(regs->eflags));
|
||||
idx += sizeof(regs->eflags) * 2;
|
||||
mem2hex ((char *)®s->xcs, &pdb_out_buffer[idx], sizeof(regs->xcs));
|
||||
idx += sizeof(regs->xcs) * 2;
|
||||
mem2hex ((char *)®s->xss, &pdb_out_buffer[idx], sizeof(regs->xss));
|
||||
idx += sizeof(regs->xss) * 2;
|
||||
mem2hex ((char *)®s->xds, &pdb_out_buffer[idx], sizeof(regs->xds));
|
||||
idx += sizeof(regs->xds) * 2;
|
||||
mem2hex ((char *)®s->xes, &pdb_out_buffer[idx], sizeof(regs->xes));
|
||||
idx += sizeof(regs->xes) * 2;
|
||||
mem2hex ((char *)®s->xfs, &pdb_out_buffer[idx], sizeof(regs->xfs));
|
||||
idx += sizeof(regs->xfs) * 2;
|
||||
mem2hex ((char *)®s->xgs, &pdb_out_buffer[idx], sizeof(regs->xgs));
|
||||
|
||||
/*
|
||||
TRC(printk (" reg: %s \n", pdb_out_buffer));
|
||||
TRC(printk (" ebx: 0x%08lx\n", regs->ebx));
|
||||
TRC(printk (" ecx: 0x%08lx\n", regs->ecx));
|
||||
TRC(printk (" edx: 0x%08lx\n", regs->edx));
|
||||
TRC(printk (" esi: 0x%08lx\n", regs->esi));
|
||||
TRC(printk (" edi: 0x%08lx\n", regs->edi));
|
||||
TRC(printk (" ebp: 0x%08lx\n", regs->ebp));
|
||||
TRC(printk (" eax: 0x%08lx\n", regs->eax));
|
||||
TRC(printk (" xds: 0x%08x\n", regs->xds));
|
||||
TRC(printk (" xes: 0x%08x\n", regs->xes));
|
||||
TRC(printk (" xfs: 0x%08x\n", regs->xfs));
|
||||
TRC(printk (" xgs: 0x%08x\n", regs->xgs));
|
||||
TRC(printk (" eip: 0x%08lx\n", regs->eip));
|
||||
TRC(printk (" xcs: 0x%08x\n", regs->xcs));
|
||||
TRC(printk (" efl: 0x%08lx\n", regs->eflags));
|
||||
TRC(printk (" esp: 0x%08lx\n", regs->esp));
|
||||
TRC(printk (" xss: 0x%08x\n", regs->xss));
|
||||
*/
|
||||
|
||||
break;
|
||||
}
|
||||
case 'G': /* set the value of the CPU registers - return OK */
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
{
|
||||
int thread;
|
||||
char *next = &ptr[1];
|
||||
if (hexToInt (&next, &thread))
|
||||
{
|
||||
if (thread > 0)
|
||||
{
|
||||
thread = thread - PDB_DOMAIN_OFFSET;
|
||||
}
|
||||
if (*ptr == 'c')
|
||||
{
|
||||
pdb_ctrl_thread = thread;
|
||||
}
|
||||
else if (*ptr == 'g')
|
||||
{
|
||||
pdb_info_thread = thread;
|
||||
}
|
||||
else
|
||||
{
|
||||
printk ("ack, unknown command %c (thread: %d)\n",
|
||||
*ptr, thread);
|
||||
}
|
||||
}
|
||||
strcpy (pdb_out_buffer, "OK");
|
||||
break;
|
||||
}
|
||||
case 'k': /* kill request */
|
||||
{
|
||||
strcpy (pdb_out_buffer, "OK"); /* ack for fun */
|
||||
printk ("don't kill bill...\n");
|
||||
ack = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'q':
|
||||
{
|
||||
pdb_process_query(ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
/* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
|
||||
case 'm':
|
||||
{
|
||||
/* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
|
||||
if (hexToInt (&ptr, (int *)&addr))
|
||||
if (*(ptr++) == ',')
|
||||
if (hexToInt (&ptr, &length))
|
||||
{
|
||||
ptr = 0;
|
||||
mem_err = 0;
|
||||
|
||||
if (pdb_info_thread >= 0)
|
||||
{
|
||||
pdb_get_values(pdb_info_thread, pdb_buffer, addr, length);
|
||||
mem2hex (pdb_buffer, pdb_out_buffer, length);
|
||||
}
|
||||
else
|
||||
mem2hex ((char *) addr, pdb_out_buffer, length);
|
||||
if (mem_err)
|
||||
{
|
||||
strcpy (pdb_out_buffer, "E03");
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
strcpy (pdb_out_buffer, "E01");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
|
||||
case 'M':
|
||||
{
|
||||
/* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
|
||||
if (hexToInt (&ptr, (int *)&addr))
|
||||
if (*(ptr++) == ',')
|
||||
if (hexToInt (&ptr, &length))
|
||||
if (*(ptr++) == ':')
|
||||
{
|
||||
mem_err = 0;
|
||||
|
||||
pdb_set_values(pdb_info_thread,
|
||||
ptr, addr, length);
|
||||
|
||||
if (mem_err)
|
||||
{
|
||||
strcpy (pdb_out_buffer, "E03");
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy (pdb_out_buffer, "OK");
|
||||
}
|
||||
|
||||
ptr = 0;
|
||||
}
|
||||
if (ptr)
|
||||
{
|
||||
strcpy (pdb_out_buffer, "E02");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'T':
|
||||
{
|
||||
int thread;
|
||||
if (hexToInt (&ptr, &thread))
|
||||
{
|
||||
thread -= PDB_DOMAIN_OFFSET;
|
||||
struct task_struct *p = find_domain_by_id(thread);
|
||||
if (p == NULL)
|
||||
{
|
||||
strcpy (pdb_out_buffer, "E00");
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy (pdb_out_buffer, "OK");
|
||||
}
|
||||
put_task_struct(p);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} /* switch */
|
||||
|
||||
/* reply to the request */
|
||||
pdb_put_packet (pdb_out_buffer, ack);
|
||||
}
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
/*
|
||||
* process an input character from the serial line.
|
||||
*
|
||||
* return "1" if the character is a gdb debug string
|
||||
* (and hence shouldn't be further processed).
|
||||
*/
|
||||
|
||||
int pdb_debug_state = 0; /* small parser state machine */
|
||||
|
||||
int pdb_serial_input(u_char c, struct pt_regs *regs)
|
||||
{
|
||||
int out = 1;
|
||||
int loop, count;
|
||||
|
||||
switch (pdb_debug_state)
|
||||
{
|
||||
case 0: /* not currently processing debug string */
|
||||
if ( c == '$' ) /* start token */
|
||||
{
|
||||
pdb_debug_state = 1;
|
||||
pdb_in_buffer_ptr = 0;
|
||||
pdb_in_checksum = 0;
|
||||
pdb_xmit_checksum = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
out = 0;
|
||||
}
|
||||
break;
|
||||
case 1: /* saw '$' */
|
||||
if ( c == '#' ) /* checksum token */
|
||||
{
|
||||
pdb_debug_state = 2;
|
||||
pdb_in_buffer[pdb_in_buffer_ptr] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pdb_in_checksum += c;
|
||||
pdb_in_buffer[pdb_in_buffer_ptr++] = c;
|
||||
}
|
||||
break;
|
||||
case 2: /* 1st checksum digit */
|
||||
pdb_xmit_checksum = hex(c) << 4;
|
||||
pdb_debug_state = 3;
|
||||
break;
|
||||
case 3: /* 2nd checksum digit */
|
||||
pdb_xmit_checksum += hex(c);
|
||||
if (pdb_in_checksum != pdb_xmit_checksum)
|
||||
{
|
||||
pdb_put_char('-'); /* checksum failure */
|
||||
printk ("checksum failure [%s.%02x.%02x]\n", pdb_in_buffer,
|
||||
pdb_in_checksum, pdb_xmit_checksum);
|
||||
}
|
||||
else
|
||||
{
|
||||
pdb_put_char('+'); /* checksum okay */
|
||||
if ( pdb_in_buffer_ptr > 1 && pdb_in_buffer[2] == ':' )
|
||||
{
|
||||
pdb_put_char(pdb_in_buffer[0]);
|
||||
pdb_put_char(pdb_in_buffer[1]);
|
||||
/* remove sequence chars from buffer */
|
||||
count = strlen(pdb_in_buffer);
|
||||
for (loop = 3; loop < count; loop++)
|
||||
pdb_in_buffer[loop - 3] = pdb_in_buffer[loop];
|
||||
}
|
||||
|
||||
pdb_process_command (pdb_in_buffer, regs);
|
||||
}
|
||||
pdb_debug_state = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
int hex(char ch)
|
||||
{
|
||||
if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
|
||||
if ((ch >= '0') && (ch <= '9')) return (ch-'0');
|
||||
if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* convert the memory pointed to by mem into hex, placing result in buf */
|
||||
/* return a pointer to the last char put in buf (null) */
|
||||
char *
|
||||
mem2hex (mem, buf, count)
|
||||
char *mem;
|
||||
char *buf;
|
||||
int count;
|
||||
{
|
||||
int i;
|
||||
unsigned char ch;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
ch = get_char (mem++);
|
||||
*buf++ = hexchars[ch >> 4];
|
||||
*buf++ = hexchars[ch % 16];
|
||||
}
|
||||
*buf = 0;
|
||||
return (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 */
|
||||
char *
|
||||
hex2mem (buf, mem, count)
|
||||
char *buf;
|
||||
char *mem;
|
||||
int count;
|
||||
{
|
||||
int i;
|
||||
unsigned char ch;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
ch = hex (*buf++) << 4;
|
||||
ch = ch + hex (*buf++);
|
||||
set_char (mem++, ch);
|
||||
}
|
||||
return (mem);
|
||||
}
|
||||
|
||||
int
|
||||
hexToInt (char **ptr, int *intValue)
|
||||
{
|
||||
int numChars = 0;
|
||||
int hexValue;
|
||||
int negative = 0;
|
||||
|
||||
*intValue = 0;
|
||||
|
||||
if (**ptr == '-')
|
||||
{
|
||||
negative = 1;
|
||||
numChars++;
|
||||
(*ptr)++;
|
||||
}
|
||||
while (**ptr)
|
||||
{
|
||||
hexValue = hex (**ptr);
|
||||
if (hexValue >= 0)
|
||||
{
|
||||
*intValue = (*intValue << 4) | hexValue;
|
||||
numChars++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
(*ptr)++;
|
||||
}
|
||||
if (negative)
|
||||
{
|
||||
*intValue *= -1;
|
||||
}
|
||||
|
||||
return (numChars);
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
/***********************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* Add a breakpoint to the list of known breakpoints.
|
||||
* For now there should only be two or three breakpoints so
|
||||
* we use a simple linked list. In the future, maybe a red-black tree?
|
||||
*/
|
||||
struct pdb_breakpoint breakpoints;
|
||||
|
||||
|
||||
void pdb_bkpt_add (unsigned long address)
|
||||
{
|
||||
struct pdb_breakpoint *bkpt;
|
||||
|
||||
bkpt = kmalloc(sizeof(struct pdb_breakpoint), GFP_KERNEL);
|
||||
INIT_LIST_HEAD(&bkpt->list);
|
||||
|
||||
bkpt->address = address;
|
||||
|
||||
list_add(&bkpt->list, &breakpoints.list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see of the breakpoint is in the list of known breakpoints
|
||||
*
|
||||
* return 1 if it has been set, 0 otherwise
|
||||
*/
|
||||
|
||||
struct pdb_breakpoint* pdb_bkpt_search (unsigned long address)
|
||||
{
|
||||
struct pdb_breakpoint *found = NULL;
|
||||
struct list_head *list_entry;
|
||||
struct pdb_breakpoint *bkpt;
|
||||
|
||||
list_for_each(list_entry, &breakpoints.list)
|
||||
{
|
||||
bkpt = list_entry(list_entry, struct pdb_breakpoint, list);
|
||||
|
||||
if (bkpt->address == address)
|
||||
{
|
||||
found = bkpt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a breakpoint to the list of known breakpoints.
|
||||
*
|
||||
* Return 1 if the element was not found, otherwise 0.
|
||||
*/
|
||||
|
||||
void pdb_bkpt_remove_ptr (struct pdb_breakpoint *bkpt)
|
||||
{
|
||||
struct list_head *list_entry = &bkpt->list;
|
||||
list_del(list_entry);
|
||||
kfree(bkpt);
|
||||
}
|
||||
|
||||
int pdb_bkpt_remove (unsigned long address)
|
||||
{
|
||||
struct list_head *list_entry;
|
||||
struct pdb_breakpoint *bkpt;
|
||||
int found = 1;
|
||||
|
||||
list_for_each(list_entry, &breakpoints.list)
|
||||
{
|
||||
bkpt = list_entry(list_entry, struct pdb_breakpoint, list);
|
||||
|
||||
if (bkpt->address == address)
|
||||
{
|
||||
pdb_bkpt_remove_ptr (bkpt);
|
||||
found = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
void breakpoint(void);
|
||||
|
||||
int pdb_initialized = 0;
|
||||
int pdb_high_bit = 1;
|
||||
|
||||
void pdb_put_char (u_char c)
|
||||
{
|
||||
extern void debug_putchar(u_char);
|
||||
u_char cc = pdb_high_bit ? c | 0x80 : c;
|
||||
debug_putchar(cc);
|
||||
}
|
||||
|
||||
u_char pdb_get_char ()
|
||||
{
|
||||
extern u_char debug_getchar();
|
||||
u_char cc = debug_getchar();
|
||||
return cc & 0x7f;
|
||||
}
|
||||
|
||||
/* send the packet in buffer. */
|
||||
void pdb_put_packet (unsigned char *buffer, int ack)
|
||||
{
|
||||
unsigned char checksum;
|
||||
int count;
|
||||
char ch;
|
||||
|
||||
/* $<packet info>#<checksum> */
|
||||
/* do */
|
||||
{
|
||||
pdb_put_char ('$');
|
||||
checksum = 0;
|
||||
count = 0;
|
||||
|
||||
while ((ch = buffer[count]))
|
||||
{
|
||||
pdb_put_char (ch);
|
||||
checksum += ch;
|
||||
count += 1;
|
||||
}
|
||||
|
||||
pdb_put_char('#');
|
||||
pdb_put_char(hexchars[checksum >> 4]);
|
||||
pdb_put_char(hexchars[checksum % 16]);
|
||||
}
|
||||
|
||||
if (ack)
|
||||
{
|
||||
if ((ch = pdb_get_char()) != '+')
|
||||
{
|
||||
printk(" pdb return error: %c 0x%x [%s]\n", ch, ch, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pdb_get_packet(char *buffer)
|
||||
{
|
||||
int count;
|
||||
char ch;
|
||||
unsigned char checksum = 0;
|
||||
unsigned char xmitcsum = 0;
|
||||
|
||||
do
|
||||
{
|
||||
while ((ch = pdb_get_char()) != '$');
|
||||
|
||||
count = 0;
|
||||
checksum = 0;
|
||||
|
||||
while (count < BUFMAX)
|
||||
{
|
||||
ch = pdb_get_char();
|
||||
if (ch == '#') break;
|
||||
checksum += ch;
|
||||
buffer[count] = ch;
|
||||
count++;
|
||||
}
|
||||
buffer[count] = 0;
|
||||
|
||||
if (ch == '#')
|
||||
{
|
||||
xmitcsum = hex(pdb_get_char()) << 4;
|
||||
xmitcsum += hex(pdb_get_char());
|
||||
|
||||
if (xmitcsum == checksum)
|
||||
{
|
||||
pdb_put_char('+');
|
||||
if (buffer[2] == ':')
|
||||
{
|
||||
printk ("gdb packet found with sequence ID\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pdb_put_char('-');
|
||||
}
|
||||
}
|
||||
} while (checksum != xmitcsum);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* process a machine interrupt or exception
|
||||
* return 1 if pdb is not interested in the exception; it should
|
||||
* be propagated to the guest os.
|
||||
*/
|
||||
|
||||
int pdb_handle_exception(int exceptionVector,
|
||||
struct pt_regs *xen_regs)
|
||||
{
|
||||
int signal = 0;
|
||||
|
||||
printk ("pdb_handle_exception [0x%x][0x%lx]\n",
|
||||
exceptionVector, xen_regs->eip);
|
||||
|
||||
/* if pdb didn't set the breakpoint, and
|
||||
pdb is not single stepping, and
|
||||
the user didn't press the magic debug key on the console,
|
||||
then pass the exception up to the guest os */
|
||||
if (pdb_bkpt_search(xen_regs->eip - 1) == NULL &&
|
||||
pdb_stepping == 0 &&
|
||||
exceptionVector != 0x88)
|
||||
{
|
||||
TRC(printk("pdb: external breakpoint at 0x%lx\n", xen_regs->eip));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pdb_stepping == 1)
|
||||
{
|
||||
xen_regs->eflags &= ~0x100;
|
||||
pdb_stepping = 0;
|
||||
}
|
||||
|
||||
if (exceptionVector == 0x03)
|
||||
{
|
||||
xen_regs->eip --;
|
||||
}
|
||||
|
||||
/* generate a signal for gdb */
|
||||
switch (exceptionVector)
|
||||
{
|
||||
case 136 : signal = 2; break; /* SIGINT */
|
||||
case 1 : signal = 5; break; /* SIGTRAP */
|
||||
case 3 : signal = 5; break; /* SIGTRAP */
|
||||
default :
|
||||
printk ("can't generate signal for unknown exception vector %d\n",
|
||||
exceptionVector);
|
||||
break;
|
||||
}
|
||||
|
||||
pdb_out_buffer[0] = 'S';
|
||||
pdb_out_buffer[1] = hexchars[signal >> 4];
|
||||
pdb_out_buffer[2] = hexchars[signal % 16];
|
||||
pdb_out_buffer[3] = 0;
|
||||
pdb_put_packet(pdb_out_buffer, 1);
|
||||
|
||||
while (1)
|
||||
{
|
||||
pdb_out_buffer[0] = 0;
|
||||
pdb_get_packet(pdb_in_buffer);
|
||||
if (pdb_process_command(pdb_in_buffer, xen_regs))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pdb_key_pressed(u_char key, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
pdb_handle_exception(136, regs);
|
||||
return;
|
||||
}
|
||||
|
||||
void initialize_pdb()
|
||||
{
|
||||
extern char opt_pdb[];
|
||||
int pdb_com_port;
|
||||
|
||||
if (strncmp(opt_pdb, "com", 3) == 0)
|
||||
{
|
||||
extern void debug_set_com_port(int port);
|
||||
|
||||
pdb_com_port = opt_pdb[3] - '0'; /* error checking ? */
|
||||
debug_set_com_port(pdb_com_port);
|
||||
pdb_high_bit = opt_pdb[4] == 'H' ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp(opt_pdb, "none") != 0)
|
||||
{
|
||||
printk ("pdb: unknown option\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
printk ("Initializing pervasive debugger (PDB) [%s] port %d, high %d\n",
|
||||
opt_pdb, pdb_com_port, pdb_high_bit);
|
||||
|
||||
breakpoints.address = 0;
|
||||
INIT_LIST_HEAD(&breakpoints.list);
|
||||
|
||||
pdb_stepping = 0;
|
||||
|
||||
/* ack any spurrious gdb packets */
|
||||
pdb_put_char ('+');
|
||||
|
||||
/* serial console */
|
||||
add_key_handler('D', pdb_key_pressed, "enter pervasive debugger");
|
||||
|
||||
pdb_initialized = 1;
|
||||
}
|
||||
|
||||
void breakpoint(void)
|
||||
{
|
||||
if (pdb_initialized)
|
||||
asm("int $3");
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <asm/apic.h>
|
||||
#include <asm/desc.h>
|
||||
#include <asm/domain_page.h>
|
||||
#include <asm/pdb.h>
|
||||
|
||||
struct cpuinfo_x86 boot_cpu_data = { 0 };
|
||||
/* Lots of nice things, since we only target PPro+. */
|
||||
@@ -428,6 +429,7 @@ void __init start_of_day(void)
|
||||
do_initcalls();
|
||||
initialize_serial(); /* setup serial 'driver' (for debugging) */
|
||||
initialize_keyboard(); /* setup keyboard (also for debugging) */
|
||||
initialize_pdb(); /* pervasive debugger */
|
||||
|
||||
if ( !setup_network_devices() )
|
||||
panic("Must have a network device!\n");
|
||||
|
||||
+63
-2
@@ -49,6 +49,7 @@
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/i387.h>
|
||||
#include <asm/pdb.h>
|
||||
|
||||
#define GTBF_TRAP 1
|
||||
#define GTBF_TRAP_NOCODE 2
|
||||
@@ -223,6 +224,30 @@ static inline void do_trap(int trapnr, char *str,
|
||||
smp_processor_id(), trapnr, str, error_code);
|
||||
}
|
||||
|
||||
static inline void do_int3_exception(int trapnr,
|
||||
struct pt_regs *regs,
|
||||
long error_code)
|
||||
{
|
||||
struct task_struct *p = current;
|
||||
struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
|
||||
trap_info_t *ti;
|
||||
|
||||
if ((regs->xcs & 3) != 3)
|
||||
{
|
||||
pdb_handle_exception(trapnr, regs);
|
||||
return;
|
||||
}
|
||||
|
||||
ti = current->thread.traps + trapnr;
|
||||
gtb->flags = GTBF_TRAP_NOCODE;
|
||||
gtb->error_code = error_code;
|
||||
gtb->cs = ti->cs;
|
||||
gtb->eip = ti->address;
|
||||
if ( TI_GET_IF(ti) )
|
||||
clear_bit(EVENTS_MASTER_ENABLE_BIT, &p->shared_info->events_mask);
|
||||
return;
|
||||
}
|
||||
|
||||
#define DO_ERROR_NOCODE(trapnr, str, name) \
|
||||
asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
|
||||
{ \
|
||||
@@ -236,7 +261,6 @@ do_trap(trapnr, str, regs, error_code, 1); \
|
||||
}
|
||||
|
||||
DO_ERROR_NOCODE( 0, "divide error", divide_error)
|
||||
DO_ERROR_NOCODE( 3, "int3", int3)
|
||||
DO_ERROR_NOCODE( 4, "overflow", overflow)
|
||||
DO_ERROR_NOCODE( 5, "bounds", bounds)
|
||||
DO_ERROR_NOCODE( 6, "invalid operand", invalid_op)
|
||||
@@ -251,6 +275,12 @@ DO_ERROR(17, "alignment check", alignment_check)
|
||||
DO_ERROR_NOCODE(18, "machine check", machine_check)
|
||||
DO_ERROR_NOCODE(19, "simd error", simd_coprocessor_error)
|
||||
|
||||
asmlinkage void do_int3(struct pt_regs * regs, long error_code)
|
||||
{
|
||||
if (pdb_initialized) do_int3_exception(3, regs, error_code);
|
||||
else do_trap(3, "int3", regs, error_code, 0);
|
||||
}
|
||||
|
||||
asmlinkage void do_double_fault(void)
|
||||
{
|
||||
extern spinlock_t console_lock;
|
||||
@@ -489,13 +519,44 @@ asmlinkage void math_state_restore(struct pt_regs *regs, long error_code)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
asmlinkage void do_debug(struct pt_regs * regs, long error_code)
|
||||
{
|
||||
unsigned int condition;
|
||||
struct task_struct *tsk = current;
|
||||
struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
|
||||
|
||||
/*
|
||||
printk("do_debug_exceptionn [%lx][%lx][%x]\n",
|
||||
error_code, regs->eip, regs->xcs);
|
||||
*/
|
||||
|
||||
__asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
|
||||
|
||||
if ((condition & (1 << 14)) != (1 << 14))
|
||||
{
|
||||
printk ("\nwarning: debug trap w/o BS bit [0x%x]\n\n", condition);
|
||||
}
|
||||
__asm__("movl %0,%%db6" : : "r" (0));
|
||||
|
||||
if (pdb_handle_exception(1, regs)) /* propagate to domain */
|
||||
{
|
||||
tsk->thread.debugreg[6] = condition;
|
||||
|
||||
gtb->flags = GTBF_TRAP_NOCODE;
|
||||
gtb->cs = tsk->thread.traps[1].cs;
|
||||
gtb->eip = tsk->thread.traps[1].address;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
asmlinkage void do_debug_orig(struct pt_regs * regs, long error_code)
|
||||
{
|
||||
unsigned int condition;
|
||||
struct task_struct *tsk = current;
|
||||
struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
|
||||
|
||||
__asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
|
||||
|
||||
/* Mask out spurious debug traps due to lazy DR7 setting */
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#include <xeno/config.h>
|
||||
#include <xeno/types.h>
|
||||
#include <xeno/lib.h>
|
||||
#include <hypervisor-ifs/dom0_ops.h>
|
||||
|
||||
#include "debug-linux.h"
|
||||
|
||||
/*
|
||||
* linux specific pdb stuff
|
||||
*/
|
||||
|
||||
/*
|
||||
static inline struct task_struct *find_task_by_pid(int pid)
|
||||
{
|
||||
struct task_struct *p, **htable = &pidhash[pid_hashfn(pid)];
|
||||
|
||||
for(p = *htable; p && p->pid != pid; p = p->pidhash_next) ;
|
||||
return p;
|
||||
}
|
||||
*/
|
||||
|
||||
/* read a byte from a process */
|
||||
u_char pdb_linux_get_value (int domain, int pid, unsigned long addr)
|
||||
{
|
||||
u_char result = 0;
|
||||
unsigned long task_struct_p, mm_p, pgd, task_struct_pid;
|
||||
unsigned long l2tab, page;
|
||||
|
||||
/* find the task_struct of the given process */
|
||||
pdb_get_values(domain, (u_char *) &task_struct_p,
|
||||
pidhash_addr + pid_hashfn(pid) * 4,
|
||||
sizeof(task_struct_p));
|
||||
|
||||
/* find the correct task struct */
|
||||
while (task_struct_p != (unsigned long)NULL)
|
||||
{
|
||||
pdb_get_values(domain, (u_char *) &task_struct_pid,
|
||||
task_struct_p + task_struct_pid_offset,
|
||||
sizeof(task_struct_pid));
|
||||
if (task_struct_pid == pid)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
pdb_get_values(domain, (u_char *) &task_struct_p,
|
||||
task_struct_p + task_struct_pidhash_next_offset,
|
||||
sizeof(task_struct_p));
|
||||
}
|
||||
if (task_struct_p == (unsigned long)NULL)
|
||||
{
|
||||
/* oops */
|
||||
printk ("error: couldn't find process 0x%x in domain %d\n", pid, domain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get the mm_struct within the task_struct */
|
||||
pdb_get_values(domain, (u_char *) &mm_p,
|
||||
task_struct_p + task_struct_mm_offset,
|
||||
sizeof(mm_p));
|
||||
/* get the page global directory (cr3) within the mm_struct */
|
||||
pdb_get_values(domain, (u_char *) &pgd,
|
||||
mm_p + mm_struct_pgd_offset,
|
||||
sizeof(pgd));
|
||||
|
||||
/* get the l2 table entry */
|
||||
pdb_get_values(domain, (u_char *) &l2tab,
|
||||
pgd + (addr >> PGDIR_SHIFT) * 4,
|
||||
sizeof(l2tab));
|
||||
l2tab = (unsigned long)__va(machine_to_phys(domain, l2tab) & PAGE_MASK);
|
||||
|
||||
/* get the page table entry */
|
||||
pdb_get_values(domain, (u_char *) &page,
|
||||
l2tab + ((addr & L1_PAGE_BITS) >> PAGE_SHIFT) * 4,
|
||||
sizeof(page));
|
||||
page = (unsigned long)__va(machine_to_phys(domain, page) & PAGE_MASK);
|
||||
|
||||
/* get the byte */
|
||||
pdb_get_values(domain, (u_char *) &result, page + (addr & ~PAGE_MASK),
|
||||
sizeof(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <asm/pdb.h>
|
||||
|
||||
/* from linux/sched.h */
|
||||
#define PIDHASH_SZ (4096 >> 2)
|
||||
#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
|
||||
|
||||
/* from asm-xeno/pgtable-2level.h */
|
||||
#define PGDIR_SHIFT 22
|
||||
#define PTRS_PER_PGD 1024
|
||||
|
||||
/* from asm-xeno/page.h */
|
||||
#define PAGE_SHIFT 12
|
||||
#define PAGE_SIZE (1UL << PAGE_SHIFT)
|
||||
#define PAGE_MASK (~(PAGE_SIZE-1))
|
||||
|
||||
#define __PAGE_OFFSET (0xC0000000)
|
||||
#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET)
|
||||
#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
|
||||
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
|
||||
|
||||
/* from debug.h */
|
||||
#define ENTRIES_PER_L1_PAGETABLE 1024
|
||||
#define L1_PAGE_BITS ( (ENTRIES_PER_L1_PAGETABLE - 1) << PAGE_SHIFT )
|
||||
|
||||
|
||||
/* adapted from asm-xeno/page.h */
|
||||
static inline unsigned long machine_to_phys(int domain, unsigned long machine)
|
||||
{
|
||||
unsigned long phys;
|
||||
pdb_get_values(domain, (u_char *) &phys,
|
||||
(unsigned long) machine_to_phys_mapping + (machine >> PAGE_SHIFT) * 4,
|
||||
sizeof(phys));
|
||||
phys = (phys << PAGE_SHIFT) | (machine & ~PAGE_MASK);
|
||||
return phys;
|
||||
}
|
||||
|
||||
|
||||
#define pidhash_addr 0xc018f260UL
|
||||
|
||||
#define task_struct_mm_offset 0x2c
|
||||
#define task_struct_pid_offset 0x7c
|
||||
#define task_struct_pidhash_next_offset 0xb0
|
||||
#define mm_struct_pgd_offset 0x0c
|
||||
|
||||
extern u_char pdb_linux_get_value (int domain, int pid, unsigned long addr);
|
||||
+177
-6
@@ -10,33 +10,182 @@
|
||||
#include <hypervisor-ifs/dom0_ops.h>
|
||||
#include <xeno/sched.h>
|
||||
#include <xeno/event.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/domain_page.h> /* [un]map_domain_mem */
|
||||
#include <asm/pdb.h>
|
||||
|
||||
#define DEBUG_TRACE
|
||||
#undef DEBUG_TRACE
|
||||
#ifdef DEBUG_TRACE
|
||||
#define TRC(_x) _x
|
||||
#else
|
||||
#define TRC(_x)
|
||||
#endif
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
int pdb_change_values (int domain, u_char *buffer, unsigned long addr,
|
||||
int length, int rw);
|
||||
|
||||
/*
|
||||
* Set memory in a domain's address space
|
||||
* Set "length" bytes at "address" from "domain" to the values in "buffer".
|
||||
* Return the number of bytes set, 0 if there was a problem.
|
||||
*
|
||||
* THIS WILL BECOME A MACRO
|
||||
*/
|
||||
|
||||
int pdb_set_values (int domain, u_char *buffer, unsigned long addr, int length)
|
||||
{
|
||||
int count;
|
||||
void *bkpt;
|
||||
count = pdb_change_values(domain, buffer, addr, length, 2);
|
||||
|
||||
/* this is a bit x86 specific at the moment... */
|
||||
if (length == 1 && buffer[0] == 'c' && buffer[1] == 'c')
|
||||
{
|
||||
/* inserting a new breakpoint */
|
||||
pdb_bkpt_add (addr);
|
||||
TRC(printk("pdb breakpoint detected at 0x%lx\n", addr));
|
||||
}
|
||||
else if ((bkpt = pdb_bkpt_search(addr)))
|
||||
{
|
||||
/* removing a breakpoint */
|
||||
TRC(printk("pdb breakpoint cleared at 0x%lx\n", addr));
|
||||
pdb_bkpt_remove_ptr(bkpt);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read memory from a domain's address space.
|
||||
* Fetch "length" bytes at "address" from "domain" into "buffer".
|
||||
* Return the number of bytes read, 0 if there was a problem.
|
||||
*
|
||||
* THIS WILL BECOME A MACRO
|
||||
*/
|
||||
|
||||
int pdb_get_values (int domain, u_char *buffer, unsigned long addr, int length)
|
||||
{
|
||||
return pdb_change_values(domain, buffer, addr, length, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change memory in a domain's address space.
|
||||
* Read or write "length" bytes at "address" from "domain" into/from "buffer".
|
||||
* Return the number of bytes read, 0 if there was a problem.
|
||||
* RW: 1 = read, 2 = write
|
||||
*/
|
||||
|
||||
int pdb_change_values (int domain, u_char *buffer, unsigned long addr,
|
||||
int length, int rw)
|
||||
{
|
||||
struct task_struct *p;
|
||||
l2_pgentry_t* l2_table = NULL;
|
||||
l1_pgentry_t* l1_table = NULL;
|
||||
u_char *page;
|
||||
int bytes = 0;
|
||||
|
||||
extern char *hex2mem (char *, char *, int);
|
||||
|
||||
p = find_domain_by_id(domain);
|
||||
|
||||
if ((addr >> PAGE_SHIFT) == ((addr + length - 1) >> PAGE_SHIFT))
|
||||
{
|
||||
l2_table = map_domain_mem(pagetable_val(p->mm.pagetable));
|
||||
l2_table += l2_table_offset(addr);
|
||||
if (!(l2_pgentry_val(*l2_table) & _PAGE_PRESENT))
|
||||
{
|
||||
printk ("L2:0x%p (0x%lx) \n", l2_table, l2_pgentry_val(*l2_table));
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
if (l2_pgentry_val(*l2_table) & _PAGE_PSE)
|
||||
{
|
||||
#define PSE_PAGE_SHIFT L2_PAGETABLE_SHIFT
|
||||
#define PSE_PAGE_SIZE (1UL << PSE_PAGE_SHIFT)
|
||||
#define PSE_PAGE_MASK (~(PSE_PAGE_SIZE-1))
|
||||
|
||||
#define L1_PAGE_BITS ( (ENTRIES_PER_L1_PAGETABLE - 1) << L1_PAGETABLE_SHIFT )
|
||||
|
||||
#define pse_pgentry_to_phys(_x) (l2_pgentry_val(_x) & PSE_PAGE_MASK)
|
||||
|
||||
page = map_domain_mem(pse_pgentry_to_phys(*l2_table) +/* 10 bits */
|
||||
(addr & L1_PAGE_BITS)); /* 10 bits */
|
||||
page += addr & (PAGE_SIZE - 1); /* 12 bits */
|
||||
}
|
||||
else
|
||||
{
|
||||
l1_table = map_domain_mem(l2_pgentry_to_phys(*l2_table));
|
||||
l1_table += l1_table_offset(addr);
|
||||
if (!(l1_pgentry_val(*l1_table) & _PAGE_PRESENT))
|
||||
{
|
||||
printk ("L2:0x%p (0x%lx) L1:0x%p (0x%lx)\n",
|
||||
l2_table, l2_pgentry_val(*l2_table),
|
||||
l1_table, l1_pgentry_val(*l1_table));
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
page = map_domain_mem(l1_pgentry_to_phys(*l1_table));
|
||||
page += addr & (PAGE_SIZE - 1);
|
||||
}
|
||||
|
||||
switch (rw)
|
||||
{
|
||||
case 1: /* read */
|
||||
memcpy (buffer, page, length);
|
||||
bytes = length;
|
||||
break;
|
||||
case 2: /* write */
|
||||
hex2mem (buffer, page, length);
|
||||
bytes = length;
|
||||
break;
|
||||
default: /* unknown */
|
||||
printk ("error: unknown RW flag: %d\n", rw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unmap_domain_mem((void *)page);
|
||||
exit1:
|
||||
if (l1_table != NULL)
|
||||
unmap_domain_mem((void *)l1_table);
|
||||
exit2:
|
||||
unmap_domain_mem((void *)l2_table);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* read spans pages. need to recurse */
|
||||
printk ("pdb memory SPAN! addr:0x%lx l: %x\n", addr, length);
|
||||
}
|
||||
|
||||
put_task_struct(p);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* interactively call pervasive debugger from a privileged domain
|
||||
*/
|
||||
void pdb_do_debug (dom0_op_t *op)
|
||||
{
|
||||
op->u.debug.status = 0;
|
||||
op->u.debug.out1 = op->u.debug.in2 + 10;
|
||||
op->u.debug.out2 = op->u.debug.in1 + 100;
|
||||
|
||||
TRC(printk("PDB: op:%c, dom:%x, in1:%x, in2:%x\n",
|
||||
TRC(printk("PDB: op:%c, dom:%x, in1:%x, in2:%x, in3:%x, in4:%x\n",
|
||||
op->u.debug.opcode, op->u.debug.domain,
|
||||
op->u.debug.in1, op->u.debug.in2));
|
||||
op->u.debug.in1, op->u.debug.in2,
|
||||
op->u.debug.in3, op->u.debug.in4));
|
||||
|
||||
/* NOT NOW
|
||||
if (op->u.debug.domain == 0)
|
||||
{
|
||||
op->u.debug.status = 1;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
switch (op->u.debug.opcode)
|
||||
{
|
||||
case 'r' :
|
||||
case 'c' :
|
||||
{
|
||||
struct task_struct * p = find_domain_by_id(op->u.debug.domain);
|
||||
if ( p != NULL )
|
||||
@@ -54,6 +203,28 @@ void pdb_do_debug (dom0_op_t *op)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'r' :
|
||||
{
|
||||
int loop;
|
||||
u_char x;
|
||||
|
||||
for (loop = 0; loop < op->u.debug.in2; loop++) /* length */
|
||||
{
|
||||
extern u_char pdb_linux_get_value (int domain, int pid, unsigned long addr);
|
||||
|
||||
if (loop % 8 == 0)
|
||||
{
|
||||
printk ("\n%08x ", op->u.debug.in1 + loop);
|
||||
}
|
||||
x = pdb_linux_get_value(op->u.debug.domain, /* domain */
|
||||
op->u.debug.in3, /* pid */
|
||||
op->u.debug.in1 + loop); /* addr */
|
||||
printk (" %02x", x);
|
||||
}
|
||||
printk ("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case 's' :
|
||||
{
|
||||
unsigned long cpu_mask;
|
||||
|
||||
@@ -69,6 +69,8 @@ int opt_noreboot=0;
|
||||
int opt_ignorebiostables=0;
|
||||
/* opt_watchdog: If true, run a watchdog NMI on each processor. */
|
||||
int opt_watchdog=0;
|
||||
/* opt_pdb: Name of serial port for Xen pervasive debugger (and enable pdb) */
|
||||
unsigned char opt_pdb[10] = "none";
|
||||
|
||||
static struct {
|
||||
unsigned char *name;
|
||||
@@ -85,6 +87,7 @@ static struct {
|
||||
{ "noreboot", OPT_BOOL, &opt_noreboot },
|
||||
{ "ignorebiostables", OPT_BOOL, &opt_ignorebiostables },
|
||||
{ "watchdog", OPT_BOOL, &opt_watchdog },
|
||||
{ "pdb", OPT_STR, &opt_pdb },
|
||||
{ NULL, 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
@@ -41,31 +41,114 @@
|
||||
#define NS16550_MCR_OUT2 0x08 /* OUT2: interrupt mask */
|
||||
#define NS16550_MCR_LOOP 0x10 /* Loop */
|
||||
|
||||
#define SERIAL_BASE 0x3f8 /* XXX SMH: horrible hardwired COM1 */
|
||||
#define LSR_DR 0x01 /* Data ready */
|
||||
#define LSR_OE 0x02 /* Overrun */
|
||||
#define LSR_PE 0x04 /* Parity error */
|
||||
#define LSR_FE 0x08 /* Framing error */
|
||||
#define LSR_BI 0x10 /* Break */
|
||||
#define LSR_THRE 0x20 /* Xmit holding register empty */
|
||||
#define LSR_TEMT 0x40 /* Xmitter empty */
|
||||
#define LSR_ERR 0x80 /* Error */
|
||||
|
||||
static int serial_echo = 0; /* default is not to echo; change with 'e' */
|
||||
#define SERIAL_COM1 0x3f8
|
||||
#define SERIAL_COM2 0x2f8
|
||||
|
||||
int serial_com_base = SERIAL_COM1;
|
||||
int debug_com_base = SERIAL_COM1;
|
||||
|
||||
|
||||
static int serial_echo = 0; /* default is not to echo; change with '~' */
|
||||
|
||||
void toggle_echo(u_char key, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
serial_echo = !serial_echo;
|
||||
}
|
||||
|
||||
void debug_set_com_port(int port)
|
||||
{
|
||||
debug_com_base = port == 1 ? SERIAL_COM1 : SERIAL_COM2;
|
||||
}
|
||||
|
||||
int debug_testchar() /* character available? */
|
||||
{
|
||||
return (inb(debug_com_base + NS16550_LSR) & LSR_DR);
|
||||
}
|
||||
|
||||
u_char debug_getchar()
|
||||
{
|
||||
while (! (inb(debug_com_base + NS16550_LSR) & LSR_DR));/* wait for char */
|
||||
return inb(debug_com_base + NS16550_RBR);
|
||||
}
|
||||
|
||||
void debug_putch(u_char c)
|
||||
{
|
||||
while (! (inb(debug_com_base + NS16550_LSR) & LSR_THRE));
|
||||
/* wait for idle */
|
||||
outb(c, debug_com_base + NS16550_RBR);
|
||||
}
|
||||
|
||||
void debug_putchar(u_char c)
|
||||
{
|
||||
debug_putch(c);
|
||||
if (c == '\n') debug_putch('\r');
|
||||
}
|
||||
|
||||
|
||||
|
||||
int serial_testchar() /* character available? */
|
||||
{
|
||||
return (inb(serial_com_base + NS16550_LSR) & LSR_DR);
|
||||
}
|
||||
|
||||
u_char serial_getchar()
|
||||
{
|
||||
while (! (inb(serial_com_base + NS16550_LSR) & LSR_DR));/* wait for char */
|
||||
return inb(serial_com_base + NS16550_RBR);
|
||||
}
|
||||
|
||||
void serial_putch(u_char c)
|
||||
{
|
||||
while (! (inb(serial_com_base + NS16550_LSR) & LSR_THRE));
|
||||
/* wait for idle */
|
||||
outb(c, serial_com_base + NS16550_RBR);
|
||||
}
|
||||
|
||||
void serial_putchar(u_char c)
|
||||
{
|
||||
serial_putch(c);
|
||||
if (c == '\n') serial_putch('\r');
|
||||
}
|
||||
|
||||
static spinlock_t serial_lock;
|
||||
|
||||
static void serial_rx_int(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
u_char c;
|
||||
key_handler *handler;
|
||||
unsigned long flags;
|
||||
|
||||
while ( (inb(SERIAL_BASE + NS16550_LSR) & 1) == 1 )
|
||||
spin_lock_irqsave(&serial_lock, flags);
|
||||
|
||||
while (serial_testchar())
|
||||
{
|
||||
c = inb(SERIAL_BASE + NS16550_RBR);
|
||||
c = serial_getchar();
|
||||
|
||||
if( (handler = get_key_handler(c)) != NULL )
|
||||
(*handler)(c, dev_id, regs);
|
||||
if (c & 0x80)
|
||||
{
|
||||
extern int pdb_serial_input(u_char, struct pt_regs *);
|
||||
pdb_serial_input(c & 0x7f, regs);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( (handler = get_key_handler(c)) != NULL )
|
||||
(*handler)(c, dev_id, regs);
|
||||
|
||||
if ( serial_echo )
|
||||
printk("%c", c);
|
||||
}
|
||||
if ( serial_echo )
|
||||
serial_putch(c);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&serial_lock, flags);
|
||||
}
|
||||
|
||||
void initialize_serial()
|
||||
@@ -74,6 +157,8 @@ void initialize_serial()
|
||||
|
||||
if ( !SERIAL_ENABLED )
|
||||
return;
|
||||
|
||||
spin_lock_init(&serial_lock);
|
||||
|
||||
/* setup key handler */
|
||||
add_key_handler('~', toggle_echo, "toggle serial echo");
|
||||
@@ -82,13 +167,13 @@ void initialize_serial()
|
||||
/* Clear FIFOs, enable, trigger at 1 byte */
|
||||
outb(NS16550_FCR_TRG1 | NS16550_FCR_ENABLE |
|
||||
NS16550_FCR_CLRX | NS16550_FCR_CLTX,
|
||||
SERIAL_BASE+NS16550_FCR);
|
||||
serial_com_base + NS16550_FCR);
|
||||
|
||||
/* Enable receive interrupts. Also remember to keep DTR/RTS asserted. */
|
||||
outb(NS16550_MCR_OUT2|NS16550_MCR_DTR|NS16550_MCR_RTS,
|
||||
SERIAL_BASE + NS16550_MCR);
|
||||
serial_com_base + NS16550_MCR);
|
||||
outb(NS16550_IER_ERDAI,
|
||||
SERIAL_BASE + NS16550_IER );
|
||||
serial_com_base + NS16550_IER );
|
||||
|
||||
if( (rc = request_irq(4, serial_rx_int, SA_NOPROFILE, "serial", 0)) )
|
||||
printk("initialize_serial: failed to get IRQ4, rc=%d\n", rc);
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
/*
|
||||
* pervasive debugger
|
||||
*
|
||||
* alex ho
|
||||
* 2004
|
||||
* university of cambridge computer laboratory
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __PDB_H__
|
||||
#define __PDB_H__
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <xeno/list.h>
|
||||
|
||||
extern int pdb_initialized;
|
||||
extern int pdb_com_port;
|
||||
extern int pdb_high_bit;
|
||||
|
||||
extern void initialize_pdb(void);
|
||||
extern int pdb_set_values (int domain, u_char *buffer,
|
||||
unsigned long addr, int length);
|
||||
extern int pdb_get_values (int domain, u_char *buffer,
|
||||
unsigned long addr, int length);
|
||||
|
||||
extern int pdb_handle_exception(int exceptionVector,
|
||||
struct pt_regs *xen_regs);
|
||||
|
||||
|
||||
struct pdb_breakpoint
|
||||
{
|
||||
struct list_head list;
|
||||
unsigned long address;
|
||||
};
|
||||
extern void pdb_bkpt_add (unsigned long address);
|
||||
extern struct pdb_breakpoint* pdb_bkpt_search (unsigned long address);
|
||||
extern void pdb_bkpt_remove_ptr (struct pdb_breakpoint *bkpt);
|
||||
extern int pdb_bkpt_remove (unsigned long address);
|
||||
|
||||
#endif /* __PDB_H__ */
|
||||
@@ -17,7 +17,7 @@
|
||||
* This makes sure that old versions of dom0 tools will stop working in a
|
||||
* well-defined way (rather than crashing the machine, for instance).
|
||||
*/
|
||||
#define DOM0_INTERFACE_VERSION 0xAAAA0003
|
||||
#define DOM0_INTERFACE_VERSION 0xAAAA0004
|
||||
|
||||
|
||||
/*
|
||||
@@ -168,9 +168,11 @@ typedef struct dom0_debug_st
|
||||
{
|
||||
/* IN variables. */
|
||||
char opcode;
|
||||
int domain, in1, in2;
|
||||
unsigned int domain;
|
||||
int in1, in2, in3, in4;
|
||||
/* OUT variables. */
|
||||
int status, out1, out2;
|
||||
unsigned int status;
|
||||
int out1, out2;
|
||||
} dom0_debug_t;
|
||||
|
||||
/*
|
||||
|
||||
@@ -159,7 +159,7 @@ struct task_struct
|
||||
* or expiring timer
|
||||
* TASK_UNINTERRUPTIBLE: Domain is blocked but may not be woken up by an
|
||||
* arbitrary event or timer.
|
||||
* TASK_STOPPED: Domain is sopped.
|
||||
* TASK_STOPPED: Domain is stopped.
|
||||
* TASK_DYING: Domain is about to cross over to the land of the dead.
|
||||
*/
|
||||
|
||||
|
||||