xref: /freebsd/usr.sbin/mpsutil/mps_cmd.c (revision 3706af423ff257540dfe88bcb91fb0ab54e60946)
1 /*-
2  * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
3  *
4  * Copyright (c) 2015 Netflix, Inc.
5  * Written by: Scott Long <scottl@freebsd.org>
6  *
7  * Copyright (c) 2008 Yahoo!, Inc.
8  * All rights reserved.
9  * Written by: John Baldwin <jhb@FreeBSD.org>
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the author nor the names of any co-contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 __RCSID("$FreeBSD$");
38 
39 #include <sys/param.h>
40 #include <sys/errno.h>
41 #include <sys/ioctl.h>
42 #include <sys/sysctl.h>
43 #include <sys/uio.h>
44 #include <sys/endian.h>
45 
46 #include <err.h>
47 #include <fcntl.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 #include "mpsutil.h"
54 #include <dev/mps/mps_ioctl.h>
55 #include <dev/mpr/mpr_ioctl.h>
56 
57 #ifndef USE_MPT_IOCTLS
58 #define USE_MPT_IOCTLS
59 #endif
60 
61 static const char *mps_ioc_status_codes[] = {
62 	"Success",				/* 0x0000 */
63 	"Invalid function",
64 	"Busy",
65 	"Invalid scatter-gather list",
66 	"Internal error",
67 	"Reserved",
68 	"Insufficient resources",
69 	"Invalid field",
70 	"Invalid state",			/* 0x0008 */
71 	"Operation state not supported",
72 	NULL,
73 	NULL,
74 	NULL,
75 	NULL,
76 	NULL,
77 	NULL,
78 	NULL,					/* 0x0010 */
79 	NULL,
80 	NULL,
81 	NULL,
82 	NULL,
83 	NULL,
84 	NULL,
85 	NULL,
86 	NULL,					/* 0x0018 */
87 	NULL,
88 	NULL,
89 	NULL,
90 	NULL,
91 	NULL,
92 	NULL,
93 	NULL,
94 	"Invalid configuration action",		/* 0x0020 */
95 	"Invalid configuration type",
96 	"Invalid configuration page",
97 	"Invalid configuration data",
98 	"No configuration defaults",
99 	"Unable to commit configuration change",
100 	NULL,
101 	NULL,
102 	NULL,					/* 0x0028 */
103 	NULL,
104 	NULL,
105 	NULL,
106 	NULL,
107 	NULL,
108 	NULL,
109 	NULL,
110 	NULL,					/* 0x0030 */
111 	NULL,
112 	NULL,
113 	NULL,
114 	NULL,
115 	NULL,
116 	NULL,
117 	NULL,
118 	NULL,					/* 0x0038 */
119 	NULL,
120 	NULL,
121 	NULL,
122 	NULL,
123 	NULL,
124 	NULL,
125 	NULL,
126 	"Recovered SCSI error",			/* 0x0040 */
127 	"Invalid SCSI bus",
128 	"Invalid SCSI target ID",
129 	"SCSI device not there",
130 	"SCSI data overrun",
131 	"SCSI data underrun",
132 	"SCSI I/O error",
133 	"SCSI protocol error",
134 	"SCSI task terminated",			/* 0x0048 */
135 	"SCSI residual mismatch",
136 	"SCSI task management failed",
137 	"SCSI I/O controller terminated",
138 	"SCSI external controller terminated",
139 	"EEDP guard error",
140 	"EEDP reference tag error",
141 	"EEDP application tag error",
142 	NULL,					/* 0x0050 */
143 	NULL,
144 	NULL,
145 	NULL,
146 	NULL,
147 	NULL,
148 	NULL,
149 	NULL,
150 	NULL,					/* 0x0058 */
151 	NULL,
152 	NULL,
153 	NULL,
154 	NULL,
155 	NULL,
156 	NULL,
157 	NULL,
158 	"SCSI target priority I/O",		/* 0x0060 */
159 	"Invalid SCSI target port",
160 	"Invalid SCSI target I/O index",
161 	"SCSI target aborted",
162 	"No connection retryable",
163 	"No connection",
164 	"FC aborted",
165 	"Invalid FC receive ID",
166 	"FC did invalid",			/* 0x0068 */
167 	"FC node logged out",
168 	"Transfer count mismatch",
169 	"STS data not set",
170 	"FC exchange canceled",
171 	"Data offset error",
172 	"Too much write data",
173 	"IU too short",
174 	"ACK NAK timeout",			/* 0x0070 */
175 	"NAK received",
176 	NULL,
177 	NULL,
178 	NULL,
179 	NULL,
180 	NULL,
181 	NULL,
182 	NULL,					/* 0x0078 */
183 	NULL,
184 	NULL,
185 	NULL,
186 	NULL,
187 	NULL,
188 	NULL,
189 	NULL,
190 	"LAN device not found",			/* 0x0080 */
191 	"LAN device failure",
192 	"LAN transmit error",
193 	"LAN transmit aborted",
194 	"LAN receive error",
195 	"LAN receive aborted",
196 	"LAN partial packet",
197 	"LAN canceled",
198 	NULL,					/* 0x0088 */
199 	NULL,
200 	NULL,
201 	NULL,
202 	NULL,
203 	NULL,
204 	NULL,
205 	NULL,
206 	"SAS SMP request failed",		/* 0x0090 */
207 	"SAS SMP data overrun",
208 	NULL,
209 	NULL,
210 	NULL,
211 	NULL,
212 	NULL,
213 	NULL,
214 	"Inband aborted",			/* 0x0098 */
215 	"No inband connection",
216 	NULL,
217 	NULL,
218 	NULL,
219 	NULL,
220 	NULL,
221 	NULL,
222 	"Diagnostic released",			/* 0x00A0 */
223 };
224 
225 struct mprs_pass_thru {
226         uint64_t        PtrRequest;
227         uint64_t        PtrReply;
228         uint64_t        PtrData;
229         uint32_t        RequestSize;
230         uint32_t        ReplySize;
231         uint32_t        DataSize;
232         uint32_t        DataDirection;
233         uint64_t        PtrDataOut;
234         uint32_t        DataOutSize;
235         uint32_t        Timeout;
236 };
237 
238 struct mprs_btdh_mapping {
239         uint16_t        TargetID;
240         uint16_t        Bus;
241         uint16_t        DevHandle;
242         uint16_t        Reserved;
243 };
244 
245 static void adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts);
246 
247 const char *
248 mps_ioc_status(U16 IOCStatus)
249 {
250 	static char buffer[16];
251 
252 	IOCStatus &= MPI2_IOCSTATUS_MASK;
253 	if (IOCStatus < sizeof(mps_ioc_status_codes) / sizeof(char *) &&
254 	    mps_ioc_status_codes[IOCStatus] != NULL)
255 		return (mps_ioc_status_codes[IOCStatus]);
256 	snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
257 	return (buffer);
258 }
259 
260 #ifdef USE_MPT_IOCTLS
261 int
262 mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus, uint16_t *target)
263 {
264 	int error;
265 	struct mprs_btdh_mapping map;
266 
267 	map.Bus = *bus;
268 	map.TargetID = *target;
269 	map.DevHandle = *devhandle;
270 
271 	if ((error = ioctl(fd, MPTIOCTL_BTDH_MAPPING, &map)) != 0) {
272 		error = errno;
273 		warn("Failed to map bus/target/device");
274 		return (error);
275 	}
276 
277 	*bus = map.Bus;
278 	*target = map.TargetID;
279 	*devhandle = map.DevHandle;
280 
281 	return (0);
282 }
283 
284 int
285 mps_set_slot_status(int fd, U16 handle, U16 slot, U32 status)
286 {
287 	MPI2_SEP_REQUEST req;
288 	MPI2_SEP_REPLY reply;
289 
290 	bzero(&req, sizeof(req));
291 	req.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR;
292 	req.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS;
293 	req.Flags = MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS;
294 	req.EnclosureHandle = handle;
295 	req.Slot = slot;
296 	req.SlotStatus = status;
297 
298 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
299 	    NULL, 0, NULL, 0, 30) != 0)
300 		return (errno);
301 
302 	if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus)))
303 		return (EIO);
304 	return (0);
305 }
306 
307 int
308 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
309     MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
310 {
311 	MPI2_CONFIG_REQUEST req;
312 	MPI2_CONFIG_REPLY reply;
313 
314 	bzero(&req, sizeof(req));
315 	req.Function = MPI2_FUNCTION_CONFIG;
316 	req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
317 	req.Header.PageType = PageType;
318 	req.Header.PageNumber = PageNumber;
319 	req.PageAddress = PageAddress;
320 
321 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
322 	    NULL, 0, NULL, 0, 30))
323 		return (errno);
324 
325 	if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus))) {
326 		if (IOCStatus != NULL)
327 			*IOCStatus = reply.IOCStatus;
328 		return (EIO);
329 	}
330 	if (header == NULL)
331 		return (EINVAL);
332 	*header = reply.Header;
333 	return (0);
334 }
335 
336 int
337 mps_read_ext_config_page_header(int fd, U8 ExtPageType, U8 PageNumber, U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header, U16 *ExtPageLength, U16 *IOCStatus)
338 {
339 	MPI2_CONFIG_REQUEST req;
340 	MPI2_CONFIG_REPLY reply;
341 
342 	bzero(&req, sizeof(req));
343 	req.Function = MPI2_FUNCTION_CONFIG;
344 	req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
345 	req.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
346 	req.ExtPageType = ExtPageType;
347 	req.Header.PageNumber = PageNumber;
348 	req.PageAddress = htole32(PageAddress);
349 
350 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
351 	    NULL, 0, NULL, 0, 30))
352 		return (errno);
353 
354 	if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus))) {
355 		if (IOCStatus != NULL)
356 			*IOCStatus = le16toh(reply.IOCStatus);
357 		return (EIO);
358 	}
359 	if ((header == NULL) || (ExtPageLength == NULL))
360 		return (EINVAL);
361 	*header = reply.Header;
362 	*ExtPageLength = reply.ExtPageLength;
363 	return (0);
364 }
365 
366 void *
367 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
368     U16 *IOCStatus)
369 {
370 	MPI2_CONFIG_REQUEST req;
371 	MPI2_CONFIG_PAGE_HEADER header;
372 	MPI2_CONFIG_REPLY reply;
373 	void *buf;
374 	int error, len;
375 
376 	bzero(&header, sizeof(header));
377 	error = mps_read_config_page_header(fd, PageType, PageNumber,
378 	    PageAddress, &header, IOCStatus);
379 	if (error) {
380 		errno = error;
381 		return (NULL);
382 	}
383 
384 	bzero(&req, sizeof(req));
385 	req.Function = MPI2_FUNCTION_CONFIG;
386 	req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
387 	req.PageAddress = htole32(PageAddress);
388 	req.Header = header;
389 	if (req.Header.PageLength == 0)
390 		req.Header.PageLength = 4;
391 
392 	len = req.Header.PageLength * 4;
393 	buf = malloc(len);
394 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
395 	    buf, len, NULL, 0, 30)) {
396 		error = errno;
397 		free(buf);
398 		errno = error;
399 		return (NULL);
400 	}
401 	reply.IOCStatus = le16toh(reply.IOCStatus);
402 	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
403 		if (IOCStatus != NULL)
404 			*IOCStatus = reply.IOCStatus;
405 		else
406 			warnx("Reading config page failed: 0x%x %s",
407 			    reply.IOCStatus, mps_ioc_status(reply.IOCStatus));
408 		free(buf);
409 		errno = EIO;
410 		return (NULL);
411 	}
412 	return (buf);
413 }
414 
415 void *
416 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
417     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
418 {
419 	MPI2_CONFIG_REQUEST req;
420 	MPI2_CONFIG_PAGE_HEADER header;
421 	MPI2_CONFIG_REPLY reply;
422 	U16 pagelen;
423 	void *buf;
424 	int error, len;
425 
426 	if (IOCStatus != NULL)
427 		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
428 	bzero(&header, sizeof(header));
429 	error = mps_read_ext_config_page_header(fd, ExtPageType, PageNumber,
430 	    PageAddress, &header, &pagelen, IOCStatus);
431 	if (error) {
432 		errno = error;
433 		return (NULL);
434 	}
435 
436 	bzero(&req, sizeof(req));
437 	req.Function = MPI2_FUNCTION_CONFIG;
438 	req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
439 	req.PageAddress = htole32(PageAddress);
440 	req.Header = header;
441 	if (pagelen == 0)
442 		pagelen = htole16(4);
443 	req.ExtPageLength = pagelen;
444 	req.ExtPageType = ExtPageType;
445 
446 	len = le16toh(pagelen) * 4;
447 	buf = malloc(len);
448 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
449 	    buf, len, NULL, 0, 30)) {
450 		error = errno;
451 		free(buf);
452 		errno = error;
453 		return (NULL);
454 	}
455 	reply.IOCStatus = le16toh(reply.IOCStatus);
456 	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
457 		if (IOCStatus != NULL)
458 			*IOCStatus = reply.IOCStatus;
459 		else
460 			warnx("Reading extended config page failed: %s",
461 			    mps_ioc_status(reply.IOCStatus));
462 		free(buf);
463 		errno = EIO;
464 		return (NULL);
465 	}
466 	return (buf);
467 }
468 
469 int
470 mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios)
471 {
472 	MPI2_FW_DOWNLOAD_REQUEST req;
473 	MPI2_FW_DOWNLOAD_REPLY reply;
474 
475 	bzero(&req, sizeof(req));
476 	bzero(&reply, sizeof(reply));
477 	req.Function = MPI2_FUNCTION_FW_DOWNLOAD;
478 	req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
479 	req.TotalImageSize = htole32(len);
480 	req.MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT;
481 
482 	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
483 	    fw, len, 0)) {
484 		return (-1);
485 	}
486 	return (0);
487 }
488 
489 int
490 mps_firmware_get(int fd, unsigned char **firmware, bool bios)
491 {
492 	MPI2_FW_UPLOAD_REQUEST req;
493 	MPI2_FW_UPLOAD_REPLY reply;
494 	int size;
495 
496 	*firmware = NULL;
497 	bzero(&req, sizeof(req));
498 	bzero(&reply, sizeof(reply));
499 	req.Function = MPI2_FUNCTION_FW_UPLOAD;
500 	req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
501 
502 	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
503 	    NULL, 0, 0)) {
504 		return (-1);
505 	}
506 	if (reply.ActualImageSize == 0) {
507 		return (-1);
508 	}
509 
510 	size = le32toh(reply.ActualImageSize);
511 	*firmware = calloc(size, sizeof(unsigned char));
512 	if (*firmware == NULL) {
513 		warn("calloc");
514 		return (-1);
515 	}
516 	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
517 	    *firmware, size, 0)) {
518 		free(*firmware);
519 		return (-1);
520 	}
521 
522 	return (size);
523 }
524 
525 #else
526 
527 int
528 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
529     MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
530 {
531 	struct mps_cfg_page_req req;
532 
533 	if (IOCStatus != NULL)
534 		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
535 	if (header == NULL)
536 		return (EINVAL);
537 	bzero(&req, sizeof(req));
538 	req.header.PageType = PageType;
539 	req.header.PageNumber = PageNumber;
540 	req.page_address = PageAddress;
541 	if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0)
542 		return (errno);
543 	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
544 		if (IOCStatus != NULL)
545 			*IOCStatus = req.ioc_status;
546 		return (EIO);
547 	}
548 	bcopy(&req.header, header, sizeof(*header));
549 	return (0);
550 }
551 
552 void *
553 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
554     U16 *IOCStatus)
555 {
556 	struct mps_cfg_page_req req;
557 	void *buf;
558 	int error;
559 
560 	error = mps_read_config_page_header(fd, PageType, PageNumber,
561 	    PageAddress, &req.header, IOCStatus);
562 	if (error) {
563 		errno = error;
564 		return (NULL);
565 	}
566 
567 	if (req.header.PageLength == 0)
568 		req.header.PageLength = 4;
569 	req.len = req.header.PageLength * 4;
570 	buf = malloc(req.len);
571 	req.buf = buf;
572 	bcopy(&req.header, buf, sizeof(req.header));
573 	if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) {
574 		error = errno;
575 		free(buf);
576 		errno = error;
577 		return (NULL);
578 	}
579 	req.ioc_status = le16toh(req.ioc_status);
580 	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
581 		if (IOCStatus != NULL)
582 			*IOCStatus = req.ioc_status;
583 		else
584 			warnx("Reading config page failed: 0x%x %s",
585 			    req.ioc_status, mps_ioc_status(req.ioc_status));
586 		free(buf);
587 		errno = EIO;
588 		return (NULL);
589 	}
590 	return (buf);
591 }
592 
593 void *
594 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
595     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
596 {
597 	struct mps_ext_cfg_page_req req;
598 	void *buf;
599 	int error;
600 
601 	if (IOCStatus != NULL)
602 		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
603 	bzero(&req, sizeof(req));
604 	req.header.PageVersion = PageVersion;
605 	req.header.PageNumber = PageNumber;
606 	req.header.ExtPageType = ExtPageType;
607 	req.page_address = htole32(PageAddress);
608 	if (ioctl(fd, MPSIO_READ_EXT_CFG_HEADER, &req) < 0)
609 		return (NULL);
610 	req.ioc_status = le16toh(req.ioc_status);
611 	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
612 		if (IOCStatus != NULL)
613 			*IOCStatus = req.ioc_status;
614 		else
615 			warnx("Reading extended config page header failed: %s",
616 			    mps_ioc_status(req.ioc_status));
617 		errno = EIO;
618 		return (NULL);
619 	}
620 	req.len = req.header.ExtPageLength * 4;
621 	buf = malloc(req.len);
622 	req.buf = buf;
623 	bcopy(&req.header, buf, sizeof(req.header));
624 	if (ioctl(fd, MPSIO_READ_EXT_CFG_PAGE, &req) < 0) {
625 		error = errno;
626 		free(buf);
627 		errno = error;
628 		return (NULL);
629 	}
630 	req.ioc_status = le16toh(req.ioc_status);
631 	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
632 		if (IOCStatus != NULL)
633 			*IOCStatus = req.ioc_status;
634 		else
635 			warnx("Reading extended config page failed: %s",
636 			    mps_ioc_status(req.ioc_status));
637 		free(buf);
638 		errno = EIO;
639 		return (NULL);
640 	}
641 	return (buf);
642 }
643 #endif
644 
645 int
646 mps_open(int unit)
647 {
648 	char path[MAXPATHLEN];
649 
650 	snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit);
651 	return (open(path, O_RDWR));
652 }
653 
654 int
655 mps_user_command(int fd, void *req, uint32_t req_len, void *reply,
656         uint32_t reply_len, void *buffer, int len, uint32_t flags)
657 {
658 	struct mps_usr_command cmd;
659 
660 	bzero(&cmd, sizeof(struct mps_usr_command));
661 	cmd.req = req;
662 	cmd.req_len = req_len;
663 	cmd.rpl = reply;
664 	cmd.rpl_len = reply_len;
665 	cmd.buf = buffer;
666 	cmd.len = len;
667 	cmd.flags = flags;
668 
669 	if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0)
670 		return (errno);
671 	return (0);
672 }
673 
674 int
675 mps_pass_command(int fd, void *req, uint32_t req_len, void *reply,
676 	uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out,
677 	uint32_t dataout_len, uint32_t timeout)
678 {
679 	struct mprs_pass_thru pass;
680 
681 	bzero(&pass, sizeof(pass));
682 	pass.PtrRequest = (uint64_t)(uintptr_t)req;
683 	pass.PtrReply = (uint64_t)(uintptr_t)reply;
684 	pass.RequestSize = req_len;
685 	pass.ReplySize = reply_len;
686 	if (datain_len && dataout_len) {
687 		pass.PtrData = (uint64_t)(uintptr_t)data_in;
688 		pass.PtrDataOut = (uint64_t)(uintptr_t)data_out;
689 		pass.DataSize = datain_len;
690 		pass.DataOutSize = dataout_len;
691 		if (is_mps) {
692 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
693 		} else {
694 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
695 		}
696 	} else if (datain_len) {
697 		pass.PtrData = (uint64_t)(uintptr_t)data_in;
698 		pass.DataSize = datain_len;
699 		if (is_mps) {
700 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
701 		} else {
702 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
703 		}
704 	} else if (dataout_len) {
705 		pass.PtrData = (uint64_t)(uintptr_t)data_out;
706 		pass.DataSize = dataout_len;
707 		if (is_mps) {
708 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
709 		} else {
710 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
711 		}
712 	} else {
713 		if (is_mps) {
714 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
715 		} else {
716 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
717 		}
718 	}
719 	pass.Timeout = timeout;
720 
721 	if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
722 		return (errno);
723 	return (0);
724 }
725 
726 MPI2_IOC_FACTS_REPLY *
727 mps_get_iocfacts(int fd)
728 {
729 	MPI2_IOC_FACTS_REPLY *facts;
730 	MPI2_IOC_FACTS_REQUEST req;
731 	char msgver[8], sysctlname[128];
732 	size_t len, factslen;
733 	int error;
734 
735 	snprintf(sysctlname, sizeof(sysctlname), "dev.%s.%d.msg_version",
736 	    is_mps ? "mps" : "mpr", mps_unit);
737 
738 	factslen = sizeof(MPI2_IOC_FACTS_REPLY);
739 	len = sizeof(msgver);
740 	error = sysctlbyname(sysctlname, msgver, &len, NULL, 0);
741 	if (error == 0) {
742 		if (strncmp(msgver, "2.6", sizeof(msgver)) == 0)
743 			factslen += 4;
744 	}
745 
746 	facts = malloc(factslen);
747 	if (facts == NULL) {
748 		errno = ENOMEM;
749 		return (NULL);
750 	}
751 
752 	bzero(&req, factslen);
753 	req.Function = MPI2_FUNCTION_IOC_FACTS;
754 
755 #if 1
756 	error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
757 	    facts, factslen, NULL, 0, NULL, 0, 10);
758 #else
759 	error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
760 	    facts, factslen, NULL, 0, 0);
761 #endif
762 	if (error) {
763 		free(facts);
764 		return (NULL);
765 	}
766 
767 	if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
768 		free(facts);
769 		errno = EINVAL;
770 		return (NULL);
771 	}
772 	adjust_iocfacts_endianness(facts);
773 	return (facts);
774 }
775 
776 static void
777 adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts)
778 {
779 	facts->MsgVersion = le16toh(facts->MsgVersion);
780 	facts->HeaderVersion = le16toh(facts->HeaderVersion);
781 	facts->Reserved1 = le16toh(facts->Reserved1);
782 	facts->IOCExceptions = le16toh(facts->IOCExceptions);
783 	facts->IOCStatus = le16toh(facts->IOCStatus);
784 	facts->IOCLogInfo = le32toh(facts->IOCLogInfo);
785 	facts->RequestCredit = le16toh(facts->RequestCredit);
786 	facts->ProductID = le16toh(facts->ProductID);
787 	facts->IOCCapabilities = le32toh(facts->IOCCapabilities);
788 	facts->IOCRequestFrameSize =
789 	    le16toh(facts->IOCRequestFrameSize);
790 	facts->FWVersion.Word = le32toh(facts->FWVersion.Word);
791 	facts->MaxInitiators = le16toh(facts->MaxInitiators);
792 	facts->MaxTargets = le16toh(facts->MaxTargets);
793 	facts->MaxSasExpanders = le16toh(facts->MaxSasExpanders);
794 	facts->MaxEnclosures = le16toh(facts->MaxEnclosures);
795 	facts->ProtocolFlags = le16toh(facts->ProtocolFlags);
796 	facts->HighPriorityCredit = le16toh(facts->HighPriorityCredit);
797 	facts->MaxReplyDescriptorPostQueueDepth =
798 	    le16toh(facts->MaxReplyDescriptorPostQueueDepth);
799 	facts->MaxDevHandle = le16toh(facts->MaxDevHandle);
800 	facts->MaxPersistentEntries =
801 	    le16toh(facts->MaxPersistentEntries);
802 	facts->MinDevHandle = le16toh(facts->MinDevHandle);
803 }
804