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