1 /*- 2 * Copyright 2016-2021 Microchip Technology, Inc. and/or its subsidiaries. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 /* $FreeBSD$ */ 27 28 /* 29 * Management interface for smartpqi driver 30 */ 31 32 #include "smartpqi_includes.h" 33 34 /* 35 * Wrapper function to copy to user from kernel 36 */ 37 int 38 os_copy_to_user(struct pqisrc_softstate *softs, void *dest_buf, 39 void *src_buf, int size, int mode) 40 { 41 return(copyout(src_buf, dest_buf, size)); 42 } 43 44 /* 45 * Wrapper function to copy from user to kernel 46 */ 47 int 48 os_copy_from_user(struct pqisrc_softstate *softs, void *dest_buf, 49 void *src_buf, int size, int mode) 50 { 51 return(copyin(src_buf, dest_buf, size)); 52 } 53 54 /* 55 * Device open function for ioctl entry 56 */ 57 static int 58 smartpqi_open(struct cdev *cdev, int flags, int devtype, 59 struct thread *td) 60 { 61 return BSD_SUCCESS; 62 } 63 64 /* 65 * Device close function for ioctl entry 66 */ 67 static int 68 smartpqi_close(struct cdev *cdev, int flags, int devtype, 69 struct thread *td) 70 { 71 return BSD_SUCCESS; 72 } 73 74 /* 75 * ioctl for getting driver info 76 */ 77 static void 78 smartpqi_get_driver_info_ioctl(caddr_t udata, struct cdev *cdev) 79 { 80 struct pqisrc_softstate *softs = cdev->si_drv1; 81 pdriver_info driver_info = (pdriver_info)udata; 82 83 DBG_FUNC("IN udata = %p cdev = %p\n", udata, cdev); 84 85 driver_info->major_version = PQISRC_OS_VERSION; 86 driver_info->minor_version = PQISRC_FEATURE_VERSION; 87 driver_info->release_version = PQISRC_PATCH_VERSION; 88 driver_info->build_revision = PQISRC_BUILD_VERSION; 89 driver_info->max_targets = PQI_MAX_DEVICES - 1; 90 driver_info->max_io = softs->max_io_for_scsi_ml; 91 driver_info->max_transfer_length = softs->pqi_cap.max_transfer_size; 92 93 DBG_FUNC("OUT\n"); 94 } 95 96 /* 97 * ioctl for getting controller info 98 */ 99 static void 100 smartpqi_get_pci_info_ioctl(caddr_t udata, struct cdev *cdev) 101 { 102 struct pqisrc_softstate *softs = cdev->si_drv1; 103 device_t dev = softs->os_specific.pqi_dev; 104 pqi_pci_info_t *pci_info = (pqi_pci_info_t *)udata; 105 uint32_t sub_vendor = 0; 106 uint32_t sub_device = 0; 107 uint32_t vendor = 0; 108 uint32_t device = 0; 109 110 DBG_FUNC("IN udata = %p cdev = %p\n", udata, cdev); 111 112 pci_info->bus = pci_get_bus(dev); 113 pci_info->dev_fn = pci_get_function(dev); 114 pci_info->domain = pci_get_domain(dev); 115 sub_vendor = pci_read_config(dev, PCIR_SUBVEND_0, 2); 116 sub_device = pci_read_config(dev, PCIR_SUBDEV_0, 2); 117 pci_info->board_id = ((sub_device << 16) & 0xffff0000) | sub_vendor; 118 vendor = pci_get_vendor(dev); 119 device = pci_get_device(dev); 120 pci_info->chip_id = ((device << 16) & 0xffff0000) | vendor; 121 DBG_FUNC("OUT\n"); 122 } 123 124 static inline int 125 pqi_status_to_bsd_ioctl_status(int pqi_status) 126 { 127 if (PQI_STATUS_SUCCESS == pqi_status) 128 return BSD_SUCCESS; 129 else 130 return EIO; 131 } 132 133 /* 134 * ioctl entry point for user 135 */ 136 static int 137 smartpqi_ioctl(struct cdev *cdev, u_long cmd, caddr_t udata, 138 int flags, struct thread *td) 139 { 140 int bsd_status, pqi_status; 141 struct pqisrc_softstate *softs = cdev->si_drv1; 142 143 DBG_FUNC("IN cmd = 0x%lx udata = %p cdev = %p\n", cmd, udata, cdev); 144 145 if (!udata) { 146 DBG_ERR("udata is null !!\n"); 147 return EINVAL; 148 } 149 150 if (pqisrc_ctrl_offline(softs)){ 151 return ENOTTY; 152 } 153 154 switch (cmd) { 155 case CCISS_GETDRIVVER: 156 smartpqi_get_driver_info_ioctl(udata, cdev); 157 bsd_status = BSD_SUCCESS; 158 break; 159 case CCISS_GETPCIINFO: 160 smartpqi_get_pci_info_ioctl(udata, cdev); 161 bsd_status = BSD_SUCCESS; 162 break; 163 case SMARTPQI_PASS_THRU: 164 case CCISS_PASSTHRU: 165 pqi_status = pqisrc_passthru_ioctl(softs, udata, 0); 166 bsd_status = pqi_status_to_bsd_ioctl_status(pqi_status); 167 break; 168 case CCISS_REGNEWD: 169 pqi_status = pqisrc_scan_devices(softs); 170 bsd_status = pqi_status_to_bsd_ioctl_status(pqi_status); 171 break; 172 default: 173 DBG_WARN( "!IOCTL cmd 0x%lx not supported\n", cmd); 174 bsd_status = ENOTTY; 175 break; 176 } 177 178 DBG_FUNC("OUT error = %d\n", bsd_status); 179 return bsd_status; 180 } 181 182 static struct cdevsw smartpqi_cdevsw = 183 { 184 .d_version = D_VERSION, 185 .d_open = smartpqi_open, 186 .d_close = smartpqi_close, 187 .d_ioctl = smartpqi_ioctl, 188 .d_name = "smartpqi", 189 }; 190 191 /* 192 * Function to create device node for ioctl 193 */ 194 int 195 create_char_dev(struct pqisrc_softstate *softs, int card_index) 196 { 197 int error = BSD_SUCCESS; 198 199 DBG_FUNC("IN idx = %d\n", card_index); 200 201 softs->os_specific.cdev = make_dev(&smartpqi_cdevsw, card_index, 202 UID_ROOT, GID_OPERATOR, 0640, 203 "smartpqi%u", card_index); 204 if(softs->os_specific.cdev) { 205 softs->os_specific.cdev->si_drv1 = softs; 206 } else { 207 error = ENXIO; 208 } 209 210 DBG_FUNC("OUT error = %d\n", error); 211 212 return error; 213 } 214 215 /* 216 * Function to destroy device node for ioctl 217 */ 218 void 219 destroy_char_dev(struct pqisrc_softstate *softs) 220 { 221 DBG_FUNC("IN\n"); 222 if (softs->os_specific.cdev) { 223 destroy_dev(softs->os_specific.cdev); 224 softs->os_specific.cdev = NULL; 225 } 226 DBG_FUNC("OUT\n"); 227 } 228 229 /* 230 * Function used to send passthru commands to adapter 231 * to support management tools. For eg. ssacli, sscon. 232 */ 233 int 234 pqisrc_passthru_ioctl(struct pqisrc_softstate *softs, void *arg, int mode) 235 { 236 int ret = PQI_STATUS_SUCCESS; 237 char *drv_buf = NULL; 238 uint32_t tag = 0; 239 IOCTL_Command_struct *iocommand = (IOCTL_Command_struct *)arg; 240 dma_mem_t ioctl_dma_buf; 241 pqisrc_raid_req_t request; 242 raid_path_error_info_elem_t error_info; 243 ib_queue_t *ib_q = &softs->op_raid_ib_q[PQI_DEFAULT_IB_QUEUE]; 244 ob_queue_t *ob_q = &softs->op_ob_q[PQI_DEFAULT_IB_QUEUE]; 245 rcb_t *rcb = NULL; 246 247 memset(&request, 0, sizeof(request)); 248 memset(&error_info, 0, sizeof(error_info)); 249 250 DBG_FUNC("IN"); 251 252 if (pqisrc_ctrl_offline(softs)) 253 return PQI_STATUS_FAILURE; 254 255 if (!arg) 256 return (PQI_STATUS_FAILURE); 257 258 if (iocommand->buf_size < 1 && 259 iocommand->Request.Type.Direction != PQIIOCTL_NONE) 260 return PQI_STATUS_FAILURE; 261 if (iocommand->Request.CDBLen > sizeof(request.cdb)) 262 return PQI_STATUS_FAILURE; 263 264 switch (iocommand->Request.Type.Direction) { 265 case PQIIOCTL_NONE: 266 case PQIIOCTL_WRITE: 267 case PQIIOCTL_READ: 268 case PQIIOCTL_BIDIRECTIONAL: 269 break; 270 default: 271 return PQI_STATUS_FAILURE; 272 } 273 274 if (iocommand->buf_size > 0) { 275 memset(&ioctl_dma_buf, 0, sizeof(struct dma_mem)); 276 ioctl_dma_buf.tag = "Ioctl_PassthruCmd_Buffer"; 277 ioctl_dma_buf.size = iocommand->buf_size; 278 ioctl_dma_buf.align = PQISRC_DEFAULT_DMA_ALIGN; 279 /* allocate memory */ 280 ret = os_dma_mem_alloc(softs, &ioctl_dma_buf); 281 if (ret) { 282 DBG_ERR("Failed to Allocate dma mem for Ioctl PassthruCmd Buffer : %d\n", ret); 283 ret = PQI_STATUS_FAILURE; 284 goto out; 285 } 286 287 DBG_INFO("ioctl_dma_buf.dma_addr = %p\n",(void*)ioctl_dma_buf.dma_addr); 288 DBG_INFO("ioctl_dma_buf.virt_addr = %p\n",(void*)ioctl_dma_buf.virt_addr); 289 290 drv_buf = (char *)ioctl_dma_buf.virt_addr; 291 if (iocommand->Request.Type.Direction & PQIIOCTL_WRITE) { 292 if ((ret = os_copy_from_user(softs, (void *)drv_buf, 293 (void *)iocommand->buf, 294 iocommand->buf_size, mode)) != 0) { 295 ret = PQI_STATUS_FAILURE; 296 goto free_mem; 297 } 298 } 299 } 300 301 request.header.iu_type = PQI_IU_TYPE_RAID_PATH_IO_REQUEST; 302 request.header.iu_length = offsetof(pqisrc_raid_req_t, sg_descriptors[1]) - 303 PQI_REQUEST_HEADER_LENGTH; 304 memcpy(request.lun_number, iocommand->LUN_info.LunAddrBytes, 305 sizeof(request.lun_number)); 306 memcpy(request.cdb, iocommand->Request.CDB, iocommand->Request.CDBLen); 307 request.additional_cdb_bytes_usage = PQI_ADDITIONAL_CDB_BYTES_0; 308 309 switch (iocommand->Request.Type.Direction) { 310 case PQIIOCTL_NONE: 311 request.data_direction = SOP_DATA_DIR_NONE; 312 break; 313 case PQIIOCTL_WRITE: 314 request.data_direction = SOP_DATA_DIR_FROM_DEVICE; 315 break; 316 case PQIIOCTL_READ: 317 request.data_direction = SOP_DATA_DIR_TO_DEVICE; 318 break; 319 case PQIIOCTL_BIDIRECTIONAL: 320 request.data_direction = SOP_DATA_DIR_BIDIRECTIONAL; 321 break; 322 } 323 324 request.task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; 325 if (iocommand->buf_size > 0) { 326 request.buffer_length = iocommand->buf_size; 327 request.sg_descriptors[0].addr = ioctl_dma_buf.dma_addr; 328 request.sg_descriptors[0].len = iocommand->buf_size; 329 request.sg_descriptors[0].flags = SG_FLAG_LAST; 330 } 331 tag = pqisrc_get_tag(&softs->taglist); 332 if (INVALID_ELEM == tag) { 333 DBG_ERR("Tag not available\n"); 334 ret = PQI_STATUS_FAILURE; 335 goto free_mem; 336 } 337 request.request_id = tag; 338 request.response_queue_id = ob_q->q_id; 339 request.error_index = request.request_id; 340 if (softs->timeout_in_passthrough) { 341 request.timeout_in_sec = iocommand->Request.Timeout; 342 } 343 344 rcb = &softs->rcb[tag]; 345 rcb->success_cmp_callback = pqisrc_process_internal_raid_response_success; 346 rcb->error_cmp_callback = pqisrc_process_internal_raid_response_error; 347 rcb->tag = tag; 348 rcb->req_pending = true; 349 /* Submit Command */ 350 ret = pqisrc_submit_cmnd(softs, ib_q, &request); 351 if (ret != PQI_STATUS_SUCCESS) { 352 DBG_ERR("Unable to submit command\n"); 353 goto err_out; 354 } 355 356 ret = pqisrc_wait_on_condition(softs, rcb, 357 PQISRC_PASSTHROUGH_CMD_TIMEOUT); 358 if (ret != PQI_STATUS_SUCCESS) { 359 DBG_ERR("Passthru IOCTL cmd timed out !!\n"); 360 goto err_out; 361 } 362 363 memset(&iocommand->error_info, 0, sizeof(iocommand->error_info)); 364 365 366 if (rcb->status) { 367 size_t sense_data_length; 368 369 memcpy(&error_info, rcb->error_info, sizeof(error_info)); 370 iocommand->error_info.ScsiStatus = error_info.status; 371 sense_data_length = error_info.sense_data_len; 372 373 if (!sense_data_length) 374 sense_data_length = error_info.resp_data_len; 375 376 if (sense_data_length && 377 (sense_data_length > sizeof(error_info.data))) 378 sense_data_length = sizeof(error_info.data); 379 380 if (sense_data_length) { 381 if (sense_data_length > 382 sizeof(iocommand->error_info.SenseInfo)) 383 sense_data_length = 384 sizeof(iocommand->error_info.SenseInfo); 385 memcpy (iocommand->error_info.SenseInfo, 386 error_info.data, sense_data_length); 387 iocommand->error_info.SenseLen = sense_data_length; 388 } 389 390 if (error_info.data_out_result == 391 PQI_RAID_DATA_IN_OUT_UNDERFLOW){ 392 rcb->status = REQUEST_SUCCESS; 393 } 394 } 395 396 if (rcb->status == REQUEST_SUCCESS && iocommand->buf_size > 0 && 397 (iocommand->Request.Type.Direction & PQIIOCTL_READ)) { 398 399 if ((ret = os_copy_to_user(softs, (void*)iocommand->buf, 400 (void*)drv_buf, iocommand->buf_size, mode)) != 0) { 401 DBG_ERR("Failed to copy the response\n"); 402 goto err_out; 403 } 404 } 405 406 os_reset_rcb(rcb); 407 pqisrc_put_tag(&softs->taglist, request.request_id); 408 if (iocommand->buf_size > 0) 409 os_dma_mem_free(softs,&ioctl_dma_buf); 410 411 DBG_FUNC("OUT\n"); 412 return ret; 413 err_out: 414 os_reset_rcb(rcb); 415 pqisrc_put_tag(&softs->taglist, request.request_id); 416 417 free_mem: 418 if (iocommand->buf_size > 0) 419 os_dma_mem_free(softs, &ioctl_dma_buf); 420 421 out: 422 DBG_FUNC("Failed OUT\n"); 423 return PQI_STATUS_FAILURE; 424 } 425