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$");
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;
61267992ShselaskySYSCTL_INT(_debug, OID_AUTO, gdbcons, CTLFLAG_RWTUN, &gdbcons_enable,
62267992Shselasky	    0, "copy console messages to GDB");
63157059Ssam
64157059Ssamstatic void
65157059Ssamgdb_cnprobe(struct consdev *cp)
66157059Ssam{
67157059Ssam	sprintf(cp->cn_name, "gdb");
68157059Ssam	cp->cn_pri = CN_LOW;		/* XXX no way to say "write only" */
69157059Ssam}
70157059Ssam
71157059Ssamstatic void
72157059Ssamgdb_cninit(struct consdev *cp)
73157059Ssam{
74157059Ssam	struct gdbcons *c = &state;
75157059Ssam
76157059Ssam	/* setup tx buffer and callout */
77157059Ssam	if (c->npending == -1) {
78157059Ssam		c->npending = 0;
79283291Sjkim		callout_init(&c->flush, 1);
80157059Ssam		cp->cn_arg = c;
81157059Ssam	}
82157059Ssam}
83157059Ssam
84158960Sphkstatic void
85158960Sphkgdb_cnterm(struct consdev *cp)
86158960Sphk{
87158960Sphk}
88158960Sphk
89228631Savgstatic void
90228631Savggdb_cngrab(struct consdev *cp)
91228631Savg{
92228631Savg}
93228631Savg
94228631Savgstatic void
95228631Savggdb_cnungrab(struct consdev *cp)
96228631Savg{
97228631Savg}
98228631Savg
99157059Ssamstatic int
100158960Sphkgdb_cngetc(struct consdev *cp)
101157059Ssam{
102157059Ssam	return -1;
103157059Ssam}
104157059Ssam
105157059Ssamstatic void
106157059Ssamgdb_tx_puthex(int c)
107157059Ssam{
108157059Ssam	const char *hex = "0123456789abcdef";
109157059Ssam
110157059Ssam	gdb_tx_char(hex[(c>>4)&0xf]);
111157059Ssam	gdb_tx_char(hex[(c>>0)&0xf]);
112157059Ssam}
113157059Ssam
114157059Ssamstatic void
115157059Ssamgdb_cnflush(void *arg)
116157059Ssam{
117157059Ssam	struct gdbcons *gc = arg;
118157059Ssam	int i;
119157059Ssam
120157059Ssam	gdb_tx_begin('O');
121157059Ssam	for (i = 0; i < gc->npending; i++)
122157059Ssam		gdb_tx_puthex(gc->buf[i]);
123157059Ssam	gdb_tx_end();
124157059Ssam	gc->npending = 0;
125157059Ssam}
126157059Ssam
127157059Ssam/*
128157059Ssam * This glop is to figure out when it's safe to use callouts
129157059Ssam * to defer buffer flushing.  There's probably a better way
130157059Ssam * and/or an earlier point in the boot process when it's ok.
131157059Ssam */
132157059Ssamstatic int calloutok = 0;
133157059Ssamstatic void
134157059Ssamoktousecallout(void *data __unused)
135157059Ssam{
136157059Ssam	calloutok = 1;
137157059Ssam}
138253604SavgSYSINIT(gdbhack, SI_SUB_LAST, SI_ORDER_MIDDLE, oktousecallout, NULL);
139157059Ssam
140157059Ssamstatic void
141157059Ssamgdb_cnputc(struct consdev *cp, int c)
142157059Ssam{
143157059Ssam	struct gdbcons *gc;
144157059Ssam
145157059Ssam	if (gdbcons_enable && gdb_cur != NULL && gdb_listening) {
146157059Ssam		gc = cp->cn_arg;
147157059Ssam		if (gc->npending != 0) {
148157059Ssam			/*
149157059Ssam			 * Cancel any pending callout and flush the
150157059Ssam			 * buffer if there's no space for this byte.
151157059Ssam			 */
152157059Ssam			if (calloutok)
153157059Ssam				callout_stop(&gc->flush);
154157059Ssam			if (gc->npending == sizeof(gc->buf))
155157059Ssam				gdb_cnflush(gc);
156157059Ssam		}
157157059Ssam		gc->buf[gc->npending++] = c;
158157059Ssam		/*
159157059Ssam		 * Flush on end of line; this is especially helpful
160157059Ssam		 * during boot when we don't have callouts to flush
161157059Ssam		 * the buffer.  Otherwise we defer flushing; a 1/4
162157059Ssam		 * second is a guess.
163157059Ssam		 */
164157059Ssam		if (c == '\n')
165157059Ssam			gdb_cnflush(gc);
166157059Ssam		else if (calloutok)
167157059Ssam			callout_reset(&gc->flush, hz/4, gdb_cnflush, gc);
168157059Ssam	}
169157059Ssam}
170157059Ssam
171158960SphkCONSOLE_DRIVER(gdb);
172157059Ssam
173157059Ssam/*
174157059Ssam * Our console device only gets attached if the system is booted
175157059Ssam * with RB_MULTIPLE set so gdb_init also calls us to attach the
176157059Ssam * console so we're setup regardless.
177157059Ssam */
178157059Ssamvoid
179157059Ssamgdb_consinit(void)
180157059Ssam{
181157059Ssam	gdb_cnprobe(&gdb_consdev);
182157059Ssam	gdb_cninit(&gdb_consdev);
183157059Ssam	cnadd(&gdb_consdev);
184157059Ssam}
185