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