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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * mdb dcmds for selected structures from 30 * usr/src/uts/common/sys/crypto/impl.h 31 */ 32 #include <stdio.h> 33 #include <sys/mdb_modapi.h> 34 #include <sys/modctl.h> 35 #include <sys/types.h> 36 #include <sys/crypto/api.h> 37 #include <sys/crypto/common.h> 38 #include <sys/crypto/impl.h> 39 #include "crypto_cmds.h" 40 41 int 42 kcf_sched_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 43 { 44 kcf_sched_info_t sched; 45 kcf_sched_info_t *sinfo = &sched; 46 47 if (!(flags & DCMD_ADDRSPEC)) { 48 if ((argc == 1) && (argv->a_type == MDB_TYPE_IMMEDIATE)) 49 sinfo = (kcf_sched_info_t *)(uintptr_t)argv->a_un.a_val; 50 else 51 return (DCMD_USAGE); 52 } else if (addr == NULL) /* not allowed with DCMD_ADDRSPEC */ 53 return (DCMD_USAGE); 54 else { 55 if (mdb_vread(sinfo, sizeof (kcf_sched_info_t), addr) == -1) { 56 mdb_warn("cannot read %p", addr); 57 return (DCMD_ERR); 58 } 59 } 60 mdb_printf("ks_ndispatches:\t%llu\n", sinfo->ks_ndispatches); 61 mdb_printf("ks_nfails:\t%llu\n", sinfo->ks_nfails); 62 mdb_printf("ks_nbusy_rval:\t%llu\n", sinfo->ks_nbusy_rval); 63 mdb_printf("ks_ntaskq:\t%p\n", sinfo->ks_taskq); 64 return (DCMD_OK); 65 } 66 67 static const char *prov_states[] = { 68 "none", 69 "KCF_PROV_ALLOCATED", 70 "KCF_PROV_UNVERIFIED", 71 "KCF_PROV_VERIFICATION_FAILED", 72 "KCF_PROV_READY", 73 "KCF_PROV_BUSY", 74 "KCF_PROV_FAILED", 75 "KCF_PROV_DISABLED", 76 "KCF_PROV_REMOVED", 77 "KCF_PROV_FREED" 78 }; 79 80 static void 81 pr_kstat_named(kstat_named_t *ks) 82 { 83 mdb_inc_indent(4); 84 85 mdb_printf("name = %s\n", ks->name); 86 mdb_printf("value = "); 87 88 /* 89 * The only data type used for the provider kstats is uint64. 90 */ 91 switch (ks->data_type) { 92 case KSTAT_DATA_UINT64: 93 #if defined(_LP64) || defined(_LONGLONG_TYPE) 94 mdb_printf("%llu\n", ks->value.ui64); 95 #endif 96 break; 97 default: 98 mdb_warn("Incorrect data type for kstat.\n"); 99 } 100 101 mdb_dec_indent(4); 102 } 103 104 /*ARGSUSED*/ 105 int 106 kcf_provider_desc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 107 { 108 kcf_provider_desc_t desc; 109 kcf_provider_desc_t *ptr; 110 char string[MAXNAMELEN + 1]; 111 int i, j; 112 crypto_mech_info_t *mech_pointer; 113 mdb_arg_t arg; 114 115 if ((flags & DCMD_ADDRSPEC) != DCMD_ADDRSPEC) 116 return (DCMD_USAGE); 117 ptr = (kcf_provider_desc_t *)addr; 118 119 #ifdef DEBUG 120 mdb_printf("DEBUG: reading kcf_provider_desc at %p\n", ptr); 121 #endif 122 123 if (mdb_vread(&desc, sizeof (kcf_provider_desc_t), (uintptr_t)ptr) 124 == -1) { 125 mdb_warn("cannot read at address %p", (uintptr_t)ptr); 126 return (DCMD_ERR); 127 } 128 mdb_printf("%<b>kcf_provider_desc at %p%</b>\n", ptr); 129 130 switch (desc.pd_prov_type) { 131 case CRYPTO_HW_PROVIDER: 132 mdb_printf("pd_prov_type:\t\tCRYPTO_HW_PROVIDER\n"); 133 break; 134 case CRYPTO_SW_PROVIDER: 135 mdb_printf("pd_prov_type:\t\tCRYPTO_SW_PROVIDER\n"); 136 break; 137 case CRYPTO_LOGICAL_PROVIDER: 138 mdb_printf("pd_prov_type:\t\tCRYPTO_LOGICAL_PROVIDER\n"); 139 break; 140 default: 141 mdb_printf("bad pd_prov_type:\t%d\n", desc.pd_prov_type); 142 } 143 144 mdb_printf("pd_prov_handle:\t\t%p\n", desc.pd_prov_handle); 145 mdb_printf("pd_kcf_prov_handle:\t%u\n", desc.pd_kcf_prov_handle); 146 mdb_printf("pd_prov_id:\t\t%u\n", desc.pd_prov_id); 147 if (desc.pd_description == NULL) 148 mdb_printf("pd_description:\t\tNULL\n"); 149 else if (mdb_readstr(string, MAXNAMELEN + 1, 150 (uintptr_t)desc.pd_description) == -1) { 151 mdb_warn("cannot read %p", desc.pd_description); 152 } else 153 mdb_printf("pd_description:\t\t%s\n", string); 154 155 mdb_printf("pd_ops_vector:\t\t%p\n", desc.pd_ops_vector); 156 mdb_printf("pd_mech_list_count:\t%u\n", desc.pd_mech_list_count); 157 /* mechanisms */ 158 mdb_inc_indent(4); 159 for (i = 0; i < desc.pd_mech_list_count; i++) { 160 mech_pointer = desc.pd_mechanisms + i; 161 mdb_call_dcmd("crypto_mech_info", 162 (uintptr_t)mech_pointer, DCMD_ADDRSPEC, 0, NULL); 163 } 164 mdb_dec_indent(4); 165 mdb_printf("pd_mech_indx:\n"); 166 mdb_inc_indent(8); 167 for (i = 0; i < KCF_OPS_CLASSSIZE; i++) { 168 for (j = 0; j < KCF_MAXMECHTAB; j++) { 169 if (desc.pd_mech_indx[i][j] == KCF_INVALID_INDX) 170 mdb_printf("N "); 171 else 172 mdb_printf("%u ", desc.pd_mech_indx[i][j]); 173 } 174 mdb_printf("\n"); 175 } 176 mdb_dec_indent(8); 177 mdb_printf("pd_ks_data.ps_ops_total:\n", desc.pd_ks_data.ps_ops_total); 178 pr_kstat_named(&desc.pd_ks_data.ps_ops_total); 179 mdb_printf("pd_ks_data.ps_ops_passed:\n", 180 desc.pd_ks_data.ps_ops_passed); 181 pr_kstat_named(&desc.pd_ks_data.ps_ops_passed); 182 mdb_printf("pd_ks_data.ps_ops_failed:\n", 183 desc.pd_ks_data.ps_ops_failed); 184 pr_kstat_named(&desc.pd_ks_data.ps_ops_failed); 185 mdb_printf("pd_ks_data.ps_ops_busy_rval:\n", 186 desc.pd_ks_data.ps_ops_busy_rval); 187 pr_kstat_named(&desc.pd_ks_data.ps_ops_busy_rval); 188 189 mdb_printf("pd_kstat:\t\t%p\n", desc.pd_kstat); 190 mdb_printf("kcf_sched_info:\n"); 191 /* print pd_sched_info via existing function */ 192 mdb_inc_indent(8); 193 arg.a_type = MDB_TYPE_IMMEDIATE; 194 arg.a_un.a_val = (uintmax_t)(uintptr_t)&desc.pd_sched_info; 195 mdb_call_dcmd("kcf_sched_info", (uintptr_t)NULL, 0, 1, &arg); 196 mdb_dec_indent(8); 197 198 mdb_printf("pd_refcnt:\t\t%u\n", desc.pd_refcnt); 199 if (desc.pd_name == NULL) 200 mdb_printf("pd_name:\t\t NULL\n"); 201 else if (mdb_readstr(string, MAXNAMELEN + 1, (uintptr_t)desc.pd_name) 202 == -1) 203 mdb_warn("could not read pd_name from %X\n", desc.pd_name); 204 else 205 mdb_printf("pd_name:\t\t%s\n", string); 206 207 mdb_printf("pd_instance:\t\t%u\n", desc.pd_instance); 208 mdb_printf("pd_module_id:\t\t%d\n", desc.pd_module_id); 209 mdb_printf("pd_mctlp:\t\t%p\n", desc.pd_mctlp); 210 mdb_printf("pd_sid:\t\t\t%u\n", desc.pd_sid); 211 mdb_printf("pd_lock:\t\t%p\n", desc.pd_lock); 212 if (desc.pd_state < KCF_PROV_ALLOCATED || 213 desc.pd_state > KCF_PROV_FREED) 214 mdb_printf("pd_state is invalid:\t%d\n", desc.pd_state); 215 else 216 mdb_printf("pd_state:\t%s\n", prov_states[desc.pd_state]); 217 218 mdb_printf("pd_resume_cv:\t\t%hd\n", desc.pd_resume_cv._opaque); 219 mdb_printf("pd_remove_cv:\t\t%hd\n", desc.pd_remove_cv._opaque); 220 mdb_printf("pd_flags:\t\t%s %s %s %s\n", 221 (desc.pd_flags & CRYPTO_HIDE_PROVIDER) ? 222 "CRYPTO_HIDE_PROVIDER" : " ", 223 (desc.pd_flags & CRYPTO_HASH_NO_UPDATE) ? 224 "CRYPTO_HASH_NO_UPDATE" : " ", 225 (desc.pd_flags & KCF_LPROV_MEMBER) ? 226 "KCF_LPROV_MEMBER" : " ", 227 (desc.pd_flags & KCF_PROV_RESTRICTED) ? 228 "KCF_PROV_RESTRICTED" : " "); 229 if (desc.pd_flags & CRYPTO_HASH_NO_UPDATE) 230 mdb_printf("pd_hash_limit:\t\t%u\n", desc.pd_hash_limit); 231 mdb_printf("pd_provider_list:\t%p\n", desc.pd_provider_list); 232 return (DCMD_OK); 233 } 234 235 #define GOT_NONE (-2) 236 237 /*ARGSUSED*/ 238 int 239 prov_tab(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 240 { 241 kcf_provider_desc_t **tab; 242 kcf_provider_desc_t desc; 243 kcf_provider_desc_t *ptr; 244 uint_t prov_tab_max; 245 int i; 246 int gotzero = GOT_NONE; 247 char string[MAXNAMELEN + 1]; 248 249 if ((flags & DCMD_ADDRSPEC) == DCMD_ADDRSPEC) { 250 return (DCMD_USAGE); 251 } else if (mdb_readsym(&ptr, sizeof (void *), "prov_tab") 252 == -1) { 253 mdb_warn("cannot read prov_tab"); 254 return (DCMD_ERR); 255 256 } else if (mdb_readvar(&prov_tab_max, "prov_tab_max") == -1) { 257 mdb_warn("cannot read prov_tab_max"); 258 return (DCMD_ERR); 259 } 260 mdb_printf("%<b>prov_tab = %p%</b>\n", ptr); 261 tab = mdb_zalloc(prov_tab_max * sizeof (kcf_provider_desc_t *), 262 UM_SLEEP| UM_GC); 263 264 #ifdef DEBUG 265 mdb_printf("DEBUG: tab = %p, prov_tab_max = %d\n", tab, prov_tab_max); 266 #endif 267 268 if (mdb_vread(tab, prov_tab_max * sizeof (kcf_provider_desc_t *), 269 (uintptr_t)ptr) == -1) { 270 mdb_warn("cannot read prov_tab"); 271 return (DCMD_ERR); 272 } 273 #ifdef DEBUG 274 mdb_printf("DEBUG: got past mdb_vread of tab\n"); 275 mdb_printf("DEBUG: *tab = %p\n", *tab); 276 #endif 277 for (i = 0; i < prov_tab_max; i++) { 278 /* save space, only print range for long list of nulls */ 279 if (tab[i] == NULL) { 280 if (gotzero == GOT_NONE) { 281 mdb_printf("prov_tab[%d", i); 282 gotzero = i; 283 } 284 } else { 285 /* first non-null in awhile, print index of prev null */ 286 if (gotzero != GOT_NONE) { 287 if (gotzero == (i - 1)) 288 mdb_printf("] = NULL\n", i - 1); 289 else 290 mdb_printf(" - %d] = NULL\n", i - 1); 291 gotzero = GOT_NONE; 292 } 293 /* interesting value, print it */ 294 mdb_printf("prov_tab[%d] = %p ", i, tab[i]); 295 296 if (mdb_vread(&desc, sizeof (kcf_provider_desc_t), 297 (uintptr_t)tab[i]) == -1) { 298 mdb_warn("cannot read at address %p", 299 (uintptr_t)tab[i]); 300 return (DCMD_ERR); 301 } 302 303 (void) mdb_readstr(string, MAXNAMELEN + 1, 304 (uintptr_t)desc.pd_name); 305 mdb_printf("(%s\t%s)\n", string, 306 prov_states[desc.pd_state]); 307 } 308 } 309 /* if we've printed the first of many nulls but left the brace open */ 310 if ((i > 0) && (tab[i-1] == NULL)) { 311 if (gotzero == GOT_NONE) 312 mdb_printf("] = NULL\n"); 313 else 314 mdb_printf(" - %d] = NULL\n", i - 1); 315 } 316 317 return (DCMD_OK); 318 } 319 320 /*ARGSUSED*/ 321 int 322 policy_tab(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 323 { 324 kcf_policy_desc_t **tab; 325 kcf_policy_desc_t *ptr; 326 uint_t policy_tab_max; 327 int num, i; 328 int gotzero = GOT_NONE; 329 330 if ((flags & DCMD_ADDRSPEC) == DCMD_ADDRSPEC) { 331 return (DCMD_USAGE); 332 } else if (mdb_readsym(&ptr, sizeof (void *), "policy_tab") 333 == -1) { 334 mdb_warn("cannot read policy_tab"); 335 return (DCMD_ERR); 336 337 } else if (mdb_readvar(&policy_tab_max, "policy_tab_max") == -1) { 338 mdb_warn("cannot read policy_tab_max"); 339 return (DCMD_ERR); 340 } 341 342 /* get the current number of descriptors in the table */ 343 if (mdb_readvar(&num, "policy_tab_num") == -1) { 344 mdb_warn("cannot read policy_tab_num"); 345 return (DCMD_ERR); 346 } 347 mdb_printf("%<b>policy_tab = %p%</b> \tpolicy_tab_num = %d\n", 348 ptr, num); 349 350 tab = mdb_zalloc(policy_tab_max * sizeof (kcf_policy_desc_t *), 351 UM_SLEEP| UM_GC); 352 353 if (mdb_vread(tab, policy_tab_max * sizeof (kcf_policy_desc_t *), 354 (uintptr_t)ptr) == -1) { 355 mdb_warn("cannot read policy_tab"); 356 return (DCMD_ERR); 357 } 358 #ifdef DEBUG 359 mdb_printf("DEBUG: got past mdb_vread of tab\n"); 360 mdb_printf("DEBUG: *tab = %p\n", *tab); 361 #endif 362 for (i = 0; i < policy_tab_max; i++) { 363 /* save space, only print range for long list of nulls */ 364 if (tab[i] == NULL) { 365 if (gotzero == GOT_NONE) { 366 mdb_printf("policy_tab[%d", i); 367 gotzero = i; 368 } 369 } else { 370 /* first non-null in awhile, print index of prev null */ 371 if (gotzero != GOT_NONE) { 372 if (gotzero == (i - 1)) 373 mdb_printf("] = NULL\n", i - 1); 374 else 375 mdb_printf(" - %d] = NULL\n", i - 1); 376 gotzero = GOT_NONE; 377 } 378 /* interesting value, print it */ 379 mdb_printf("policy_tab[%d] = %p\n", i, tab[i]); 380 } 381 } 382 /* if we've printed the first of many nulls but left the brace open */ 383 if ((i > 0) && (tab[i-1] == NULL)) { 384 if (gotzero == GOT_NONE) 385 mdb_printf("] = NULL\n"); 386 else 387 mdb_printf(" - %d] = NULL\n", i - 1); 388 } 389 390 return (DCMD_OK); 391 } 392 393 static void 394 prt_mechs(int count, crypto_mech_name_t *mechs) 395 { 396 int i; 397 char name[CRYPTO_MAX_MECH_NAME + 1]; 398 char name2[CRYPTO_MAX_MECH_NAME + 3]; 399 400 for (i = 0; i < count; i++) { 401 if (mdb_readstr(name, CRYPTO_MAX_MECH_NAME, 402 (uintptr_t)((char *)mechs)) == -1) 403 continue; 404 /* put in quotes */ 405 (void) mdb_snprintf(name2, sizeof (name2), "\"%s\"", name); 406 /* yes, length is 32, but then it will wrap */ 407 /* this shorter size formats nicely for most cases */ 408 mdb_printf("mechs[%d]=%-28s", i, name2); 409 mdb_printf("%s", i%2 ? "\n" : " "); /* 2-columns */ 410 mechs++; 411 } 412 } 413 414 /* ARGSUSED2 */ 415 static int 416 prt_soft_conf_entry(kcf_soft_conf_entry_t *addr, kcf_soft_conf_entry_t *entry, 417 void *cbdata) 418 { 419 char name[MAXNAMELEN + 1]; 420 421 mdb_printf("\n%<b>kcf_soft_conf_entry_t at %p:%</b>\n", addr); 422 mdb_printf("ce_next: %p", entry->ce_next); 423 424 if (entry->ce_name == NULL) 425 mdb_printf("\tce_name: NULL\n"); 426 else if (mdb_readstr(name, MAXNAMELEN, (uintptr_t)entry->ce_name) 427 == -1) 428 mdb_printf("could not read ce_name from %p\n", 429 entry->ce_name); 430 else 431 mdb_printf("\tce_name: %s\n", name); 432 433 mdb_printf("ce_count: %d\n", entry->ce_count); 434 prt_mechs(entry->ce_count, entry->ce_mechs); 435 return (WALK_NEXT); 436 } 437 438 int 439 soft_conf_walk_init(mdb_walk_state_t *wsp) 440 { 441 uintptr_t *soft; 442 443 if (mdb_readsym(&soft, sizeof (kcf_soft_conf_entry_t *), 444 "soft_config_list") == -1) { 445 mdb_warn("failed to find 'soft_config_list'"); 446 return (WALK_ERR); 447 } 448 wsp->walk_addr = (uintptr_t)soft; 449 wsp->walk_data = mdb_alloc(sizeof (kcf_soft_conf_entry_t), UM_SLEEP); 450 wsp->walk_callback = (mdb_walk_cb_t)prt_soft_conf_entry; 451 return (WALK_NEXT); 452 } 453 454 /* 455 * At each step, read a kcf_soft_conf_entry_t into our private storage, then 456 * invoke the callback function. We terminate when we reach a NULL ce_next 457 * pointer. 458 */ 459 int 460 soft_conf_walk_step(mdb_walk_state_t *wsp) 461 { 462 int status; 463 464 if (wsp->walk_addr == NULL) /* then we're done */ 465 return (WALK_DONE); 466 #ifdef DEBUG 467 else 468 mdb_printf("DEBUG: wsp->walk_addr == %p\n", wsp->walk_addr); 469 #endif 470 471 if (mdb_vread(wsp->walk_data, sizeof (kcf_soft_conf_entry_t), 472 wsp->walk_addr) == -1) { 473 mdb_warn("failed to read kcf_soft_conf_entry at %p", 474 wsp->walk_addr); 475 return (WALK_DONE); 476 } 477 478 status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, 479 wsp->walk_cbdata); 480 481 wsp->walk_addr = 482 (uintptr_t)(((kcf_soft_conf_entry_t *)wsp->walk_data)->ce_next); 483 return (status); 484 } 485 486 /* 487 * The walker's fini function is invoked at the end of each walk. Since we 488 * dynamically allocated a kcf_soft_conf_entry_t in soft_conf_walk_init, 489 * we must free it now. 490 */ 491 void 492 soft_conf_walk_fini(mdb_walk_state_t *wsp) 493 { 494 #ifdef DEBUG 495 mdb_printf("...end of kcf_soft_conf_entry walk\n"); 496 #endif 497 mdb_free(wsp->walk_data, sizeof (kcf_soft_conf_entry_t)); 498 } 499 /* ARGSUSED2 */ 500 int 501 kcf_soft_conf_entry(uintptr_t addr, uint_t flags, int argc, 502 const mdb_arg_t *argv) 503 { 504 kcf_soft_conf_entry_t entry; 505 kcf_soft_conf_entry_t *ptr; 506 507 if ((flags & DCMD_ADDRSPEC) == DCMD_ADDRSPEC) { 508 if (addr == NULL) /* not allowed with DCMD_ADDRSPEC */ 509 return (DCMD_USAGE); 510 else 511 ptr = (kcf_soft_conf_entry_t *)addr; 512 } else if (mdb_readsym(&ptr, sizeof (void *), "soft_config_list") 513 == -1) { 514 mdb_warn("cannot read soft_config_list"); 515 return (DCMD_ERR); 516 } else 517 mdb_printf("soft_config_list = %p\n", ptr); 518 519 if (ptr == NULL) 520 return (DCMD_OK); 521 522 if (mdb_vread(&entry, sizeof (kcf_soft_conf_entry_t), (uintptr_t)ptr) 523 == -1) { 524 mdb_warn("cannot read at address %p", (uintptr_t)ptr); 525 return (DCMD_ERR); 526 } 527 528 /* this could change in the future to have more than one ret val */ 529 if (prt_soft_conf_entry(ptr, &entry, NULL) != WALK_ERR) 530 return (DCMD_OK); 531 return (DCMD_ERR); 532 } 533 534 /* ARGSUSED1 */ 535 int 536 kcf_policy_desc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 537 { 538 kcf_policy_desc_t desc; 539 char name[MAXNAMELEN + 1]; 540 541 542 if ((flags & DCMD_ADDRSPEC) != DCMD_ADDRSPEC) 543 return (DCMD_USAGE); 544 545 if (mdb_vread(&desc, sizeof (kcf_policy_desc_t), (uintptr_t)addr) 546 == -1) { 547 mdb_warn("Could not read kcf_policy_desc_t at %p\n", addr); 548 return (DCMD_ERR); 549 } 550 mdb_printf("pd_prov_type: %s", 551 desc.pd_prov_type == CRYPTO_HW_PROVIDER ? "CRYPTO_HW_PROVIDER" 552 : "CRYPTO_SW_PROVIDER"); 553 554 if (desc.pd_name == NULL) 555 mdb_printf("\tpd_name: NULL\n"); 556 else if (mdb_readstr(name, MAXNAMELEN, (uintptr_t)desc.pd_name) 557 == -1) 558 mdb_printf("could not read pd_name from %p\n", 559 desc.pd_name); 560 else 561 mdb_printf("\tpd_name: %s\n", name); 562 563 mdb_printf("pd_instance: %d ", desc.pd_instance); 564 mdb_printf("\t\tpd_refcnt: %d\n", desc.pd_refcnt); 565 mdb_printf("pd_mutex: %p", desc.pd_mutex); 566 mdb_printf("\t\tpd_disabled_count: %d", desc.pd_disabled_count); 567 mdb_printf("\npd_disabled_mechs:\n"); 568 mdb_inc_indent(4); 569 prt_mechs(desc.pd_disabled_count, desc.pd_disabled_mechs); 570 mdb_dec_indent(4); 571 return (DCMD_OK); 572 } 573