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