1/*	$NetBSD: db_variables.c,v 1.47 2020/03/10 15:58:37 christos Exp $	*/
2
3/*
4 * Mach Operating System
5 * Copyright (c) 1991,1990 Carnegie Mellon University
6 * All Rights Reserved.
7 *
8 * Permission to use, copy, modify and distribute this software and its
9 * documentation is hereby granted, provided that both the copyright
10 * notice and this permission notice appear in all copies of the
11 * software, derivative works or modified versions, and any portions
12 * thereof, and that both notices appear in supporting documentation.
13 *
14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17 *
18 * Carnegie Mellon requests users of this software to return to
19 *
20 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21 *  School of Computer Science
22 *  Carnegie Mellon University
23 *  Pittsburgh PA 15213-3890
24 *
25 * any improvements or extensions that they make and grant Carnegie the
26 * rights to redistribute these changes.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: db_variables.c,v 1.47 2020/03/10 15:58:37 christos Exp $");
31
32#ifdef _KERNEL_OPT
33#include "opt_ddbparam.h"
34#endif
35
36#include <sys/param.h>
37#include <sys/proc.h>
38#include <uvm/uvm_extern.h>
39#include <sys/sysctl.h>
40
41#include <ddb/ddb.h>
42#include <ddb/ddbvar.h>
43
44/*
45 * If this is non-zero, the DDB will be entered when the system
46 * panics.  Initialize it so that it's patchable.
47 */
48#ifndef DDB_ONPANIC
49#define DDB_ONPANIC	1
50#endif
51int		db_onpanic = DDB_ONPANIC;
52
53/*
54 * Can  DDB can be entered from the console?
55 */
56#ifndef	DDB_FROMCONSOLE
57#define	DDB_FROMCONSOLE 1
58#endif
59int		db_fromconsole = DDB_FROMCONSOLE;
60
61/*
62 * Output DDB output to the message buffer?
63 */
64#ifndef DDB_TEE_MSGBUF
65#define DDB_TEE_MSGBUF 0
66#endif
67int		db_tee_msgbuf = DDB_TEE_MSGBUF;
68
69#ifndef DDB_PANICSTACKFRAMES
70#define DDB_PANICSTACKFRAMES 65535
71#endif
72int		db_panicstackframes = DDB_PANICSTACKFRAMES;
73
74#ifndef DDB_DUMPSTACK
75#define DDB_DUMPSTACK 1
76#endif
77int 		db_dumpstack = DDB_DUMPSTACK;
78
79static int	db_rw_internal_variable(const struct db_variable *, db_expr_t *,
80		    int);
81static int	db_find_variable(const struct db_variable **);
82
83/* XXX must all be ints for sysctl. */
84const struct db_variable db_vars[] = {
85	{
86		.name = "fromconsole",
87		.valuep = &db_fromconsole,
88		.fcn = db_rw_internal_variable,
89		.modif = NULL,
90	},
91	{
92		.name = "maxoff",
93		.valuep = &db_maxoff,
94		.fcn = db_rw_internal_variable,
95		.modif = NULL,
96	},
97	{
98		.name = "maxwidth",
99		.valuep = &db_max_width,
100		.fcn = db_rw_internal_variable,
101		.modif = NULL,
102	},
103	{
104		.name = "lines",
105		.valuep = &db_max_line,
106		.fcn = db_rw_internal_variable,
107		.modif = NULL,
108	},
109	{
110		.name = "onpanic",
111		.valuep = &db_onpanic,
112		.fcn = db_rw_internal_variable,
113		.modif = NULL,
114	},
115	{
116		.name = "panicstackframes",
117		.valuep = &db_panicstackframes,
118		.fcn = db_rw_internal_variable,
119		.modif = NULL,
120	},
121	{
122		.name = "dumpstack",
123		.valuep = &db_dumpstack,
124		.fcn = db_rw_internal_variable,
125		.modif = NULL,
126	},
127	{
128		.name = "radix",
129		.valuep = &db_radix,
130		.fcn = db_rw_internal_variable,
131		.modif = NULL,
132	},
133	{
134		.name = "tabstops",
135		.valuep = &db_tab_stop_width,
136		.fcn = db_rw_internal_variable,
137		.modif = NULL,
138	},
139	{
140		.name = "tee_msgbuf",
141		.valuep = &db_tee_msgbuf,
142		.fcn = db_rw_internal_variable,
143		.modif = NULL,
144	},
145};
146const struct db_variable * const db_evars = db_vars + __arraycount(db_vars);
147
148/*
149 * ddb command line access to the DDB variables defined above.
150 */
151static int
152db_rw_internal_variable(const struct db_variable *vp, db_expr_t *valp, int rw)
153{
154
155	if (rw == DB_VAR_GET)
156		*valp = *(int *)vp->valuep;
157	else
158		*(int *)vp->valuep = *valp;
159	return (0);
160}
161
162/*
163 * sysctl(3) access to the DDB variables defined above.
164 */
165#ifdef _KERNEL
166SYSCTL_SETUP(sysctl_ddb_setup, "sysctl ddb subtree setup")
167{
168
169	sysctl_createv(clog, 0, NULL, NULL,
170		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
171		       CTLTYPE_INT, "radix",
172		       SYSCTL_DESCR("Input and output radix"),
173		       NULL, 0, &db_radix, 0,
174		       CTL_DDB, DDBCTL_RADIX, CTL_EOL);
175	sysctl_createv(clog, 0, NULL, NULL,
176		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
177		       CTLTYPE_INT, "maxoff",
178		       SYSCTL_DESCR("Maximum symbol offset"),
179		       NULL, 0, &db_maxoff, 0,
180		       CTL_DDB, DDBCTL_MAXOFF, CTL_EOL);
181	sysctl_createv(clog, 0, NULL, NULL,
182		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
183		       CTLTYPE_INT, "maxwidth",
184		       SYSCTL_DESCR("Maximum output line width"),
185		       NULL, 0, &db_max_width, 0,
186		       CTL_DDB, DDBCTL_MAXWIDTH, CTL_EOL);
187	sysctl_createv(clog, 0, NULL, NULL,
188		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
189		       CTLTYPE_INT, "lines",
190		       SYSCTL_DESCR("Number of display lines"),
191		       NULL, 0, &db_max_line, 0,
192		       CTL_DDB, DDBCTL_LINES, CTL_EOL);
193	sysctl_createv(clog, 0, NULL, NULL,
194		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
195		       CTLTYPE_INT, "tabstops",
196		       SYSCTL_DESCR("Output tab width"),
197		       NULL, 0, &db_tab_stop_width, 0,
198		       CTL_DDB, DDBCTL_TABSTOPS, CTL_EOL);
199	sysctl_createv(clog, 0, NULL, NULL,
200		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
201		       CTLTYPE_INT, "onpanic",
202		       SYSCTL_DESCR("Whether to enter ddb on a kernel panic"),
203		       NULL, 0, &db_onpanic, 0,
204		       CTL_DDB, DDBCTL_ONPANIC, CTL_EOL);
205	sysctl_createv(clog, 0, NULL, NULL,
206		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
207		       CTLTYPE_INT, "fromconsole",
208		       SYSCTL_DESCR("Whether ddb can be entered from the "
209				    "console"),
210		       NULL, 0, &db_fromconsole, 0,
211		       CTL_DDB, DDBCTL_FROMCONSOLE, CTL_EOL);
212	sysctl_createv(clog, 0, NULL, NULL,
213		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
214		       CTLTYPE_INT, "tee_msgbuf",
215		       SYSCTL_DESCR("Whether to tee ddb output to the msgbuf"),
216		       NULL, 0, &db_tee_msgbuf, 0,
217		       CTL_DDB, CTL_CREATE, CTL_EOL);
218	sysctl_createv(clog, 0, NULL, NULL,
219		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
220		       CTLTYPE_STRING, "commandonenter",
221		       SYSCTL_DESCR("Command to be executed on each ddb enter"),
222		       NULL, 0, db_cmd_on_enter, DB_LINE_MAXLEN,
223		       CTL_DDB, CTL_CREATE, CTL_EOL);
224	sysctl_createv(clog, 0, NULL, NULL,
225		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
226		       CTLTYPE_INT, "panicstackframes",
227		       SYSCTL_DESCR("Number of stack frames to print on panic"),
228		       NULL, 0, &db_panicstackframes, 0,
229		       CTL_DDB, CTL_CREATE, CTL_EOL);
230	sysctl_createv(clog, 0, NULL, NULL,
231		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
232		       CTLTYPE_INT, "dumpstack",
233		       SYSCTL_DESCR("On panic print stack trace"),
234		       NULL, 0, &db_dumpstack, 0,
235		       CTL_DDB, CTL_CREATE, CTL_EOL);
236}
237#endif	/* _KERNEL */
238
239int
240db_find_variable(const struct db_variable **varp)
241{
242	int	t;
243	const struct db_variable *vp;
244
245	t = db_read_token();
246	if (t == tIDENT) {
247		for (vp = db_vars; vp < db_evars; vp++) {
248			if (!strcmp(db_tok_string, vp->name)) {
249				*varp = vp;
250				return (1);
251			}
252		}
253#ifdef _KERNEL
254		for (vp = db_regs; vp < db_eregs; vp++) {
255			if (!strcmp(db_tok_string, vp->name)) {
256				*varp = vp;
257				return (1);
258			}
259		}
260#endif
261	}
262	db_error("Unknown variable\n");
263	/*NOTREACHED*/
264	return 0;
265}
266
267int
268db_get_variable(db_expr_t *valuep)
269{
270	const struct db_variable *vp;
271
272	if (!db_find_variable(&vp))
273		return (0);
274
275	db_read_variable(vp, valuep);
276
277	return (1);
278}
279
280int
281db_set_variable(db_expr_t value)
282{
283	const struct db_variable *vp;
284
285	if (!db_find_variable(&vp))
286		return (0);
287
288	db_write_variable(vp, &value);
289
290	return (1);
291}
292
293
294void
295db_read_variable(const struct db_variable *vp, db_expr_t *valuep)
296{
297	int (*func)(const struct db_variable *, db_expr_t *, int) = vp->fcn;
298
299	if (func == FCN_NULL)
300		*valuep = *(db_expr_t *)vp->valuep;
301	else
302		(*func)(vp, valuep, DB_VAR_GET);
303}
304
305void
306db_write_variable(const struct db_variable *vp, db_expr_t *valuep)
307{
308	int (*func)(const struct db_variable *, db_expr_t *, int) = vp->fcn;
309
310	if (func == FCN_NULL)
311		*(db_expr_t *)vp->valuep = *valuep;
312	else
313		(*func)(vp, valuep, DB_VAR_SET);
314}
315
316/*ARGSUSED*/
317void
318db_set_cmd(db_expr_t addr, bool have_addr,
319    db_expr_t count, const char *modif)
320{
321	db_expr_t	value;
322	db_expr_t	old_value;
323	const struct db_variable *vp = NULL;	/* XXX: GCC */
324	int	t;
325
326	t = db_read_token();
327	if (t != tDOLLAR) {
328		db_error("Unknown variable\n");
329		/*NOTREACHED*/
330	}
331	if (!db_find_variable(&vp)) {
332		db_error("Unknown variable\n");
333		/*NOTREACHED*/
334	}
335
336	t = db_read_token();
337	if (t != tEQ)
338		db_unread_token(t);
339
340	if (!db_expression(&value)) {
341		db_error("No value\n");
342		/*NOTREACHED*/
343	}
344	if (db_read_token() != tEOL) {
345		db_error("?\n");
346		/*NOTREACHED*/
347	}
348
349	db_read_variable(vp, &old_value);
350	db_printf("$%s\t\t%s = ", vp->name, db_num_to_str(old_value));
351	db_printf("%s\n", db_num_to_str(value));
352	db_write_variable(vp, &value);
353}
354