1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28 #include <sys/stat.h>
29 #include <sys/conf.h>
30 #include <sys/modctl.h>
31 #include <sys/ddi.h>
32 #include <sys/rmc_comm_dp.h>
33 #include <sys/rmc_comm_dp_boot.h>
34 #include <sys/rmc_comm_drvintf.h>
35 #include <sys/cyclic.h>
36 #include <sys/rmc_comm.h>
37 #include <sys/machsystm.h>
38 #include <sys/file.h>
39 #include <sys/rmcadm.h>
40
41 /*
42 * functions local to this driver.
43 */
44 static int rmcadm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
45 void **resultp);
46 static int rmcadm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
47 static int rmcadm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
48 static int rmcadm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
49 static int rmcadm_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
50 static int rmcadm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
51 cred_t *cred_p, int *rval_p);
52
53 /*
54 * Driver entry points
55 */
56 static struct cb_ops rmcadm_cb_ops = {
57 rmcadm_open, /* open */
58 rmcadm_close, /* close */
59 nodev, /* strategy() */
60 nodev, /* print() */
61 nodev, /* dump() */
62 nodev, /* read() */
63 nodev, /* write() */
64 rmcadm_ioctl, /* ioctl() */
65 nodev, /* devmap() */
66 nodev, /* mmap() */
67 ddi_segmap, /* segmap() */
68 nochpoll, /* poll() */
69 ddi_prop_op, /* prop_op() */
70 NULL, /* cb_str */
71 D_NEW | D_MP /* cb_flag */
72 };
73
74
75 static struct dev_ops rmcadm_ops = {
76 DEVO_REV,
77 0, /* ref count */
78 rmcadm_getinfo, /* getinfo() */
79 nulldev, /* identify() */
80 nulldev, /* probe() */
81 rmcadm_attach, /* attach() */
82 rmcadm_detach, /* detach */
83 nodev, /* reset */
84 &rmcadm_cb_ops, /* pointer to cb_ops structure */
85 (struct bus_ops *)NULL,
86 nulldev, /* power() */
87 ddi_quiesce_not_needed, /* quiesce */
88 };
89
90 /*
91 * Loadable module support.
92 */
93 extern struct mod_ops mod_driverops;
94
95 static struct modldrv modldrv = {
96 &mod_driverops, /* Type of module. This is a driver */
97 "rmcadm control driver", /* Name of the module */
98 &rmcadm_ops /* pointer to the dev_ops structure */
99 };
100
101 static struct modlinkage modlinkage = {
102 MODREV_1,
103 &modldrv,
104 NULL
105 };
106
107 static dev_info_t *rmcadm_dip = NULL;
108
109 extern void pmugpio_reset();
110
111 /*
112 * Utilities...
113 */
114
115 /*
116 * to return the errno from the rmc_comm error status
117 */
118 int
rmcadm_get_errno(int status)119 rmcadm_get_errno(int status)
120 {
121 int retval = EIO;
122
123 /* errors from RMC */
124 switch (status) {
125 case RCENOSOFTSTATE:
126 /* invalid/NULL soft state structure */
127 retval = EIO;
128 break;
129 case RCENODATALINK:
130 /* data protocol not available (down) */
131 retval = EIO;
132 break;
133 case RCENOMEM:
134 /* memory problems */
135 retval = ENOMEM;
136 break;
137 case RCECANTRESEND:
138 /* resend failed */
139 retval = EIO;
140 break;
141 case RCEMAXRETRIES:
142 /* reply not received - retries exceeded */
143 retval = EINTR;
144 break;
145 case RCETIMEOUT:
146 /* reply not received - command has timed out */
147 retval = EINTR;
148 break;
149 case RCEINVCMD:
150 /* data protocol cmd not supported */
151 retval = ENOTSUP;
152 break;
153 case RCEINVARG:
154 /* invalid argument(s) */
155 retval = ENOTSUP;
156 break;
157 case RCEGENERIC:
158 /* generic error */
159 retval = EIO;
160 break;
161 default:
162 retval = EIO;
163 break;
164 }
165 return (retval);
166 }
167
168 int
_init(void)169 _init(void)
170 {
171 int error = 0;
172
173 error = mod_install(&modlinkage);
174 return (error);
175 }
176
177
178 int
_info(struct modinfo * modinfop)179 _info(struct modinfo *modinfop)
180 {
181 return (mod_info(&modlinkage, modinfop));
182 }
183
184
185 int
_fini(void)186 _fini(void)
187 {
188 int error = 0;
189
190 error = mod_remove(&modlinkage);
191 if (error)
192 return (error);
193 return (error);
194 }
195
196
197 /* ARGSUSED */
198 static int
rmcadm_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)199 rmcadm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
200 {
201 minor_t m = getminor((dev_t)arg);
202
203 switch (cmd) {
204 case DDI_INFO_DEVT2DEVINFO:
205 if ((m != 0) || (rmcadm_dip == NULL)) {
206 *resultp = NULL;
207 return (DDI_FAILURE);
208 }
209 *resultp = rmcadm_dip;
210 return (DDI_SUCCESS);
211 case DDI_INFO_DEVT2INSTANCE:
212 *resultp = (void *)(uintptr_t)m;
213 return (DDI_SUCCESS);
214 default:
215 return (DDI_FAILURE);
216 }
217 }
218
219
220 static int
rmcadm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)221 rmcadm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
222 {
223 int instance;
224 int err;
225
226 switch (cmd) {
227 case DDI_ATTACH:
228 /*
229 * only allow one instance
230 */
231 instance = ddi_get_instance(dip);
232 if (instance != 0)
233 return (DDI_FAILURE);
234
235 err = ddi_create_minor_node(dip, "rmcadm", S_IFCHR,
236 instance, DDI_PSEUDO, NULL);
237 if (err != DDI_SUCCESS)
238 return (DDI_FAILURE);
239
240 /*
241 * Register with rmc_comm to prevent it being detached
242 */
243 err = rmc_comm_register();
244 if (err != DDI_SUCCESS) {
245 ddi_remove_minor_node(dip, NULL);
246 return (DDI_FAILURE);
247 }
248
249 /* Remember the dev info */
250 rmcadm_dip = dip;
251
252 ddi_report_dev(dip);
253 return (DDI_SUCCESS);
254 case DDI_RESUME:
255 return (DDI_SUCCESS);
256 default:
257 return (DDI_FAILURE);
258 }
259 }
260
261
262 static int
rmcadm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)263 rmcadm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
264 {
265 int instance;
266
267 switch (cmd) {
268 case DDI_DETACH:
269 instance = ddi_get_instance(dip);
270 if (instance != 0)
271 return (DDI_FAILURE);
272
273 rmcadm_dip = NULL;
274 ddi_remove_minor_node(dip, NULL);
275 rmc_comm_unregister();
276 return (DDI_SUCCESS);
277 case DDI_SUSPEND:
278 return (DDI_SUCCESS);
279 default:
280 return (DDI_FAILURE);
281 }
282 }
283
284 /*ARGSUSED*/
285 static int
rmcadm_open(dev_t * dev_p,int flag,int otyp,cred_t * cred_p)286 rmcadm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
287 {
288 int error = 0;
289 int instance = getminor(*dev_p);
290
291 if (instance != 0)
292 return (ENXIO);
293
294 if ((error = drv_priv(cred_p)) != 0) {
295 cmn_err(CE_WARN, "rmcadm: inst %d drv_priv failed",
296 instance);
297 return (error);
298 }
299 return (error);
300 }
301
302 /*ARGSUSED*/
303 static int
rmcadm_close(dev_t dev,int flag,int otyp,cred_t * cred_p)304 rmcadm_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
305 {
306 return (DDI_SUCCESS);
307 }
308
309 /*ARGSUSED*/
310 static int
rmcadm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)311 rmcadm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
312 int *rval_p)
313 {
314 int instance = getminor(dev);
315 int retval = 0;
316 rmcadm_request_response_t rr;
317 rmcadm_send_srecord_bp_t ssbp;
318 rmc_comm_msg_t rmc_req, *rmc_reqp = &rmc_req;
319 rmc_comm_msg_t rmc_resp, *rmc_respp = &rmc_resp;
320 caddr_t user_req_buf;
321 caddr_t user_data_buf;
322 caddr_t user_resp_buf;
323
324 if (instance != 0)
325 return (ENXIO);
326
327 switch (cmd) {
328
329 case RMCADM_REQUEST_RESPONSE:
330 case RMCADM_REQUEST_RESPONSE_BP:
331
332 /*
333 * first copy in the request_response structure
334 */
335 #ifdef _MULTI_DATAMODEL
336 switch (ddi_model_convert_from(mode & FMODELS)) {
337 case DDI_MODEL_ILP32:
338 {
339 /*
340 * For use when a 32 bit app makes a call into a
341 * 64 bit ioctl
342 */
343 rmcadm_request_response32_t rr32;
344
345 if (ddi_copyin((caddr_t)arg, (caddr_t)&rr32,
346 sizeof (rr32), mode)) {
347 return (EFAULT);
348 }
349 rr.req.msg_type = rr32.req.msg_type;
350 rr.req.msg_len = rr32.req.msg_len;
351 rr.req.msg_bytes = rr32.req.msg_bytes;
352 rr.req.msg_buf = (caddr_t)(uintptr_t)rr32.req.msg_buf;
353 rr.resp.msg_type = rr32.resp.msg_type;
354 rr.resp.msg_len = rr32.resp.msg_len;
355 rr.resp.msg_bytes = rr32.resp.msg_bytes;
356 rr.resp.msg_buf = (caddr_t)(uintptr_t)rr32.resp.msg_buf;
357 rr.wait_time = rr32.wait_time;
358 break;
359 }
360 case DDI_MODEL_NONE:
361 if (ddi_copyin((caddr_t)arg, (caddr_t)&rr,
362 sizeof (rr), mode)) {
363 return (EFAULT);
364 }
365 break;
366 }
367 #else /* ! _MULTI_DATAMODEL */
368 if (ddi_copyin((caddr_t)arg, (caddr_t)&rr,
369 sizeof (rr), mode) != 0) {
370 return (EFAULT);
371 }
372 #endif /* _MULTI_DATAMODEL */
373
374 /*
375 * save the user request buffer pointer
376 */
377 user_req_buf = rr.req.msg_buf;
378
379 if (user_req_buf != NULL) {
380 /*
381 * copy in the request data
382 */
383 rr.req.msg_buf = kmem_alloc(rr.req.msg_len, KM_SLEEP);
384
385 if (ddi_copyin(user_req_buf, rr.req.msg_buf,
386 rr.req.msg_len, mode) != 0) {
387
388 kmem_free(rr.req.msg_buf, rr.req.msg_len);
389 rr.req.msg_buf = user_req_buf;
390 return (EFAULT);
391 }
392 } else {
393 if (rr.req.msg_len > 0)
394 /*
395 * msg_len should be 0 if buffer is NULL!
396 */
397 return (EINVAL);
398 }
399
400 /*
401 * save the user request buffer pointer
402 */
403 user_resp_buf = rr.resp.msg_buf;
404 if (user_resp_buf != NULL) {
405 rr.resp.msg_buf = kmem_alloc(rr.resp.msg_len, KM_SLEEP);
406 }
407
408 /*
409 * send the request (or BP request) via the rmc_comm driver
410 */
411 rmc_reqp->msg_type = rr.req.msg_type;
412 rmc_reqp->msg_buf = rr.req.msg_buf;
413 rmc_reqp->msg_len = rr.req.msg_len;
414 rmc_reqp->msg_bytes = rr.req.msg_bytes;
415
416 if (cmd == RMCADM_REQUEST_RESPONSE) {
417
418 /*
419 * check if response is expected. If so, fill in
420 * the response data structure
421 */
422 if (rr.resp.msg_type != DP_NULL_MSG) {
423
424 rmc_respp->msg_type = rr.resp.msg_type;
425 rmc_respp->msg_buf = rr.resp.msg_buf;
426 rmc_respp->msg_len = rr.resp.msg_len;
427 rmc_respp->msg_bytes = rr.resp.msg_bytes;
428
429 } else {
430
431 rmc_respp = (rmc_comm_msg_t *)NULL;
432 }
433
434 rr.status = rmc_comm_request_response(
435 rmc_reqp, rmc_respp, rr.wait_time);
436
437 } else { /* RMCADM_REQUEST_RESPONSE_BP */
438
439 /*
440 * check if a BP message is expected back. If so,
441 * fill in the response data structure
442 */
443 if (rr.resp.msg_buf != NULL) {
444
445 rmc_respp->msg_type = rr.resp.msg_type;
446 rmc_respp->msg_buf = rr.resp.msg_buf;
447 rmc_respp->msg_len = rr.resp.msg_len;
448 rmc_respp->msg_bytes = rr.resp.msg_bytes;
449
450 } else {
451
452 rmc_respp = (rmc_comm_msg_t *)NULL;
453 }
454
455 rr.status = rmc_comm_request_response_bp(
456 rmc_reqp, rmc_respp, rr.wait_time);
457 }
458
459 /*
460 * if a response was expected, copy back the (actual) number
461 * of bytes of the response returned by the
462 * rmc_comm_request_response function (msg_bytes field)
463 */
464 if (rmc_respp != NULL) {
465 rr.resp.msg_bytes = rmc_respp->msg_bytes;
466 }
467
468 if (rr.status != RCNOERR) {
469
470 retval = rmcadm_get_errno(rr.status);
471
472 } else if (user_resp_buf != NULL) {
473 /*
474 * copy out the user response buffer
475 */
476 if (ddi_copyout(rr.resp.msg_buf, user_resp_buf,
477 rr.resp.msg_bytes, mode) != 0) {
478 retval = EFAULT;
479 }
480 }
481
482 /*
483 * now copy out the updated request_response structure
484 */
485 if (rr.req.msg_buf)
486 kmem_free(rr.req.msg_buf, rr.req.msg_len);
487 if (rr.resp.msg_buf)
488 kmem_free(rr.resp.msg_buf, rr.resp.msg_len);
489
490 rr.req.msg_buf = user_req_buf;
491 rr.resp.msg_buf = user_resp_buf;
492
493 #ifdef _MULTI_DATAMODEL
494 switch (ddi_model_convert_from(mode & FMODELS)) {
495 case DDI_MODEL_ILP32:
496 {
497 /*
498 * For use when a 32 bit app makes a call into a
499 * 64 bit ioctl
500 */
501 rmcadm_request_response32_t rr32;
502
503 rr32.req.msg_type = rr.req.msg_type;
504 rr32.req.msg_len = rr.req.msg_len;
505 rr32.req.msg_bytes = rr.req.msg_bytes;
506 rr32.req.msg_buf = (caddr32_t)(uintptr_t)rr.req.msg_buf;
507 rr32.resp.msg_type = rr.resp.msg_type;
508 rr32.resp.msg_len = rr.resp.msg_len;
509 rr32.resp.msg_bytes = rr.resp.msg_bytes;
510 rr32.resp.msg_buf =
511 (caddr32_t)(uintptr_t)rr.resp.msg_buf;
512 rr32.wait_time = rr.wait_time;
513 rr32.status = rr.status;
514 if (ddi_copyout((caddr_t)&rr32, (caddr_t)arg,
515 sizeof (rr32), mode)) {
516 return (EFAULT);
517 }
518 break;
519 }
520 case DDI_MODEL_NONE:
521 if (ddi_copyout((caddr_t)&rr, (caddr_t)arg,
522 sizeof (rr), mode))
523 return (EFAULT);
524 break;
525 }
526 #else /* ! _MULTI_DATAMODEL */
527 if (ddi_copyout((caddr_t)&rr, (caddr_t)arg, sizeof (rr),
528 mode) != 0)
529 return (EFAULT);
530 #endif /* _MULTI_DATAMODEL */
531 break;
532
533
534 case RMCADM_SEND_SRECORD_BP:
535
536 /*
537 * first copy in the request_response structure
538 */
539 #ifdef _MULTI_DATAMODEL
540 switch (ddi_model_convert_from(mode & FMODELS)) {
541 case DDI_MODEL_ILP32:
542 {
543 /*
544 * For use when a 32 bit app makes a call into a
545 * 64 bit ioctl
546 */
547 rmcadm_send_srecord_bp32_t ssbp32;
548
549 if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp32,
550 sizeof (ssbp32), mode)) {
551 return (EFAULT);
552 }
553 ssbp.data_len = ssbp32.data_len;
554 ssbp.data_buf = (caddr_t)(uintptr_t)ssbp32.data_buf;
555 ssbp.resp_bp.msg_type = ssbp32.resp_bp.msg_type;
556 ssbp.resp_bp.msg_len = ssbp32.resp_bp.msg_len;
557 ssbp.resp_bp.msg_bytes = ssbp32.resp_bp.msg_bytes;
558 ssbp.resp_bp.msg_buf =
559 (caddr_t)(uintptr_t)ssbp32.resp_bp.msg_buf;
560 ssbp.wait_time = ssbp32.wait_time;
561 break;
562 }
563 case DDI_MODEL_NONE:
564 if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp,
565 sizeof (ssbp), mode))
566 return (EFAULT);
567 break;
568 }
569 #else /* ! _MULTI_DATAMODEL */
570 if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp,
571 sizeof (ssbp), mode) != 0)
572 return (EFAULT);
573 #endif /* _MULTI_DATAMODEL */
574
575 /*
576 * save the user data buffer pointer
577 */
578 user_data_buf = ssbp.data_buf;
579
580 if (user_data_buf != NULL) {
581 /*
582 * copy in the srecord data
583 */
584 ssbp.data_buf = kmem_alloc(ssbp.data_len, KM_SLEEP);
585
586 if (ddi_copyin(user_data_buf, ssbp.data_buf,
587 ssbp.data_len, mode) != 0) {
588
589 kmem_free(ssbp.data_buf, ssbp.data_len);
590 ssbp.data_buf = user_data_buf;
591 return (EFAULT);
592 }
593 } else {
594 return (EINVAL); /* request can't be NULL! */
595 }
596
597 /*
598 * save the user request buffer pointer
599 */
600 user_resp_buf = ssbp.resp_bp.msg_buf;
601 if (user_resp_buf != NULL) {
602 ssbp.resp_bp.msg_buf =
603 kmem_alloc(ssbp.resp_bp.msg_len, KM_SLEEP);
604 } else {
605
606 kmem_free(ssbp.data_buf, ssbp.data_len);
607 return (EINVAL);
608 }
609
610 /*
611 * send the srecord via the rmc_comm driver and get the reply
612 * back (BP message)
613 */
614
615 rmc_respp->msg_type = ssbp.resp_bp.msg_type;
616 rmc_respp->msg_buf = ssbp.resp_bp.msg_buf;
617 rmc_respp->msg_len = ssbp.resp_bp.msg_len;
618 rmc_respp->msg_bytes = ssbp.resp_bp.msg_bytes;
619
620 ssbp.status = rmc_comm_send_srecord_bp(ssbp.data_buf,
621 ssbp.data_len, rmc_respp, ssbp.wait_time);
622
623 /*
624 * copy back the actual size of the returned message
625 */
626 ssbp.resp_bp.msg_bytes = rmc_respp->msg_bytes;
627
628 if (ssbp.status != RCNOERR) {
629 retval = rmcadm_get_errno(ssbp.status);
630
631 } else if (user_resp_buf != NULL) {
632 /*
633 * copy out the user BP response buffer
634 */
635 if (ddi_copyout(ssbp.resp_bp.msg_buf, user_resp_buf,
636 ssbp.resp_bp.msg_bytes, mode) != 0) {
637 retval = EFAULT;
638 }
639 }
640
641 /*
642 * now copy out the updated request_response structure
643 */
644 if (ssbp.data_buf)
645 kmem_free(ssbp.data_buf, ssbp.data_len);
646 if (ssbp.resp_bp.msg_buf)
647 kmem_free(ssbp.resp_bp.msg_buf, ssbp.resp_bp.msg_len);
648
649 ssbp.data_buf = user_data_buf;
650 ssbp.resp_bp.msg_buf = user_resp_buf;
651
652 #ifdef _MULTI_DATAMODEL
653 switch (ddi_model_convert_from(mode & FMODELS)) {
654 case DDI_MODEL_ILP32:
655 {
656 /*
657 * For use when a 32 bit app makes a call into a
658 * 64 bit ioctl
659 */
660 rmcadm_send_srecord_bp32_t ssbp32;
661
662 ssbp32.data_len = ssbp.data_len;
663 ssbp32.data_buf = (caddr32_t)(uintptr_t)ssbp.data_buf;
664 ssbp32.resp_bp.msg_type = ssbp.resp_bp.msg_type;
665 ssbp32.resp_bp.msg_len = ssbp.resp_bp.msg_len;
666 ssbp32.resp_bp.msg_bytes = ssbp.resp_bp.msg_bytes;
667 ssbp32.resp_bp.msg_buf =
668 (caddr32_t)(uintptr_t)ssbp.resp_bp.msg_buf;
669 ssbp32.wait_time = ssbp.wait_time;
670
671 if (ddi_copyout((caddr_t)&ssbp32, (caddr_t)arg,
672 sizeof (ssbp32), mode)) {
673 return (EFAULT);
674 }
675 break;
676 }
677 case DDI_MODEL_NONE:
678 if (ddi_copyout((caddr_t)&ssbp, (caddr_t)arg,
679 sizeof (ssbp), mode))
680 return (EFAULT);
681 break;
682 }
683 #else /* ! _MULTI_DATAMODEL */
684 if (ddi_copyout((caddr_t)&ssbp, (caddr_t)arg, sizeof (ssbp),
685 mode) != 0)
686 return (EFAULT);
687 #endif /* _MULTI_DATAMODEL */
688 break;
689
690
691 case RMCADM_RESET_SP:
692 pmugpio_reset();
693 retval = 0;
694 break;
695 default:
696 retval = ENOTSUP;
697 break;
698 }
699 return (retval);
700 }
701