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 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 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 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 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 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 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 } 333 334 if (mfi_hdr->mh_sense_len != 0) { 335 /* This ioctl needs a sense buffer. */ 336 ret = lmrc_dma_alloc(lmrc, lmrc->l_dma_attr, &sense, 337 mfi_hdr->mh_sense_len, 1, DDI_DMA_CONSISTENT); 338 if (ret != DDI_SUCCESS) { 339 ret = EINVAL; 340 goto out; 341 } 342 343 lmrc_dma_set_addr64(&sense, mfi_senseaddr); 344 } 345 346 mutex_enter(&mfi->mfi_lock); 347 lmrc_issue_mfi(lmrc, mfi, lmrc_wakeup_mfi); 348 ret = lmrc_wait_mfi(lmrc, mfi, LMRC_INTERNAL_CMD_WAIT_TIME); 349 mutex_exit(&mfi->mfi_lock); 350 351 if (ret != DDI_SUCCESS) { 352 ret = EAGAIN; 353 goto out; 354 } 355 356 /* If this ioctl did a DMA read, copy out to the user buffer. */ 357 if (xferlen != 0 && (mfi_hdr->mh_flags & MFI_FRAME_DIR_READ) != 0) { 358 ret = ddi_copyout(mfi->mfi_data_dma.ld_buf, xferbuf, xferlen, 359 mode); 360 if (ret != DDI_SUCCESS) { 361 ret = EFAULT; 362 goto out; 363 } 364 } 365 366 /* If there is sense data, copy out to the user sense buffer. */ 367 if (mfi_hdr->mh_sense_len != 0) { 368 void *sensebuf = (void *)(uintptr_t)*ioc_senseaddr; 369 370 (void) ddi_dma_sync(sense.ld_hdl, 0, sense.ld_len, 371 DDI_DMA_SYNC_FORKERNEL); 372 ret = ddi_copyout(sense.ld_buf, sensebuf, sense.ld_len, mode); 373 if (ret != DDI_SUCCESS) { 374 ret = EFAULT; 375 goto out; 376 } 377 } 378 379 out: 380 ioc_hdr->mh_cmd_status = mfi_hdr->mh_cmd_status; 381 ioc_hdr->mh_scsi_status = mfi_hdr->mh_scsi_status; 382 383 if (xferlen != 0) 384 lmrc_dma_free(&mfi->mfi_data_dma); 385 386 if (mfi_hdr->mh_sense_len != 0) 387 lmrc_dma_free(&sense); 388 389 lmrc_put_mfi(mfi); 390 if (ret != 0) 391 dev_err(lmrc->l_dip, CE_WARN, 392 "%s: failing MFI ioctl, ret = %d", 393 __func__, ret); 394 return (ret); 395 } 396 397 /* 398 * lmrc_fw_ioctl 399 * 400 * Process a firmware ioctl request. This includes driver ioctls (which are 401 * actually handled by the driver) and MFI passthru ioctls. 402 */ 403 static int 404 lmrc_fw_ioctl(lmrc_t *lmrc, intptr_t arg, int mode) 405 { 406 lmrc_ioctl_t *ioc; 407 int ret = EINVAL; 408 409 ioc = kmem_zalloc(sizeof (lmrc_ioctl_t), KM_SLEEP); 410 if (ddi_copyin((void *)arg, ioc, sizeof (*ioc), mode) != 0) { 411 ret = EFAULT; 412 goto out; 413 } 414 415 if (ioc->ioc_control_code == LMRC_DRIVER_IOCTL_COMMON) { 416 ret = lmrc_drv_ioctl(lmrc, ioc, mode); 417 } else { 418 sema_p(&lmrc->l_ioctl_sema); 419 ret = lmrc_mfi_ioctl(lmrc, ioc, mode); 420 sema_v(&lmrc->l_ioctl_sema); 421 } 422 423 if (ddi_copyout(ioc, (void *)arg, sizeof (*ioc) - 1, mode) != 0) { 424 ret = EFAULT; 425 goto out; 426 } 427 428 out: 429 kmem_free(ioc, sizeof (lmrc_ioctl_t)); 430 return (ret); 431 } 432 433 /* 434 * lmrc_mfi_aen_ioctl 435 * 436 * Supposedly, this will one day send an AEN to the firmware on behalf of 437 * user space. 438 */ 439 static int 440 lmrc_mfi_aen_ioctl(lmrc_t *lmrc, lmrc_aen_t *aen) 441 { 442 dev_err(lmrc->l_dip, CE_WARN, "!unimplemented ioctl: MFI AEN"); 443 return (EINVAL); 444 } 445 446 /* 447 * lmrc_aen_ioctl 448 * 449 * Process a AEN ioctl request. 450 */ 451 static int 452 lmrc_aen_ioctl(lmrc_t *lmrc, intptr_t arg, int mode) 453 { 454 int ret = EINVAL; 455 lmrc_aen_t aen; 456 457 if (ddi_copyin((void *)arg, &aen, sizeof (aen), mode) != 0) 458 return (EFAULT); 459 460 ret = lmrc_mfi_aen_ioctl(lmrc, &aen); 461 if (ret != 0) 462 goto out; 463 464 if (ddi_copyout(&aen, (void *)arg, sizeof (aen), mode) != 0) 465 return (EFAULT); 466 out: 467 return (ret); 468 } 469 470 /* 471 * DDI ioctl(9e) entry point. 472 * 473 * Get the ioctl cmd and call the appropriate handlers. 474 */ 475 int 476 lmrc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 477 int *rval) 478 { 479 lmrc_t *lmrc; 480 int inst = MINOR2INST(getminor(dev)); 481 int ret; 482 483 if (secpolicy_sys_config(credp, B_FALSE) != 0) 484 return (EPERM); 485 486 ret = scsi_hba_ioctl(dev, cmd, arg, mode, credp, rval); 487 if (ret != ENOTTY) 488 return (ret); 489 490 lmrc = ddi_get_soft_state(lmrc_state, inst); 491 if (lmrc == NULL) 492 return (ENXIO); 493 494 if (lmrc->l_fw_fault) 495 return (EIO); 496 497 switch ((uint_t)cmd) { 498 case LMRC_IOCTL_FIRMWARE: 499 ret = lmrc_fw_ioctl(lmrc, arg, mode); 500 break; 501 502 case LMRC_IOCTL_AEN: 503 ret = lmrc_aen_ioctl(lmrc, arg, mode); 504 break; 505 506 default: 507 ret = ENOTTY; 508 break; 509 } 510 511 return (ret); 512 } 513