]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mpsutil/mps_cmd.c
zfs: merge openzfs/zfs@a9d6b0690
[FreeBSD/FreeBSD.git] / usr.sbin / mpsutil / mps_cmd.c
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 }