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>
39 #include <sys/param.h>
40 #include <sys/errno.h>
41 #include <sys/ioctl.h>
42 #include <sys/sysctl.h>
53 #include <dev/mps/mps_ioctl.h>
54 #include <dev/mpr/mpr_ioctl.h>
56 #ifndef USE_MPT_IOCTLS
57 #define USE_MPT_IOCTLS
60 static const char *mps_ioc_status_codes[] = {
61 "Success", /* 0x0000 */
64 "Invalid scatter-gather list",
67 "Insufficient resources",
69 "Invalid state", /* 0x0008 */
70 "Operation state not supported",
93 "Invalid configuration action", /* 0x0020 */
94 "Invalid configuration type",
95 "Invalid configuration page",
96 "Invalid configuration data",
97 "No configuration defaults",
98 "Unable to commit configuration change",
125 "Recovered SCSI error", /* 0x0040 */
127 "Invalid SCSI target ID",
128 "SCSI device not there",
130 "SCSI data underrun",
132 "SCSI protocol error",
133 "SCSI task terminated", /* 0x0048 */
134 "SCSI residual mismatch",
135 "SCSI task management failed",
136 "SCSI I/O controller terminated",
137 "SCSI external controller terminated",
139 "EEDP reference tag error",
140 "EEDP application tag error",
157 "SCSI target priority I/O", /* 0x0060 */
158 "Invalid SCSI target port",
159 "Invalid SCSI target I/O index",
160 "SCSI target aborted",
161 "No connection retryable",
164 "Invalid FC receive ID",
165 "FC did invalid", /* 0x0068 */
166 "FC node logged out",
167 "Transfer count mismatch",
169 "FC exchange canceled",
171 "Too much write data",
173 "ACK NAK timeout", /* 0x0070 */
189 "LAN device not found", /* 0x0080 */
190 "LAN device failure",
191 "LAN transmit error",
192 "LAN transmit aborted",
194 "LAN receive aborted",
195 "LAN partial packet",
205 "SAS SMP request failed", /* 0x0090 */
206 "SAS SMP data overrun",
213 "Inband aborted", /* 0x0098 */
214 "No inband connection",
221 "Diagnostic released", /* 0x00A0 */
224 struct mprs_pass_thru {
228 uint32_t RequestSize;
231 uint32_t DataDirection;
233 uint32_t DataOutSize;
237 struct mprs_btdh_mapping {
245 mps_ioc_status(U16 IOCStatus)
247 static char buffer[16];
249 IOCStatus &= MPI2_IOCSTATUS_MASK;
250 if (IOCStatus < sizeof(mps_ioc_status_codes) / sizeof(char *) &&
251 mps_ioc_status_codes[IOCStatus] != NULL)
252 return (mps_ioc_status_codes[IOCStatus]);
253 snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
257 #ifdef USE_MPT_IOCTLS
259 mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus, uint16_t *target)
262 struct mprs_btdh_mapping map;
265 map.TargetID = *target;
266 map.DevHandle = *devhandle;
268 if ((error = ioctl(fd, MPTIOCTL_BTDH_MAPPING, &map)) != 0) {
270 warn("Failed to map bus/target/device");
275 *target = map.TargetID;
276 *devhandle = map.DevHandle;
282 mps_set_slot_status(int fd, U16 handle, U16 slot, U32 status)
284 MPI2_SEP_REQUEST req;
285 MPI2_SEP_REPLY reply;
287 bzero(&req, sizeof(req));
288 req.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR;
289 req.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS;
290 req.Flags = MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS;
291 req.EnclosureHandle = handle;
293 req.SlotStatus = status;
295 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
296 NULL, 0, NULL, 0, 30) != 0)
299 if (!IOC_STATUS_SUCCESS(reply.IOCStatus))
305 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
306 MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
308 MPI2_CONFIG_REQUEST req;
309 MPI2_CONFIG_REPLY reply;
311 bzero(&req, sizeof(req));
312 req.Function = MPI2_FUNCTION_CONFIG;
313 req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
314 req.Header.PageType = PageType;
315 req.Header.PageNumber = PageNumber;
316 req.PageAddress = PageAddress;
318 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
319 NULL, 0, NULL, 0, 30))
322 if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
323 if (IOCStatus != NULL)
324 *IOCStatus = reply.IOCStatus;
329 *header = reply.Header;
334 mps_read_ext_config_page_header(int fd, U8 ExtPageType, U8 PageNumber, U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header, U16 *ExtPageLength, U16 *IOCStatus)
336 MPI2_CONFIG_REQUEST req;
337 MPI2_CONFIG_REPLY reply;
339 bzero(&req, sizeof(req));
340 req.Function = MPI2_FUNCTION_CONFIG;
341 req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
342 req.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
343 req.ExtPageType = ExtPageType;
344 req.Header.PageNumber = PageNumber;
345 req.PageAddress = PageAddress;
347 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
348 NULL, 0, NULL, 0, 30))
351 if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
352 if (IOCStatus != NULL)
353 *IOCStatus = reply.IOCStatus;
356 if ((header == NULL) || (ExtPageLength == NULL))
358 *header = reply.Header;
359 *ExtPageLength = reply.ExtPageLength;
364 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
367 MPI2_CONFIG_REQUEST req;
368 MPI2_CONFIG_PAGE_HEADER header;
369 MPI2_CONFIG_REPLY reply;
373 bzero(&header, sizeof(header));
374 error = mps_read_config_page_header(fd, PageType, PageNumber,
375 PageAddress, &header, IOCStatus);
381 bzero(&req, sizeof(req));
382 req.Function = MPI2_FUNCTION_CONFIG;
383 req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
384 req.PageAddress = PageAddress;
386 if (req.Header.PageLength == 0)
387 req.Header.PageLength = 4;
389 len = req.Header.PageLength * 4;
391 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
392 buf, len, NULL, 0, 30)) {
398 if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
399 if (IOCStatus != NULL)
400 *IOCStatus = reply.IOCStatus;
402 warnx("Reading config page failed: 0x%x %s",
403 reply.IOCStatus, mps_ioc_status(reply.IOCStatus));
412 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
413 U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
415 MPI2_CONFIG_REQUEST req;
416 MPI2_CONFIG_PAGE_HEADER header;
417 MPI2_CONFIG_REPLY reply;
422 if (IOCStatus != NULL)
423 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
424 bzero(&header, sizeof(header));
425 error = mps_read_ext_config_page_header(fd, ExtPageType, PageNumber,
426 PageAddress, &header, &pagelen, IOCStatus);
432 bzero(&req, sizeof(req));
433 req.Function = MPI2_FUNCTION_CONFIG;
434 req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
435 req.PageAddress = PageAddress;
439 req.ExtPageLength = pagelen;
440 req.ExtPageType = ExtPageType;
444 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
445 buf, len, NULL, 0, 30)) {
451 if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
452 if (IOCStatus != NULL)
453 *IOCStatus = reply.IOCStatus;
455 warnx("Reading extended config page failed: %s",
456 mps_ioc_status(reply.IOCStatus));
465 mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios)
467 MPI2_FW_DOWNLOAD_REQUEST req;
468 MPI2_FW_DOWNLOAD_REPLY reply;
470 bzero(&req, sizeof(req));
471 bzero(&reply, sizeof(reply));
472 req.Function = MPI2_FUNCTION_FW_DOWNLOAD;
473 req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
474 req.TotalImageSize = len;
475 req.MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT;
477 if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
485 mps_firmware_get(int fd, unsigned char **firmware, bool bios)
487 MPI2_FW_UPLOAD_REQUEST req;
488 MPI2_FW_UPLOAD_REPLY reply;
492 bzero(&req, sizeof(req));
493 bzero(&reply, sizeof(reply));
494 req.Function = MPI2_FUNCTION_FW_UPLOAD;
495 req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
497 if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
501 if (reply.ActualImageSize == 0) {
505 size = reply.ActualImageSize;
506 *firmware = calloc(size, sizeof(unsigned char));
507 if (*firmware == NULL) {
511 if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
512 *firmware, size, 0)) {
523 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
524 MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
526 struct mps_cfg_page_req req;
528 if (IOCStatus != NULL)
529 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
532 bzero(&req, sizeof(req));
533 req.header.PageType = PageType;
534 req.header.PageNumber = PageNumber;
535 req.page_address = PageAddress;
536 if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0)
538 if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
539 if (IOCStatus != NULL)
540 *IOCStatus = req.ioc_status;
543 bcopy(&req.header, header, sizeof(*header));
548 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
551 struct mps_cfg_page_req req;
555 error = mps_read_config_page_header(fd, PageType, PageNumber,
556 PageAddress, &req.header, IOCStatus);
562 if (req.header.PageLength == 0)
563 req.header.PageLength = 4;
564 req.len = req.header.PageLength * 4;
565 buf = malloc(req.len);
567 bcopy(&req.header, buf, sizeof(req.header));
568 if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) {
574 if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
575 if (IOCStatus != NULL)
576 *IOCStatus = req.ioc_status;
578 warnx("Reading config page failed: 0x%x %s",
579 req.ioc_status, mps_ioc_status(req.ioc_status));
588 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
589 U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
591 struct mps_ext_cfg_page_req req;
595 if (IOCStatus != NULL)
596 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
597 bzero(&req, sizeof(req));
598 req.header.PageVersion = PageVersion;
599 req.header.PageNumber = PageNumber;
600 req.header.ExtPageType = ExtPageType;
601 req.page_address = PageAddress;
602 if (ioctl(fd, MPSIO_READ_EXT_CFG_HEADER, &req) < 0)
604 if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
605 if (IOCStatus != NULL)
606 *IOCStatus = req.ioc_status;
608 warnx("Reading extended config page header failed: %s",
609 mps_ioc_status(req.ioc_status));
613 req.len = req.header.ExtPageLength * 4;
614 buf = malloc(req.len);
616 bcopy(&req.header, buf, sizeof(req.header));
617 if (ioctl(fd, MPSIO_READ_EXT_CFG_PAGE, &req) < 0) {
623 if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
624 if (IOCStatus != NULL)
625 *IOCStatus = req.ioc_status;
627 warnx("Reading extended config page failed: %s",
628 mps_ioc_status(req.ioc_status));
640 char path[MAXPATHLEN];
642 snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit);
643 return (open(path, O_RDWR));
647 mps_user_command(int fd, void *req, uint32_t req_len, void *reply,
648 uint32_t reply_len, void *buffer, int len, uint32_t flags)
650 struct mps_usr_command cmd;
652 bzero(&cmd, sizeof(struct mps_usr_command));
654 cmd.req_len = req_len;
656 cmd.rpl_len = reply_len;
661 if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0)
667 mps_pass_command(int fd, void *req, uint32_t req_len, void *reply,
668 uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out,
669 uint32_t dataout_len, uint32_t timeout)
671 struct mprs_pass_thru pass;
673 bzero(&pass, sizeof(pass));
674 pass.PtrRequest = (uint64_t)(uintptr_t)req;
675 pass.PtrReply = (uint64_t)(uintptr_t)reply;
676 pass.RequestSize = req_len;
677 pass.ReplySize = reply_len;
678 if (datain_len && dataout_len) {
679 pass.PtrData = (uint64_t)(uintptr_t)data_in;
680 pass.PtrDataOut = (uint64_t)(uintptr_t)data_out;
681 pass.DataSize = datain_len;
682 pass.DataOutSize = dataout_len;
684 pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
686 pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
688 } else if (datain_len) {
689 pass.PtrData = (uint64_t)(uintptr_t)data_in;
690 pass.DataSize = datain_len;
692 pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
694 pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
696 } else if (dataout_len) {
697 pass.PtrData = (uint64_t)(uintptr_t)data_out;
698 pass.DataSize = dataout_len;
700 pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
702 pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
706 pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
708 pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
711 pass.Timeout = timeout;
713 if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
718 MPI2_IOC_FACTS_REPLY *
719 mps_get_iocfacts(int fd)
721 MPI2_IOC_FACTS_REPLY *facts;
722 MPI2_IOC_FACTS_REQUEST req;
723 char msgver[8], sysctlname[128];
724 size_t len, factslen;
727 snprintf(sysctlname, sizeof(sysctlname), "dev.%s.%d.msg_version",
728 is_mps ? "mps" : "mpr", mps_unit);
730 factslen = sizeof(MPI2_IOC_FACTS_REPLY);
731 len = sizeof(msgver);
732 error = sysctlbyname(sysctlname, msgver, &len, NULL, 0);
734 if (strncmp(msgver, "2.6", sizeof(msgver)) == 0)
738 facts = malloc(factslen);
744 bzero(&req, factslen);
745 req.Function = MPI2_FUNCTION_IOC_FACTS;
748 error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
749 facts, factslen, NULL, 0, NULL, 0, 10);
751 error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
752 facts, factslen, NULL, 0, 0);
759 if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {