]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mpsutil/mps_cmd.c
libarchive: merge security fix from vendor branch
[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 #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 MPI2_IOC_FACTS_REPLY *
725 mps_get_iocfacts(int fd)
726 {
727         MPI2_IOC_FACTS_REPLY *facts;
728         MPI2_IOC_FACTS_REQUEST req;
729         char msgver[8], sysctlname[128];
730         size_t len, factslen;
731         int error;
732
733         snprintf(sysctlname, sizeof(sysctlname), "dev.%s.%d.msg_version",
734             is_mps ? "mps" : "mpr", mps_unit);
735
736         factslen = sizeof(MPI2_IOC_FACTS_REPLY);
737         len = sizeof(msgver);
738         error = sysctlbyname(sysctlname, msgver, &len, NULL, 0);
739         if (error == 0) {
740                 if (strncmp(msgver, "2.6", sizeof(msgver)) == 0)
741                         factslen += 4;
742         }
743
744         facts = malloc(factslen);
745         if (facts == NULL) {
746                 errno = ENOMEM;
747                 return (NULL);
748         }
749
750         bzero(&req, factslen);
751         req.Function = MPI2_FUNCTION_IOC_FACTS;
752
753 #if 1
754         error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
755             facts, factslen, NULL, 0, NULL, 0, 10);
756 #else
757         error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
758             facts, factslen, NULL, 0, 0);
759 #endif
760         if (error) {
761                 free(facts);
762                 return (NULL);
763         }
764
765         if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
766                 free(facts);
767                 errno = EINVAL;
768                 return (NULL);
769         }
770         adjust_iocfacts_endianness(facts);
771         return (facts);
772 }
773
774 static void
775 adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts)
776 {
777         facts->MsgVersion = le16toh(facts->MsgVersion);
778         facts->HeaderVersion = le16toh(facts->HeaderVersion);
779         facts->Reserved1 = le16toh(facts->Reserved1);
780         facts->IOCExceptions = le16toh(facts->IOCExceptions);
781         facts->IOCStatus = le16toh(facts->IOCStatus);
782         facts->IOCLogInfo = le32toh(facts->IOCLogInfo);
783         facts->RequestCredit = le16toh(facts->RequestCredit);
784         facts->ProductID = le16toh(facts->ProductID);
785         facts->IOCCapabilities = le32toh(facts->IOCCapabilities);
786         facts->IOCRequestFrameSize =
787             le16toh(facts->IOCRequestFrameSize);
788         facts->FWVersion.Word = le32toh(facts->FWVersion.Word);
789         facts->MaxInitiators = le16toh(facts->MaxInitiators);
790         facts->MaxTargets = le16toh(facts->MaxTargets);
791         facts->MaxSasExpanders = le16toh(facts->MaxSasExpanders);
792         facts->MaxEnclosures = le16toh(facts->MaxEnclosures);
793         facts->ProtocolFlags = le16toh(facts->ProtocolFlags);
794         facts->HighPriorityCredit = le16toh(facts->HighPriorityCredit);
795         facts->MaxReplyDescriptorPostQueueDepth =
796             le16toh(facts->MaxReplyDescriptorPostQueueDepth);
797         facts->MaxDevHandle = le16toh(facts->MaxDevHandle);
798         facts->MaxPersistentEntries =
799             le16toh(facts->MaxPersistentEntries);
800         facts->MinDevHandle = le16toh(facts->MinDevHandle);
801 }