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