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