1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2024 Racktop Systems, Inc.
14  */
15 
16 /*
17  * This file implements the ioctl interface as employed by closed-source
18  * the closed-source RAID management utility storcli. As there is no source
19  * and no documentation, this closely follows the ioctl implementation of
20  * the existing mr_sas(4D) driver for older MegaRAID HBAs.
21  *
22  * This driver supports three kinds of ioctls:
23  * - SCSA HBA ioctls, which are handled by scsi_hba_ioctl()
24  * - AEN ioctls, which currently have no known consumer as it seems storcli
25  *   doesn't use them. They are left unimplemented for now, logging a warning
26  *   if used.
27  * - Firmware ioctls as used by storcli, which can be divided into two kinds
28  *   - MFI passthru ioctls which are used to send MFI frames containing DCMDs,
29  *     LD SCSI I/O, or PD SCSI I/O requests from userspace directly to the HBA.
30  *     See the comment at the beginning of lmrc.c for a description of the MFI.
31  *   - Driver ioctls, which look like MFI DCMD frames but are actually handled
32  *     by the driver. They are used by storcli to query the driver version and
33  *     get PCI information of the HBA, including PCI config space header.
34  */
35 #include <sys/cred.h>
36 #include <sys/file.h>
37 #include <sys/types.h>
38 #include <sys/errno.h>
39 #include <sys/ddi.h>
40 #include <sys/sunddi.h>
41 #include <sys/policy.h>
42 
43 #include <sys/ddifm.h>
44 #include <sys/fm/io/ddi.h>
45 
46 #include <sys/scsi/adapters/mfi/mfi_evt.h>
47 #include <sys/scsi/adapters/mfi/mfi_ioctl.h>
48 
49 #include "lmrc.h"
50 #include "lmrc_reg.h"
51 #include "lmrc_raid.h"
52 #include "lmrc_ioctl.h"
53 
54 static int lmrc_drv_ioctl_drv_version(lmrc_t *, void *, size_t, int);
55 static int lmrc_drv_ioctl_pci_info(lmrc_t *, void *, size_t, int);
56 static int lmrc_drv_ioctl(lmrc_t *, mfi_ioctl_t *, int);
57 
58 static void lmrc_mfi_ioctl_scsi_io(lmrc_t *, mfi_ioctl_t *, lmrc_mfi_cmd_t *,
59     uintptr_t *, uintptr_t *);
60 static void lmrc_mfi_ioctl_dcmd(lmrc_t *, mfi_ioctl_t *, lmrc_mfi_cmd_t *,
61     uintptr_t *);
62 static int lmrc_mfi_ioctl(lmrc_t *, mfi_ioctl_t *, int);
63 static int lmrc_mfi_aen_ioctl(lmrc_t *, mfi_aen_t *);
64 static int lmrc_fw_ioctl(lmrc_t *, intptr_t, int);
65 static int lmrc_aen_ioctl(lmrc_t *, intptr_t, int);
66 
67 /*
68  * lmrc_drv_ioctl_drv_version
69  *
70  * Return the driver version information back to userspace.
71  */
72 static int
lmrc_drv_ioctl_drv_version(lmrc_t * lmrc,void * ubuf,size_t len,int mode)73 lmrc_drv_ioctl_drv_version(lmrc_t *lmrc, void *ubuf, size_t len, int mode)
74 {
75 	static mfi_drv_ver_t dv = {
76 		.dv_signature = "$ILLUMOS$",
77 		.dv_os_name = "illumos",
78 		.dv_drv_name = "lmrc",
79 		.dv_drv_ver = "0.1",
80 		.dv_drv_rel_date = "Feb 09, 2023"
81 	};
82 
83 	int ret;
84 
85 	ret = ddi_copyout(&dv, ubuf, len, mode);
86 	if (ret != DDI_SUCCESS)
87 		return (EFAULT);
88 
89 	return (0);
90 }
91 
92 /*
93  * lmrc_drv_ioctl_drv_version
94  *
95  * Return PCI bus interface information back to userspace.
96  */
97 static int
lmrc_drv_ioctl_pci_info(lmrc_t * lmrc,void * ubuf,size_t len,int mode)98 lmrc_drv_ioctl_pci_info(lmrc_t *lmrc, void *ubuf, size_t len, int mode)
99 {
100 	int *props = NULL;
101 	ddi_acc_handle_t pcih;
102 	mfi_pci_info_t pi;
103 	uint_t nprop;
104 	int ret;
105 	int i;
106 
107 	ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, lmrc->l_dip, 0, "reg",
108 	    &props, &nprop);
109 	if (ret != DDI_SUCCESS)
110 		return (EINVAL);
111 
112 	bzero(&pi, sizeof (pi));
113 	pi.pi_bus = (props[0] >> 16) & 0xff;
114 	pi.pi_dev = (props[0] >> 11) & 0x1f;
115 	pi.pi_func = (props[0] >> 8) & 0x7;
116 
117 	ddi_prop_free(props);
118 
119 	if (pci_config_setup(lmrc->l_dip, &pcih) != DDI_SUCCESS)
120 		return (EINVAL);
121 
122 	for (i = 0; i != ARRAY_SIZE(pi.pi_header); i++)
123 		pi.pi_header[i] = pci_config_get8(pcih, i);
124 
125 	if (lmrc_check_acc_handle(lmrc->l_reghandle) != DDI_SUCCESS) {
126 		pci_config_teardown(&pcih);
127 		lmrc_fm_ereport(lmrc, DDI_FM_DEVICE_NO_RESPONSE);
128 		ddi_fm_service_impact(lmrc->l_dip, DDI_SERVICE_LOST);
129 		return (EIO);
130 	}
131 
132 	pci_config_teardown(&pcih);
133 
134 	ret = ddi_copyout(&pi, ubuf, len, mode);
135 	if (ret != DDI_SUCCESS)
136 		return (EFAULT);
137 
138 	return (0);
139 }
140 
141 /*
142  * lmrc_drv_ioctl
143  *
144  * Process a driver information ioctl request. These come in the form of a
145  * MFI DCMD but are processed by the driver and not sent to the hardware.
146  */
147 static int
lmrc_drv_ioctl(lmrc_t * lmrc,mfi_ioctl_t * ioc,int mode)148 lmrc_drv_ioctl(lmrc_t *lmrc, mfi_ioctl_t *ioc, int mode)
149 {
150 	mfi_header_t *hdr = &ioc->ioc_frame.mf_hdr;
151 	mfi_dcmd_payload_t *dcmd = &ioc->ioc_frame.mf_dcmd;
152 	size_t xferlen = dcmd->md_sgl.ms64_length;
153 	void *ubuf = (void *)dcmd->md_sgl.ms64_phys_addr;
154 	int ret = EINVAL;
155 
156 #ifdef _MULTI_DATAMODEL
157 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
158 		xferlen = dcmd->md_sgl.ms32_length;
159 		ubuf = (void *)(uintptr_t)dcmd->md_sgl.ms32_phys_addr;
160 	} else {
161 #endif
162 		xferlen = dcmd->md_sgl.ms64_length;
163 		ubuf = (void *)(uintptr_t)dcmd->md_sgl.ms64_phys_addr;
164 #ifdef _MULTI_DATAMODEL
165 	}
166 #endif
167 
168 	switch (dcmd->md_opcode) {
169 	case MFI_DRIVER_IOCTL_DRIVER_VERSION:
170 		ret = lmrc_drv_ioctl_drv_version(lmrc, ubuf, xferlen, mode);
171 		break;
172 
173 	case MFI_DRIVER_IOCTL_PCI_INFORMATION:
174 		ret = lmrc_drv_ioctl_pci_info(lmrc, ubuf, xferlen, mode);
175 		break;
176 
177 	default:
178 		dev_err(lmrc->l_dip, CE_WARN,
179 		    "!%s: invalid driver ioctl, cmd = %d",
180 		    __func__, dcmd->md_opcode);
181 
182 		ret = EINVAL;
183 		break;
184 	}
185 
186 	if (ret != 0)
187 		hdr->mh_cmd_status = MFI_STAT_INVALID_CMD;
188 	else
189 		hdr->mh_cmd_status = MFI_STAT_OK;
190 
191 	return (ret);
192 }
193 
194 /*
195  * lmrc_mfi_ioctl_scsi_io
196  *
197  * Prepare MFI cmd for SCSI I/O passthru.
198  */
199 static void
lmrc_mfi_ioctl_scsi_io(lmrc_t * lmrc,mfi_ioctl_t * ioc,lmrc_mfi_cmd_t * mfi,uintptr_t * sgloff,uintptr_t * senseoff)200 lmrc_mfi_ioctl_scsi_io(lmrc_t *lmrc, mfi_ioctl_t *ioc, lmrc_mfi_cmd_t *mfi,
201     uintptr_t *sgloff, uintptr_t *senseoff)
202 {
203 	mfi_pthru_payload_t *ioc_pthru = &ioc->ioc_frame.mf_pthru;
204 	mfi_pthru_payload_t *mfi_pthru = &mfi->mfi_frame->mf_pthru;
205 
206 	bcopy(ioc_pthru->mp_cdb, mfi_pthru->mp_cdb, sizeof (mfi_pthru->mp_cdb));
207 
208 	*sgloff = offsetof(mfi_pthru_payload_t, mp_sgl);
209 	*senseoff = offsetof(mfi_pthru_payload_t, mp_sense_buf_phys_addr);
210 }
211 
212 /*
213  * lmrc_mfi_ioctl_dcmd
214  *
215  * Prepare MFI cmd for DMCD passthru.
216  */
217 static void
lmrc_mfi_ioctl_dcmd(lmrc_t * lmrc,mfi_ioctl_t * ioc,lmrc_mfi_cmd_t * mfi,uintptr_t * sgloff)218 lmrc_mfi_ioctl_dcmd(lmrc_t *lmrc, mfi_ioctl_t *ioc, lmrc_mfi_cmd_t *mfi,
219     uintptr_t *sgloff)
220 {
221 	mfi_dcmd_payload_t *ioc_dcmd = &ioc->ioc_frame.mf_dcmd;
222 	mfi_dcmd_payload_t *mfi_dcmd = &mfi->mfi_frame->mf_dcmd;
223 
224 	mfi_dcmd->md_opcode = ioc_dcmd->md_opcode;
225 	bcopy(ioc_dcmd->md_mbox_8, mfi_dcmd->md_mbox_8,
226 	    sizeof (mfi_dcmd->md_mbox_8));
227 
228 	*sgloff = offsetof(mfi_dcmd_payload_t, md_sgl);
229 }
230 
231 /*
232  * lmrc_mfi_ioctl
233  *
234  * Process a MFI passthru ioctl request. Handle DMA read/write and sense data
235  * in a uniform way for all supported MFI commands.
236  */
237 static int
lmrc_mfi_ioctl(lmrc_t * lmrc,mfi_ioctl_t * ioc,int mode)238 lmrc_mfi_ioctl(lmrc_t *lmrc, mfi_ioctl_t *ioc, int mode)
239 {
240 	uint64_t *mfi_senseaddr = NULL, *ioc_senseaddr = NULL;
241 	lmrc_dma_t sense;
242 	size_t xferlen = 0;
243 
244 	mfi_header_t *mfi_hdr, *ioc_hdr;
245 	mfi_sgl_t *mfi_sgl, *ioc_sgl;
246 	lmrc_mfi_cmd_t *mfi;
247 	uintptr_t sgloff;
248 	void *xferbuf;
249 	int ret;
250 
251 	ioc_hdr = &ioc->ioc_frame.mf_hdr;
252 	if (ioc_hdr->mh_sense_len > MFI_IOC_SENSE_LEN)
253 		return (EINVAL);
254 
255 	mfi = lmrc_get_mfi(lmrc);
256 	mfi_hdr = &mfi->mfi_frame->mf_hdr;
257 
258 	mfi_hdr->mh_cmd = ioc_hdr->mh_cmd;
259 	mfi_hdr->mh_sense_len = ioc_hdr->mh_sense_len;
260 	mfi_hdr->mh_drv_opts = ioc_hdr->mh_drv_opts;
261 	mfi_hdr->mh_flags = ioc_hdr->mh_flags & ~MFI_FRAME_SGL64;
262 	mfi_hdr->mh_timeout = ioc_hdr->mh_timeout;
263 	mfi_hdr->mh_data_xfer_len = ioc_hdr->mh_data_xfer_len;
264 
265 	switch (mfi_hdr->mh_cmd) {
266 	case MFI_CMD_LD_SCSI_IO:
267 	case MFI_CMD_PD_SCSI_IO: {
268 		uintptr_t senseoff;
269 
270 		lmrc_mfi_ioctl_scsi_io(lmrc, ioc, mfi, &sgloff, &senseoff);
271 
272 		mfi_senseaddr = (uint64_t *)&mfi->mfi_frame->mf_raw[senseoff];
273 		ioc_senseaddr = (uint64_t *)&ioc->ioc_frame.mf_raw[senseoff];
274 
275 		break;
276 	}
277 	case MFI_CMD_DCMD:
278 		if (mfi_hdr->mh_sense_len != 0) {
279 			ret = EINVAL;
280 			goto out;
281 		}
282 
283 		lmrc_mfi_ioctl_dcmd(lmrc, ioc, mfi, &sgloff);
284 		break;
285 
286 	default:
287 		dev_err(lmrc->l_dip, CE_WARN,
288 		    "!%s: invalid MFI ioctl, cmd = %d",
289 		    __func__, mfi_hdr->mh_cmd);
290 		ret = EINVAL;
291 		goto out;
292 
293 	}
294 
295 	ASSERT3U(sgloff, !=, 0);
296 	ioc_sgl = (mfi_sgl_t *)&ioc->ioc_frame.mf_raw[sgloff];
297 	mfi_sgl = (mfi_sgl_t *)&mfi->mfi_frame->mf_raw[sgloff];
298 
299 #ifdef _MULTI_DATAMODEL
300 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
301 		xferlen = ioc_sgl->ms32_length;
302 		xferbuf = (void *)(uintptr_t)ioc_sgl->ms32_phys_addr;
303 	} else {
304 #endif
305 		xferlen = ioc_sgl->ms64_length;
306 		xferbuf = (void *)(uintptr_t)ioc_sgl->ms64_phys_addr;
307 #ifdef _MULTI_DATAMODEL
308 	}
309 #endif
310 
311 	if (xferlen != 0) {
312 		/* This ioctl uses DMA. */
313 		ret = lmrc_dma_alloc(lmrc, lmrc->l_dma_attr,
314 		    &mfi->mfi_data_dma, xferlen, 1, DDI_DMA_CONSISTENT);
315 		if (ret != DDI_SUCCESS) {
316 			ret = EINVAL;
317 			goto out;
318 		}
319 
320 		/* If this ioctl does a DMA write, copy in the user buffer. */
321 		if ((mfi_hdr->mh_flags & MFI_FRAME_DIR_WRITE) != 0) {
322 			ret = ddi_copyin(xferbuf, mfi->mfi_data_dma.ld_buf,
323 			    xferlen, mode);
324 			if (ret != DDI_SUCCESS) {
325 				ret = EFAULT;
326 				goto out;
327 			}
328 		}
329 
330 		mfi_hdr->mh_flags |= MFI_FRAME_SGL64;
331 
332 		lmrc_dma_set_addr64(&mfi->mfi_data_dma,
333 		    &mfi_sgl->ms64_phys_addr);
334 		mfi_sgl->ms64_length = lmrc_dma_get_size(&mfi->mfi_data_dma);
335 	} else {
336 		mfi_hdr->mh_flags &= ~MFI_FRAME_DIR_BOTH;
337 	}
338 
339 	if (mfi_hdr->mh_sense_len != 0) {
340 		/* This ioctl needs a sense buffer. */
341 		ret = lmrc_dma_alloc(lmrc, lmrc->l_dma_attr, &sense,
342 		    mfi_hdr->mh_sense_len, 1, DDI_DMA_CONSISTENT);
343 		if (ret != DDI_SUCCESS) {
344 			ret = EINVAL;
345 			goto out;
346 		}
347 
348 		lmrc_dma_set_addr64(&sense, mfi_senseaddr);
349 	}
350 
351 	mutex_enter(&mfi->mfi_lock);
352 	lmrc_issue_mfi(lmrc, mfi, lmrc_wakeup_mfi);
353 	ret = lmrc_wait_mfi(lmrc, mfi, LMRC_INTERNAL_CMD_WAIT_TIME);
354 	mutex_exit(&mfi->mfi_lock);
355 
356 	if (ret != DDI_SUCCESS) {
357 		ret = EAGAIN;
358 		goto out;
359 	}
360 
361 	/* If this ioctl did a DMA read, copy out to the user buffer. */
362 	if (xferlen != 0 && (mfi_hdr->mh_flags & MFI_FRAME_DIR_READ) != 0) {
363 		ret = ddi_copyout(mfi->mfi_data_dma.ld_buf, xferbuf, xferlen,
364 		    mode);
365 		if (ret != DDI_SUCCESS) {
366 			ret = EFAULT;
367 			goto out;
368 		}
369 	}
370 
371 	/* If there is sense data, copy out to the user sense buffer. */
372 	if (mfi_hdr->mh_sense_len != 0) {
373 		void *sensebuf = (void *)(uintptr_t)*ioc_senseaddr;
374 
375 		(void) ddi_dma_sync(sense.ld_hdl, 0, sense.ld_len,
376 		    DDI_DMA_SYNC_FORKERNEL);
377 		ret = ddi_copyout(sense.ld_buf, sensebuf, sense.ld_len, mode);
378 		if (ret != DDI_SUCCESS) {
379 			ret = EFAULT;
380 			goto out;
381 		}
382 	}
383 
384 out:
385 	ioc_hdr->mh_cmd_status = mfi_hdr->mh_cmd_status;
386 	ioc_hdr->mh_scsi_status = mfi_hdr->mh_scsi_status;
387 
388 	if (xferlen != 0)
389 		lmrc_dma_free(&mfi->mfi_data_dma);
390 
391 	if (mfi_hdr->mh_sense_len != 0)
392 		lmrc_dma_free(&sense);
393 
394 	lmrc_put_mfi(mfi);
395 	if (ret != 0)
396 		dev_err(lmrc->l_dip, CE_WARN,
397 		    "%s: failing MFI ioctl, ret = %d",
398 		    __func__, ret);
399 	return (ret);
400 }
401 
402 /*
403  * lmrc_fw_ioctl
404  *
405  * Process a firmware ioctl request. This includes driver ioctls (which are
406  * actually handled by the driver) and MFI passthru ioctls.
407  */
408 static int
lmrc_fw_ioctl(lmrc_t * lmrc,intptr_t arg,int mode)409 lmrc_fw_ioctl(lmrc_t *lmrc, intptr_t arg, int mode)
410 {
411 	mfi_ioctl_t *ioc;
412 	int ret = EINVAL;
413 
414 	ioc = kmem_zalloc(sizeof (mfi_ioctl_t), KM_SLEEP);
415 	if (ddi_copyin((void *)arg, ioc, sizeof (*ioc), mode) != 0) {
416 		ret = EFAULT;
417 		goto out;
418 	}
419 
420 	if (ioc->ioc_control_code == MFI_DRIVER_IOCTL_COMMON) {
421 		ret = lmrc_drv_ioctl(lmrc, ioc, mode);
422 	} else {
423 		sema_p(&lmrc->l_ioctl_sema);
424 		ret = lmrc_mfi_ioctl(lmrc, ioc, mode);
425 		sema_v(&lmrc->l_ioctl_sema);
426 	}
427 
428 	if (ddi_copyout(ioc, (void *)arg, sizeof (*ioc) - 1, mode) != 0) {
429 		ret = EFAULT;
430 		goto out;
431 	}
432 
433 out:
434 	kmem_free(ioc, sizeof (mfi_ioctl_t));
435 	return (ret);
436 }
437 
438 /*
439  * lmrc_mfi_aen_ioctl
440  *
441  * Supposedly, this will one day send an AEN to the firmware on behalf of
442  * user space.
443  */
444 static int
lmrc_mfi_aen_ioctl(lmrc_t * lmrc,mfi_aen_t * aen)445 lmrc_mfi_aen_ioctl(lmrc_t *lmrc, mfi_aen_t *aen)
446 {
447 	dev_err(lmrc->l_dip, CE_WARN, "!unimplemented ioctl: MFI AEN");
448 	return (EINVAL);
449 }
450 
451 /*
452  * lmrc_aen_ioctl
453  *
454  * Process a AEN ioctl request.
455  */
456 static int
lmrc_aen_ioctl(lmrc_t * lmrc,intptr_t arg,int mode)457 lmrc_aen_ioctl(lmrc_t *lmrc, intptr_t arg, int mode)
458 {
459 	int ret = EINVAL;
460 	mfi_aen_t aen;
461 
462 	if (ddi_copyin((void *)arg, &aen, sizeof (aen), mode) != 0)
463 		return (EFAULT);
464 
465 	ret = lmrc_mfi_aen_ioctl(lmrc, &aen);
466 	if (ret != 0)
467 		goto out;
468 
469 	if (ddi_copyout(&aen, (void *)arg, sizeof (aen), mode) != 0)
470 		return (EFAULT);
471 out:
472 	return (ret);
473 }
474 
475 /*
476  * DDI ioctl(9e) entry point.
477  *
478  * Get the ioctl cmd and call the appropriate handlers.
479  */
480 int
lmrc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rval)481 lmrc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
482     int *rval)
483 {
484 	lmrc_t *lmrc;
485 	int inst = MINOR2INST(getminor(dev));
486 	int ret;
487 
488 	if (secpolicy_sys_config(credp, B_FALSE) != 0)
489 		return (EPERM);
490 
491 	ret = scsi_hba_ioctl(dev, cmd, arg, mode, credp, rval);
492 	if (ret != ENOTTY)
493 		return (ret);
494 
495 	lmrc = ddi_get_soft_state(lmrc_state, inst);
496 	if (lmrc == NULL)
497 		return (ENXIO);
498 
499 	if (lmrc->l_fw_fault)
500 		return (EIO);
501 
502 	switch ((uint_t)cmd) {
503 	case MFI_IOCTL_FIRMWARE:
504 		ret = lmrc_fw_ioctl(lmrc, arg, mode);
505 		break;
506 
507 	case MFI_IOCTL_AEN:
508 		ret = lmrc_aen_ioctl(lmrc, arg, mode);
509 		break;
510 
511 	default:
512 		ret = ENOTTY;
513 		break;
514 	}
515 
516 	return (ret);
517 }
518