gdb_cons.c revision 157059
1157059Ssam/*-
2157059Ssam * Copyright (c) 2006 Sam Leffler
3157059Ssam * All rights reserved.
4157059Ssam *
5157059Ssam * Redistribution and use in source and binary forms, with or without
6157059Ssam * modification, are permitted provided that the following conditions
7157059Ssam * are met:
8157059Ssam *
9157059Ssam * 1. Redistributions of source code must retain the above copyright
10157059Ssam *    notice, this list of conditions and the following disclaimer.
11157059Ssam * 2. Redistributions in binary form must reproduce the above copyright
12157059Ssam *    notice, this list of conditions and the following disclaimer in the
13157059Ssam *    documentation and/or other materials provided with the distribution.
14157059Ssam *
15157059Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
16157059Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17157059Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18157059Ssam * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
19157059Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20157059Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21157059Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22157059Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23157059Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24157059Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25157059Ssam */
26157059Ssam
27157059Ssam/*
28157059Ssam * Support for redirecting console msgs to gdb.  We register
29157059Ssam * a pseudo console to hook cnputc and send stuff to the gdb
30157059Ssam * port.  The only trickiness here is buffering output so this
31157059Ssam * isn't dog slow.
32157059Ssam */
33157059Ssam
34157059Ssam#include <sys/cdefs.h>
35157059Ssam__FBSDID("$FreeBSD: head/sys/gdb/gdb_cons.c 157059 2006-03-23 23:06:14Z sam $");
36157059Ssam
37157059Ssam#include <sys/param.h>
38157059Ssam#include <sys/systm.h>
39157059Ssam#include <sys/cons.h>
40157059Ssam#include <sys/kdb.h>
41157059Ssam#include <sys/kernel.h>
42157059Ssam#include <sys/malloc.h>
43157059Ssam#include <sys/reboot.h>
44157059Ssam#include <sys/sysctl.h>
45157059Ssam
46157059Ssam#include <machine/gdb_machdep.h>
47157059Ssam#include <machine/kdb.h>
48157059Ssam
49157059Ssam#include <gdb/gdb.h>
50157059Ssam#include <gdb/gdb_int.h>
51157059Ssam
52157059Ssamstruct gdbcons {
53157059Ssam	int	npending;
54157059Ssam	/* /2 for hex conversion, -6 for protocol glue */
55157059Ssam	char	buf[GDB_BUFSZ/2 - 6];
56157059Ssam	struct callout flush;
57157059Ssam};
58157059Ssamstatic struct gdbcons state = { -1 };
59157059Ssam
60157059Ssamstatic	int gdbcons_enable = 0;
61157059SsamSYSCTL_INT(_debug, OID_AUTO, gdbcons, CTLFLAG_RW, &gdbcons_enable,
62157059Ssam	    0, "copy console messages to gdb");
63157059SsamTUNABLE_INT("debug.gdbcons", &gdbcons_enable);
64157059Ssam
65157059Ssamstatic void
66157059Ssamgdb_cnprobe(struct consdev *cp)
67157059Ssam{
68157059Ssam	sprintf(cp->cn_name, "gdb");
69157059Ssam	cp->cn_pri = CN_LOW;		/* XXX no way to say "write only" */
70157059Ssam}
71157059Ssam
72157059Ssamstatic void
73157059Ssamgdb_cninit(struct consdev *cp)
74157059Ssam{
75157059Ssam	struct gdbcons *c = &state;
76157059Ssam
77157059Ssam	/* setup tx buffer and callout */
78157059Ssam	if (c->npending == -1) {
79157059Ssam		c->npending = 0;
80157059Ssam		callout_init(&c->flush, CALLOUT_MPSAFE);
81157059Ssam		cp->cn_arg = c;
82157059Ssam	}
83157059Ssam}
84157059Ssam
85157059Ssamstatic int
86157059Ssamgdb_nogetc(struct consdev *cp)
87157059Ssam{
88157059Ssam	return -1;
89157059Ssam}
90157059Ssam
91157059Ssamstatic void
92157059Ssamgdb_tx_puthex(int c)
93157059Ssam{
94157059Ssam	const char *hex = "0123456789abcdef";
95157059Ssam
96157059Ssam	gdb_tx_char(hex[(c>>4)&0xf]);
97157059Ssam	gdb_tx_char(hex[(c>>0)&0xf]);
98157059Ssam}
99157059Ssam
100157059Ssamstatic void
101157059Ssamgdb_cnflush(void *arg)
102157059Ssam{
103157059Ssam	struct gdbcons *gc = arg;
104157059Ssam	int i;
105157059Ssam
106157059Ssam	gdb_tx_begin('O');
107157059Ssam	for (i = 0; i < gc->npending; i++)
108157059Ssam		gdb_tx_puthex(gc->buf[i]);
109157059Ssam	gdb_tx_end();
110157059Ssam	gc->npending = 0;
111157059Ssam}
112157059Ssam
113157059Ssam/*
114157059Ssam * This glop is to figure out when it's safe to use callouts
115157059Ssam * to defer buffer flushing.  There's probably a better way
116157059Ssam * and/or an earlier point in the boot process when it's ok.
117157059Ssam */
118157059Ssamstatic int calloutok = 0;
119157059Ssamstatic void
120157059Ssamoktousecallout(void *data __unused)
121157059Ssam{
122157059Ssam	calloutok = 1;
123157059Ssam}
124157059SsamSYSINIT(gdbhack, SI_SUB_RUN_SCHEDULER, SI_ORDER_ANY, oktousecallout, NULL)
125157059Ssam
126157059Ssamstatic void
127157059Ssamgdb_cnputc(struct consdev *cp, int c)
128157059Ssam{
129157059Ssam	struct gdbcons *gc;
130157059Ssam
131157059Ssam	if (gdbcons_enable && gdb_cur != NULL && gdb_listening) {
132157059Ssam		gc = cp->cn_arg;
133157059Ssam		if (gc->npending != 0) {
134157059Ssam			/*
135157059Ssam			 * Cancel any pending callout and flush the
136157059Ssam			 * buffer if there's no space for this byte.
137157059Ssam			 */
138157059Ssam			if (calloutok)
139157059Ssam				callout_stop(&gc->flush);
140157059Ssam			if (gc->npending == sizeof(gc->buf))
141157059Ssam				gdb_cnflush(gc);
142157059Ssam		}
143157059Ssam		gc->buf[gc->npending++] = c;
144157059Ssam		/*
145157059Ssam		 * Flush on end of line; this is especially helpful
146157059Ssam		 * during boot when we don't have callouts to flush
147157059Ssam		 * the buffer.  Otherwise we defer flushing; a 1/4
148157059Ssam		 * second is a guess.
149157059Ssam		 */
150157059Ssam		if (c == '\n')
151157059Ssam			gdb_cnflush(gc);
152157059Ssam		else if (calloutok)
153157059Ssam			callout_reset(&gc->flush, hz/4, gdb_cnflush, gc);
154157059Ssam	}
155157059Ssam}
156157059Ssam
157157059Ssam/* NB: no get interface, we supply nogetc for checkc too */
158157059SsamCONS_DRIVER(gdb, gdb_cnprobe, gdb_cninit, NULL, gdb_nogetc, gdb_nogetc,
159157059Ssam	gdb_cnputc, NULL);
160157059Ssam
161157059Ssam/*
162157059Ssam * Our console device only gets attached if the system is booted
163157059Ssam * with RB_MULTIPLE set so gdb_init also calls us to attach the
164157059Ssam * console so we're setup regardless.
165157059Ssam */
166157059Ssamvoid
167157059Ssamgdb_consinit(void)
168157059Ssam{
169157059Ssam	gdb_cnprobe(&gdb_consdev);
170157059Ssam	gdb_cninit(&gdb_consdev);
171157059Ssam	cnadd(&gdb_consdev);
172157059Ssam}
173