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 */
44static int	rmcadm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
45    void **resultp);
46static int	rmcadm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
47static int	rmcadm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
48static int	rmcadm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
49static int	rmcadm_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
50static 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 */
56static 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
75static 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 */
93extern struct mod_ops mod_driverops;
94
95static 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
101static struct modlinkage modlinkage = {
102	MODREV_1,
103	&modldrv,
104	NULL
105};
106
107static dev_info_t		*rmcadm_dip = NULL;
108
109extern void pmugpio_reset();
110
111/*
112 * Utilities...
113 */
114
115/*
116 * to return the errno from the rmc_comm error status
117 */
118int
119rmcadm_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
168int
169_init(void)
170{
171	int	error = 0;
172
173	error = mod_install(&modlinkage);
174	return (error);
175}
176
177
178int
179_info(struct modinfo *modinfop)
180{
181	return (mod_info(&modlinkage, modinfop));
182}
183
184
185int
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 */
198static int
199rmcadm_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
220static int
221rmcadm_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, 0);
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
262static int
263rmcadm_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*/
285static int
286rmcadm_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*/
303static int
304rmcadm_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
305{
306	return (DDI_SUCCESS);
307}
308
309/*ARGSUSED*/
310static int
311rmcadm_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