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
49jmp_buf *kmdb_dpi_fault_pcb;
50jmp_buf kmdb_dpi_resume_pcb;
51jmp_buf kmdb_dpi_entry_pcb;
52
53static int kmdb_dpi_state;
54static int kmdb_dpi_state_why;
55
56uint_t kmdb_dpi_resume_requested;
57uint_t kmdb_dpi_switch_target = (uint_t)-1;
58
59/* Used by the style-specific resume interfaces to signal the driver */
60void (*kmdb_dpi_wrintr_fire)(void);
61
62int
63kmdb_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*/
74void
75kmdb_activate(kdi_debugvec_t **dvecp, uint_t flags)
76{
77	mdb.m_dpi->dpo_debugger_activate(dvecp, flags);
78}
79
80void
81kmdb_deactivate(void)
82{
83	mdb.m_dpi->dpo_debugger_deactivate();
84}
85
86int
87kmdb_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
119void
120kmdb_dpi_enter_mon(void)
121{
122	mdb.m_dpi->dpo_enter_mon();
123}
124
125void
126kmdb_dpi_modchg_register(void (*func)(struct modctl *, int))
127{
128	mdb.m_dpi->dpo_modchg_register(func);
129}
130
131void
132kmdb_dpi_modchg_cancel(void)
133{
134	mdb.m_dpi->dpo_modchg_cancel();
135}
136
137int
138kmdb_dpi_get_cpu_state(int cpuid)
139{
140	return (mdb.m_dpi->dpo_get_cpu_state(cpuid));
141}
142
143int
144kmdb_dpi_get_master_cpuid(void)
145{
146	return (mdb.m_dpi->dpo_get_master_cpuid());
147}
148
149const mdb_tgt_gregset_t *
150kmdb_dpi_get_gregs(int cpuid)
151{
152	return (mdb.m_dpi->dpo_get_gregs(cpuid));
153}
154
155jmp_buf *
156kmdb_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
165void
166kmdb_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 */
176int
177kmdb_dpi_work_required(void)
178{
179	return (kmdb_kdi_get_unload_request() ||
180	    !kmdb_wr_driver_notify_isempty());
181}
182
183void
184kmdb_dpi_resume_master(void)
185{
186	kmdb_dpi_resume_common(KMDB_DPI_CMD_RESUME_MASTER);
187}
188
189void
190kmdb_dpi_resume(void)
191{
192	kmdb_dpi_resume_common(KMDB_DPI_CMD_RESUME_ALL);
193}
194
195void
196kmdb_dpi_resume_unload(void)
197{
198	kmdb_dpi_resume_common(KMDB_DPI_CMD_RESUME_UNLOAD);
199}
200
201int
202kmdb_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
213void
214kmdb_dpi_flush_slave_caches(void)
215{
216	kmdb_dpi_resume_common(KMDB_DPI_CMD_FLUSH_CACHES);
217}
218
219typedef struct work_results {
220	mdb_nv_t res_loads;
221	mdb_nv_t res_unloads;
222} work_results_t;
223
224static int
225kmdb_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
320static void
321print_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
330void
331kmdb_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
357int
358kmdb_dpi_step(void)
359{
360	return (mdb.m_dpi->dpo_step());
361}
362
363uintptr_t
364kmdb_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
369int
370kmdb_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
382int
383kmdb_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
396int
397kmdb_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
405int
406kmdb_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
417void
418kmdb_dpi_wapt_release(kmdb_wapt_t *wp)
419{
420	mdb.m_dpi->dpo_wapt_release(wp);
421}
422
423void
424kmdb_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
432void
433kmdb_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
441int
442kmdb_dpi_wapt_match(kmdb_wapt_t *wp)
443{
444	return (mdb.m_dpi->dpo_wapt_match(wp));
445}
446
447void
448kmdb_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
459int
460kmdb_dpi_get_state(int *whyp)
461{
462	if (whyp != NULL)
463		*whyp = kmdb_dpi_state_why;
464
465	return (kmdb_dpi_state);
466}
467
468void
469kmdb_dpi_dump_crumbs(uintptr_t addr, int cpuid)
470{
471	mdb.m_dpi->dpo_dump_crumbs(addr, cpuid);
472}
473