gdb_cons.c revision 283291
1177633Sdfr/*-
2177633Sdfr * Copyright (c) 2006 Sam Leffler
3177633Sdfr * All rights reserved.
4177633Sdfr *
5177633Sdfr * Redistribution and use in source and binary forms, with or without
6177633Sdfr * modification, are permitted provided that the following conditions
7177633Sdfr * are met:
8177633Sdfr *
9177633Sdfr * 1. Redistributions of source code must retain the above copyright
10177633Sdfr *    notice, this list of conditions and the following disclaimer.
11177633Sdfr * 2. Redistributions in binary form must reproduce the above copyright
12177633Sdfr *    notice, this list of conditions and the following disclaimer in the
13177633Sdfr *    documentation and/or other materials provided with the distribution.
14177633Sdfr *
15177633Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
16177633Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17177633Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18177633Sdfr * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
19177633Sdfr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20177633Sdfr * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21177633Sdfr * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22177633Sdfr * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23177633Sdfr * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24177633Sdfr * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25177633Sdfr */
26177633Sdfr
27177633Sdfr/*
28177633Sdfr * Support for redirecting console msgs to gdb.  We register
29177633Sdfr * a pseudo console to hook cnputc and send stuff to the gdb
30177633Sdfr * port.  The only trickiness here is buffering output so this
31177633Sdfr * isn't dog slow.
32177633Sdfr */
33177633Sdfr
34177633Sdfr#include <sys/cdefs.h>
35177633Sdfr__FBSDID("$FreeBSD: head/sys/gdb/gdb_cons.c 283291 2015-05-22 17:05:21Z jkim $");
36177633Sdfr
37177633Sdfr#include <sys/param.h>
38177633Sdfr#include <sys/systm.h>
39177633Sdfr#include <sys/cons.h>
40177633Sdfr#include <sys/kdb.h>
41177633Sdfr#include <sys/kernel.h>
42177633Sdfr#include <sys/malloc.h>
43177633Sdfr#include <sys/reboot.h>
44177633Sdfr#include <sys/sysctl.h>
45177685Sdfr
46177633Sdfr#include <machine/gdb_machdep.h>
47177633Sdfr#include <machine/kdb.h>
48177633Sdfr
49177633Sdfr#include <gdb/gdb.h>
50177633Sdfr#include <gdb/gdb_int.h>
51177633Sdfr
52177633Sdfrstruct gdbcons {
53177633Sdfr	int	npending;
54177633Sdfr	/* /2 for hex conversion, -6 for protocol glue */
55177633Sdfr	char	buf[GDB_BUFSZ/2 - 6];
56177633Sdfr	struct callout flush;
57177633Sdfr};
58177633Sdfrstatic struct gdbcons state = { -1 };
59177633Sdfr
60177633Sdfrstatic	int gdbcons_enable = 0;
61177633SdfrSYSCTL_INT(_debug, OID_AUTO, gdbcons, CTLFLAG_RWTUN, &gdbcons_enable,
62177633Sdfr	    0, "copy console messages to GDB");
63177633Sdfr
64177633Sdfrstatic void
65177633Sdfrgdb_cnprobe(struct consdev *cp)
66177633Sdfr{
67177633Sdfr	sprintf(cp->cn_name, "gdb");
68177633Sdfr	cp->cn_pri = CN_LOW;		/* XXX no way to say "write only" */
69177633Sdfr}
70177633Sdfr
71177633Sdfrstatic void
72177633Sdfrgdb_cninit(struct consdev *cp)
73177633Sdfr{
74177633Sdfr	struct gdbcons *c = &state;
75177633Sdfr
76177633Sdfr	/* setup tx buffer and callout */
77177633Sdfr	if (c->npending == -1) {
78177633Sdfr		c->npending = 0;
79177633Sdfr		callout_init(&c->flush, 1);
80177633Sdfr		cp->cn_arg = c;
81177633Sdfr	}
82177633Sdfr}
83177633Sdfr
84177633Sdfrstatic void
85177633Sdfrgdb_cnterm(struct consdev *cp)
86177633Sdfr{
87177633Sdfr}
88177633Sdfr
89177633Sdfrstatic void
90177633Sdfrgdb_cngrab(struct consdev *cp)
91177633Sdfr{
92177633Sdfr}
93177633Sdfr
94177633Sdfrstatic void
95177633Sdfrgdb_cnungrab(struct consdev *cp)
96177633Sdfr{
97177633Sdfr}
98177633Sdfr
99177633Sdfrstatic int
100177633Sdfrgdb_cngetc(struct consdev *cp)
101177633Sdfr{
102177633Sdfr	return -1;
103177633Sdfr}
104177633Sdfr
105177633Sdfrstatic void
106177633Sdfrgdb_tx_puthex(int c)
107177633Sdfr{
108177633Sdfr	const char *hex = "0123456789abcdef";
109177633Sdfr
110177633Sdfr	gdb_tx_char(hex[(c>>4)&0xf]);
111177633Sdfr	gdb_tx_char(hex[(c>>0)&0xf]);
112177633Sdfr}
113177633Sdfr
114177633Sdfrstatic void
115177633Sdfrgdb_cnflush(void *arg)
116177633Sdfr{
117177633Sdfr	struct gdbcons *gc = arg;
118177633Sdfr	int i;
119177633Sdfr
120177633Sdfr	gdb_tx_begin('O');
121177633Sdfr	for (i = 0; i < gc->npending; i++)
122177633Sdfr		gdb_tx_puthex(gc->buf[i]);
123177633Sdfr	gdb_tx_end();
124177633Sdfr	gc->npending = 0;
125177633Sdfr}
126177633Sdfr
127177633Sdfr/*
128177633Sdfr * This glop is to figure out when it's safe to use callouts
129177633Sdfr * to defer buffer flushing.  There's probably a better way
130177633Sdfr * and/or an earlier point in the boot process when it's ok.
131177633Sdfr */
132177633Sdfrstatic int calloutok = 0;
133177633Sdfrstatic void
134177633Sdfroktousecallout(void *data __unused)
135177633Sdfr{
136177633Sdfr	calloutok = 1;
137177633Sdfr}
138177633SdfrSYSINIT(gdbhack, SI_SUB_LAST, SI_ORDER_MIDDLE, oktousecallout, NULL);
139177633Sdfr
140177633Sdfrstatic void
141177633Sdfrgdb_cnputc(struct consdev *cp, int c)
142178112Sdfr{
143178112Sdfr	struct gdbcons *gc;
144178112Sdfr
145177633Sdfr	if (gdbcons_enable && gdb_cur != NULL && gdb_listening) {
146177633Sdfr		gc = cp->cn_arg;
147177633Sdfr		if (gc->npending != 0) {
148177633Sdfr			/*
149177633Sdfr			 * Cancel any pending callout and flush the
150177633Sdfr			 * buffer if there's no space for this byte.
151177633Sdfr			 */
152177633Sdfr			if (calloutok)
153177633Sdfr				callout_stop(&gc->flush);
154177633Sdfr			if (gc->npending == sizeof(gc->buf))
155177633Sdfr				gdb_cnflush(gc);
156177633Sdfr		}
157177633Sdfr		gc->buf[gc->npending++] = c;
158177633Sdfr		/*
159177633Sdfr		 * Flush on end of line; this is especially helpful
160177633Sdfr		 * during boot when we don't have callouts to flush
161177633Sdfr		 * the buffer.  Otherwise we defer flushing; a 1/4
162177633Sdfr		 * second is a guess.
163177633Sdfr		 */
164177633Sdfr		if (c == '\n')
165177633Sdfr			gdb_cnflush(gc);
166177633Sdfr		else if (calloutok)
167177633Sdfr			callout_reset(&gc->flush, hz/4, gdb_cnflush, gc);
168177633Sdfr	}
169178112Sdfr}
170178112Sdfr
171178112SdfrCONSOLE_DRIVER(gdb);
172178112Sdfr
173178112Sdfr/*
174177633Sdfr * Our console device only gets attached if the system is booted
175177633Sdfr * with RB_MULTIPLE set so gdb_init also calls us to attach the
176177633Sdfr * console so we're setup regardless.
177177633Sdfr */
178177633Sdfrvoid
179177633Sdfrgdb_consinit(void)
180177633Sdfr{
181177633Sdfr	gdb_cnprobe(&gdb_consdev);
182177633Sdfr	gdb_cninit(&gdb_consdev);
183177633Sdfr	cnadd(&gdb_consdev);
184177633Sdfr}
185177633Sdfr