xref: /illumos-gate/usr/src/cmd/mdb/common/kmdb/kmdb_dpi.c (revision 4eaa471005973e11a6110b69fe990530b3b95a38)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * The DPI, or debugger/PROM interface, is used to isolate the debugger from the
31  * means by which we use the PROM to control the machine.
32  */
33 
34 #include <sys/types.h>
35 #include <setjmp.h>
36 
37 #include <kmdb/kmdb_dpi_impl.h>
38 #include <kmdb/kmdb_kdi.h>
39 #include <kmdb/kmdb_auxv.h>
40 #include <kmdb/kmdb_wr_impl.h>
41 #include <kmdb/kmdb_module.h>
42 #include <kmdb/kmdb_start.h>
43 #include <kmdb/kmdb_asmutil.h>
44 #include <mdb/mdb_debug.h>
45 #include <mdb/mdb_err.h>
46 #include <mdb/mdb_string.h>
47 #include <mdb/mdb.h>
48 
49 jmp_buf *kmdb_dpi_fault_pcb;
50 jmp_buf kmdb_dpi_resume_pcb;
51 jmp_buf kmdb_dpi_entry_pcb;
52 
53 static int kmdb_dpi_state;
54 static int kmdb_dpi_state_why;
55 
56 uint_t kmdb_dpi_resume_requested;
57 uint_t kmdb_dpi_switch_target = (uint_t)-1;
58 
59 /* Used by the style-specific resume interfaces to signal the driver */
60 void (*kmdb_dpi_wrintr_fire)(void);
61 
62 int
63 kmdb_dpi_init(kmdb_auxv_t *kav)
64 {
65 	kmdb_dpi_state = DPI_STATE_INIT;
66 	kmdb_dpi_resume_requested = 0;
67 	kmdb_dpi_wrintr_fire = kav->kav_wrintr_fire;
68 
69 	mdb.m_dpi = &kmdb_dpi_ops;
70 	return (mdb.m_dpi->dpo_init(kav));
71 }
72 
73 /*ARGSUSED1*/
74 void
75 kmdb_activate(kdi_debugvec_t **dvecp, uint_t flags)
76 {
77 	mdb.m_dpi->dpo_debugger_activate(dvecp, flags);
78 }
79 
80 void
81 kmdb_deactivate(void)
82 {
83 	mdb.m_dpi->dpo_debugger_deactivate();
84 }
85 
86 int
87 kmdb_dpi_reenter(void)
88 {
89 	int cmd;
90 
91 	kmdb_kdi_system_claim();
92 
93 	if ((cmd = setjmp(kmdb_dpi_entry_pcb)) == 0) {
94 		/* Direct entry from the driver */
95 		if (kmdb_dpi_resume_requested)
96 			longjmp(kmdb_dpi_resume_pcb, 1);
97 
98 		kmdb_first_start();
99 
100 		fail("kmdb_first_start returned");
101 		/*NOTREACHED*/
102 	}
103 
104 	mdb_dprintf(MDB_DBG_DPI, "returning to driver - cmd %d%s\n", cmd,
105 	    (kmdb_dpi_work_required() ? " (work required)" : ""));
106 
107 	kmdb_kdi_system_release();
108 
109 	membar_producer();
110 
111 	/*
112 	 * The debugger wants us to do something - it returned a command
113 	 * via the setjmp().  The driver will know what to do with the
114 	 * command.
115 	 */
116 	return (cmd);
117 }
118 
119 void
120 kmdb_dpi_enter_mon(void)
121 {
122 	mdb.m_dpi->dpo_enter_mon();
123 }
124 
125 void
126 kmdb_dpi_modchg_register(void (*func)(struct modctl *, int))
127 {
128 	mdb.m_dpi->dpo_modchg_register(func);
129 }
130 
131 void
132 kmdb_dpi_modchg_cancel(void)
133 {
134 	mdb.m_dpi->dpo_modchg_cancel();
135 }
136 
137 int
138 kmdb_dpi_get_cpu_state(int cpuid)
139 {
140 	return (mdb.m_dpi->dpo_get_cpu_state(cpuid));
141 }
142 
143 int
144 kmdb_dpi_get_master_cpuid(void)
145 {
146 	return (mdb.m_dpi->dpo_get_master_cpuid());
147 }
148 
149 const mdb_tgt_gregset_t *
150 kmdb_dpi_get_gregs(int cpuid)
151 {
152 	return (mdb.m_dpi->dpo_get_gregs(cpuid));
153 }
154 
155 jmp_buf *
156 kmdb_dpi_set_fault_hdlr(jmp_buf *jb)
157 {
158 	jmp_buf *oldpcb = kmdb_dpi_fault_pcb;
159 
160 	kmdb_dpi_fault_pcb = jb;
161 
162 	return (oldpcb);
163 }
164 
165 void
166 kmdb_dpi_restore_fault_hdlr(jmp_buf *jb)
167 {
168 	(void) kmdb_dpi_set_fault_hdlr(jb);
169 }
170 
171 /*
172  * Used to tell the driver that it needs to do work after the resume.
173  *
174  * CAUTION: This routine may be called *after* mdb_destroy
175  */
176 int
177 kmdb_dpi_work_required(void)
178 {
179 	return (kmdb_kdi_get_unload_request() ||
180 	    !kmdb_wr_driver_notify_isempty());
181 }
182 
183 void
184 kmdb_dpi_resume_master(void)
185 {
186 	kmdb_dpi_resume_common(KMDB_DPI_CMD_RESUME_MASTER);
187 }
188 
189 void
190 kmdb_dpi_resume(void)
191 {
192 	kmdb_dpi_resume_common(KMDB_DPI_CMD_RESUME_ALL);
193 }
194 
195 void
196 kmdb_dpi_resume_unload(void)
197 {
198 	kmdb_dpi_resume_common(KMDB_DPI_CMD_RESUME_UNLOAD);
199 }
200 
201 int
202 kmdb_dpi_switch_master(int tgt_cpuid)
203 {
204 	if (kmdb_dpi_get_cpu_state(tgt_cpuid) < 0)
205 		return (-1); /* errno is set for us */
206 
207 	kmdb_dpi_switch_target = tgt_cpuid;
208 	kmdb_dpi_resume_common(KMDB_DPI_CMD_SWITCH_CPU);
209 
210 	return (0);
211 }
212 
213 void
214 kmdb_dpi_flush_slave_caches(void)
215 {
216 	kmdb_dpi_resume_common(KMDB_DPI_CMD_FLUSH_CACHES);
217 }
218 
219 typedef struct work_results {
220 	mdb_nv_t res_loads;
221 	mdb_nv_t res_unloads;
222 } work_results_t;
223 
224 static int
225 kmdb_dbgnotify_cb(kmdb_wr_t *wn, void *arg)
226 {
227 	work_results_t *res = arg;
228 
229 	switch (WR_TASK(wn)) {
230 	case WNTASK_DMOD_LOAD: {
231 		/*
232 		 * If this is an ack, the driver finished processing a load we
233 		 * requested.  We process it and free the message.  If this
234 		 * isn't an ack, then it's a driver-initiated load.  We process
235 		 * the message, and send it back as an ack so the driver can
236 		 * free it.
237 		 */
238 		kmdb_wr_load_t *dlr = (kmdb_wr_load_t *)wn;
239 
240 		mdb_dprintf(MDB_DBG_DPI, "received module load message\n");
241 
242 		if (kmdb_module_loaded(dlr) && res != NULL) {
243 			(void) mdb_nv_insert(&res->res_loads,
244 			    strbasename(dlr->dlr_fname), NULL, 0, 0);
245 		}
246 
247 		if (WR_ISACK(dlr)) {
248 			kmdb_module_load_ack(dlr);
249 			return (0);
250 		}
251 
252 		/* Send it back as an ack */
253 		mdb_dprintf(MDB_DBG_DPI, "Sending load request for %s back "
254 		    "as an ack\n", dlr->dlr_fname);
255 		WR_ACK(wn);
256 		kmdb_wr_driver_notify(wn);
257 		return (0);
258 	}
259 
260 	case WNTASK_DMOD_LOAD_ALL:
261 		/*
262 		 * We initiated the load-all, so this must be an ack.  The
263 		 * individual module load messages will arrive separately -
264 		 * there's no need to do anything further with this message.
265 		 */
266 		ASSERT(WR_ISACK(wn));
267 
268 		mdb_dprintf(MDB_DBG_DPI, "received module load all ack\n");
269 
270 		kmdb_module_load_all_ack(wn);
271 		return (0);
272 
273 	case WNTASK_DMOD_UNLOAD: {
274 		/*
275 		 * The debugger received an unload message.  The driver isn't
276 		 * supposed to initiate unloads, so we shouldn't see anything
277 		 * but acks.  We tell the dmod subsystem that the module has
278 		 * been unloaded, and we free the message.
279 		 */
280 		kmdb_wr_unload_t *dur = (kmdb_wr_unload_t *)wn;
281 
282 		ASSERT(WR_ISACK(dur));
283 
284 		mdb_dprintf(MDB_DBG_DPI, "received module unload ack\n");
285 
286 		if (kmdb_module_unloaded(dur) && res != NULL) {
287 			(void) mdb_nv_insert(&res->res_unloads,
288 			    dur->dur_modname, NULL, 0, 0);
289 		}
290 
291 		/* Done with message */
292 		kmdb_module_unload_ack(dur);
293 		return (0);
294 	}
295 
296 	case WNTASK_DMOD_PATH_CHANGE: {
297 		/*
298 		 * The debugger received a path change message.  The driver
299 		 * can't initiate these, so it must be an acknowledgement.
300 		 * There's no processing to be done, so just free the message.
301 		 */
302 		kmdb_wr_path_t *dpth = (kmdb_wr_path_t *)wn;
303 
304 		ASSERT(WR_ISACK(dpth));
305 
306 		mdb_dprintf(MDB_DBG_DPI, "received path change ack\n");
307 
308 		kmdb_module_path_ack(dpth);
309 		return (0);
310 	}
311 
312 	default:
313 		mdb_warn("Received unknown message type %d from driver\n",
314 		    wn->wn_task);
315 		/* Ignore it */
316 		return (0);
317 	}
318 }
319 
320 static void
321 print_modules(mdb_nv_t *mods)
322 {
323 	mdb_var_t *v;
324 
325 	mdb_nv_rewind(mods);
326 	while ((v = mdb_nv_advance(mods)) != NULL)
327 		mdb_printf(" %s", mdb_nv_get_name(v));
328 }
329 
330 void
331 kmdb_dpi_process_work_queue(void)
332 {
333 	work_results_t res;
334 
335 	(void) mdb_nv_create(&res.res_loads, UM_SLEEP);
336 	(void) mdb_nv_create(&res.res_unloads, UM_SLEEP);
337 
338 	mdb_dprintf(MDB_DBG_DPI, "processing work queue\n");
339 	(void) kmdb_wr_debugger_process(kmdb_dbgnotify_cb, &res);
340 
341 	if (mdb_nv_size(&res.res_loads)) {
342 		mdb_printf("Loaded modules: [");
343 		print_modules(&res.res_loads);
344 		mdb_printf(" ]\n");
345 	}
346 
347 	if (mdb_nv_size(&res.res_unloads)) {
348 		mdb_printf("Unloaded modules: [");
349 		print_modules(&res.res_unloads);
350 		mdb_printf(" ]\n");
351 	}
352 
353 	mdb_nv_destroy(&res.res_loads);
354 	mdb_nv_destroy(&res.res_unloads);
355 }
356 
357 int
358 kmdb_dpi_step(void)
359 {
360 	return (mdb.m_dpi->dpo_step());
361 }
362 
363 uintptr_t
364 kmdb_dpi_call(uintptr_t func, uint_t argc, const uintptr_t *argv)
365 {
366 	return (mdb.m_dpi->dpo_call(func, argc, argv));
367 }
368 
369 int
370 kmdb_dpi_brkpt_arm(uintptr_t addr, mdb_instr_t *instrp)
371 {
372 	int rc;
373 
374 	if ((rc = mdb.m_dpi->dpo_brkpt_arm(addr, instrp)) < 0)
375 		mdb_warn("failed to arm breakpoint at %a", addr);
376 
377 	mdb_dprintf(MDB_DBG_DPI, "brkpt armed at %p %A\n", (void *)addr, addr);
378 
379 	return (rc);
380 }
381 
382 int
383 kmdb_dpi_brkpt_disarm(uintptr_t addr, mdb_instr_t instrp)
384 {
385 	int rc;
386 
387 	if ((rc = mdb.m_dpi->dpo_brkpt_disarm(addr, instrp)) < 0)
388 		mdb_warn("failed to disarm breakpoint at %a", addr);
389 
390 	mdb_dprintf(MDB_DBG_DPI, "brkpt disarmed at %p %A\n", (void *)addr,
391 	    addr);
392 
393 	return (rc);
394 }
395 
396 int
397 kmdb_dpi_wapt_validate(kmdb_wapt_t *wp)
398 {
399 	if (mdb.m_dpi->dpo_wapt_validate(wp) < 0)
400 		return (-1); /* errno is set for us */
401 
402 	return (0);
403 }
404 
405 int
406 kmdb_dpi_wapt_reserve(kmdb_wapt_t *wp)
407 {
408 	if (mdb.m_dpi->dpo_wapt_reserve(wp) < 0)
409 		return (-1); /* errno is set for us */
410 
411 	mdb_dprintf(MDB_DBG_DPI, "wapt reserve type %d at %p, priv %p\n",
412 	    wp->wp_type, (void *)wp->wp_addr, wp->wp_priv);
413 
414 	return (0);
415 }
416 
417 void
418 kmdb_dpi_wapt_release(kmdb_wapt_t *wp)
419 {
420 	mdb.m_dpi->dpo_wapt_release(wp);
421 }
422 
423 void
424 kmdb_dpi_wapt_arm(kmdb_wapt_t *wp)
425 {
426 	mdb.m_dpi->dpo_wapt_arm(wp);
427 
428 	mdb_dprintf(MDB_DBG_DPI, "wapt armed at %p (type %d, priv %p)\n",
429 	    (void *)wp->wp_addr, wp->wp_type, wp->wp_priv);
430 }
431 
432 void
433 kmdb_dpi_wapt_disarm(kmdb_wapt_t *wp)
434 {
435 	mdb.m_dpi->dpo_wapt_disarm(wp);
436 
437 	mdb_dprintf(MDB_DBG_DPI, "wapt disarmed at %p (type %d, priv %p)\n",
438 	    (void *)wp->wp_addr, wp->wp_type, wp->wp_priv);
439 }
440 
441 int
442 kmdb_dpi_wapt_match(kmdb_wapt_t *wp)
443 {
444 	return (mdb.m_dpi->dpo_wapt_match(wp));
445 }
446 
447 void
448 kmdb_dpi_set_state(int state, int why)
449 {
450 	if (kmdb_dpi_state != DPI_STATE_LOST) {
451 		mdb_dprintf(MDB_DBG_DPI, "dpi_set_state %d why %d\n",
452 		    state, why);
453 
454 		kmdb_dpi_state = state;
455 		kmdb_dpi_state_why = why;
456 	}
457 }
458 
459 int
460 kmdb_dpi_get_state(int *whyp)
461 {
462 	if (whyp != NULL)
463 		*whyp = kmdb_dpi_state_why;
464 
465 	return (kmdb_dpi_state);
466 }
467 
468 void
469 kmdb_dpi_dump_crumbs(uintptr_t addr, int cpuid)
470 {
471 	mdb.m_dpi->dpo_dump_crumbs(addr, cpuid);
472 }
473