xref: /illumos-gate/usr/src/cmd/scsi/smp/common/smp.c (revision 5328fc53d11d7151861fa272e4fb0248b8f0e145)
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 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2015 PALO, Richard
25  * Copyright 2019, Joyent, Inc.
26  */
27 #include <sys/types.h>
28 #include <sys/scsi/generic/smp_frames.h>
29 #include <sys/scsi/generic/commands.h>
30 #include <sys/scsi/impl/commands.h>
31 #include <sys/ccompile.h>
32 #include <sys/byteorder.h>
33 
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <strings.h>
41 #include <ctype.h>
42 
43 #include <scsi/libsmp.h>
44 #include <scsi/libsmp_plugin.h>
45 
46 static char *yes = "Yes";
47 static char *no = "No";
48 
49 static void fatal(int, const char *, ...) __NORETURN;
50 
51 static smp_target_t *tp = NULL;
52 static smp_action_t *ap = NULL;
53 static smp_function_t func;
54 static smp_result_t result;
55 static smp_target_def_t tdef;
56 static uint8_t *smp_resp;
57 static size_t smp_resp_len;
58 
59 static void
60 fatal(int err, const char *fmt, ...)
61 {
62 	va_list ap;
63 
64 	va_start(ap, fmt);
65 	(void) vfprintf(stderr, fmt, ap);
66 	va_end(ap);
67 
68 	(void) fprintf(stderr, "\n");
69 	(void) fflush(stderr);
70 
71 	_exit(err);
72 }
73 
74 /*
75  * Print out a buffer of SMP character array data. The data in str is guaranteed
76  * to be at most len bytes long. While it is supposed to be ascii, we should not
77  * assume as such.
78  */
79 static void
80 smp_print_ascii(const char *header, const char *str, size_t len)
81 {
82 	size_t i, last = len;
83 
84 	while (last > 0 && str[last - 1] == ' ')
85 		last--;
86 
87 	(void) printf("%s: ", header);
88 	for (i = 0; i < last; i++) {
89 		if (isascii(str[i]) != 0 && isalnum(str[i]) != 0) {
90 			(void) putchar(str[i]);
91 		} else {
92 			(void) printf("\\x%x", str[i]);
93 		}
94 	}
95 
96 	(void) putchar('\n');
97 }
98 
99 static char *
100 smp_get_result(smp_result_t result)
101 {
102 	switch (result) {
103 	case SMP_RES_FUNCTION_ACCEPTED:
104 		return ("Function accepted");
105 		break;
106 	case SMP_RES_UNKNOWN_FUNCTION:
107 		return ("Unknown function");
108 		break;
109 	case SMP_RES_FUNCTION_FAILED:
110 		return ("Function failed");
111 		break;
112 	case SMP_RES_INVALID_REQUEST_FRAME_LENGTH:
113 		return ("Invalid request frame length");
114 		break;
115 	case SMP_RES_INVALID_EXPANDER_CHANGE_COUNT:
116 		return ("Invalid expander change count");
117 		break;
118 	case SMP_RES_BUSY:
119 		return ("Busy");
120 		break;
121 	case SMP_RES_INCOMPLETE_DESCRIPTOR_LIST:
122 		return ("Incomplete descriptor list");
123 		break;
124 	case SMP_RES_PHY_DOES_NOT_EXIST:
125 		return ("PHY does not exist");
126 		break;
127 	case SMP_RES_INDEX_DOES_NOT_EXIST:
128 		return ("Index does not exist");
129 		break;
130 	case SMP_RES_PHY_DOES_NOT_SUPPORT_SATA:
131 		return ("PHY does not support SATA");
132 		break;
133 	case SMP_RES_UNKNOWN_PHY_OPERATION:
134 		return ("Unknown PHY operation");
135 		break;
136 	case SMP_RES_UNKNOWN_PHY_TEST_FUNCTION:
137 		return ("Unknown PHY test function");
138 		break;
139 	case SMP_RES_PHY_TEST_IN_PROGRESS:
140 		return ("PHY test in progress");
141 		break;
142 	case SMP_RES_PHY_VACANT:
143 		return ("PHY vacant");
144 		break;
145 	case SMP_RES_UNKNOWN_PHY_EVENT_SOURCE:
146 		return ("Unknown PHY event source");
147 		break;
148 	case SMP_RES_UNKNOWN_DESCRIPTOR_TYPE:
149 		return ("Unknown descriptor type");
150 		break;
151 	case SMP_RES_UNKNOWN_PHY_FILTER:
152 		return ("Unknown PHY filter");
153 		break;
154 	case SMP_RES_AFFILIATION_VIOLATION:
155 		return ("Affiliation violation");
156 		break;
157 	case SMP_RES_ZONE_VIOLATION:
158 		return ("Zone violation");
159 		break;
160 	case SMP_RES_NO_MANAGEMENT_ACCESS_RIGHTS:
161 		return ("No management access rights");
162 		break;
163 	case SMP_RES_UNKNOWN_ENABLE_DISABLE_ZONING:
164 		return ("Unknown enable/disable zoning value");
165 		break;
166 	case SMP_RES_ZONE_LOCK_VIOLATION:
167 		return ("Zone lock violation");
168 		break;
169 	case SMP_RES_NOT_ACTIVATED:
170 		return ("Not activated");
171 		break;
172 	case SMP_RES_ZONE_GROUP_OUT_OF_RANGE:
173 		return ("Zone group out of range");
174 		break;
175 	case SMP_RES_NO_PHYSICAL_PRESENCE:
176 		return ("No physical presence");
177 		break;
178 	case SMP_RES_SAVING_NOT_SUPPORTED:
179 		return ("Saving not supported");
180 		break;
181 	case SMP_RES_SOURCE_ZONE_GROUP_DNE:
182 		return ("Source zone group does not exist");
183 		break;
184 	case SMP_RES_DISABLED_PW_NOT_SUPPORTED:
185 		return ("Disabled password not supported");
186 		break;
187 	default:
188 		break;
189 	}
190 
191 	return (NULL);
192 }
193 
194 static void
195 smp_execute()
196 {
197 	if (smp_exec(ap, tp) != 0) {
198 		smp_close(tp);
199 		smp_action_free(ap);
200 		smp_fini();
201 		fatal(-4, "exec failed: %s", smp_errmsg());
202 	}
203 }
204 
205 static void
206 smp_cmd_failed(smp_result_t result)
207 {
208 	char *smp_result_str = smp_get_result(result);
209 
210 	if (smp_result_str == NULL) {
211 		fatal(-5, "Command failed: Unknown result (0x%x)",
212 		    result);
213 	} else {
214 		fatal(-5, "Command failed: %s", smp_result_str);
215 	}
216 }
217 
218 static void
219 smp_get_response(boolean_t close_on_fail)
220 {
221 	smp_action_get_response(ap, &result, (void **)&smp_resp, &smp_resp_len);
222 
223 	if (close_on_fail && (result != SMP_RES_FUNCTION_ACCEPTED)) {
224 		smp_close(tp);
225 		smp_action_free(ap);
226 		smp_fini();
227 		smp_cmd_failed(result);
228 	}
229 }
230 
231 static void
232 smp_cleanup()
233 {
234 	if (tp) {
235 		smp_close(tp);
236 		tp = NULL;
237 	}
238 	smp_action_free(ap);
239 	smp_fini();
240 }
241 
242 /* ARGSUSED */
243 static void
244 smp_handle_report_route_info(int argc, char *argv[])
245 {
246 	smp_report_route_info_req_t *rp;
247 	smp_report_route_info_resp_t *rirp;
248 	uint16_t route_indexes = smp_target_get_exp_route_indexes(tp);
249 	uint8_t num_phys = smp_target_get_number_of_phys(tp);
250 	uint16_t rt_idx_req, ri_idx, ri_end;
251 	uint8_t phy_id_req, pi_idx, pi_end;
252 	boolean_t enabled_entries = B_FALSE;
253 
254 	/*
255 	 * Verify the expander supports the PHY-based expander route table
256 	 */
257 	if (route_indexes == 0) {
258 		smp_cleanup();
259 		fatal(-6, "Expander does not support PHY-based route table\n");
260 	}
261 
262 	rt_idx_req = strtol(argv[3], NULL, 0);
263 	phy_id_req = strtol(argv[4], NULL, 0);
264 
265 	if (((int16_t)rt_idx_req == -1) && ((int8_t)phy_id_req == -1)) {
266 		ri_idx = 0;
267 		ri_end = route_indexes - 1;
268 		pi_idx = 0;
269 		pi_end = num_phys - 1;
270 	} else if (((int16_t)rt_idx_req < 0) || (rt_idx_req >= route_indexes) ||
271 	    ((int8_t)phy_id_req < 0) || (phy_id_req >= num_phys)) {
272 		smp_cleanup();
273 		fatal(-1, "Invalid route index (%d) or PHY ID (%d)\n",
274 		    rt_idx_req, phy_id_req);
275 	} else {
276 		ri_end = ri_idx = rt_idx_req;
277 		pi_end = pi_idx = phy_id_req;
278 	}
279 
280 	(void) printf("%6s %6s %3s %14s\n",
281 	    "RT Idx", "PHY ID", "DIS", "Routed SASAddr");
282 
283 	smp_action_get_request(ap, (void **)&rp, NULL);
284 
285 	while (ri_idx <= ri_end) {
286 		while (pi_idx <= pi_end) {
287 			rp->srrir_phy_identifier = pi_idx;
288 			rp->srrir_exp_route_index = ri_idx;
289 
290 			smp_execute();
291 			smp_get_response(B_FALSE);
292 
293 			if (result != SMP_RES_FUNCTION_ACCEPTED) {
294 				pi_idx++;
295 				continue;
296 			}
297 
298 			rirp = (smp_report_route_info_resp_t *)smp_resp;
299 
300 			if (rirp->srrir_exp_route_entry_disabled == 0) {
301 				enabled_entries = B_TRUE;
302 				(void) printf("%6d %6d %3d %016llx\n",
303 				    rirp->srrir_exp_route_index,
304 				    rirp->srrir_phy_identifier,
305 				    rirp->srrir_exp_route_entry_disabled,
306 				    BE_64(rirp->srrir_routed_sas_addr));
307 			}
308 
309 			pi_idx++;
310 		}
311 
312 		ri_idx++;
313 		pi_idx = 0;
314 	}
315 
316 	if (!enabled_entries) {
317 		(void) printf("No enabled entries in the table.\n");
318 	}
319 
320 	smp_cleanup();
321 	exit(0);
322 }
323 
324 static char *
325 smp_phy_event_src_str(smp_phy_event_source_t src, boolean_t *peak_detector)
326 {
327 	char *src_str;
328 
329 	*peak_detector = B_FALSE;
330 
331 	switch (src) {
332 	case SMP_PHY_EVENT_NO_EVENT:
333 		src_str = "No event";
334 		break;
335 	case SMP_PHY_EVENT_INVALID_DWORD_COUNT:
336 		src_str = "Invalid DWORD count";
337 		break;
338 	case SMP_PHY_EVENT_RUNNING_DISPARITY_ERROR_COUNT:
339 		src_str = "Running disparity error count";
340 		break;
341 	case SMP_PHY_EVENT_LOSS_OF_DWORD_SYNC_COUNT:
342 		src_str = "Loss of DWORD sync count";
343 		break;
344 	case SMP_PHY_EVENT_PHY_RESET_PROBLEM_COUNT:
345 		src_str = "PHY reset problem count";
346 		break;
347 	case SMP_PHY_EVENT_ELASTICITY_BUFFER_OVERFLOW_COUNT:
348 		src_str = "Elasticity buffer overflow count";
349 		break;
350 	case SMP_PHY_EVENT_RX_ERROR_COUNT:
351 		src_str = "Received ERROR count";
352 		break;
353 	case SMP_PHY_EVENT_RX_ADDR_FRAME_ERROR_COUNT:
354 		src_str = "Received address frame error count";
355 		break;
356 	case SMP_PHY_EVENT_TX_ABANDON_CLASS_OPEN_REJ_COUNT:
357 		src_str = "Transmitted abandon-class OPEN_REJECT count";
358 		break;
359 	case SMP_PHY_EVENT_RX_ABANDON_CLASS_OPEN_REJ_COUNT:
360 		src_str = "Received abandon-class OPEN_REJECT count";
361 		break;
362 	case SMP_PHY_EVENT_TX_RETRY_CLASS_OPEN_REJ_COUNT:
363 		src_str = "Transmitted retry-class OPEN_REJECT count";
364 		break;
365 	case SMP_PHY_EVENT_RX_RETRY_CLASS_OPEN_REJ_COUNT:
366 		src_str = "Received retry-class OPEN_REJECT count";
367 		break;
368 	case SMP_PHY_EVENT_RX_AIP_W_O_PARTIAL_COUNT:
369 		src_str = "Received AIP (WAITING ON PARTIAL) count";
370 		break;
371 	case SMP_PHY_EVENT_RX_AIP_W_O_CONN_COUNT:
372 		src_str = "Received AIP (WAITING ON CONNECTION) count";
373 		break;
374 	case SMP_PHY_EVENT_TX_BREAK_COUNT:
375 		src_str = "Transmitted BREAK count";
376 		break;
377 	case SMP_PHY_EVENT_RX_BREAK_COUNT:
378 		src_str = "Received BREAK count";
379 		break;
380 	case SMP_PHY_EVENT_BREAK_TIMEOUT_COUNT:
381 		src_str = "BREAK timeout count";
382 		break;
383 	case SMP_PHY_EVENT_CONNECTION_COUNT:
384 		src_str = "Connection count";
385 		break;
386 	case SMP_PHY_EVENT_PEAK_TX_PATHWAY_BLOCKED_COUNT:
387 		src_str = "Peak transmitted pathway blocked count";
388 		*peak_detector = B_TRUE;
389 		break;
390 	case SMP_PHY_EVENT_PEAK_TX_ARB_WAIT_TIME:
391 		src_str = "Peak transmitted arbitration wait time";
392 		*peak_detector = B_TRUE;
393 		break;
394 	case SMP_PHY_EVENT_PEAK_ARB_TIME:
395 		src_str = "Peak arbitration time";
396 		*peak_detector = B_TRUE;
397 		break;
398 	case SMP_PHY_EVENT_PEAK_CONNECTION_TIME:
399 		src_str = "Peak connection time";
400 		*peak_detector = B_TRUE;
401 		break;
402 	case SMP_PHY_EVENT_TX_SSP_FRAME_COUNT:
403 		src_str = "Transmitted SSP frame count";
404 		break;
405 	case SMP_PHY_EVENT_RX_SSP_FRAME_COUNT:
406 		src_str = "Received SSP frame count";
407 		break;
408 	case SMP_PHY_EVENT_TX_SSP_FRAME_ERROR_COUNT:
409 		src_str = "Transmitted SSP frame error count";
410 		break;
411 	case SMP_PHY_EVENT_RX_SSP_FRAME_ERROR_COUNT:
412 		src_str = "Received SSP frame error count";
413 		break;
414 	case SMP_PHY_EVENT_TX_CREDIT_BLOCKED_COUNT:
415 		src_str = "Transmitted CREDIT_BLOCKED count";
416 		break;
417 	case SMP_PHY_EVENT_RX_CREDIT_BLOCKED_COUNT:
418 		src_str = "Received CREDIT_BLOCKED count";
419 		break;
420 	case SMP_PHY_EVENT_TX_SATA_FRAME_COUNT:
421 		src_str = "Transmitted SATA frame count";
422 		break;
423 	case SMP_PHY_EVENT_RX_SATA_FRAME_COUNT:
424 		src_str = "Received SATA frame count";
425 		break;
426 	case SMP_PHY_EVENT_SATA_FLOW_CTRL_BUF_OVERFLOW_COUNT:
427 		src_str = "SATA flow control buffer overflow count";
428 		break;
429 	case SMP_PHY_EVENT_TX_SMP_FRAME_COUNT:
430 		src_str = "Transmitted SMP frame count";
431 		break;
432 	case SMP_PHY_EVENT_RX_SMP_FRAME_COUNT:
433 		src_str = "Received SMP frame count";
434 		break;
435 	case SMP_PHY_EVENT_RX_SMP_FRAME_ERROR_COUNT:
436 		src_str = "Received SMP frame error count";
437 		break;
438 	default:
439 		src_str = "<Unknown>";
440 		break;
441 	}
442 
443 	return (src_str);
444 }
445 
446 static void
447 smp_validate_args(int argc, char *argv[])
448 {
449 	errno = 0;
450 
451 	if (argc < 3)
452 		fatal(-1, "Usage: %s <device> <function> ...\n", argv[0]);
453 
454 	func = strtoul(argv[2], NULL, 0);
455 
456 	if (errno != 0)
457 		fatal(-1, "Usage: %s <device> <function> ...\n", argv[0]);
458 
459 	switch (func) {
460 	case SMP_FUNC_REPORT_GENERAL:
461 	case SMP_FUNC_REPORT_MANUFACTURER_INFO:
462 		if (argc != 3) {
463 			fatal(-1, "Usage: %s <device> 0x%x\n", argv[0], func);
464 		}
465 		break;
466 	case SMP_FUNC_DISCOVER:
467 	case SMP_FUNC_REPORT_PHY_EVENT:
468 	case SMP_FUNC_REPORT_PHY_ERROR_LOG: {
469 		if (argc != 4) {
470 			fatal(-1,
471 			    "Usage: %s <device> 0x%x <phy identifier>\n",
472 			    argv[0], func);
473 		}
474 		break;
475 	}
476 	case SMP_FUNC_REPORT_EXP_ROUTE_TABLE_LIST: {
477 		if (argc < 4) {
478 			fatal(-1,
479 			    "Usage: %s <device> 0x%x <SAS Address Index>\n",
480 			    argv[0], func);
481 		}
482 		break;
483 	}
484 	case SMP_FUNC_REPORT_ZONE_MANAGER_PASSWORD: {
485 		if (argc < 4) {
486 			fatal(-1,
487 			    "Usage: %s <device> 0x%x <report type>\n",
488 			    argv[0], func);
489 		}
490 		break;
491 	}
492 	case SMP_FUNC_ENABLE_DISABLE_ZONING: {
493 		if (argc != 4) {
494 			fatal(-1,
495 			    "Usage: %s <device> 0x%x "
496 			    "[0(no change) | 1(enable)| 2(disable)]\n",
497 			    argv[0], func);
498 		}
499 		break;
500 	}
501 	case SMP_FUNC_REPORT_BROADCAST: {
502 		if (argc != 4) {
503 			fatal(-1, "Usage: %s <device> 0x%x <bcast type>\n",
504 			    argv[0], func);
505 		}
506 		break;
507 	}
508 	case SMP_FUNC_REPORT_ROUTE_INFO: {
509 		if (argc != 5) {
510 			fatal(-1,
511 			    "Usage: %s <device> 0x%x <exp_route_idx> "
512 			    "<phy_identifier>\n", argv[0], func);
513 		}
514 		break;
515 	}
516 	case SMP_FUNC_PHY_CONTROL: {
517 		if (argc != 5) {
518 			fatal(-1,
519 			    "Usage: %s <device> 0x%x <phy identifier> "
520 			    " <phy operation>\n",
521 			    argv[0], func);
522 		}
523 		break;
524 	}
525 	default: {
526 		fatal(-1, "Usage: %s <device> <function> ...\n", argv[0]);
527 		break;
528 	}
529 	}
530 }
531 
532 int
533 main(int argc, char *argv[])
534 {
535 	uint_t i, j;
536 	char *yesorno;
537 	uint16_t exp_change_count;
538 
539 	/*
540 	 * If the arguments are invalid, this function will not return.
541 	 */
542 	smp_validate_args(argc, argv);
543 
544 	if (smp_init(LIBSMP_VERSION) != 0)
545 		fatal(-1, "libsmp initialization failed: %s", smp_errmsg());
546 
547 	bzero(&tdef, sizeof (smp_target_def_t));
548 	tdef.std_def = argv[1];
549 
550 	if ((tp = smp_open(&tdef)) == NULL) {
551 		smp_fini();
552 		fatal(-2, "failed to open %s: %s", argv[1], smp_errmsg());
553 	}
554 
555 	exp_change_count = smp_target_get_change_count(tp);
556 
557 	(void) printf("%s\n", argv[0]);
558 	(void) printf("\tSAS Address: %016llx\n", smp_target_addr(tp));
559 	(void) printf("\tVendor/Product/Revision: %s/%s/%s\n",
560 	    smp_target_vendor(tp), smp_target_product(tp),
561 	    smp_target_revision(tp));
562 	(void) printf("\tExp Vendor/ID/Rev: %s/%04x/%02x\n",
563 	    smp_target_component_vendor(tp), smp_target_component_id(tp),
564 	    smp_target_component_revision(tp));
565 	(void) printf("\tExpander change count: 0x%04x\n", exp_change_count);
566 
567 	ap = smp_action_alloc(func, tp, 0);
568 	if (ap == NULL) {
569 		smp_close(tp);
570 		smp_fini();
571 		fatal(-3, "failed to allocate action: %s", smp_errmsg());
572 	}
573 
574 	switch (func) {
575 	case SMP_FUNC_REPORT_GENERAL:
576 	case SMP_FUNC_REPORT_MANUFACTURER_INFO:
577 		/*
578 		 * These functions have no additional request bytes. therefore
579 		 * there is nothing for us to get and fill in here.
580 		 */
581 		break;
582 	case SMP_FUNC_DISCOVER: {
583 		smp_discover_req_t *dp;
584 
585 		smp_action_get_request(ap, (void **)&dp, NULL);
586 		dp->sdr_phy_identifier = strtoul(argv[3], NULL, 0);
587 		break;
588 	}
589 	case SMP_FUNC_REPORT_ROUTE_INFO: {
590 		smp_handle_report_route_info(argc, argv);
591 		break;
592 	}
593 	case SMP_FUNC_ENABLE_DISABLE_ZONING: {
594 		smp_enable_disable_zoning_req_t *rp;
595 
596 		smp_action_get_request(ap, (void **)&rp, NULL);
597 		rp->sedzr_enable_disable_zoning = strtoul(argv[3], NULL, 0);
598 		break;
599 	}
600 	case SMP_FUNC_PHY_CONTROL: {
601 		smp_phy_control_req_t *rp;
602 
603 		smp_action_get_request(ap, (void **)&rp, NULL);
604 		rp->spcr_phy_identifier = strtoul(argv[3], NULL, 0);
605 		rp->spcr_phy_operation = strtoul(argv[4], NULL, 0);
606 		break;
607 	}
608 	case SMP_FUNC_REPORT_EXP_ROUTE_TABLE_LIST: {
609 		smp_report_exp_route_table_list_req_t *rp;
610 
611 		smp_action_get_request(ap, (void **)&rp, NULL);
612 		SCSI_WRITE16(&rp->srertlr_max_descrs, 64);
613 		SCSI_WRITE16(&rp->srertlr_starting_routed_sas_addr_index,
614 		    strtoull(argv[3], NULL, 0));
615 		rp->srertlr_starting_phy_identifier = 0;
616 		break;
617 	}
618 	case SMP_FUNC_REPORT_PHY_ERROR_LOG: {
619 		smp_report_phy_error_log_req_t *pelp;
620 
621 		smp_action_get_request(ap, (void **)&pelp, NULL);
622 		pelp->srpelr_phy_identifier = strtoul(argv[3], NULL, 0);
623 		break;
624 	}
625 	case SMP_FUNC_REPORT_PHY_EVENT: {
626 		smp_report_phy_event_req_t *rpep;
627 
628 		smp_action_get_request(ap, (void **)&rpep, NULL);
629 		rpep->srper_phy_identifier = strtoul(argv[3], NULL, 0);
630 		break;
631 	}
632 	case SMP_FUNC_REPORT_ZONE_MANAGER_PASSWORD: {
633 		smp_report_zone_mgr_password_req_t *rzmprp;
634 
635 		smp_action_get_request(ap, (void **)&rzmprp, NULL);
636 		rzmprp->srzmpr_rpt_type = strtoul(argv[3], NULL, 0);
637 		break;
638 	}
639 	case SMP_FUNC_REPORT_BROADCAST: {
640 		smp_report_broadcast_req_t *rbrp;
641 
642 		smp_action_get_request(ap, (void **)&rbrp, NULL);
643 		rbrp->srbr_broadcast_type = strtoul(argv[3], NULL, 0);
644 		break;
645 	}
646 	default:
647 		smp_close(tp);
648 		smp_action_free(ap);
649 		smp_fini();
650 		smp_cmd_failed(result);
651 	}
652 
653 	smp_execute();
654 	smp_get_response(B_TRUE);
655 
656 	switch (func) {
657 	case SMP_FUNC_REPORT_GENERAL: {
658 		smp_report_general_resp_t *gr =
659 		    (smp_report_general_resp_t *)smp_resp;
660 
661 		(void) printf("Expander Route Indexes: %u\n",
662 		    SCSI_READ16(&gr->srgr_exp_route_indexes));
663 		(void) printf("Long Responses: %s\n",
664 		    gr->srgr_long_response != 0 ? "Supported" : "Unsupported");
665 		(void) printf("Phys: %d\n", gr->srgr_number_of_phys);
666 		(void) printf("Features:\n");
667 		if (gr->srgr_externally_configurable_route_table != 0) {
668 			(void) printf("\tExternally Configurable Route "
669 			    "Table\n");
670 		}
671 		if (gr->srgr_configuring != 0) {
672 			(void) printf("\tConfiguring\n");
673 		}
674 		if (gr->srgr_configures_others != 0) {
675 			(void) printf("\tConfigures Others\n");
676 		}
677 		if (gr->srgr_open_reject_retry_supported != 0) {
678 			(void) printf("\tOpen Reject Retry\n");
679 		}
680 		if (gr->srgr_stp_continue_awt != 0) {
681 			(void) printf("\tSTP Continue AWT\n");
682 		}
683 		if (gr->srgr_table_to_table_supported != 0) {
684 			(void) printf("\tTable to Table\n");
685 		}
686 
687 		(void) printf("Logical Identify: %016llx\n",
688 		    SCSI_READ64(&gr->srgr_enclosure_logical_identifier));
689 
690 		(void) printf("STP Bus Inactivity Time Limit: %u us\n",
691 		    SCSI_READ16(&gr->srgr_stp_bus_inactivity_time_limit) * 100);
692 		(void) printf("STP Maximum Connect Time Limit: %u us\n",
693 		    SCSI_READ16(&gr->srgr_stp_maximum_connect_time_limit) *
694 		    100);
695 		(void) printf("STP SMP I_T Nexus Loss Time: ");
696 		if (gr->srgr_stp_smp_nexus_loss_time == 0) {
697 			(void) printf("Vendor Specific\n");
698 		} else if (gr->srgr_stp_smp_nexus_loss_time == UINT16_MAX) {
699 			(void) printf("Retries Forever\n");
700 		} else {
701 			(void) printf("%u ms\n",
702 			    SCSI_READ16(&gr->srgr_stp_smp_nexus_loss_time));
703 		}
704 
705 		(void) printf("Physical Presence: %s, %s\n",
706 		    gr->srgr_physical_presence_supported ? "Supported" :
707 		    "Unsupported",
708 		    gr->srgr_physical_presence_asserted ? "Enabled" :
709 		    "Disabled");
710 
711 		(void) printf("Zoning:\n");
712 		if (gr->srgr_zoning_supported != 0) {
713 			(void) printf("\tSupported\n");
714 		} else {
715 			(void) printf("\tUnsupported\n");
716 		}
717 		if (gr->srgr_zoning_enabled != 0) {
718 			(void) printf("\tEnabled\n");
719 		} else {
720 			(void) printf("\tDisabled\n");
721 		}
722 		if (gr->srgr_zone_locked != 0) {
723 			(void) printf("\tLocked\n");
724 		} else {
725 			(void) printf("\tUnlocked\n");
726 		}
727 		if (gr->srgr_saving_zoning_enabled_supported != 0) {
728 			(void) printf("\tSaving Zoning Enabled Supported\n");
729 		}
730 		if (gr->srgr_saving_zone_perm_table_supported != 0) {
731 			(void) printf("\tSaving Zone Perm Table Supported\n");
732 		}
733 		if (gr->srgr_saving_zone_phy_info_supported != 0) {
734 			(void) printf("\tSaving Zone Phy Info Supported\n");
735 		}
736 		if (gr->srgr_saving != 0) {
737 			(void) printf("\tSaving\n");
738 		}
739 		(void) printf("\tActive Zone Manager SAS Address: %016llx\n",
740 		    SCSI_READ64(&gr->srgr_active_zm_sas_addr));
741 		(void) printf("\tZone Lock Inactivity Limit: %u ms\n",
742 		    SCSI_READ16(&gr->srgr_zone_lock_inactivity_limit) * 100);
743 
744 		(void) printf("Maximum Routed SAS Addresses: %u\n",
745 		    SCSI_READ16(&gr->srgr_max_routed_sas_addrs));
746 
747 		(void) printf("First Enclosure Connector Element Index: %u\n",
748 		    gr->srgr_first_encl_conn_elem_idx);
749 		(void) printf("Number of Enclosure Connector Elements: %u\n",
750 		    gr->srgr_number_encl_conn_elem_idxs);
751 
752 		if (gr->srgr_reduced_functionality != 0) {
753 			(void) printf("Time to Reduced Functionality: %u ms\n",
754 			    gr->srgr_time_to_reduced_functionality * 100);
755 		}
756 		(void) printf("Initial Time to Reduced Functionality: %u ms\n",
757 		    gr->srgr_initial_time_to_reduced_functionality * 100);
758 		(void) printf("Maximum Time to Reduced Functionality: %u ms\n",
759 		    gr->srgr_max_reduced_functionality_time * 100);
760 		(void) printf("Last Self-configuration Status Index: %u\n",
761 		    SCSI_READ16(&gr->srgr_last_self_conf_status_descr_idx));
762 		(void) printf("Maximum Stored Self-configuration Statuses: "
763 		    "%u\n", SCSI_READ16(
764 		    &gr->srgr_max_stored_self_config_status_descrs));
765 		(void) printf("Last Phy Event List Descriptor Index: %u\n",
766 		    SCSI_READ16(&gr->srgr_last_phy_event_list_descr_idx));
767 		(void) printf("Maximum Stored Phy Event List Descriptors: "
768 		    "%u\n", SCSI_READ16(
769 		    &gr->srgr_max_stored_phy_event_list_descrs));
770 		(void) printf("STP Reject to Open Limit: %u us\n",
771 		    SCSI_READ16(&gr->srgr_stp_reject_to_open_limit) * 10);
772 		break;
773 	}
774 	case SMP_FUNC_REPORT_MANUFACTURER_INFO: {
775 		smp_report_manufacturer_info_resp_t *mir =
776 		    (smp_report_manufacturer_info_resp_t *)smp_resp;
777 
778 		smp_print_ascii("Vendor", mir->srmir_vendor_identification,
779 		    sizeof (mir->srmir_vendor_identification));
780 		smp_print_ascii("Product", mir->srmir_product_identification,
781 		    sizeof (mir->srmir_product_identification));
782 		smp_print_ascii("Revision", mir->srmir_product_revision_level,
783 		    sizeof (mir->srmir_product_revision_level));
784 		/*
785 		 * The format of the following section was changed in the SAS
786 		 * 1.1 specification. If this bit is not present, it is vendor
787 		 * specific and therefore we don't print them.
788 		 */
789 		if (mir->srmir_sas_1_1_format == 0) {
790 			break;
791 		}
792 		smp_print_ascii("Component Vendor",
793 		    mir->srmir_component_vendor_identification,
794 		    sizeof (mir->srmir_component_vendor_identification));
795 		(void) printf("Component ID: 0x%x\n",
796 		    SCSI_READ16(&mir->srmir_component_id));
797 		(void) printf("Component Revision: 0x%x\n",
798 		    mir->srmir_component_revision_level);
799 		break;
800 	}
801 	case SMP_FUNC_DISCOVER: {
802 		smp_discover_resp_t *rp = (smp_discover_resp_t *)smp_resp;
803 		(void) printf("Addr: %016llx Phy: %02x\n",
804 		    SCSI_READ64(&rp->sdr_sas_addr), rp->sdr_phy_identifier);
805 		(void) printf("Peer: %016llx Phy: %02x\n",
806 		    SCSI_READ64(&rp->sdr_attached_sas_addr),
807 		    rp->sdr_attached_phy_identifier);
808 		(void) printf("Device type: %01x\n",
809 		    rp->sdr_attached_device_type);
810 		break;
811 	}
812 	case SMP_FUNC_REPORT_ZONE_MANAGER_PASSWORD: {
813 		smp_report_zone_mgr_password_resp_t *rp =
814 		    (smp_report_zone_mgr_password_resp_t *)smp_resp;
815 		char *rpt_type = NULL;
816 		int idx;
817 		switch (rp->srzmpr_rpt_type) {
818 			case SMP_ZMP_TYPE_CURRENT:
819 				rpt_type = "Current";
820 				break;
821 			case SMP_ZMP_TYPE_SAVED:
822 				rpt_type = "Saved";
823 				break;
824 			case SMP_ZMP_TYPE_DEFAULT:
825 				rpt_type = "Default";
826 				break;
827 			default:
828 				rpt_type = "(Unknown Type)";
829 				break;
830 		}
831 		(void) printf("%s zone manager password: 0x", rpt_type);
832 		for (idx = 0; idx < 32; idx++) {
833 			(void) printf("%02x",
834 			    rp->srzmpr_zone_mgr_password[idx]);
835 		}
836 		(void) printf("\n");
837 		break;
838 	}
839 	case SMP_FUNC_REPORT_EXP_ROUTE_TABLE_LIST: {
840 		smp_report_exp_route_table_list_resp_t *rtlr =
841 		    (smp_report_exp_route_table_list_resp_t *)smp_resp;
842 		smp_route_table_descr_t *descp = &rtlr->srertlr_descrs[0];
843 		int idx, idxx, ndescrs, zoning, startnum;
844 
845 		(void) printf("Expander change count: 0x%04x\n",
846 		    BE_16(rtlr->srertlr_exp_change_count));
847 		(void) printf("Expander route table change count: 0x%04x\n",
848 		    BE_16(rtlr->srertlr_route_table_change_count));
849 
850 		if (rtlr->srertlr_zoning_enabled) {
851 			yesorno = yes;
852 			zoning = 1;
853 		} else {
854 			yesorno = no;
855 			zoning = 0;
856 		}
857 		(void) printf("Zoning enabled: %s\n", yesorno);
858 
859 		if (rtlr->srertlr_configuring) {
860 			yesorno = yes;
861 		} else {
862 			yesorno = no;
863 		}
864 		(void) printf("Configuring: %s\n", yesorno);
865 
866 		ndescrs = rtlr->srertlr_n_descrs;
867 		(void) printf("Number of descriptors: %d\n", ndescrs);
868 		startnum = BE_16(rtlr->srertlr_first_routed_sas_addr_index);
869 		(void) printf("First/Last routed SAS address index: %d/%d\n",
870 		    startnum, BE_16(rtlr->srertlr_last_routed_sas_addr_index));
871 		(void) printf("Starting PHY identifier: %d\n",
872 		    rtlr->srertlr_starting_phy_identifier);
873 
874 		for (idx = 0; idx < ndescrs; idx++, descp++) {
875 			(void) printf("#%03d: Routed SAS addr: %016llx  ",
876 			    idx + startnum, BE_64(descp->srtd_routed_sas_addr));
877 			(void) printf("PHY bitmap: 0x");
878 			for (idxx = 0; idxx < 6; idxx++) {
879 				(void) printf("%02x",
880 				    descp->srtd_phy_bitmap[idxx]);
881 			}
882 			(void) printf("\n");
883 			if (zoning) {
884 				(void) printf("\tZone group: %d\n",
885 				    descp->srtd_zone_group);
886 			}
887 		}
888 
889 		(void) printf("\n");
890 		break;
891 	}
892 	case SMP_FUNC_REPORT_PHY_ERROR_LOG: {
893 		smp_report_phy_error_log_resp_t *pelr =
894 		    (smp_report_phy_error_log_resp_t *)smp_resp;
895 		(void) printf("PHY error log for PHY %d:\n",
896 		    pelr->srpelr_phy_identifier);
897 		(void) printf("\tInvalid DWORD count: %d\n",
898 		    BE_32(pelr->srpelr_invalid_dword_count));
899 		(void) printf("\tRunning disparity error count: %d\n",
900 		    BE_32(pelr->srpelr_running_disparity_error_count));
901 		(void) printf("\tLoss of DWORD sync count: %d\n",
902 		    BE_32(pelr->srpelr_loss_dword_sync_count));
903 		(void) printf("\tPHY reset problem count: %d\n",
904 		    BE_32(pelr->srpelr_phy_reset_problem_count));
905 		break;
906 	}
907 	case SMP_FUNC_REPORT_PHY_EVENT: {
908 		smp_report_phy_event_resp_t *rper =
909 		    (smp_report_phy_event_resp_t *)smp_resp;
910 		smp_phy_event_report_descr_t *perd =
911 		    &rper->srper_phy_event_descrs[0];
912 		boolean_t peak;
913 		int idx;
914 
915 		(void) printf("PHY event for PHY %d:\n",
916 		    rper->srper_phy_identifier);
917 		(void) printf("Number of PHY event descriptors: %d\n",
918 		    rper->srper_n_phy_event_descrs);
919 
920 		for (idx = 0; idx < rper->srper_n_phy_event_descrs; idx++) {
921 			(void) printf("%50s : %d\n",
922 			    smp_phy_event_src_str(perd->sped_phy_event_source,
923 			    &peak), BE_32(perd->sped_phy_event));
924 			if (peak) {
925 				(void) printf("\tPeak value detector "
926 				    "threshold: %d\n",
927 				    BE_32(perd->sped_peak_detector_threshold));
928 			}
929 			perd++;
930 		}
931 
932 		break;
933 	}
934 	case SMP_FUNC_REPORT_BROADCAST: {
935 		smp_report_broadcast_resp_t *brp =
936 		    (smp_report_broadcast_resp_t *)smp_resp;
937 		smp_broadcast_descr_t *bdp = &brp->srbr_descrs[0];
938 		uint16_t bcount, idx;
939 
940 		bcount = brp->srbr_number_broadcast_descrs;
941 
942 		(void) printf("\tNumber of broadcast descriptors: %d\n",
943 		    bcount);
944 		(void) printf("\t%7s %5s %5s %8s\n",
945 		    "BCType", "PhyID", "BCRsn", "BC Count");
946 		for (idx = 0; idx < bcount; idx++) {
947 			(void) printf("\t%7s %5s %5s %8s\n",
948 			    bdp->sbd_broadcast_type, bdp->sbd_phy_identifier,
949 			    bdp->sbd_broadcast_reason,
950 			    bdp->sbd_broadcast_count);
951 			bdp++;
952 		}
953 
954 		break;
955 	}
956 	default:
957 		(void) printf("Response: (len %d)\n", smp_resp_len);
958 		for (i = 0; i < smp_resp_len; i += 8) {
959 			(void) printf("%02x: ", i);
960 			for (j = i; j < i + 8; j++)
961 				if (j < smp_resp_len)
962 					(void) printf("%02x ", smp_resp[j]);
963 				else
964 					(void) printf("   ");
965 			for (j = i; j < i + 8; j++)
966 				(void) printf("%c",
967 				    j < smp_resp_len && isprint(smp_resp[j]) ?
968 				    smp_resp[j] : j < smp_resp_len ? '.' :
969 				    '\0');
970 			(void) printf("\n");
971 		}
972 		break;
973 	}
974 
975 	smp_cleanup();
976 	return (0);
977 }
978