Home | History | Annotate | Download | only in kmdb
      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