11e091e43SHans Rosenfeld /*
21e091e43SHans Rosenfeld  * This file and its contents are supplied under the terms of the
31e091e43SHans Rosenfeld  * Common Development and Distribution License ("CDDL"), version 1.0.
41e091e43SHans Rosenfeld  * You may only use this file in accordance with the terms of version
51e091e43SHans Rosenfeld  * 1.0 of the CDDL.
61e091e43SHans Rosenfeld  *
71e091e43SHans Rosenfeld  * A full copy of the text of the CDDL should have accompanied this
81e091e43SHans Rosenfeld  * source.  A copy of the CDDL is also available via the Internet at
91e091e43SHans Rosenfeld  * http://www.illumos.org/license/CDDL.
101e091e43SHans Rosenfeld  */
111e091e43SHans Rosenfeld 
121e091e43SHans Rosenfeld /*
131e091e43SHans Rosenfeld  * Copyright 2023 Racktop Systems, Inc.
141e091e43SHans Rosenfeld  */
151e091e43SHans Rosenfeld 
161e091e43SHans Rosenfeld /*
171e091e43SHans Rosenfeld  * This file implements the ioctl interface as employed by closed-source
181e091e43SHans Rosenfeld  * the closed-source RAID management utility storcli. As there is no source
191e091e43SHans Rosenfeld  * and no documentation, this closely follows the ioctl implementation of
201e091e43SHans Rosenfeld  * the existing mr_sas(4D) driver for older MegaRAID HBAs.
211e091e43SHans Rosenfeld  *
221e091e43SHans Rosenfeld  * This driver supports three kinds of ioctls:
231e091e43SHans Rosenfeld  * - SCSA HBA ioctls, which are handled by scsi_hba_ioctl()
241e091e43SHans Rosenfeld  * - AEN ioctls, which currently have no known consumer as it seems storcli
251e091e43SHans Rosenfeld  *   doesn't use them. They are left unimplemented for now, logging a warning
261e091e43SHans Rosenfeld  *   if used.
271e091e43SHans Rosenfeld  * - Firmware ioctls as used by storcli, which can be divided into two kinds
281e091e43SHans Rosenfeld  *   - MFI passthru ioctls which are used to send MFI frames containing DCMDs,
291e091e43SHans Rosenfeld  *     LD SCSI I/O, or PD SCSI I/O requests from userspace directly to the HBA.
301e091e43SHans Rosenfeld  *     See the comment at the beginning of lmrc.c for a description of the MFI.
311e091e43SHans Rosenfeld  *   - Driver ioctls, which look like MFI DCMD frames but are actually handled
321e091e43SHans Rosenfeld  *     by the driver. They are used by storcli to query the driver version and
331e091e43SHans Rosenfeld  *     get PCI information of the HBA, including PCI config space header.
341e091e43SHans Rosenfeld  */
351e091e43SHans Rosenfeld #include <sys/cred.h>
361e091e43SHans Rosenfeld #include <sys/file.h>
371e091e43SHans Rosenfeld #include <sys/types.h>
381e091e43SHans Rosenfeld #include <sys/errno.h>
391e091e43SHans Rosenfeld #include <sys/ddi.h>
401e091e43SHans Rosenfeld #include <sys/sunddi.h>
411e091e43SHans Rosenfeld #include <sys/policy.h>
421e091e43SHans Rosenfeld 
431e091e43SHans Rosenfeld #include <sys/ddifm.h>
441e091e43SHans Rosenfeld #include <sys/fm/io/ddi.h>
451e091e43SHans Rosenfeld 
461e091e43SHans Rosenfeld #include "lmrc.h"
471e091e43SHans Rosenfeld #include "lmrc_reg.h"
481e091e43SHans Rosenfeld #include "lmrc_raid.h"
491e091e43SHans Rosenfeld #include "lmrc_ioctl.h"
501e091e43SHans Rosenfeld 
511e091e43SHans Rosenfeld static int lmrc_drv_ioctl_drv_version(lmrc_t *, void *, size_t, int);
521e091e43SHans Rosenfeld static int lmrc_drv_ioctl_pci_info(lmrc_t *, void *, size_t, int);
531e091e43SHans Rosenfeld static int lmrc_drv_ioctl(lmrc_t *, lmrc_ioctl_t *, int);
541e091e43SHans Rosenfeld 
551e091e43SHans Rosenfeld static void lmrc_mfi_ioctl_scsi_io(lmrc_t *, lmrc_ioctl_t *, lmrc_mfi_cmd_t *,
561e091e43SHans Rosenfeld     uintptr_t *, uintptr_t *);
571e091e43SHans Rosenfeld static void lmrc_mfi_ioctl_dcmd(lmrc_t *, lmrc_ioctl_t *, lmrc_mfi_cmd_t *,
581e091e43SHans Rosenfeld     uintptr_t *);
591e091e43SHans Rosenfeld static int lmrc_mfi_ioctl(lmrc_t *, lmrc_ioctl_t *, int);
601e091e43SHans Rosenfeld static int lmrc_mfi_aen_ioctl(lmrc_t *, lmrc_aen_t *);
611e091e43SHans Rosenfeld static int lmrc_fw_ioctl(lmrc_t *, intptr_t, int);
621e091e43SHans Rosenfeld static int lmrc_aen_ioctl(lmrc_t *, intptr_t, int);
631e091e43SHans Rosenfeld 
641e091e43SHans Rosenfeld /*
651e091e43SHans Rosenfeld  * lmrc_drv_ioctl_drv_version
661e091e43SHans Rosenfeld  *
671e091e43SHans Rosenfeld  * Return the driver version information back to userspace.
681e091e43SHans Rosenfeld  */
691e091e43SHans Rosenfeld static int
lmrc_drv_ioctl_drv_version(lmrc_t * lmrc,void * ubuf,size_t len,int mode)701e091e43SHans Rosenfeld lmrc_drv_ioctl_drv_version(lmrc_t *lmrc, void *ubuf, size_t len, int mode)
711e091e43SHans Rosenfeld {
721e091e43SHans Rosenfeld 	static lmrc_drv_ver_t dv = {
731e091e43SHans Rosenfeld 		.dv_signature = "$ILLUMOS$",
741e091e43SHans Rosenfeld 		.dv_os_name = "illumos",
751e091e43SHans Rosenfeld 		.dv_drv_name = "lmrc",
761e091e43SHans Rosenfeld 		.dv_drv_ver = "0.1",
771e091e43SHans Rosenfeld 		.dv_drv_rel_date = "Feb 09, 2023"
781e091e43SHans Rosenfeld 	};
791e091e43SHans Rosenfeld 
801e091e43SHans Rosenfeld 	int ret;
811e091e43SHans Rosenfeld 
821e091e43SHans Rosenfeld 	ret = ddi_copyout(&dv, ubuf, len, mode);
831e091e43SHans Rosenfeld 	if (ret != DDI_SUCCESS)
841e091e43SHans Rosenfeld 		return (EFAULT);
851e091e43SHans Rosenfeld 
861e091e43SHans Rosenfeld 	return (0);
871e091e43SHans Rosenfeld }
881e091e43SHans Rosenfeld 
891e091e43SHans Rosenfeld /*
901e091e43SHans Rosenfeld  * lmrc_drv_ioctl_drv_version
911e091e43SHans Rosenfeld  *
921e091e43SHans Rosenfeld  * Return PCI bus interface information back to userspace.
931e091e43SHans Rosenfeld  */
941e091e43SHans Rosenfeld static int
lmrc_drv_ioctl_pci_info(lmrc_t * lmrc,void * ubuf,size_t len,int mode)951e091e43SHans Rosenfeld lmrc_drv_ioctl_pci_info(lmrc_t *lmrc, void *ubuf, size_t len, int mode)
961e091e43SHans Rosenfeld {
971e091e43SHans Rosenfeld 	int *props = NULL;
981e091e43SHans Rosenfeld 	ddi_acc_handle_t pcih;
991e091e43SHans Rosenfeld 	lmrc_pci_info_t pi;
1001e091e43SHans Rosenfeld 	uint_t nprop;
1011e091e43SHans Rosenfeld 	int ret;
1021e091e43SHans Rosenfeld 	int i;
1031e091e43SHans Rosenfeld 
1041e091e43SHans Rosenfeld 	ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, lmrc->l_dip, 0, "reg",
1051e091e43SHans Rosenfeld 	    &props, &nprop);
1061e091e43SHans Rosenfeld 	if (ret != DDI_SUCCESS)
1071e091e43SHans Rosenfeld 		return (EINVAL);
1081e091e43SHans Rosenfeld 
1091e091e43SHans Rosenfeld 	bzero(&pi, sizeof (pi));
1101e091e43SHans Rosenfeld 	pi.pi_bus = (props[0] >> 16) & 0xff;
1111e091e43SHans Rosenfeld 	pi.pi_dev = (props[0] >> 11) & 0x1f;
1121e091e43SHans Rosenfeld 	pi.pi_func = (props[0] >> 8) & 0x7;
1131e091e43SHans Rosenfeld 
1141e091e43SHans Rosenfeld 	ddi_prop_free(props);
1151e091e43SHans Rosenfeld 
1161e091e43SHans Rosenfeld 	if (pci_config_setup(lmrc->l_dip, &pcih) != DDI_SUCCESS)
1171e091e43SHans Rosenfeld 		return (EINVAL);
1181e091e43SHans Rosenfeld 
1191e091e43SHans Rosenfeld 	for (i = 0; i != ARRAY_SIZE(pi.pi_header); i++)
1201e091e43SHans Rosenfeld 		pi.pi_header[i] = pci_config_get8(pcih, i);
1211e091e43SHans Rosenfeld 
1221e091e43SHans Rosenfeld 	if (lmrc_check_acc_handle(lmrc->l_reghandle) != DDI_SUCCESS) {
1231e091e43SHans Rosenfeld 		pci_config_teardown(&pcih);
1241e091e43SHans Rosenfeld 		lmrc_fm_ereport(lmrc, DDI_FM_DEVICE_NO_RESPONSE);
1251e091e43SHans Rosenfeld 		ddi_fm_service_impact(lmrc->l_dip, DDI_SERVICE_LOST);
1261e091e43SHans Rosenfeld 		return (EIO);
1271e091e43SHans Rosenfeld 	}
1281e091e43SHans Rosenfeld 
1291e091e43SHans Rosenfeld 	pci_config_teardown(&pcih);
1301e091e43SHans Rosenfeld 
1311e091e43SHans Rosenfeld 	ret = ddi_copyout(&pi, ubuf, len, mode);
1321e091e43SHans Rosenfeld 	if (ret != DDI_SUCCESS)
1331e091e43SHans Rosenfeld 		return (EFAULT);
1341e091e43SHans Rosenfeld 
1351e091e43SHans Rosenfeld 	return (0);
1361e091e43SHans Rosenfeld }
1371e091e43SHans Rosenfeld 
1381e091e43SHans Rosenfeld /*
1391e091e43SHans Rosenfeld  * lmrc_drv_ioctl
1401e091e43SHans Rosenfeld  *
1411e091e43SHans Rosenfeld  * Process a driver information ioctl request. These come in the form of a
1421e091e43SHans Rosenfeld  * MFI DCMD but are processed by the driver and not sent to the hardware.
1431e091e43SHans Rosenfeld  */
1441e091e43SHans Rosenfeld static int
lmrc_drv_ioctl(lmrc_t * lmrc,lmrc_ioctl_t * ioc,int mode)1451e091e43SHans Rosenfeld lmrc_drv_ioctl(lmrc_t *lmrc, lmrc_ioctl_t *ioc, int mode)
1461e091e43SHans Rosenfeld {
1471e091e43SHans Rosenfeld 	lmrc_mfi_header_t *hdr = &ioc->ioc_frame.mf_hdr;
1481e091e43SHans Rosenfeld 	lmrc_mfi_dcmd_payload_t *dcmd = &ioc->ioc_frame.mf_dcmd;
1491e091e43SHans Rosenfeld 	size_t xferlen = dcmd->md_sgl.ms64_length;
1501e091e43SHans Rosenfeld 	void *ubuf = (void *)dcmd->md_sgl.ms64_phys_addr;
1511e091e43SHans Rosenfeld 	int ret = EINVAL;
1521e091e43SHans Rosenfeld 
1531e091e43SHans Rosenfeld #ifdef _MULTI_DATAMODEL
1541e091e43SHans Rosenfeld 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
1551e091e43SHans Rosenfeld 		xferlen = dcmd->md_sgl.ms32_length;
1561e091e43SHans Rosenfeld 		ubuf = (void *)(uintptr_t)dcmd->md_sgl.ms32_phys_addr;
1571e091e43SHans Rosenfeld 	} else {
1581e091e43SHans Rosenfeld #endif
1591e091e43SHans Rosenfeld 		xferlen = dcmd->md_sgl.ms64_length;
1601e091e43SHans Rosenfeld 		ubuf = (void *)(uintptr_t)dcmd->md_sgl.ms64_phys_addr;
1611e091e43SHans Rosenfeld #ifdef _MULTI_DATAMODEL
1621e091e43SHans Rosenfeld 	}
1631e091e43SHans Rosenfeld #endif
1641e091e43SHans Rosenfeld 
1651e091e43SHans Rosenfeld 	switch (dcmd->md_opcode) {
1661e091e43SHans Rosenfeld 	case LMRC_DRIVER_IOCTL_DRIVER_VERSION:
1671e091e43SHans Rosenfeld 		ret = lmrc_drv_ioctl_drv_version(lmrc, ubuf, xferlen, mode);
1681e091e43SHans Rosenfeld 		break;
1691e091e43SHans Rosenfeld 
1701e091e43SHans Rosenfeld 	case LMRC_DRIVER_IOCTL_PCI_INFORMATION:
1711e091e43SHans Rosenfeld 		ret = lmrc_drv_ioctl_pci_info(lmrc, ubuf, xferlen, mode);
1721e091e43SHans Rosenfeld 		break;
1731e091e43SHans Rosenfeld 
1741e091e43SHans Rosenfeld 	default:
1751e091e43SHans Rosenfeld 		dev_err(lmrc->l_dip, CE_WARN,
1761e091e43SHans Rosenfeld 		    "!%s: invalid driver ioctl, cmd = %d",
1771e091e43SHans Rosenfeld 		    __func__, dcmd->md_opcode);
1781e091e43SHans Rosenfeld 
1791e091e43SHans Rosenfeld 		ret = EINVAL;
1801e091e43SHans Rosenfeld 		break;
1811e091e43SHans Rosenfeld 	}
1821e091e43SHans Rosenfeld 
1831e091e43SHans Rosenfeld 	if (ret != 0)
1841e091e43SHans Rosenfeld 		hdr->mh_cmd_status = MFI_STAT_INVALID_CMD;
1851e091e43SHans Rosenfeld 	else
1861e091e43SHans Rosenfeld 		hdr->mh_cmd_status = MFI_STAT_OK;
1871e091e43SHans Rosenfeld 
1881e091e43SHans Rosenfeld 	return (ret);
1891e091e43SHans Rosenfeld }
1901e091e43SHans Rosenfeld 
1911e091e43SHans Rosenfeld /*
1921e091e43SHans Rosenfeld  * lmrc_mfi_ioctl_scsi_io
1931e091e43SHans Rosenfeld  *
1941e091e43SHans Rosenfeld  * Prepare MFI cmd for SCSI I/O passthru.
1951e091e43SHans Rosenfeld  */
1961e091e43SHans Rosenfeld static void
lmrc_mfi_ioctl_scsi_io(lmrc_t * lmrc,lmrc_ioctl_t * ioc,lmrc_mfi_cmd_t * mfi,uintptr_t * sgloff,uintptr_t * senseoff)1971e091e43SHans Rosenfeld lmrc_mfi_ioctl_scsi_io(lmrc_t *lmrc, lmrc_ioctl_t *ioc,
1981e091e43SHans Rosenfeld     lmrc_mfi_cmd_t *mfi, uintptr_t *sgloff, uintptr_t *senseoff)
1991e091e43SHans Rosenfeld {
2001e091e43SHans Rosenfeld 	lmrc_mfi_pthru_payload_t *ioc_pthru = &ioc->ioc_frame.mf_pthru;
2011e091e43SHans Rosenfeld 	lmrc_mfi_pthru_payload_t *mfi_pthru = &mfi->mfi_frame->mf_pthru;
2021e091e43SHans Rosenfeld 
2031e091e43SHans Rosenfeld 	bcopy(ioc_pthru->mp_cdb, mfi_pthru->mp_cdb, sizeof (mfi_pthru->mp_cdb));
2041e091e43SHans Rosenfeld 
2051e091e43SHans Rosenfeld 	*sgloff = offsetof(lmrc_mfi_pthru_payload_t, mp_sgl);
2061e091e43SHans Rosenfeld 	*senseoff = offsetof(lmrc_mfi_pthru_payload_t, mp_sense_buf_phys_addr);
2071e091e43SHans Rosenfeld }
2081e091e43SHans Rosenfeld 
2091e091e43SHans Rosenfeld /*
2101e091e43SHans Rosenfeld  * lmrc_mfi_ioctl_dcmd
2111e091e43SHans Rosenfeld  *
2121e091e43SHans Rosenfeld  * Prepare MFI cmd for DMCD passthru.
2131e091e43SHans Rosenfeld  */
2141e091e43SHans Rosenfeld static void
lmrc_mfi_ioctl_dcmd(lmrc_t * lmrc,lmrc_ioctl_t * ioc,lmrc_mfi_cmd_t * mfi,uintptr_t * sgloff)2151e091e43SHans Rosenfeld lmrc_mfi_ioctl_dcmd(lmrc_t *lmrc, lmrc_ioctl_t *ioc,
2161e091e43SHans Rosenfeld     lmrc_mfi_cmd_t *mfi, uintptr_t *sgloff)
2171e091e43SHans Rosenfeld {
2181e091e43SHans Rosenfeld 	lmrc_mfi_dcmd_payload_t *ioc_dcmd = &ioc->ioc_frame.mf_dcmd;
2191e091e43SHans Rosenfeld 	lmrc_mfi_dcmd_payload_t *mfi_dcmd = &mfi->mfi_frame->mf_dcmd;
2201e091e43SHans Rosenfeld 
2211e091e43SHans Rosenfeld 	mfi_dcmd->md_opcode = ioc_dcmd->md_opcode;
2221e091e43SHans Rosenfeld 	bcopy(ioc_dcmd->md_mbox_8, mfi_dcmd->md_mbox_8,
2231e091e43SHans Rosenfeld 	    sizeof (mfi_dcmd->md_mbox_8));
2241e091e43SHans Rosenfeld 
2251e091e43SHans Rosenfeld 	*sgloff = offsetof(lmrc_mfi_dcmd_payload_t, md_sgl);
2261e091e43SHans Rosenfeld }
2271e091e43SHans Rosenfeld 
2281e091e43SHans Rosenfeld /*
2291e091e43SHans Rosenfeld  * lmrc_mfi_ioctl
2301e091e43SHans Rosenfeld  *
2311e091e43SHans Rosenfeld  * Process a MFI passthru ioctl request. Handle DMA read/write and sense data
2321e091e43SHans Rosenfeld  * in a uniform way for all supported MFI commands.
2331e091e43SHans Rosenfeld  */
2341e091e43SHans Rosenfeld static int
lmrc_mfi_ioctl(lmrc_t * lmrc,lmrc_ioctl_t * ioc,int mode)2351e091e43SHans Rosenfeld lmrc_mfi_ioctl(lmrc_t *lmrc, lmrc_ioctl_t *ioc, int mode)
2361e091e43SHans Rosenfeld {
2371e091e43SHans Rosenfeld 	uint64_t *mfi_senseaddr = NULL, *ioc_senseaddr = NULL;
2381e091e43SHans Rosenfeld 	lmrc_dma_t sense;
2391e091e43SHans Rosenfeld 	size_t xferlen = 0;
2401e091e43SHans Rosenfeld 
2411e091e43SHans Rosenfeld 	lmrc_mfi_header_t *mfi_hdr, *ioc_hdr;
2421e091e43SHans Rosenfeld 	lmrc_mfi_sgl_t *mfi_sgl, *ioc_sgl;
2431e091e43SHans Rosenfeld 	lmrc_mfi_cmd_t *mfi;
2441e091e43SHans Rosenfeld 	uintptr_t sgloff;
2451e091e43SHans Rosenfeld 	void *xferbuf;
2461e091e43SHans Rosenfeld 	int ret;
2471e091e43SHans Rosenfeld 
2481e091e43SHans Rosenfeld 	ioc_hdr = &ioc->ioc_frame.mf_hdr;
2491e091e43SHans Rosenfeld 	if (ioc_hdr->mh_sense_len > LMRC_IOC_SENSE_LEN)
2501e091e43SHans Rosenfeld 		return (EINVAL);
2511e091e43SHans Rosenfeld 
2521e091e43SHans Rosenfeld 	mfi = lmrc_get_mfi(lmrc);
2531e091e43SHans Rosenfeld 	mfi_hdr = &mfi->mfi_frame->mf_hdr;
2541e091e43SHans Rosenfeld 
2551e091e43SHans Rosenfeld 	mfi_hdr->mh_cmd = ioc_hdr->mh_cmd;
2561e091e43SHans Rosenfeld 	mfi_hdr->mh_sense_len = ioc_hdr->mh_sense_len;
2571e091e43SHans Rosenfeld 	mfi_hdr->mh_drv_opts = ioc_hdr->mh_drv_opts;
2581e091e43SHans Rosenfeld 	mfi_hdr->mh_flags = ioc_hdr->mh_flags & ~MFI_FRAME_SGL64;
2591e091e43SHans Rosenfeld 	mfi_hdr->mh_timeout = ioc_hdr->mh_timeout;
2601e091e43SHans Rosenfeld 	mfi_hdr->mh_data_xfer_len = ioc_hdr->mh_data_xfer_len;
2611e091e43SHans Rosenfeld 
2621e091e43SHans Rosenfeld 	switch (mfi_hdr->mh_cmd) {
2631e091e43SHans Rosenfeld 	case MFI_CMD_LD_SCSI_IO:
2641e091e43SHans Rosenfeld 	case MFI_CMD_PD_SCSI_IO: {
2651e091e43SHans Rosenfeld 		uintptr_t senseoff;
2661e091e43SHans Rosenfeld 
2671e091e43SHans Rosenfeld 		lmrc_mfi_ioctl_scsi_io(lmrc, ioc, mfi, &sgloff, &senseoff);
2681e091e43SHans Rosenfeld 
2691e091e43SHans Rosenfeld 		mfi_senseaddr = (uint64_t *)&mfi->mfi_frame->mf_raw[senseoff];
2701e091e43SHans Rosenfeld 		ioc_senseaddr = (uint64_t *)&ioc->ioc_frame.mf_raw[senseoff];
2711e091e43SHans Rosenfeld 
2721e091e43SHans Rosenfeld 		break;
2731e091e43SHans Rosenfeld 	}
2741e091e43SHans Rosenfeld 	case MFI_CMD_DCMD:
2751e091e43SHans Rosenfeld 		if (mfi_hdr->mh_sense_len != 0) {
2761e091e43SHans Rosenfeld 			ret = EINVAL;
2771e091e43SHans Rosenfeld 			goto out;
2781e091e43SHans Rosenfeld 		}
2791e091e43SHans Rosenfeld 
2801e091e43SHans Rosenfeld 		lmrc_mfi_ioctl_dcmd(lmrc, ioc, mfi, &sgloff);
2811e091e43SHans Rosenfeld 		break;
2821e091e43SHans Rosenfeld 
2831e091e43SHans Rosenfeld 	default:
2841e091e43SHans Rosenfeld 		dev_err(lmrc->l_dip, CE_WARN,
2851e091e43SHans Rosenfeld 		    "!%s: invalid MFI ioctl, cmd = %d",
2861e091e43SHans Rosenfeld 		    __func__, mfi_hdr->mh_cmd);
2871e091e43SHans Rosenfeld 		ret = EINVAL;
2881e091e43SHans Rosenfeld 		goto out;
2891e091e43SHans Rosenfeld 
2901e091e43SHans Rosenfeld 	}
2911e091e43SHans Rosenfeld 
2921e091e43SHans Rosenfeld 	ASSERT3U(sgloff, !=, 0);
2931e091e43SHans Rosenfeld 	ioc_sgl = (lmrc_mfi_sgl_t *)&ioc->ioc_frame.mf_raw[sgloff];
2941e091e43SHans Rosenfeld 	mfi_sgl = (lmrc_mfi_sgl_t *)&mfi->mfi_frame->mf_raw[sgloff];
2951e091e43SHans Rosenfeld 
2961e091e43SHans Rosenfeld #ifdef _MULTI_DATAMODEL
2971e091e43SHans Rosenfeld 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
2981e091e43SHans Rosenfeld 		xferlen = ioc_sgl->ms32_length;
2991e091e43SHans Rosenfeld 		xferbuf = (void *)(uintptr_t)ioc_sgl->ms32_phys_addr;
3001e091e43SHans Rosenfeld 	} else {
3011e091e43SHans Rosenfeld #endif
3021e091e43SHans Rosenfeld 		xferlen = ioc_sgl->ms64_length;
3031e091e43SHans Rosenfeld 		xferbuf = (void *)(uintptr_t)ioc_sgl->ms64_phys_addr;
3041e091e43SHans Rosenfeld #ifdef _MULTI_DATAMODEL
3051e091e43SHans Rosenfeld 	}
3061e091e43SHans Rosenfeld #endif
3071e091e43SHans Rosenfeld 
3081e091e43SHans Rosenfeld 	if (xferlen != 0) {
3091e091e43SHans Rosenfeld 		/* This ioctl uses DMA. */
3101e091e43SHans Rosenfeld 		ret = lmrc_dma_alloc(lmrc, lmrc->l_dma_attr,
3111e091e43SHans Rosenfeld 		    &mfi->mfi_data_dma, xferlen, 1, DDI_DMA_CONSISTENT);
3121e091e43SHans Rosenfeld 		if (ret != DDI_SUCCESS) {
3131e091e43SHans Rosenfeld 			ret = EINVAL;
3141e091e43SHans Rosenfeld 			goto out;
3151e091e43SHans Rosenfeld 		}
3161e091e43SHans Rosenfeld 
3171e091e43SHans Rosenfeld 		/* If this ioctl does a DMA write, copy in the user buffer. */
3181e091e43SHans Rosenfeld 		if ((mfi_hdr->mh_flags & MFI_FRAME_DIR_WRITE) != 0) {
3191e091e43SHans Rosenfeld 			ret = ddi_copyin(xferbuf, mfi->mfi_data_dma.ld_buf,
3201e091e43SHans Rosenfeld 			    xferlen, mode);
3211e091e43SHans Rosenfeld 			if (ret != DDI_SUCCESS) {
3221e091e43SHans Rosenfeld 				ret = EFAULT;
3231e091e43SHans Rosenfeld 				goto out;
3241e091e43SHans Rosenfeld 			}
3251e091e43SHans Rosenfeld 		}
3261e091e43SHans Rosenfeld 
3271e091e43SHans Rosenfeld 		mfi_hdr->mh_flags |= MFI_FRAME_SGL64;
3281e091e43SHans Rosenfeld 
3291e091e43SHans Rosenfeld 		lmrc_dma_set_addr64(&mfi->mfi_data_dma,
3301e091e43SHans Rosenfeld 		    &mfi_sgl->ms64_phys_addr);
3311e091e43SHans Rosenfeld 		mfi_sgl->ms64_length = lmrc_dma_get_size(&mfi->mfi_data_dma);
332*50c0d5ceSHans Rosenfeld 	} else {
333*50c0d5ceSHans Rosenfeld 		mfi_hdr->mh_flags &= ~MFI_FRAME_DIR_BOTH;
3341e091e43SHans Rosenfeld 	}
3351e091e43SHans Rosenfeld 
3361e091e43SHans Rosenfeld 	if (mfi_hdr->mh_sense_len != 0) {
3371e091e43SHans Rosenfeld 		/* This ioctl needs a sense buffer. */
3381e091e43SHans Rosenfeld 		ret = lmrc_dma_alloc(lmrc, lmrc->l_dma_attr, &sense,
3391e091e43SHans Rosenfeld 		    mfi_hdr->mh_sense_len, 1, DDI_DMA_CONSISTENT);
3401e091e43SHans Rosenfeld 		if (ret != DDI_SUCCESS) {
3411e091e43SHans Rosenfeld 			ret = EINVAL;
3421e091e43SHans Rosenfeld 			goto out;
3431e091e43SHans Rosenfeld 		}
3441e091e43SHans Rosenfeld 
3451e091e43SHans Rosenfeld 		lmrc_dma_set_addr64(&sense, mfi_senseaddr);
3461e091e43SHans Rosenfeld 	}
3471e091e43SHans Rosenfeld 
3481e091e43SHans Rosenfeld 	mutex_enter(&mfi->mfi_lock);
3491e091e43SHans Rosenfeld 	lmrc_issue_mfi(lmrc, mfi, lmrc_wakeup_mfi);
3501e091e43SHans Rosenfeld 	ret = lmrc_wait_mfi(lmrc, mfi, LMRC_INTERNAL_CMD_WAIT_TIME);
3511e091e43SHans Rosenfeld 	mutex_exit(&mfi->mfi_lock);
3521e091e43SHans Rosenfeld 
3531e091e43SHans Rosenfeld 	if (ret != DDI_SUCCESS) {
3541e091e43SHans Rosenfeld 		ret = EAGAIN;
3551e091e43SHans Rosenfeld 		goto out;
3561e091e43SHans Rosenfeld 	}
3571e091e43SHans Rosenfeld 
3581e091e43SHans Rosenfeld 	/* If this ioctl did a DMA read, copy out to the user buffer. */
3591e091e43SHans Rosenfeld 	if (xferlen != 0 && (mfi_hdr->mh_flags & MFI_FRAME_DIR_READ) != 0) {
3601e091e43SHans Rosenfeld 		ret = ddi_copyout(mfi->mfi_data_dma.ld_buf, xferbuf, xferlen,
3611e091e43SHans Rosenfeld 		    mode);
3621e091e43SHans Rosenfeld 		if (ret != DDI_SUCCESS) {
3631e091e43SHans Rosenfeld 			ret = EFAULT;
3641e091e43SHans Rosenfeld 			goto out;
3651e091e43SHans Rosenfeld 		}
3661e091e43SHans Rosenfeld 	}
3671e091e43SHans Rosenfeld 
3681e091e43SHans Rosenfeld 	/* If there is sense data, copy out to the user sense buffer. */
3691e091e43SHans Rosenfeld 	if (mfi_hdr->mh_sense_len != 0) {
3701e091e43SHans Rosenfeld 		void *sensebuf = (void *)(uintptr_t)*ioc_senseaddr;
3711e091e43SHans Rosenfeld 
3721e091e43SHans Rosenfeld 		(void) ddi_dma_sync(sense.ld_hdl, 0, sense.ld_len,
3731e091e43SHans Rosenfeld 		    DDI_DMA_SYNC_FORKERNEL);
3741e091e43SHans Rosenfeld 		ret = ddi_copyout(sense.ld_buf, sensebuf, sense.ld_len, mode);
3751e091e43SHans Rosenfeld 		if (ret != DDI_SUCCESS) {
3761e091e43SHans Rosenfeld 			ret = EFAULT;
3771e091e43SHans Rosenfeld 			goto out;
3781e091e43SHans Rosenfeld 		}
3791e091e43SHans Rosenfeld 	}
3801e091e43SHans Rosenfeld 
3811e091e43SHans Rosenfeld out:
3821e091e43SHans Rosenfeld 	ioc_hdr->mh_cmd_status = mfi_hdr->mh_cmd_status;
3831e091e43SHans Rosenfeld 	ioc_hdr->mh_scsi_status = mfi_hdr->mh_scsi_status;
3841e091e43SHans Rosenfeld 
3851e091e43SHans Rosenfeld 	if (xferlen != 0)
3861e091e43SHans Rosenfeld 		lmrc_dma_free(&mfi->mfi_data_dma);
3871e091e43SHans Rosenfeld 
3881e091e43SHans Rosenfeld 	if (mfi_hdr->mh_sense_len != 0)
3891e091e43SHans Rosenfeld 		lmrc_dma_free(&sense);
3901e091e43SHans Rosenfeld 
3911e091e43SHans Rosenfeld 	lmrc_put_mfi(mfi);
3921e091e43SHans Rosenfeld 	if (ret != 0)
3931e091e43SHans Rosenfeld 		dev_err(lmrc->l_dip, CE_WARN,
3941e091e43SHans Rosenfeld 		    "%s: failing MFI ioctl, ret = %d",
3951e091e43SHans Rosenfeld 		    __func__, ret);
3961e091e43SHans Rosenfeld 	return (ret);
3971e091e43SHans Rosenfeld }
3981e091e43SHans Rosenfeld 
3991e091e43SHans Rosenfeld /*
4001e091e43SHans Rosenfeld  * lmrc_fw_ioctl
4011e091e43SHans Rosenfeld  *
4021e091e43SHans Rosenfeld  * Process a firmware ioctl request. This includes driver ioctls (which are
4031e091e43SHans Rosenfeld  * actually handled by the driver) and MFI passthru ioctls.
4041e091e43SHans Rosenfeld  */
4051e091e43SHans Rosenfeld static int
lmrc_fw_ioctl(lmrc_t * lmrc,intptr_t arg,int mode)4061e091e43SHans Rosenfeld lmrc_fw_ioctl(lmrc_t *lmrc, intptr_t arg, int mode)
4071e091e43SHans Rosenfeld {
4081e091e43SHans Rosenfeld 	lmrc_ioctl_t *ioc;
4091e091e43SHans Rosenfeld 	int ret = EINVAL;
4101e091e43SHans Rosenfeld 
4111e091e43SHans Rosenfeld 	ioc = kmem_zalloc(sizeof (lmrc_ioctl_t), KM_SLEEP);
4121e091e43SHans Rosenfeld 	if (ddi_copyin((void *)arg, ioc, sizeof (*ioc), mode) != 0) {
4131e091e43SHans Rosenfeld 		ret = EFAULT;
4141e091e43SHans Rosenfeld 		goto out;
4151e091e43SHans Rosenfeld 	}
4161e091e43SHans Rosenfeld 
4171e091e43SHans Rosenfeld 	if (ioc->ioc_control_code == LMRC_DRIVER_IOCTL_COMMON) {
4181e091e43SHans Rosenfeld 		ret = lmrc_drv_ioctl(lmrc, ioc, mode);
4191e091e43SHans Rosenfeld 	} else {
4201e091e43SHans Rosenfeld 		sema_p(&lmrc->l_ioctl_sema);
4211e091e43SHans Rosenfeld 		ret = lmrc_mfi_ioctl(lmrc, ioc, mode);
4221e091e43SHans Rosenfeld 		sema_v(&lmrc->l_ioctl_sema);
4231e091e43SHans Rosenfeld 	}
4241e091e43SHans Rosenfeld 
4251e091e43SHans Rosenfeld 	if (ddi_copyout(ioc, (void *)arg, sizeof (*ioc) - 1, mode) != 0) {
4261e091e43SHans Rosenfeld 		ret = EFAULT;
4271e091e43SHans Rosenfeld 		goto out;
4281e091e43SHans Rosenfeld 	}
4291e091e43SHans Rosenfeld 
4301e091e43SHans Rosenfeld out:
4311e091e43SHans Rosenfeld 	kmem_free(ioc, sizeof (lmrc_ioctl_t));
4321e091e43SHans Rosenfeld 	return (ret);
4331e091e43SHans Rosenfeld }
4341e091e43SHans Rosenfeld 
4351e091e43SHans Rosenfeld /*
4361e091e43SHans Rosenfeld  * lmrc_mfi_aen_ioctl
4371e091e43SHans Rosenfeld  *
4381e091e43SHans Rosenfeld  * Supposedly, this will one day send an AEN to the firmware on behalf of
4391e091e43SHans Rosenfeld  * user space.
4401e091e43SHans Rosenfeld  */
4411e091e43SHans Rosenfeld static int
lmrc_mfi_aen_ioctl(lmrc_t * lmrc,lmrc_aen_t * aen)4421e091e43SHans Rosenfeld lmrc_mfi_aen_ioctl(lmrc_t *lmrc, lmrc_aen_t *aen)
4431e091e43SHans Rosenfeld {
4441e091e43SHans Rosenfeld 	dev_err(lmrc->l_dip, CE_WARN, "!unimplemented ioctl: MFI AEN");
4451e091e43SHans Rosenfeld 	return (EINVAL);
4461e091e43SHans Rosenfeld }
4471e091e43SHans Rosenfeld 
4481e091e43SHans Rosenfeld /*
4491e091e43SHans Rosenfeld  * lmrc_aen_ioctl
4501e091e43SHans Rosenfeld  *
4511e091e43SHans Rosenfeld  * Process a AEN ioctl request.
4521e091e43SHans Rosenfeld  */
4531e091e43SHans Rosenfeld static int
lmrc_aen_ioctl(lmrc_t * lmrc,intptr_t arg,int mode)4541e091e43SHans Rosenfeld lmrc_aen_ioctl(lmrc_t *lmrc, intptr_t arg, int mode)
4551e091e43SHans Rosenfeld {
4561e091e43SHans Rosenfeld 	int ret = EINVAL;
4571e091e43SHans Rosenfeld 	lmrc_aen_t	aen;
4581e091e43SHans Rosenfeld 
4591e091e43SHans Rosenfeld 	if (ddi_copyin((void *)arg, &aen, sizeof (aen), mode) != 0)
4601e091e43SHans Rosenfeld 		return (EFAULT);
4611e091e43SHans Rosenfeld 
4621e091e43SHans Rosenfeld 	ret = lmrc_mfi_aen_ioctl(lmrc, &aen);
4631e091e43SHans Rosenfeld 	if (ret != 0)
4641e091e43SHans Rosenfeld 		goto out;
4651e091e43SHans Rosenfeld 
4661e091e43SHans Rosenfeld 	if (ddi_copyout(&aen, (void *)arg, sizeof (aen), mode) != 0)
4671e091e43SHans Rosenfeld 		return (EFAULT);
4681e091e43SHans Rosenfeld out:
4691e091e43SHans Rosenfeld 	return (ret);
4701e091e43SHans Rosenfeld }
4711e091e43SHans Rosenfeld 
4721e091e43SHans Rosenfeld /*
4731e091e43SHans Rosenfeld  * DDI ioctl(9e) entry point.
4741e091e43SHans Rosenfeld  *
4751e091e43SHans Rosenfeld  * Get the ioctl cmd and call the appropriate handlers.
4761e091e43SHans Rosenfeld  */
4771e091e43SHans Rosenfeld int
lmrc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rval)4781e091e43SHans Rosenfeld lmrc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
4791e091e43SHans Rosenfeld     int *rval)
4801e091e43SHans Rosenfeld {
4811e091e43SHans Rosenfeld 	lmrc_t *lmrc;
4821e091e43SHans Rosenfeld 	int inst = MINOR2INST(getminor(dev));
4831e091e43SHans Rosenfeld 	int ret;
4841e091e43SHans Rosenfeld 
4851e091e43SHans Rosenfeld 	if (secpolicy_sys_config(credp, B_FALSE) != 0)
4861e091e43SHans Rosenfeld 		return (EPERM);
4871e091e43SHans Rosenfeld 
4881e091e43SHans Rosenfeld 	ret = scsi_hba_ioctl(dev, cmd, arg, mode, credp, rval);
4891e091e43SHans Rosenfeld 	if (ret != ENOTTY)
4901e091e43SHans Rosenfeld 		return (ret);
4911e091e43SHans Rosenfeld 
4921e091e43SHans Rosenfeld 	lmrc = ddi_get_soft_state(lmrc_state, inst);
4931e091e43SHans Rosenfeld 	if (lmrc == NULL)
4941e091e43SHans Rosenfeld 		return (ENXIO);
4951e091e43SHans Rosenfeld 
4961e091e43SHans Rosenfeld 	if (lmrc->l_fw_fault)
4971e091e43SHans Rosenfeld 		return (EIO);
4981e091e43SHans Rosenfeld 
4991e091e43SHans Rosenfeld 	switch ((uint_t)cmd) {
5001e091e43SHans Rosenfeld 	case LMRC_IOCTL_FIRMWARE:
5011e091e43SHans Rosenfeld 		ret = lmrc_fw_ioctl(lmrc, arg, mode);
5021e091e43SHans Rosenfeld 		break;
5031e091e43SHans Rosenfeld 
5041e091e43SHans Rosenfeld 	case LMRC_IOCTL_AEN:
5051e091e43SHans Rosenfeld 		ret = lmrc_aen_ioctl(lmrc, arg, mode);
5061e091e43SHans Rosenfeld 		break;
5071e091e43SHans Rosenfeld 
5081e091e43SHans Rosenfeld 	default:
5091e091e43SHans Rosenfeld 		ret = ENOTTY;
5101e091e43SHans Rosenfeld 		break;
5111e091e43SHans Rosenfeld 	}
5121e091e43SHans Rosenfeld 
5131e091e43SHans Rosenfeld 	return (ret);
5141e091e43SHans Rosenfeld }
515