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