xref: /freebsd/sys/dev/mps/mps_user.c (revision 109c1de8bad542e1ce21e866a36b2b86dbe8d49d)
1 /*-
2  * Copyright (c) 2008 Yahoo!, Inc.
3  * All rights reserved.
4  * Written by: John Baldwin <jhb@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the author nor the names of any co-contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * LSI MPS-Fusion Host Adapter FreeBSD userland interface
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/selinfo.h>
41 #include <sys/module.h>
42 #include <sys/bus.h>
43 #include <sys/conf.h>
44 #include <sys/bio.h>
45 #include <sys/malloc.h>
46 #include <sys/uio.h>
47 #include <sys/sysctl.h>
48 #include <sys/ioccom.h>
49 #include <sys/endian.h>
50 
51 #include <machine/bus.h>
52 #include <machine/resource.h>
53 #include <sys/rman.h>
54 
55 #include <cam/scsi/scsi_all.h>
56 
57 #include <dev/mps/mpi/mpi2_type.h>
58 #include <dev/mps/mpi/mpi2.h>
59 #include <dev/mps/mpi/mpi2_ioc.h>
60 #include <dev/mps/mpi/mpi2_cnfg.h>
61 #include <dev/mps/mpsvar.h>
62 #include <dev/mps/mps_table.h>
63 #include <dev/mps/mps_ioctl.h>
64 
65 static d_open_t		mps_open;
66 static d_close_t	mps_close;
67 static d_ioctl_t	mps_ioctl;
68 
69 static struct cdevsw mps_cdevsw = {
70 	.d_version =	D_VERSION,
71 	.d_flags =	0,
72 	.d_open =	mps_open,
73 	.d_close =	mps_close,
74 	.d_ioctl =	mps_ioctl,
75 	.d_name =	"mps",
76 };
77 
78 static MALLOC_DEFINE(M_MPSUSER, "mps_user", "Buffers for mps(4) ioctls");
79 
80 int
81 mps_attach_user(struct mps_softc *sc)
82 {
83 	int unit;
84 
85 	unit = device_get_unit(sc->mps_dev);
86 	sc->mps_cdev = make_dev(&mps_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640,
87 	    "mps%d", unit);
88 	if (sc->mps_cdev == NULL) {
89 		return (ENOMEM);
90 	}
91 	sc->mps_cdev->si_drv1 = sc;
92 	return (0);
93 }
94 
95 void
96 mps_detach_user(struct mps_softc *sc)
97 {
98 
99 	/* XXX: do a purge of pending requests? */
100 	destroy_dev(sc->mps_cdev);
101 
102 }
103 
104 static int
105 mps_open(struct cdev *dev, int flags, int fmt, struct thread *td)
106 {
107 
108 	return (0);
109 }
110 
111 static int
112 mps_close(struct cdev *dev, int flags, int fmt, struct thread *td)
113 {
114 
115 	return (0);
116 }
117 
118 static int
119 mps_user_read_cfg_header(struct mps_softc *sc,
120     struct mps_cfg_page_req *page_req)
121 {
122 	MPI2_CONFIG_PAGE_HEADER *hdr;
123 	struct mps_config_params params;
124 	int	    error;
125 
126 	hdr = &params.hdr.Struct;
127 	params.action = MPI2_CONFIG_ACTION_PAGE_HEADER;
128 	params.page_address = le32toh(page_req->page_address);
129 	hdr->PageVersion = 0;
130 	hdr->PageLength = 0;
131 	hdr->PageNumber = page_req->header.PageNumber;
132 	hdr->PageType = page_req->header.PageType;
133 	params.buffer = NULL;
134 	params.length = 0;
135 	params.callback = NULL;
136 
137 	if ((error = mps_read_config_page(sc, &params)) != 0) {
138 		/*
139 		 * Leave the request. Without resetting the chip, it's
140 		 * still owned by it and we'll just get into trouble
141 		 * freeing it now. Mark it as abandoned so that if it
142 		 * shows up later it can be freed.
143 		 */
144 		mps_printf(sc, "read_cfg_header timed out\n");
145 		return (ETIMEDOUT);
146 	}
147 
148 	page_req->ioc_status = htole16(params.status);
149 	if ((page_req->ioc_status & MPI2_IOCSTATUS_MASK) ==
150 	    MPI2_IOCSTATUS_SUCCESS) {
151 		bcopy(hdr, &page_req->header, sizeof(page_req->header));
152 	}
153 
154 	return (0);
155 }
156 
157 static int
158 mps_user_read_cfg_page(struct mps_softc *sc, struct mps_cfg_page_req *page_req,
159     void *buf)
160 {
161 	MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr;
162 	struct mps_config_params params;
163 	int	      error;
164 
165 	reqhdr = buf;
166 	hdr = &params.hdr.Struct;
167 	hdr->PageVersion = reqhdr->PageVersion;
168 	hdr->PageLength = reqhdr->PageLength;
169 	hdr->PageNumber = reqhdr->PageNumber;
170 	hdr->PageType = reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK;
171 	params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
172 	params.page_address = le32toh(page_req->page_address);
173 	params.buffer = buf;
174 	params.length = le32toh(page_req->len);
175 	params.callback = NULL;
176 
177 	if ((error = mps_read_config_page(sc, &params)) != 0) {
178 		mps_printf(sc, "mps_user_read_cfg_page timed out\n");
179 		return (ETIMEDOUT);
180 	}
181 
182 	page_req->ioc_status = htole16(params.status);
183 	return (0);
184 }
185 
186 static int
187 mps_user_read_extcfg_header(struct mps_softc *sc,
188     struct mps_ext_cfg_page_req *ext_page_req)
189 {
190 	MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr;
191 	struct mps_config_params params;
192 	int	    error;
193 
194 	hdr = &params.hdr.Ext;
195 	params.action = MPI2_CONFIG_ACTION_PAGE_HEADER;
196 	hdr->PageVersion = ext_page_req->header.PageVersion;
197 	hdr->ExtPageLength = 0;
198 	hdr->PageNumber = ext_page_req->header.PageNumber;
199 	hdr->ExtPageType = ext_page_req->header.ExtPageType;
200 	params.page_address = le32toh(ext_page_req->page_address);
201 	if ((error = mps_read_config_page(sc, &params)) != 0) {
202 		/*
203 		 * Leave the request. Without resetting the chip, it's
204 		 * still owned by it and we'll just get into trouble
205 		 * freeing it now. Mark it as abandoned so that if it
206 		 * shows up later it can be freed.
207 		 */
208 		mps_printf(sc, "mps_user_read_extcfg_header timed out\n");
209 		return (ETIMEDOUT);
210 	}
211 
212 	ext_page_req->ioc_status = htole16(params.status);
213 	if ((ext_page_req->ioc_status & MPI2_IOCSTATUS_MASK) ==
214 	    MPI2_IOCSTATUS_SUCCESS) {
215 		ext_page_req->header.PageVersion = hdr->PageVersion;
216 		ext_page_req->header.PageNumber = hdr->PageNumber;
217 		ext_page_req->header.PageType = hdr->PageType;
218 		ext_page_req->header.ExtPageLength = hdr->ExtPageLength;
219 		ext_page_req->header.ExtPageType = hdr->ExtPageType;
220 	}
221 
222 	return (0);
223 }
224 
225 static int
226 mps_user_read_extcfg_page(struct mps_softc *sc,
227     struct mps_ext_cfg_page_req *ext_page_req, void *buf)
228 {
229 	MPI2_CONFIG_EXTENDED_PAGE_HEADER *reqhdr, *hdr;
230 	struct mps_config_params params;
231 	int error;
232 
233 	reqhdr = buf;
234 	hdr = &params.hdr.Ext;
235 	params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
236 	params.page_address = le32toh(ext_page_req->page_address);
237 	hdr->PageVersion = reqhdr->PageVersion;
238 	hdr->PageNumber = reqhdr->PageNumber;
239 	hdr->ExtPageType = reqhdr->ExtPageType;
240 	hdr->ExtPageLength = reqhdr->ExtPageLength;
241 	params.buffer = buf;
242 	params.length = le32toh(ext_page_req->len);
243 	params.callback = NULL;
244 
245 	if ((error = mps_read_config_page(sc, &params)) != 0) {
246 		mps_printf(sc, "mps_user_read_extcfg_page timed out\n");
247 		return (ETIMEDOUT);
248 	}
249 
250 	ext_page_req->ioc_status = htole16(params.status);
251 	return (0);
252 }
253 
254 static int
255 mps_user_write_cfg_page(struct mps_softc *sc,
256     struct mps_cfg_page_req *page_req, void *buf)
257 {
258 	MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr;
259 	struct mps_config_params params;
260 	u_int	      hdr_attr;
261 	int	      error;
262 
263 	reqhdr = buf;
264 	hdr = &params.hdr.Struct;
265 	hdr_attr = reqhdr->PageType & MPI2_CONFIG_PAGEATTR_MASK;
266 	if (hdr_attr != MPI2_CONFIG_PAGEATTR_CHANGEABLE &&
267 	    hdr_attr != MPI2_CONFIG_PAGEATTR_PERSISTENT) {
268 		mps_printf(sc, "page type 0x%x not changeable\n",
269 			reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK);
270 		return (EINVAL);
271 	}
272 
273 	/*
274 	 * There isn't any point in restoring stripped out attributes
275 	 * if you then mask them going down to issue the request.
276 	 */
277 
278 	hdr->PageVersion = reqhdr->PageVersion;
279 	hdr->PageLength = reqhdr->PageLength;
280 	hdr->PageNumber = reqhdr->PageNumber;
281 	hdr->PageType = reqhdr->PageType;
282 	params.action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
283 	params.page_address = le32toh(page_req->page_address);
284 	params.buffer = buf;
285 	params.length = le32toh(page_req->len);
286 	params.callback = NULL;
287 
288 	if ((error = mps_write_config_page(sc, &params)) != 0) {
289 		mps_printf(sc, "mps_write_cfg_page timed out\n");
290 		return (ETIMEDOUT);
291 	}
292 
293 	page_req->ioc_status = htole16(params.status);
294 	return (0);
295 }
296 
297 struct mps_user_func {
298 	U8 Func;
299 	U8 SgOff;
300 } mps_user_func_list[] = {
301 	{ MPI2_FUNCTION_IOC_FACTS,	0 },
302 	{ MPI2_FUNCTION_PORT_FACTS,	0 },
303 	{ MPI2_FUNCTION_FW_DOWNLOAD, 	offsetof(Mpi2FWDownloadRequest,SGL)},
304 	{ MPI2_FUNCTION_FW_UPLOAD,	offsetof(Mpi2FWUploadRequest_t,SGL)},
305 	{ MPI2_FUNCTION_SATA_PASSTHROUGH,offsetof(Mpi2SataPassthroughRequest_t,SGL)},
306 	{ MPI2_FUNCTION_SMP_PASSTHROUGH, offsetof(Mpi2SmpPassthroughRequest_t,SGL)},
307 	{ MPI2_FUNCTION_CONFIG,		offsetof(Mpi2ConfigRequest_t,PageBufferSGE)},
308 	{ MPI2_FUNCTION_SAS_IO_UNIT_CONTROL,	0 },
309 };
310 
311 static int
312 mps_user_verify_request(MPI2_REQUEST_HEADER *hdr, MPI2_SGE_IO_UNION **psgl)
313 {
314 	int i, err = EINVAL;
315 
316 	for (i = 0; i < sizeof(mps_user_func_list) /
317 	    sizeof(mps_user_func_list[0]); i++ ) {
318 		struct mps_user_func *func = &mps_user_func_list[i];
319 
320 		if (hdr->Function == func->Func) {
321 			if (psgl != NULL) {
322 				if (func->SgOff != 0)
323 					*psgl = (PTR_MPI2_SGE_IO_UNION)
324 					    ((char*)hdr + func->SgOff);
325 				else
326 					*psgl = NULL;
327 				err = 0;
328 				break;
329 			}
330 		}
331 	}
332 
333 	return err;
334 }
335 
336 static int
337 mps_user_command(struct mps_softc *sc, struct mps_usr_command *cmd)
338 {
339 	MPI2_REQUEST_HEADER *hdr;
340 	MPI2_DEFAULT_REPLY *rpl;
341 	MPI2_SGE_IO_UNION *sgl;
342 	void *buf;
343 	struct mps_command *cm;
344 	int err = 0;
345 	int sz;
346 
347 	mps_lock(sc);
348 	cm = mps_alloc_command(sc);
349 
350 	if (cm == NULL) {
351 		mps_printf(sc, "mps_user_command: no mps requests\n");
352 		err = ENOMEM;
353 		goto Ret;
354 	}
355 	mps_unlock(sc);
356 
357 	hdr = (MPI2_REQUEST_HEADER *)cm->cm_req;
358 
359 	mps_dprint(sc, MPS_INFO, "mps_user_command: req %p %d  rpl %p %d\n",
360 		    cmd->req, cmd->req_len, cmd->rpl, cmd->rpl_len );
361 
362 	copyin(cmd->req, hdr, cmd->req_len);
363 
364 	mps_dprint(sc, MPS_INFO, "mps_user_command: Function %02X  "
365 	    "MsgFlags %02X\n", hdr->Function, hdr->MsgFlags );
366 
367 	err = mps_user_verify_request(hdr, &sgl);
368 	if (err != 0) {
369 		mps_printf(sc, "mps_user_command: unsupported function 0x%X\n",
370 		    hdr->Function );
371 		goto RetFree;
372 	}
373 
374 	if (cmd->len > 0) {
375 		buf = malloc(cmd->len, M_MPSUSER, M_WAITOK|M_ZERO);
376 		cm->cm_data = buf;
377 		cm->cm_length = cmd->len;
378 	} else {
379 		buf = NULL;
380 		cm->cm_data = NULL;
381 		cm->cm_length = 0;
382 	}
383 
384 	cm->cm_sge = sgl;
385 	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
386 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_WAKEUP;
387 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
388 
389 	mps_lock(sc);
390 	err = mps_map_command(sc, cm);
391 
392 	if (err != 0) {
393 		mps_printf(sc, "mps_user_command: request timed out\n");
394 		goto Ret;
395 	}
396 	msleep(cm, &sc->mps_mtx, 0, "mpsuser", 0); /* 30 seconds */
397 
398 	rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply;
399 	sz = rpl->MsgLength * 4;
400 
401 	if (sz > cmd->rpl_len) {
402 		mps_printf(sc,
403 		    "mps_user_command: reply buffer too small %d required %d\n",
404 		    cmd->rpl_len, sz );
405 		err = EINVAL;
406 		sz = cmd->rpl_len;
407 	}
408 
409 	mps_unlock(sc);
410 	copyout(rpl, cmd->rpl, sz);
411 	if (buf != NULL) {
412 		copyout(buf, cmd->buf, cmd->len);
413 		free(buf, M_MPSUSER);
414 	}
415 	mps_lock(sc);
416 
417 	mps_dprint(sc, MPS_INFO, "mps_user_command: reply size %d\n", sz );
418 
419 RetFree:
420 	mps_free_command(sc, cm);
421 
422 Ret:
423 	mps_unlock(sc);
424 	return err;
425 }
426 
427 #ifdef __amd64__
428 #define	PTRIN(p)		((void *)(uintptr_t)(p))
429 #define PTROUT(v)		((u_int32_t)(uintptr_t)(v))
430 #endif
431 
432 static int
433 mps_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag,
434     struct thread *td)
435 {
436 	struct mps_softc *sc;
437 	struct mps_cfg_page_req *page_req;
438 	struct mps_ext_cfg_page_req *ext_page_req;
439 	void *mps_page;
440 #ifdef __amd64__
441 	struct mps_cfg_page_req32 *page_req32;
442 	struct mps_cfg_page_req page_req_swab;
443 	struct mps_ext_cfg_page_req32 *ext_page_req32;
444 	struct mps_ext_cfg_page_req ext_page_req_swab;
445 #endif
446 	int error;
447 
448 	mps_page = NULL;
449 	sc = dev->si_drv1;
450 	page_req = (void *)arg;
451 	ext_page_req = (void *)arg;
452 
453 #ifdef __amd64__
454 	/* Convert 32-bit structs to native ones. */
455 	page_req32 = (void *)arg;
456 	ext_page_req32 = (void *)arg;
457 	switch (cmd) {
458 	case MPSIO_READ_CFG_HEADER32:
459 	case MPSIO_READ_CFG_PAGE32:
460 	case MPSIO_WRITE_CFG_PAGE32:
461 		page_req = &page_req_swab;
462 		page_req->header = page_req32->header;
463 		page_req->page_address = page_req32->page_address;
464 		page_req->buf = PTRIN(page_req32->buf);
465 		page_req->len = page_req32->len;
466 		page_req->ioc_status = page_req32->ioc_status;
467 		break;
468 	case MPSIO_READ_EXT_CFG_HEADER32:
469 	case MPSIO_READ_EXT_CFG_PAGE32:
470 		ext_page_req = &ext_page_req_swab;
471 		ext_page_req->header = ext_page_req32->header;
472 		ext_page_req->page_address = ext_page_req32->page_address;
473 		ext_page_req->buf = PTRIN(ext_page_req32->buf);
474 		ext_page_req->len = ext_page_req32->len;
475 		ext_page_req->ioc_status = ext_page_req32->ioc_status;
476 		break;
477 	default:
478 		return (ENOIOCTL);
479 	}
480 #endif
481 
482 	switch (cmd) {
483 #ifdef __amd64__
484 	case MPSIO_READ_CFG_HEADER32:
485 #endif
486 	case MPSIO_READ_CFG_HEADER:
487 		mps_lock(sc);
488 		error = mps_user_read_cfg_header(sc, page_req);
489 		mps_unlock(sc);
490 		break;
491 #ifdef __amd64__
492 	case MPSIO_READ_CFG_PAGE32:
493 #endif
494 	case MPSIO_READ_CFG_PAGE:
495 		mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK | M_ZERO);
496 		error = copyin(page_req->buf, mps_page,
497 		    sizeof(MPI2_CONFIG_PAGE_HEADER));
498 		if (error)
499 			break;
500 		mps_lock(sc);
501 		error = mps_user_read_cfg_page(sc, page_req, mps_page);
502 		mps_unlock(sc);
503 		if (error)
504 			break;
505 		error = copyout(mps_page, page_req->buf, page_req->len);
506 		break;
507 #ifdef __amd64__
508 	case MPSIO_READ_EXT_CFG_HEADER32:
509 #endif
510 	case MPSIO_READ_EXT_CFG_HEADER:
511 		mps_lock(sc);
512 		error = mps_user_read_extcfg_header(sc, ext_page_req);
513 		mps_unlock(sc);
514 		break;
515 #ifdef __amd64__
516 	case MPSIO_READ_EXT_CFG_PAGE32:
517 #endif
518 	case MPSIO_READ_EXT_CFG_PAGE:
519 		mps_page = malloc(ext_page_req->len, M_MPSUSER, M_WAITOK|M_ZERO);
520 		error = copyin(ext_page_req->buf, mps_page,
521 		    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
522 		if (error)
523 			break;
524 		mps_lock(sc);
525 		error = mps_user_read_extcfg_page(sc, ext_page_req, mps_page);
526 		mps_unlock(sc);
527 		if (error)
528 			break;
529 		error = copyout(mps_page, ext_page_req->buf, ext_page_req->len);
530 		break;
531 #ifdef __amd64__
532 	case MPSIO_WRITE_CFG_PAGE32:
533 #endif
534 	case MPSIO_WRITE_CFG_PAGE:
535 		mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK|M_ZERO);
536 		error = copyin(page_req->buf, mps_page, page_req->len);
537 		if (error)
538 			break;
539 		mps_lock(sc);
540 		error = mps_user_write_cfg_page(sc, page_req, mps_page);
541 		mps_unlock(sc);
542 		break;
543 	case MPSIO_MPS_COMMAND:
544 		error = mps_user_command(sc, (struct mps_usr_command *)arg);
545 		break;
546 	default:
547 		error = ENOIOCTL;
548 		break;
549 	}
550 
551 	if (mps_page != NULL)
552 		free(mps_page, M_MPSUSER);
553 
554 	if (error)
555 		return (error);
556 
557 #ifdef __amd64__
558 	/* Convert native structs to 32-bit ones. */
559 	switch (cmd) {
560 	case MPSIO_READ_CFG_HEADER32:
561 	case MPSIO_READ_CFG_PAGE32:
562 	case MPSIO_WRITE_CFG_PAGE32:
563 		page_req32->header = page_req->header;
564 		page_req32->page_address = page_req->page_address;
565 		page_req32->buf = PTROUT(page_req->buf);
566 		page_req32->len = page_req->len;
567 		page_req32->ioc_status = page_req->ioc_status;
568 		break;
569 	case MPSIO_READ_EXT_CFG_HEADER32:
570 	case MPSIO_READ_EXT_CFG_PAGE32:
571 		ext_page_req32->header = ext_page_req->header;
572 		ext_page_req32->page_address = ext_page_req->page_address;
573 		ext_page_req32->buf = PTROUT(ext_page_req->buf);
574 		ext_page_req32->len = ext_page_req->len;
575 		ext_page_req32->ioc_status = ext_page_req->ioc_status;
576 		break;
577 	default:
578 		return (ENOIOCTL);
579 	}
580 #endif
581 
582 	return (0);
583 }
584