xref: /freebsd/sys/dev/mps/mps_user.c (revision aa0a1e58)
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 "opt_compat.h"
37 
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/selinfo.h>
43 #include <sys/module.h>
44 #include <sys/bus.h>
45 #include <sys/conf.h>
46 #include <sys/bio.h>
47 #include <sys/malloc.h>
48 #include <sys/uio.h>
49 #include <sys/sysctl.h>
50 #include <sys/ioccom.h>
51 #include <sys/endian.h>
52 #include <sys/proc.h>
53 #include <sys/sysent.h>
54 
55 #include <machine/bus.h>
56 #include <machine/resource.h>
57 #include <sys/rman.h>
58 
59 #include <cam/scsi/scsi_all.h>
60 
61 #include <dev/mps/mpi/mpi2_type.h>
62 #include <dev/mps/mpi/mpi2.h>
63 #include <dev/mps/mpi/mpi2_ioc.h>
64 #include <dev/mps/mpi/mpi2_cnfg.h>
65 #include <dev/mps/mpsvar.h>
66 #include <dev/mps/mps_table.h>
67 #include <dev/mps/mps_ioctl.h>
68 
69 static d_open_t		mps_open;
70 static d_close_t	mps_close;
71 static d_ioctl_t	mps_ioctl_devsw;
72 
73 static struct cdevsw mps_cdevsw = {
74 	.d_version =	D_VERSION,
75 	.d_flags =	0,
76 	.d_open =	mps_open,
77 	.d_close =	mps_close,
78 	.d_ioctl =	mps_ioctl_devsw,
79 	.d_name =	"mps",
80 };
81 
82 typedef int (mps_user_f)(struct mps_command *, struct mps_usr_command *);
83 static mps_user_f	mpi_pre_ioc_facts;
84 static mps_user_f	mpi_pre_port_facts;
85 static mps_user_f	mpi_pre_fw_download;
86 static mps_user_f	mpi_pre_fw_upload;
87 static mps_user_f	mpi_pre_sata_passthrough;
88 static mps_user_f	mpi_pre_smp_passthrough;
89 static mps_user_f	mpi_pre_config;
90 static mps_user_f	mpi_pre_sas_io_unit_control;
91 
92 static int mps_user_read_cfg_header(struct mps_softc *,
93 				    struct mps_cfg_page_req *);
94 static int mps_user_read_cfg_page(struct mps_softc *,
95 				  struct mps_cfg_page_req *, void *);
96 static int mps_user_read_extcfg_header(struct mps_softc *,
97 				     struct mps_ext_cfg_page_req *);
98 static int mps_user_read_extcfg_page(struct mps_softc *,
99 				     struct mps_ext_cfg_page_req *, void *);
100 static int mps_user_write_cfg_page(struct mps_softc *,
101 				   struct mps_cfg_page_req *, void *);
102 static int mps_user_setup_request(struct mps_command *,
103 				  struct mps_usr_command *);
104 static int mps_user_command(struct mps_softc *, struct mps_usr_command *);
105 
106 static MALLOC_DEFINE(M_MPSUSER, "mps_user", "Buffers for mps(4) ioctls");
107 
108 int
109 mps_attach_user(struct mps_softc *sc)
110 {
111 	int unit;
112 
113 	unit = device_get_unit(sc->mps_dev);
114 	sc->mps_cdev = make_dev(&mps_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640,
115 	    "mps%d", unit);
116 	if (sc->mps_cdev == NULL) {
117 		return (ENOMEM);
118 	}
119 	sc->mps_cdev->si_drv1 = sc;
120 	return (0);
121 }
122 
123 void
124 mps_detach_user(struct mps_softc *sc)
125 {
126 
127 	/* XXX: do a purge of pending requests? */
128 	destroy_dev(sc->mps_cdev);
129 
130 }
131 
132 static int
133 mps_open(struct cdev *dev, int flags, int fmt, struct thread *td)
134 {
135 
136 	return (0);
137 }
138 
139 static int
140 mps_close(struct cdev *dev, int flags, int fmt, struct thread *td)
141 {
142 
143 	return (0);
144 }
145 
146 static int
147 mps_user_read_cfg_header(struct mps_softc *sc,
148     struct mps_cfg_page_req *page_req)
149 {
150 	MPI2_CONFIG_PAGE_HEADER *hdr;
151 	struct mps_config_params params;
152 	int	    error;
153 
154 	hdr = &params.hdr.Struct;
155 	params.action = MPI2_CONFIG_ACTION_PAGE_HEADER;
156 	params.page_address = le32toh(page_req->page_address);
157 	hdr->PageVersion = 0;
158 	hdr->PageLength = 0;
159 	hdr->PageNumber = page_req->header.PageNumber;
160 	hdr->PageType = page_req->header.PageType;
161 	params.buffer = NULL;
162 	params.length = 0;
163 	params.callback = NULL;
164 
165 	if ((error = mps_read_config_page(sc, &params)) != 0) {
166 		/*
167 		 * Leave the request. Without resetting the chip, it's
168 		 * still owned by it and we'll just get into trouble
169 		 * freeing it now. Mark it as abandoned so that if it
170 		 * shows up later it can be freed.
171 		 */
172 		mps_printf(sc, "read_cfg_header timed out\n");
173 		return (ETIMEDOUT);
174 	}
175 
176 	page_req->ioc_status = htole16(params.status);
177 	if ((page_req->ioc_status & MPI2_IOCSTATUS_MASK) ==
178 	    MPI2_IOCSTATUS_SUCCESS) {
179 		bcopy(hdr, &page_req->header, sizeof(page_req->header));
180 	}
181 
182 	return (0);
183 }
184 
185 static int
186 mps_user_read_cfg_page(struct mps_softc *sc, struct mps_cfg_page_req *page_req,
187     void *buf)
188 {
189 	MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr;
190 	struct mps_config_params params;
191 	int	      error;
192 
193 	reqhdr = buf;
194 	hdr = &params.hdr.Struct;
195 	hdr->PageVersion = reqhdr->PageVersion;
196 	hdr->PageLength = reqhdr->PageLength;
197 	hdr->PageNumber = reqhdr->PageNumber;
198 	hdr->PageType = reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK;
199 	params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
200 	params.page_address = le32toh(page_req->page_address);
201 	params.buffer = buf;
202 	params.length = le32toh(page_req->len);
203 	params.callback = NULL;
204 
205 	if ((error = mps_read_config_page(sc, &params)) != 0) {
206 		mps_printf(sc, "mps_user_read_cfg_page timed out\n");
207 		return (ETIMEDOUT);
208 	}
209 
210 	page_req->ioc_status = htole16(params.status);
211 	return (0);
212 }
213 
214 static int
215 mps_user_read_extcfg_header(struct mps_softc *sc,
216     struct mps_ext_cfg_page_req *ext_page_req)
217 {
218 	MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr;
219 	struct mps_config_params params;
220 	int	    error;
221 
222 	hdr = &params.hdr.Ext;
223 	params.action = MPI2_CONFIG_ACTION_PAGE_HEADER;
224 	hdr->PageVersion = ext_page_req->header.PageVersion;
225 	hdr->ExtPageLength = 0;
226 	hdr->PageNumber = ext_page_req->header.PageNumber;
227 	hdr->ExtPageType = ext_page_req->header.ExtPageType;
228 	params.page_address = le32toh(ext_page_req->page_address);
229 	if ((error = mps_read_config_page(sc, &params)) != 0) {
230 		/*
231 		 * Leave the request. Without resetting the chip, it's
232 		 * still owned by it and we'll just get into trouble
233 		 * freeing it now. Mark it as abandoned so that if it
234 		 * shows up later it can be freed.
235 		 */
236 		mps_printf(sc, "mps_user_read_extcfg_header timed out\n");
237 		return (ETIMEDOUT);
238 	}
239 
240 	ext_page_req->ioc_status = htole16(params.status);
241 	if ((ext_page_req->ioc_status & MPI2_IOCSTATUS_MASK) ==
242 	    MPI2_IOCSTATUS_SUCCESS) {
243 		ext_page_req->header.PageVersion = hdr->PageVersion;
244 		ext_page_req->header.PageNumber = hdr->PageNumber;
245 		ext_page_req->header.PageType = hdr->PageType;
246 		ext_page_req->header.ExtPageLength = hdr->ExtPageLength;
247 		ext_page_req->header.ExtPageType = hdr->ExtPageType;
248 	}
249 
250 	return (0);
251 }
252 
253 static int
254 mps_user_read_extcfg_page(struct mps_softc *sc,
255     struct mps_ext_cfg_page_req *ext_page_req, void *buf)
256 {
257 	MPI2_CONFIG_EXTENDED_PAGE_HEADER *reqhdr, *hdr;
258 	struct mps_config_params params;
259 	int error;
260 
261 	reqhdr = buf;
262 	hdr = &params.hdr.Ext;
263 	params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
264 	params.page_address = le32toh(ext_page_req->page_address);
265 	hdr->PageVersion = reqhdr->PageVersion;
266 	hdr->PageNumber = reqhdr->PageNumber;
267 	hdr->ExtPageType = reqhdr->ExtPageType;
268 	hdr->ExtPageLength = reqhdr->ExtPageLength;
269 	params.buffer = buf;
270 	params.length = le32toh(ext_page_req->len);
271 	params.callback = NULL;
272 
273 	if ((error = mps_read_config_page(sc, &params)) != 0) {
274 		mps_printf(sc, "mps_user_read_extcfg_page timed out\n");
275 		return (ETIMEDOUT);
276 	}
277 
278 	ext_page_req->ioc_status = htole16(params.status);
279 	return (0);
280 }
281 
282 static int
283 mps_user_write_cfg_page(struct mps_softc *sc,
284     struct mps_cfg_page_req *page_req, void *buf)
285 {
286 	MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr;
287 	struct mps_config_params params;
288 	u_int	      hdr_attr;
289 	int	      error;
290 
291 	reqhdr = buf;
292 	hdr = &params.hdr.Struct;
293 	hdr_attr = reqhdr->PageType & MPI2_CONFIG_PAGEATTR_MASK;
294 	if (hdr_attr != MPI2_CONFIG_PAGEATTR_CHANGEABLE &&
295 	    hdr_attr != MPI2_CONFIG_PAGEATTR_PERSISTENT) {
296 		mps_printf(sc, "page type 0x%x not changeable\n",
297 			reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK);
298 		return (EINVAL);
299 	}
300 
301 	/*
302 	 * There isn't any point in restoring stripped out attributes
303 	 * if you then mask them going down to issue the request.
304 	 */
305 
306 	hdr->PageVersion = reqhdr->PageVersion;
307 	hdr->PageLength = reqhdr->PageLength;
308 	hdr->PageNumber = reqhdr->PageNumber;
309 	hdr->PageType = reqhdr->PageType;
310 	params.action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
311 	params.page_address = le32toh(page_req->page_address);
312 	params.buffer = buf;
313 	params.length = le32toh(page_req->len);
314 	params.callback = NULL;
315 
316 	if ((error = mps_write_config_page(sc, &params)) != 0) {
317 		mps_printf(sc, "mps_write_cfg_page timed out\n");
318 		return (ETIMEDOUT);
319 	}
320 
321 	page_req->ioc_status = htole16(params.status);
322 	return (0);
323 }
324 
325 void
326 mpi_init_sge(struct mps_command *cm, void *req, void *sge)
327 {
328 	int off, space;
329 
330 	space = (int)cm->cm_sc->facts->IOCRequestFrameSize * 4;
331 	off = (uintptr_t)sge - (uintptr_t)req;
332 
333 	KASSERT(off < space, ("bad pointers %p %p, off %d, space %d",
334             req, sge, off, space));
335 
336 	cm->cm_sge = sge;
337 	cm->cm_sglsize = space - off;
338 }
339 
340 /*
341  * Prepare the mps_command for an IOC_FACTS request.
342  */
343 static int
344 mpi_pre_ioc_facts(struct mps_command *cm, struct mps_usr_command *cmd)
345 {
346 	MPI2_IOC_FACTS_REQUEST *req = (void *)cm->cm_req;
347 	MPI2_IOC_FACTS_REPLY *rpl;
348 
349 	if (cmd->req_len != sizeof *req)
350 		return (EINVAL);
351 	if (cmd->rpl_len != sizeof *rpl)
352 		return (EINVAL);
353 
354 	cm->cm_sge = NULL;
355 	cm->cm_sglsize = 0;
356 	return (0);
357 }
358 
359 /*
360  * Prepare the mps_command for a PORT_FACTS request.
361  */
362 static int
363 mpi_pre_port_facts(struct mps_command *cm, struct mps_usr_command *cmd)
364 {
365 	MPI2_PORT_FACTS_REQUEST *req = (void *)cm->cm_req;
366 	MPI2_PORT_FACTS_REPLY *rpl;
367 
368 	if (cmd->req_len != sizeof *req)
369 		return (EINVAL);
370 	if (cmd->rpl_len != sizeof *rpl)
371 		return (EINVAL);
372 
373 	cm->cm_sge = NULL;
374 	cm->cm_sglsize = 0;
375 	return (0);
376 }
377 
378 /*
379  * Prepare the mps_command for a FW_DOWNLOAD request.
380  */
381 static int
382 mpi_pre_fw_download(struct mps_command *cm, struct mps_usr_command *cmd)
383 {
384 	MPI2_FW_DOWNLOAD_REQUEST *req = (void *)cm->cm_req;
385 	MPI2_FW_DOWNLOAD_REPLY *rpl;
386 	MPI2_FW_DOWNLOAD_TCSGE tc;
387 	int error;
388 
389 	/*
390 	 * This code assumes there is room in the request's SGL for
391 	 * the TransactionContext plus at least a SGL chain element.
392 	 */
393 	CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE);
394 
395 	if (cmd->req_len != sizeof *req)
396 		return (EINVAL);
397 	if (cmd->rpl_len != sizeof *rpl)
398 		return (EINVAL);
399 
400 	if (cmd->len == 0)
401 		return (EINVAL);
402 
403 	error = copyin(cmd->buf, cm->cm_data, cmd->len);
404 	if (error != 0)
405 		return (error);
406 
407 	mpi_init_sge(cm, req, &req->SGL);
408 	bzero(&tc, sizeof tc);
409 
410 	/*
411 	 * For now, the F/W image must be provided in a single request.
412 	 */
413 	if ((req->MsgFlags & MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT) == 0)
414 		return (EINVAL);
415 	if (req->TotalImageSize != cmd->len)
416 		return (EINVAL);
417 
418 	/*
419 	 * The value of the first two elements is specified in the
420 	 * Fusion-MPT Message Passing Interface document.
421 	 */
422 	tc.ContextSize = 0;
423 	tc.DetailsLength = 12;
424 	tc.ImageOffset = 0;
425 	tc.ImageSize = cmd->len;
426 
427 	cm->cm_flags |= MPS_CM_FLAGS_DATAOUT;
428 
429 	return (mps_push_sge(cm, &tc, sizeof tc, 0));
430 }
431 
432 /*
433  * Prepare the mps_command for a FW_UPLOAD request.
434  */
435 static int
436 mpi_pre_fw_upload(struct mps_command *cm, struct mps_usr_command *cmd)
437 {
438 	MPI2_FW_UPLOAD_REQUEST *req = (void *)cm->cm_req;
439 	MPI2_FW_UPLOAD_REPLY *rpl;
440 	MPI2_FW_UPLOAD_TCSGE tc;
441 
442 	/*
443 	 * This code assumes there is room in the request's SGL for
444 	 * the TransactionContext plus at least a SGL chain element.
445 	 */
446 	CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE);
447 
448 	if (cmd->req_len != sizeof *req)
449 		return (EINVAL);
450 	if (cmd->rpl_len != sizeof *rpl)
451 		return (EINVAL);
452 
453 	mpi_init_sge(cm, req, &req->SGL);
454 	if (cmd->len == 0) {
455 		/* Perhaps just asking what the size of the fw is? */
456 		return (0);
457 	}
458 
459 	bzero(&tc, sizeof tc);
460 
461 	/*
462 	 * The value of the first two elements is specified in the
463 	 * Fusion-MPT Message Passing Interface document.
464 	 */
465 	tc.ContextSize = 0;
466 	tc.DetailsLength = 12;
467 	/*
468 	 * XXX Is there any reason to fetch a partial image?  I.e. to
469 	 * set ImageOffset to something other than 0?
470 	 */
471 	tc.ImageOffset = 0;
472 	tc.ImageSize = cmd->len;
473 
474 	return (mps_push_sge(cm, &tc, sizeof tc, 0));
475 }
476 
477 /*
478  * Prepare the mps_command for a SATA_PASSTHROUGH request.
479  */
480 static int
481 mpi_pre_sata_passthrough(struct mps_command *cm, struct mps_usr_command *cmd)
482 {
483 	MPI2_SATA_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req;
484 	MPI2_SATA_PASSTHROUGH_REPLY *rpl;
485 
486 	if (cmd->req_len != sizeof *req)
487 		return (EINVAL);
488 	if (cmd->rpl_len != sizeof *rpl)
489 		return (EINVAL);
490 
491 	mpi_init_sge(cm, req, &req->SGL);
492 	return (0);
493 }
494 
495 /*
496  * Prepare the mps_command for a SMP_PASSTHROUGH request.
497  */
498 static int
499 mpi_pre_smp_passthrough(struct mps_command *cm, struct mps_usr_command *cmd)
500 {
501 	MPI2_SMP_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req;
502 	MPI2_SMP_PASSTHROUGH_REPLY *rpl;
503 
504 	if (cmd->req_len != sizeof *req)
505 		return (EINVAL);
506 	if (cmd->rpl_len != sizeof *rpl)
507 		return (EINVAL);
508 
509 	mpi_init_sge(cm, req, &req->SGL);
510 	return (0);
511 }
512 
513 /*
514  * Prepare the mps_command for a CONFIG request.
515  */
516 static int
517 mpi_pre_config(struct mps_command *cm, struct mps_usr_command *cmd)
518 {
519 	MPI2_CONFIG_REQUEST *req = (void *)cm->cm_req;
520 	MPI2_CONFIG_REPLY *rpl;
521 
522 	if (cmd->req_len != sizeof *req)
523 		return (EINVAL);
524 	if (cmd->rpl_len != sizeof *rpl)
525 		return (EINVAL);
526 
527 	mpi_init_sge(cm, req, &req->PageBufferSGE);
528 	return (0);
529 }
530 
531 /*
532  * Prepare the mps_command for a SAS_IO_UNIT_CONTROL request.
533  */
534 static int
535 mpi_pre_sas_io_unit_control(struct mps_command *cm,
536 			     struct mps_usr_command *cmd)
537 {
538 
539 	cm->cm_sge = NULL;
540 	cm->cm_sglsize = 0;
541 	return (0);
542 }
543 
544 /*
545  * A set of functions to prepare an mps_command for the various
546  * supported requests.
547  */
548 struct mps_user_func {
549 	U8		Function;
550 	mps_user_f	*f_pre;
551 } mps_user_func_list[] = {
552 	{ MPI2_FUNCTION_IOC_FACTS,		mpi_pre_ioc_facts },
553 	{ MPI2_FUNCTION_PORT_FACTS,		mpi_pre_port_facts },
554 	{ MPI2_FUNCTION_FW_DOWNLOAD, 		mpi_pre_fw_download },
555 	{ MPI2_FUNCTION_FW_UPLOAD,		mpi_pre_fw_upload },
556 	{ MPI2_FUNCTION_SATA_PASSTHROUGH,	mpi_pre_sata_passthrough },
557 	{ MPI2_FUNCTION_SMP_PASSTHROUGH,	mpi_pre_smp_passthrough},
558 	{ MPI2_FUNCTION_CONFIG,			mpi_pre_config},
559 	{ MPI2_FUNCTION_SAS_IO_UNIT_CONTROL,	mpi_pre_sas_io_unit_control },
560 	{ 0xFF,					NULL } /* list end */
561 };
562 
563 static int
564 mps_user_setup_request(struct mps_command *cm, struct mps_usr_command *cmd)
565 {
566 	MPI2_REQUEST_HEADER *hdr = (MPI2_REQUEST_HEADER *)cm->cm_req;
567 	struct mps_user_func *f;
568 
569 	for (f = mps_user_func_list; f->f_pre != NULL; f++) {
570 		if (hdr->Function == f->Function)
571 			return (f->f_pre(cm, cmd));
572 	}
573 	return (EINVAL);
574 }
575 
576 static int
577 mps_user_command(struct mps_softc *sc, struct mps_usr_command *cmd)
578 {
579 	MPI2_REQUEST_HEADER *hdr;
580 	MPI2_DEFAULT_REPLY *rpl;
581 	void *buf = NULL;
582 	struct mps_command *cm = NULL;
583 	int err = 0;
584 	int sz;
585 
586 	mps_lock(sc);
587 	cm = mps_alloc_command(sc);
588 
589 	if (cm == NULL) {
590 		mps_printf(sc, "mps_user_command: no mps requests\n");
591 		err = ENOMEM;
592 		goto Ret;
593 	}
594 	mps_unlock(sc);
595 
596 	hdr = (MPI2_REQUEST_HEADER *)cm->cm_req;
597 
598 	mps_dprint(sc, MPS_INFO, "mps_user_command: req %p %d  rpl %p %d\n",
599 		    cmd->req, cmd->req_len, cmd->rpl, cmd->rpl_len );
600 
601 	if (cmd->req_len > (int)sc->facts->IOCRequestFrameSize * 4) {
602 		err = EINVAL;
603 		goto RetFreeUnlocked;
604 	}
605 	err = copyin(cmd->req, hdr, cmd->req_len);
606 	if (err != 0)
607 		goto RetFreeUnlocked;
608 
609 	mps_dprint(sc, MPS_INFO, "mps_user_command: Function %02X  "
610 	    "MsgFlags %02X\n", hdr->Function, hdr->MsgFlags );
611 
612 	err = mps_user_setup_request(cm, cmd);
613 	if (err != 0) {
614 		mps_printf(sc, "mps_user_command: unsupported function 0x%X\n",
615 		    hdr->Function );
616 		goto RetFreeUnlocked;
617 	}
618 
619 	if (cmd->len > 0) {
620 		buf = malloc(cmd->len, M_MPSUSER, M_WAITOK|M_ZERO);
621 		cm->cm_data = buf;
622 		cm->cm_length = cmd->len;
623 	} else {
624 		cm->cm_data = NULL;
625 		cm->cm_length = 0;
626 	}
627 
628 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_WAKEUP;
629 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
630 
631 	mps_lock(sc);
632 	err = mps_map_command(sc, cm);
633 
634 	if (err != 0 && err != EINPROGRESS) {
635 		mps_printf(sc, "%s: invalid request: error %d\n",
636 		    __func__, err);
637 		goto Ret;
638 	}
639 	msleep(cm, &sc->mps_mtx, 0, "mpsuser", 0);
640 
641 	rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply;
642 	sz = rpl->MsgLength * 4;
643 
644 	if (sz > cmd->rpl_len) {
645 		mps_printf(sc,
646 		    "mps_user_command: reply buffer too small %d required %d\n",
647 		    cmd->rpl_len, sz );
648 		err = EINVAL;
649 		sz = cmd->rpl_len;
650 	}
651 
652 	mps_unlock(sc);
653 	copyout(rpl, cmd->rpl, sz);
654 	if (buf != NULL)
655 		copyout(buf, cmd->buf, cmd->len);
656 	mps_dprint(sc, MPS_INFO, "mps_user_command: reply size %d\n", sz );
657 
658 RetFreeUnlocked:
659 	mps_lock(sc);
660 	if (cm != NULL)
661 		mps_free_command(sc, cm);
662 Ret:
663 	mps_unlock(sc);
664 	if (buf != NULL)
665 		free(buf, M_MPSUSER);
666 	return (err);
667 }
668 
669 static int
670 mps_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag,
671     struct thread *td)
672 {
673 	struct mps_softc *sc;
674 	struct mps_cfg_page_req *page_req;
675 	struct mps_ext_cfg_page_req *ext_page_req;
676 	void *mps_page;
677 	int error;
678 
679 	mps_page = NULL;
680 	sc = dev->si_drv1;
681 	page_req = (void *)arg;
682 	ext_page_req = (void *)arg;
683 
684 	switch (cmd) {
685 	case MPSIO_READ_CFG_HEADER:
686 		mps_lock(sc);
687 		error = mps_user_read_cfg_header(sc, page_req);
688 		mps_unlock(sc);
689 		break;
690 	case MPSIO_READ_CFG_PAGE:
691 		mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK | M_ZERO);
692 		error = copyin(page_req->buf, mps_page,
693 		    sizeof(MPI2_CONFIG_PAGE_HEADER));
694 		if (error)
695 			break;
696 		mps_lock(sc);
697 		error = mps_user_read_cfg_page(sc, page_req, mps_page);
698 		mps_unlock(sc);
699 		if (error)
700 			break;
701 		error = copyout(mps_page, page_req->buf, page_req->len);
702 		break;
703 	case MPSIO_READ_EXT_CFG_HEADER:
704 		mps_lock(sc);
705 		error = mps_user_read_extcfg_header(sc, ext_page_req);
706 		mps_unlock(sc);
707 		break;
708 	case MPSIO_READ_EXT_CFG_PAGE:
709 		mps_page = malloc(ext_page_req->len, M_MPSUSER, M_WAITOK|M_ZERO);
710 		error = copyin(ext_page_req->buf, mps_page,
711 		    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
712 		if (error)
713 			break;
714 		mps_lock(sc);
715 		error = mps_user_read_extcfg_page(sc, ext_page_req, mps_page);
716 		mps_unlock(sc);
717 		if (error)
718 			break;
719 		error = copyout(mps_page, ext_page_req->buf, ext_page_req->len);
720 		break;
721 	case MPSIO_WRITE_CFG_PAGE:
722 		mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK|M_ZERO);
723 		error = copyin(page_req->buf, mps_page, page_req->len);
724 		if (error)
725 			break;
726 		mps_lock(sc);
727 		error = mps_user_write_cfg_page(sc, page_req, mps_page);
728 		mps_unlock(sc);
729 		break;
730 	case MPSIO_MPS_COMMAND:
731 		error = mps_user_command(sc, (struct mps_usr_command *)arg);
732 		break;
733 	default:
734 		error = ENOIOCTL;
735 		break;
736 	}
737 
738 	if (mps_page != NULL)
739 		free(mps_page, M_MPSUSER);
740 
741 	return (error);
742 }
743 
744 #ifdef COMPAT_FREEBSD32
745 
746 /* Macros from compat/freebsd32/freebsd32.h */
747 #define	PTRIN(v)	(void *)(uintptr_t)(v)
748 #define	PTROUT(v)	(uint32_t)(uintptr_t)(v)
749 
750 #define	CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0)
751 #define	PTRIN_CP(src,dst,fld)				\
752 	do { (dst).fld = PTRIN((src).fld); } while (0)
753 #define	PTROUT_CP(src,dst,fld) \
754 	do { (dst).fld = PTROUT((src).fld); } while (0)
755 
756 struct mps_cfg_page_req32 {
757 	MPI2_CONFIG_PAGE_HEADER header;
758 	uint32_t page_address;
759 	uint32_t buf;
760 	int	len;
761 	uint16_t ioc_status;
762 };
763 
764 struct mps_ext_cfg_page_req32 {
765 	MPI2_CONFIG_EXTENDED_PAGE_HEADER header;
766 	uint32_t page_address;
767 	uint32_t buf;
768 	int	len;
769 	uint16_t ioc_status;
770 };
771 
772 struct mps_raid_action32 {
773 	uint8_t action;
774 	uint8_t volume_bus;
775 	uint8_t volume_id;
776 	uint8_t phys_disk_num;
777 	uint32_t action_data_word;
778 	uint32_t buf;
779 	int len;
780 	uint32_t volume_status;
781 	uint32_t action_data[4];
782 	uint16_t action_status;
783 	uint16_t ioc_status;
784 	uint8_t write;
785 };
786 
787 struct mps_usr_command32 {
788 	uint32_t req;
789 	uint32_t req_len;
790 	uint32_t rpl;
791 	uint32_t rpl_len;
792 	uint32_t buf;
793 	int len;
794 	uint32_t flags;
795 };
796 
797 #define	MPSIO_READ_CFG_HEADER32	_IOWR('M', 200, struct mps_cfg_page_req32)
798 #define	MPSIO_READ_CFG_PAGE32	_IOWR('M', 201, struct mps_cfg_page_req32)
799 #define	MPSIO_READ_EXT_CFG_HEADER32 _IOWR('M', 202, struct mps_ext_cfg_page_req32)
800 #define	MPSIO_READ_EXT_CFG_PAGE32 _IOWR('M', 203, struct mps_ext_cfg_page_req32)
801 #define	MPSIO_WRITE_CFG_PAGE32	_IOWR('M', 204, struct mps_cfg_page_req32)
802 #define	MPSIO_RAID_ACTION32	_IOWR('M', 205, struct mps_raid_action32)
803 #define	MPSIO_MPS_COMMAND32	_IOWR('M', 210, struct mps_usr_command32)
804 
805 static int
806 mps_ioctl32(struct cdev *dev, u_long cmd32, void *_arg, int flag,
807     struct thread *td)
808 {
809 	struct mps_cfg_page_req32 *page32 = _arg;
810 	struct mps_ext_cfg_page_req32 *ext32 = _arg;
811 	struct mps_raid_action32 *raid32 = _arg;
812 	struct mps_usr_command32 *user32 = _arg;
813 	union {
814 		struct mps_cfg_page_req page;
815 		struct mps_ext_cfg_page_req ext;
816 		struct mps_raid_action raid;
817 		struct mps_usr_command user;
818 	} arg;
819 	u_long cmd;
820 	int error;
821 
822 	switch (cmd32) {
823 	case MPSIO_READ_CFG_HEADER32:
824 	case MPSIO_READ_CFG_PAGE32:
825 	case MPSIO_WRITE_CFG_PAGE32:
826 		if (cmd32 == MPSIO_READ_CFG_HEADER32)
827 			cmd = MPSIO_READ_CFG_HEADER;
828 		else if (cmd32 == MPSIO_READ_CFG_PAGE32)
829 			cmd = MPSIO_READ_CFG_PAGE;
830 		else
831 			cmd = MPSIO_WRITE_CFG_PAGE;
832 		CP(*page32, arg.page, header);
833 		CP(*page32, arg.page, page_address);
834 		PTRIN_CP(*page32, arg.page, buf);
835 		CP(*page32, arg.page, len);
836 		CP(*page32, arg.page, ioc_status);
837 		break;
838 
839 	case MPSIO_READ_EXT_CFG_HEADER32:
840 	case MPSIO_READ_EXT_CFG_PAGE32:
841 		if (cmd32 == MPSIO_READ_EXT_CFG_HEADER32)
842 			cmd = MPSIO_READ_EXT_CFG_HEADER;
843 		else
844 			cmd = MPSIO_READ_EXT_CFG_PAGE;
845 		CP(*ext32, arg.ext, header);
846 		CP(*ext32, arg.ext, page_address);
847 		PTRIN_CP(*ext32, arg.ext, buf);
848 		CP(*ext32, arg.ext, len);
849 		CP(*ext32, arg.ext, ioc_status);
850 		break;
851 
852 	case MPSIO_RAID_ACTION32:
853 		cmd = MPSIO_RAID_ACTION;
854 		CP(*raid32, arg.raid, action);
855 		CP(*raid32, arg.raid, volume_bus);
856 		CP(*raid32, arg.raid, volume_id);
857 		CP(*raid32, arg.raid, phys_disk_num);
858 		CP(*raid32, arg.raid, action_data_word);
859 		PTRIN_CP(*raid32, arg.raid, buf);
860 		CP(*raid32, arg.raid, len);
861 		CP(*raid32, arg.raid, volume_status);
862 		bcopy(raid32->action_data, arg.raid.action_data,
863 		    sizeof arg.raid.action_data);
864 		CP(*raid32, arg.raid, ioc_status);
865 		CP(*raid32, arg.raid, write);
866 		break;
867 
868 	case MPSIO_MPS_COMMAND32:
869 		cmd = MPSIO_MPS_COMMAND;
870 		PTRIN_CP(*user32, arg.user, req);
871 		CP(*user32, arg.user, req_len);
872 		PTRIN_CP(*user32, arg.user, rpl);
873 		CP(*user32, arg.user, rpl_len);
874 		PTRIN_CP(*user32, arg.user, buf);
875 		CP(*user32, arg.user, len);
876 		CP(*user32, arg.user, flags);
877 		break;
878 	default:
879 		return (ENOIOCTL);
880 	}
881 
882 	error = mps_ioctl(dev, cmd, &arg, flag, td);
883 	if (error == 0 && (cmd32 & IOC_OUT) != 0) {
884 		switch (cmd32) {
885 		case MPSIO_READ_CFG_HEADER32:
886 		case MPSIO_READ_CFG_PAGE32:
887 		case MPSIO_WRITE_CFG_PAGE32:
888 			CP(arg.page, *page32, header);
889 			CP(arg.page, *page32, page_address);
890 			PTROUT_CP(arg.page, *page32, buf);
891 			CP(arg.page, *page32, len);
892 			CP(arg.page, *page32, ioc_status);
893 			break;
894 
895 		case MPSIO_READ_EXT_CFG_HEADER32:
896 		case MPSIO_READ_EXT_CFG_PAGE32:
897 			CP(arg.ext, *ext32, header);
898 			CP(arg.ext, *ext32, page_address);
899 			PTROUT_CP(arg.ext, *ext32, buf);
900 			CP(arg.ext, *ext32, len);
901 			CP(arg.ext, *ext32, ioc_status);
902 			break;
903 
904 		case MPSIO_RAID_ACTION32:
905 			CP(arg.raid, *raid32, action);
906 			CP(arg.raid, *raid32, volume_bus);
907 			CP(arg.raid, *raid32, volume_id);
908 			CP(arg.raid, *raid32, phys_disk_num);
909 			CP(arg.raid, *raid32, action_data_word);
910 			PTROUT_CP(arg.raid, *raid32, buf);
911 			CP(arg.raid, *raid32, len);
912 			CP(arg.raid, *raid32, volume_status);
913 			bcopy(arg.raid.action_data, raid32->action_data,
914 			    sizeof arg.raid.action_data);
915 			CP(arg.raid, *raid32, ioc_status);
916 			CP(arg.raid, *raid32, write);
917 			break;
918 
919 		case MPSIO_MPS_COMMAND32:
920 			PTROUT_CP(arg.user, *user32, req);
921 			CP(arg.user, *user32, req_len);
922 			PTROUT_CP(arg.user, *user32, rpl);
923 			CP(arg.user, *user32, rpl_len);
924 			PTROUT_CP(arg.user, *user32, buf);
925 			CP(arg.user, *user32, len);
926 			CP(arg.user, *user32, flags);
927 			break;
928 		}
929 	}
930 
931 	return (error);
932 }
933 #endif /* COMPAT_FREEBSD32 */
934 
935 static int
936 mps_ioctl_devsw(struct cdev *dev, u_long com, caddr_t arg, int flag,
937     struct thread *td)
938 {
939 #ifdef COMPAT_FREEBSD32
940 	if (SV_CURPROC_FLAG(SV_ILP32))
941 		return (mps_ioctl32(dev, com, arg, flag, td));
942 #endif
943 	return (mps_ioctl(dev, com, arg, flag, td));
944 }
945