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