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
51 int                 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
59 int                 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
67 int                 db_tee_msgbuf = DDB_TEE_MSGBUF;
68 
69 #ifndef DDB_PANICSTACKFRAMES
70 #define DDB_PANICSTACKFRAMES 65535
71 #endif
72 int                 db_panicstackframes = DDB_PANICSTACKFRAMES;
73 
74 #ifndef DDB_DUMPSTACK
75 #define DDB_DUMPSTACK 1
76 #endif
77 int                 db_dumpstack = DDB_DUMPSTACK;
78 
79 static int          db_rw_internal_variable(const struct db_variable *, db_expr_t *,
80                         int);
81 static int          db_find_variable(const struct db_variable **);
82 
83 /* XXX must all be ints for sysctl. */
84 const 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 };
146 const 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  */
151 static int
db_rw_internal_variable(const struct db_variable * vp,db_expr_t * valp,int rw)152 db_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
166 SYSCTL_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 
239 int
db_find_variable(const struct db_variable ** varp)240 db_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 
267 int
db_get_variable(db_expr_t * valuep)268 db_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 
280 int
db_set_variable(db_expr_t value)281 db_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 
294 void
db_read_variable(const struct db_variable * vp,db_expr_t * valuep)295 db_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 
305 void
db_write_variable(const struct db_variable * vp,db_expr_t * valuep)306 db_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*/
317 void
db_set_cmd(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)318 db_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