2 * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
4 * Copyright (c) 2015 Netflix, Inc.
5 * Written by: Scott Long <scottl@freebsd.org>
7 * Copyright (c) 2008 Yahoo!, Inc.
9 * Written by: John Baldwin <jhb@FreeBSD.org>
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
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>
42 #include <sys/endian.h>
52 #include <dev/mps/mps_ioctl.h>
53 #include <dev/mpr/mpr_ioctl.h>
55 #ifndef USE_MPT_IOCTLS
56 #define USE_MPT_IOCTLS
59 static const char *mps_ioc_status_codes[] = {
60 "Success", /* 0x0000 */
63 "Invalid scatter-gather list",
66 "Insufficient resources",
68 "Invalid state", /* 0x0008 */
69 "Operation state not supported",
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",
124 "Recovered SCSI error", /* 0x0040 */
126 "Invalid SCSI target ID",
127 "SCSI device not there",
129 "SCSI data underrun",
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",
138 "EEDP reference tag error",
139 "EEDP application tag error",
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",
163 "Invalid FC receive ID",
164 "FC did invalid", /* 0x0068 */
165 "FC node logged out",
166 "Transfer count mismatch",
168 "FC exchange canceled",
170 "Too much write data",
172 "ACK NAK timeout", /* 0x0070 */
188 "LAN device not found", /* 0x0080 */
189 "LAN device failure",
190 "LAN transmit error",
191 "LAN transmit aborted",
193 "LAN receive aborted",
194 "LAN partial packet",
204 "SAS SMP request failed", /* 0x0090 */
205 "SAS SMP data overrun",
212 "Inband aborted", /* 0x0098 */
213 "No inband connection",
220 "Diagnostic released", /* 0x00A0 */
223 struct mprs_pass_thru {
227 uint32_t RequestSize;
230 uint32_t DataDirection;
232 uint32_t DataOutSize;
236 struct mprs_btdh_mapping {
243 static void adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts);
246 mps_ioc_status(U16 IOCStatus)
248 static char buffer[16];
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);
258 #ifdef USE_MPT_IOCTLS
260 mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus, uint16_t *target)
263 struct mprs_btdh_mapping map;
266 map.TargetID = *target;
267 map.DevHandle = *devhandle;
269 if ((error = ioctl(fd, MPTIOCTL_BTDH_MAPPING, &map)) != 0) {
271 warn("Failed to map bus/target/device");
276 *target = map.TargetID;
277 *devhandle = map.DevHandle;
283 mps_set_slot_status(int fd, U16 handle, U16 slot, U32 status)
285 MPI2_SEP_REQUEST req;
286 MPI2_SEP_REPLY reply;
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;
294 req.SlotStatus = status;
296 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
297 NULL, 0, NULL, 0, 30) != 0)
300 if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus)))
306 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
307 MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
309 MPI2_CONFIG_REQUEST req;
310 MPI2_CONFIG_REPLY reply;
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;
319 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
320 NULL, 0, NULL, 0, 30))
323 if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus))) {
324 if (IOCStatus != NULL)
325 *IOCStatus = reply.IOCStatus;
330 *header = reply.Header;
335 mps_read_ext_config_page_header(int fd, U8 ExtPageType, U8 PageNumber, U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header, U16 *ExtPageLength, U16 *IOCStatus)
337 MPI2_CONFIG_REQUEST req;
338 MPI2_CONFIG_REPLY reply;
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);
348 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
349 NULL, 0, NULL, 0, 30))
352 if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus))) {
353 if (IOCStatus != NULL)
354 *IOCStatus = le16toh(reply.IOCStatus);
357 if ((header == NULL) || (ExtPageLength == NULL))
359 *header = reply.Header;
360 *ExtPageLength = reply.ExtPageLength;
365 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
368 MPI2_CONFIG_REQUEST req;
369 MPI2_CONFIG_PAGE_HEADER header;
370 MPI2_CONFIG_REPLY reply;
374 bzero(&header, sizeof(header));
375 error = mps_read_config_page_header(fd, PageType, PageNumber,
376 PageAddress, &header, IOCStatus);
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);
387 if (req.Header.PageLength == 0)
388 req.Header.PageLength = 4;
390 len = req.Header.PageLength * 4;
392 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
393 buf, len, NULL, 0, 30)) {
399 reply.IOCStatus = le16toh(reply.IOCStatus);
400 if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
401 if (IOCStatus != NULL)
402 *IOCStatus = reply.IOCStatus;
404 warnx("Reading config page failed: 0x%x %s",
405 reply.IOCStatus, mps_ioc_status(reply.IOCStatus));
414 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
415 U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
417 MPI2_CONFIG_REQUEST req;
418 MPI2_CONFIG_PAGE_HEADER header;
419 MPI2_CONFIG_REPLY reply;
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);
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);
440 pagelen = htole16(4);
441 req.ExtPageLength = pagelen;
442 req.ExtPageType = ExtPageType;
444 len = le16toh(pagelen) * 4;
446 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
447 buf, len, NULL, 0, 30)) {
453 reply.IOCStatus = le16toh(reply.IOCStatus);
454 if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
455 if (IOCStatus != NULL)
456 *IOCStatus = reply.IOCStatus;
458 warnx("Reading extended config page failed: %s",
459 mps_ioc_status(reply.IOCStatus));
468 mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios)
470 MPI2_FW_DOWNLOAD_REQUEST req;
471 MPI2_FW_DOWNLOAD_REPLY reply;
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;
480 if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
488 mps_firmware_get(int fd, unsigned char **firmware, bool bios)
490 MPI2_FW_UPLOAD_REQUEST req;
491 MPI2_FW_UPLOAD_REPLY reply;
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;
500 if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
504 if (reply.ActualImageSize == 0) {
508 size = le32toh(reply.ActualImageSize);
509 *firmware = calloc(size, sizeof(unsigned char));
510 if (*firmware == NULL) {
514 if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
515 *firmware, size, 0)) {
526 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
527 MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
529 struct mps_cfg_page_req req;
531 if (IOCStatus != NULL)
532 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
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)
541 if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
542 if (IOCStatus != NULL)
543 *IOCStatus = req.ioc_status;
546 bcopy(&req.header, header, sizeof(*header));
551 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
554 struct mps_cfg_page_req req;
558 error = mps_read_config_page_header(fd, PageType, PageNumber,
559 PageAddress, &req.header, IOCStatus);
565 if (req.header.PageLength == 0)
566 req.header.PageLength = 4;
567 req.len = req.header.PageLength * 4;
568 buf = malloc(req.len);
570 bcopy(&req.header, buf, sizeof(req.header));
571 if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) {
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;
582 warnx("Reading config page failed: 0x%x %s",
583 req.ioc_status, mps_ioc_status(req.ioc_status));
592 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
593 U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
595 struct mps_ext_cfg_page_req req;
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)
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;
613 warnx("Reading extended config page header failed: %s",
614 mps_ioc_status(req.ioc_status));
618 req.len = req.header.ExtPageLength * 4;
619 buf = malloc(req.len);
621 bcopy(&req.header, buf, sizeof(req.header));
622 if (ioctl(fd, MPSIO_READ_EXT_CFG_PAGE, &req) < 0) {
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;
633 warnx("Reading extended config page failed: %s",
634 mps_ioc_status(req.ioc_status));
646 char path[MAXPATHLEN];
648 snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit);
649 return (open(path, O_RDWR));
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)
656 struct mps_usr_command cmd;
658 bzero(&cmd, sizeof(struct mps_usr_command));
660 cmd.req_len = req_len;
662 cmd.rpl_len = reply_len;
667 if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0)
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)
677 struct mprs_pass_thru pass;
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;
690 pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
692 pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
694 } else if (datain_len) {
695 pass.PtrData = (uint64_t)(uintptr_t)data_in;
696 pass.DataSize = datain_len;
698 pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
700 pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
702 } else if (dataout_len) {
703 pass.PtrData = (uint64_t)(uintptr_t)data_out;
704 pass.DataSize = dataout_len;
706 pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
708 pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
712 pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
714 pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
717 pass.Timeout = timeout;
719 if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
724 /* Return the length in bytes of the device's MPI2_IOC_FACTS reply */
726 mps_get_ioc_factslen(int fd)
728 MPI2_IOC_FACTS_REQUEST req;
729 const size_t factslen = 4;
730 char factsbuf[4] = {0};
731 MPI2_IOC_FACTS_REPLY *facts = (MPI2_IOC_FACTS_REPLY*)factsbuf;
734 bzero(&req, sizeof(req));
735 req.Function = MPI2_FUNCTION_IOC_FACTS;
736 error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
737 factsbuf, factslen, NULL, 0, NULL, 0, 10);
742 /* The card's response is measured in dwords */
743 return (facts->MsgLength * 4);
746 MPI2_IOC_FACTS_REPLY *
747 mps_get_iocfacts(int fd)
749 MPI2_IOC_FACTS_REPLY *facts;
750 MPI2_IOC_FACTS_REQUEST req;
754 factslen = mps_get_ioc_factslen(fd);
758 facts = malloc(factslen);
764 bzero(&req, sizeof(req));
765 req.Function = MPI2_FUNCTION_IOC_FACTS;
768 error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
769 facts, factslen, NULL, 0, NULL, 0, 10);
771 error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
772 facts, factslen, NULL, 0, 0);
779 if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
784 adjust_iocfacts_endianness(facts);
789 adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts)
791 facts->MsgVersion = le16toh(facts->MsgVersion);
792 facts->HeaderVersion = le16toh(facts->HeaderVersion);
793 facts->Reserved1 = le16toh(facts->Reserved1);
794 facts->IOCExceptions = le16toh(facts->IOCExceptions);
795 facts->IOCStatus = le16toh(facts->IOCStatus);
796 facts->IOCLogInfo = le32toh(facts->IOCLogInfo);
797 facts->RequestCredit = le16toh(facts->RequestCredit);
798 facts->ProductID = le16toh(facts->ProductID);
799 facts->IOCCapabilities = le32toh(facts->IOCCapabilities);
800 facts->IOCRequestFrameSize =
801 le16toh(facts->IOCRequestFrameSize);
802 facts->FWVersion.Word = le32toh(facts->FWVersion.Word);
803 facts->MaxInitiators = le16toh(facts->MaxInitiators);
804 facts->MaxTargets = le16toh(facts->MaxTargets);
805 facts->MaxSasExpanders = le16toh(facts->MaxSasExpanders);
806 facts->MaxEnclosures = le16toh(facts->MaxEnclosures);
807 facts->ProtocolFlags = le16toh(facts->ProtocolFlags);
808 facts->HighPriorityCredit = le16toh(facts->HighPriorityCredit);
809 facts->MaxReplyDescriptorPostQueueDepth =
810 le16toh(facts->MaxReplyDescriptorPostQueueDepth);
811 facts->MaxDevHandle = le16toh(facts->MaxDevHandle);
812 facts->MaxPersistentEntries =
813 le16toh(facts->MaxPersistentEntries);
814 facts->MinDevHandle = le16toh(facts->MinDevHandle);