xref: /titanic_50/usr/src/uts/common/avs/ns/nsctl/nsctl.c (revision 8e7248e505faa19396d4e853604e3fa7cd2cb3b5)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/ksynch.h>
28 #include <sys/kmem.h>
29 #include <sys/file.h>
30 #include <sys/errno.h>
31 #include <sys/open.h>
32 #include <sys/cred.h>
33 #include <sys/conf.h>
34 #include <sys/uio.h>
35 #include <sys/cmn_err.h>
36 #include <sys/modctl.h>
37 #include <sys/ddi.h>
38 
39 #define	__NSC_GEN__
40 #include <sys/nsctl/nsc_dev.h>
41 #include <sys/nsctl/nsc_gen.h>
42 #include <sys/nsctl/nsc_ioctl.h>
43 #include <sys/nsctl/nsc_power.h>
44 #include <sys/nsctl/nsc_mem.h>
45 #include "../nsctl.h"
46 
47 #include <sys/nsctl/nsvers.h>
48 
49 #ifdef DS_DDICT
50 #include "../contract.h"
51 #endif
52 
53 extern void nscsetup();
54 extern int _nsc_init_raw();
55 extern void _nsc_deinit_raw();
56 extern void _nsc_init_start();
57 extern void _nsc_init_os(), _nsc_deinit_os();
58 extern void _nsc_init_dev(), _nsc_init_mem();
59 extern void _nsc_init_gen(), _nsc_init_rmlock();
60 extern void _nsc_init_resv(), _nsc_deinit_resv();
61 extern void _nsc_init_frz(), _nsc_deinit_frz();
62 extern void _nsc_init_ncio(), _nsc_deinit_ncio();
63 extern void _nsc_deinit_mem(), _nsc_deinit_rmlock();
64 extern void _nsc_deinit_dev();
65 
66 extern int _nsc_frz_start(), _nsc_frz_stop(), _nsc_frz_isfrozen();
67 
68 extern nsc_mem_t *_nsc_local_mem;
69 extern nsc_rmhdr_t *_nsc_rmhdr_ptr;
70 extern nsc_def_t _nsc_raw_def[];
71 extern int _nsc_raw_flags;
72 
73 int nsc_devflag = D_MP;
74 
75 int _nsc_init_done = 0;
76 
77 kmutex_t _nsc_drv_lock;
78 nsc_io_t *_nsc_file_io;
79 nsc_io_t *_nsc_vchr_io;
80 nsc_io_t *_nsc_raw_io;
81 
82 nsc_fd_t **_nsc_minor_fd;
83 kmutex_t **_nsc_minor_slp;
84 
85 
86 /* Maximum number of devices - tunable in nsctl.conf */
87 static int _nsc_max_devices;
88 
89 /* Internal version of _nsc_max_devices */
90 int _nsc_maxdev;
91 
92 extern void _nsc_global_setup(void);
93 
94 static int nsc_load(), nsc_unload();
95 
96 /*
97  * Solaris specific driver module interface code.
98  */
99 
100 extern int nscopen(dev_t *, int, int, cred_t *);
101 extern int nscioctl(dev_t, int, intptr_t, int, cred_t *, int *);
102 extern int nscclose(dev_t, int, int, cred_t *);
103 extern int nscread(dev_t, uio_t *, cred_t *);
104 extern int nscwrite(dev_t, uio_t *, cred_t *);
105 
106 static dev_info_t *nsctl_dip;		/* Single DIP for driver */
107 
108 static int _nsctl_print(dev_t, char *);
109 
110 static	struct	cb_ops nsctl_cb_ops = {
111 	nscopen,		/* open */
112 	nscclose,	/* close */
113 	nodev,		/* not a block driver, strategy not an entry point */
114 	_nsctl_print,	/* no print routine */
115 	nodev,		/* no dump routine */
116 	nscread,		/* read */
117 	nscwrite,	/* write */
118 	(int (*)()) nscioctl,	/* ioctl */
119 	nodev,		/* no devmap routine */
120 	nodev,		/* no mmap routine */
121 	nodev,		/* no segmap routine */
122 	nochpoll,	/* no chpoll routine */
123 	ddi_prop_op,
124 	0,		/* not a STREAMS driver, no cb_str routine */
125 	D_NEW | D_MP | D_64BIT,	/* safe for multi-thread/multi-processor */
126 	CB_REV,
127 	nodev,		/* aread */
128 	nodev,		/* awrite */
129 };
130 
131 static int _nsctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
132 static int _nsctl_attach(dev_info_t *, ddi_attach_cmd_t);
133 static int _nsctl_detach(dev_info_t *, ddi_detach_cmd_t);
134 
135 static	struct	dev_ops nsctl_ops = {
136 	DEVO_REV,			/* Driver build version */
137 	0,				/* device reference count */
138 	_nsctl_getinfo,
139 	nulldev,			/* Identify */
140 	nulldev,			/* Probe */
141 	_nsctl_attach,
142 	_nsctl_detach,
143 	nodev,				/* Reset */
144 	&nsctl_cb_ops,
145 	(struct bus_ops *)0
146 };
147 
148 static struct modldrv nsctl_ldrv = {
149 	&mod_driverops,
150 	"nws:Control:" ISS_VERSION_STR,
151 	&nsctl_ops
152 };
153 
154 static	struct modlinkage nsctl_modlinkage = {
155 	MODREV_1,
156 	&nsctl_ldrv,
157 	NULL
158 };
159 
160 /*
161  * Solaris module load time code
162  */
163 
164 int nsc_min_nodeid;
165 int nsc_max_nodeid;
166 
167 int
168 _init(void)
169 {
170 	int err;
171 
172 	err = nsc_load();
173 
174 	if (!err)
175 		err = mod_install(&nsctl_modlinkage);
176 
177 	if (err) {
178 		(void) nsc_unload();
179 		cmn_err(CE_NOTE, "nsctl_init: err %d", err);
180 	}
181 
182 	return (err);
183 
184 }
185 
186 /*
187  * Solaris module unload time code
188  */
189 
190 int
191 _fini(void)
192 {
193 	int err;
194 
195 	if ((err = mod_remove(&nsctl_modlinkage)) == 0) {
196 		err = nsc_unload();
197 	}
198 	return (err);
199 }
200 
201 /*
202  * Solaris module info code
203  */
204 int
205 _info(struct modinfo *modinfop)
206 {
207 	return (mod_info(&nsctl_modlinkage, modinfop));
208 }
209 
210 /*
211  * Attach an instance of the device. This happens before an open
212  * can succeed.
213  */
214 static int
215 _nsctl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
216 {
217 	int rc;
218 
219 	if (cmd == DDI_ATTACH) {
220 		nsctl_dip = dip;
221 
222 		/* Announce presence of the device */
223 		ddi_report_dev(dip);
224 
225 		/*
226 		 * Get the node parameters now that we can look up.
227 		 */
228 		nsc_min_nodeid = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
229 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
230 		    "nsc_min_nodeid", 0);
231 
232 		nsc_max_nodeid = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
233 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
234 		    "nsc_max_nodeid", 5);
235 
236 		_nsc_max_devices = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
237 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
238 		    "nsc_max_devices", 128);
239 
240 		_nsc_maxdev = _nsc_max_devices;
241 		nscsetup();
242 
243 		/*
244 		 * Init raw requires the _nsc_max_devices value and so
245 		 * cannot be done before the nsc_max_devices property has
246 		 * been read which can only be done after the module is
247 		 * attached and we have a dip.
248 		 */
249 
250 		if ((rc = _nsc_init_raw(_nsc_max_devices)) != 0) {
251 			cmn_err(CE_WARN,
252 			    "nsctl: unable to initialize raw io provider: %d",
253 			    rc);
254 			return (DDI_FAILURE);
255 		}
256 
257 		/*
258 		 * Init rest of soft state structure
259 		 */
260 
261 		rc = ddi_create_minor_node(dip, "c,nsctl", S_IFCHR, 0,
262 		    DDI_PSEUDO, 0);
263 		if (rc != DDI_SUCCESS) {
264 			/* free anything we allocated here */
265 			cmn_err(CE_WARN,
266 			    "_nsctl_attach: ddi_create_minor_node failed %d",
267 			    rc);
268 			return (DDI_FAILURE);
269 		}
270 
271 		/* Announce presence of the device */
272 		ddi_report_dev(dip);
273 
274 		/* mark the device as attached, opens may proceed */
275 		return (DDI_SUCCESS);
276 	} else
277 		return (DDI_FAILURE);
278 }
279 
280 static int
281 _nsctl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
282 {
283 	if (cmd == DDI_DETACH) {
284 		_nsc_deinit_raw();
285 
286 		ddi_remove_minor_node(dip, NULL);
287 		nsctl_dip = NULL;
288 
289 		return (DDI_SUCCESS);
290 	}
291 	else
292 		return (DDI_FAILURE);
293 }
294 
295 
296 /* ARGSUSED */
297 static int
298 _nsctl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
299 {
300 	dev_t dev;
301 	int rc;
302 
303 	switch (cmd) {
304 		case DDI_INFO_DEVT2INSTANCE:
305 			/* The "instance" number is the minor number */
306 			dev = (dev_t)arg;
307 			*result = (void *)(unsigned long)getminor(dev);
308 			rc = DDI_SUCCESS;
309 			break;
310 
311 		case DDI_INFO_DEVT2DEVINFO:
312 			*result = nsctl_dip;
313 			rc = DDI_SUCCESS;
314 			break;
315 
316 		default:
317 			rc = DDI_FAILURE;
318 			break;
319 	}
320 
321 	return (rc);
322 }
323 
324 
325 /* ARGSUSED */
326 static int
327 _nsctl_print(dev_t dev, char *s)
328 {
329 	cmn_err(CE_WARN, "nsctl:%s", s);
330 	return (0);
331 }
332 
333 
334 void
335 nsc_init()
336 {
337 	if (_nsc_init_done)
338 		return;
339 
340 	_nsc_init_start();
341 	_nsc_init_gen();
342 	_nsc_init_svc();
343 	_nsc_init_mem();
344 	_nsc_init_dev();
345 	_nsc_init_rmlock();
346 	_nsc_init_resv();
347 	_nsc_init_os();
348 	(void) _nsc_init_power();
349 
350 	/*
351 	 * When using mc, nscsetup is done through mc callback to global_init.
352 	 */
353 	nscsetup();
354 
355 	mutex_init(&_nsc_drv_lock, NULL, MUTEX_DRIVER, NULL);
356 
357 	_nsc_raw_io = nsc_register_io("raw",
358 	    NSC_RAW_ID | _nsc_raw_flags, _nsc_raw_def);
359 
360 	if (!_nsc_raw_io)
361 		cmn_err(CE_WARN, "_nsc_init: register io failed - raw");
362 
363 	_nsc_init_ncio();
364 	_nsc_init_frz();
365 
366 	_nsc_init_done = 1;
367 }
368 
369 
370 /*
371  * Called after the mc refresh is complete (SEG_INIT callbacks have
372  * been received) and module _attach() is done.  Only does any real
373  * work when all of the above conditions have been met.
374  */
375 void
376 nscsetup()
377 {
378 	if (nsc_max_devices() == 0)
379 		return;
380 
381 	_nsc_minor_fd = nsc_kmem_zalloc(sizeof (nsc_fd_t *)*_nsc_maxdev,
382 	    0, _nsc_local_mem);
383 
384 	_nsc_minor_slp = nsc_kmem_zalloc(sizeof (kmutex_t *)*_nsc_maxdev,
385 	    0, _nsc_local_mem);
386 
387 	if (!_nsc_minor_fd || !_nsc_minor_slp)
388 		cmn_err(CE_WARN, "nscsetup - alloc failed");
389 }
390 
391 
392 int
393 nsc_load()
394 {
395 	nsc_init();
396 	return (0);
397 }
398 
399 
400 int
401 nsc_unload()
402 {
403 	if (!_nsc_init_done) {
404 		return (0);
405 	}
406 
407 	(void) _nsc_deinit_power();
408 	_nsc_deinit_resv();
409 	_nsc_deinit_mem();
410 	_nsc_deinit_rmlock();
411 	_nsc_deinit_svc();
412 	_nsc_deinit_frz();
413 	_nsc_deinit_ncio();
414 
415 	if (_nsc_vchr_io)
416 		(void) nsc_unregister_io(_nsc_vchr_io, 0);
417 
418 	if (_nsc_file_io)
419 		(void) nsc_unregister_io(_nsc_file_io, 0);
420 
421 	_nsc_vchr_io = NULL;
422 	_nsc_file_io = NULL;
423 
424 	if (_nsc_raw_io)
425 		(void) nsc_unregister_io(_nsc_raw_io, 0);
426 
427 	_nsc_raw_io = NULL;
428 
429 	_nsc_deinit_dev();
430 	_nsc_deinit_os();
431 
432 	_nsc_init_done = 0;
433 	return (0);
434 }
435 
436 
437 /* ARGSUSED */
438 
439 int
440 nscopen(dev_t *devp, int flag, int otyp, cred_t *crp)
441 {
442 	kmutex_t *slp;
443 	int i, error;
444 
445 	if (error = drv_priv(crp))
446 		return (error);
447 
448 	if (!_nsc_minor_fd || !_nsc_minor_slp)
449 		return (ENXIO);
450 
451 	if (getminor(*devp) != 0)
452 		return (ENXIO);
453 
454 	slp = nsc_kmem_alloc(sizeof (kmutex_t), 0, _nsc_local_mem);
455 	mutex_init(slp, NULL, MUTEX_DRIVER, NULL);
456 
457 	mutex_enter(&_nsc_drv_lock);
458 
459 	for (i = 1; i < _nsc_maxdev; i++) {
460 		if (_nsc_minor_slp[i] == NULL) {
461 			_nsc_minor_slp[i] = slp;
462 			break;
463 		}
464 	}
465 
466 	mutex_exit(&_nsc_drv_lock);
467 
468 	if (i >= _nsc_maxdev) {
469 		mutex_destroy(slp);
470 		nsc_kmem_free(slp, sizeof (kmutex_t));
471 		return (EAGAIN);
472 	}
473 
474 	*devp = makedevice(getmajor(*devp), i);
475 
476 	return (0);
477 }
478 
479 
480 int
481 _nscopen(dev_t dev, intptr_t arg, int mode, int *rvp)
482 {
483 	minor_t mindev = getminor(dev);
484 	struct nscioc_open *op;
485 	nsc_fd_t *fd;
486 	int rc;
487 
488 	op = nsc_kmem_alloc(sizeof (*op), KM_SLEEP, _nsc_local_mem);
489 	if (op == NULL) {
490 		return (ENOMEM);
491 	}
492 
493 	if (ddi_copyin((void *)arg, op, sizeof (*op), mode) < 0) {
494 		nsc_kmem_free(op, sizeof (*op));
495 		return (EFAULT);
496 	}
497 
498 	mutex_enter(_nsc_minor_slp[mindev]);
499 
500 	if (_nsc_minor_fd[mindev]) {
501 		mutex_exit(_nsc_minor_slp[mindev]);
502 		nsc_kmem_free(op, sizeof (*op));
503 		return (EBUSY);
504 	}
505 
506 	op->path[sizeof (op->path)-1] = 0;
507 
508 	fd = nsc_open(op->path, (op->flag & NSC_TYPES), 0, 0, &rc);
509 
510 	if (fd == NULL) {
511 		mutex_exit(_nsc_minor_slp[mindev]);
512 		nsc_kmem_free(op, sizeof (*op));
513 		return (rc);
514 	}
515 
516 	mode |= (op->mode - FOPEN);
517 
518 	if (mode & (FWRITE|FEXCL)) {
519 		if ((rc = nsc_reserve(fd, NSC_PCATCH)) != 0) {
520 			mutex_exit(_nsc_minor_slp[mindev]);
521 			(void) nsc_close(fd);
522 			nsc_kmem_free(op, sizeof (*op));
523 			return (rc);
524 		}
525 	}
526 
527 	*rvp = 0;
528 	_nsc_minor_fd[mindev] = fd;
529 
530 	mutex_exit(_nsc_minor_slp[mindev]);
531 	nsc_kmem_free(op, sizeof (*op));
532 	return (0);
533 }
534 
535 
536 /* ARGSUSED */
537 
538 int
539 nscclose(dev_t dev, int flag, int otyp, cred_t *crp)
540 {
541 	minor_t mindev = getminor(dev);
542 	kmutex_t *slp;
543 	nsc_fd_t *fd;
544 
545 	if (!_nsc_minor_fd || !_nsc_minor_slp)
546 		return (0);
547 
548 	if ((slp = _nsc_minor_slp[mindev]) == 0)
549 		return (0);
550 
551 	if ((fd = _nsc_minor_fd[mindev]) != NULL)
552 		(void) nsc_close(fd);
553 
554 	_nsc_minor_fd[mindev] = NULL;
555 	_nsc_minor_slp[mindev] = NULL;
556 
557 	mutex_destroy(slp);
558 	nsc_kmem_free(slp, sizeof (kmutex_t));
559 	return (0);
560 }
561 
562 
563 /* ARGSUSED */
564 
565 int
566 nscread(dev_t dev, uio_t *uiop, cred_t *crp)
567 {
568 	minor_t mindev = getminor(dev);
569 	int rc, resv;
570 	nsc_fd_t *fd;
571 
572 	if ((fd = _nsc_minor_fd[mindev]) == 0)
573 		return (EIO);
574 
575 	mutex_enter(_nsc_minor_slp[mindev]);
576 
577 	resv = (nsc_held(fd) == 0);
578 
579 	if (resv && (rc = nsc_reserve(fd, NSC_PCATCH)) != 0) {
580 		mutex_exit(_nsc_minor_slp[mindev]);
581 		return (rc);
582 	}
583 
584 	rc = nsc_uread(fd, uiop, crp);
585 
586 	if (resv)
587 		nsc_release(fd);
588 
589 	mutex_exit(_nsc_minor_slp[mindev]);
590 	return (rc);
591 }
592 
593 
594 /* ARGSUSED */
595 
596 int
597 nscwrite(dev_t dev, uio_t *uiop, cred_t *crp)
598 {
599 	minor_t mindev = getminor(dev);
600 	int rc, resv;
601 	nsc_fd_t *fd;
602 
603 	if ((fd = _nsc_minor_fd[mindev]) == 0)
604 		return (EIO);
605 
606 	mutex_enter(_nsc_minor_slp[mindev]);
607 
608 	resv = (nsc_held(fd) == 0);
609 
610 	if (resv && (rc = nsc_reserve(fd, NSC_PCATCH)) != 0) {
611 		mutex_exit(_nsc_minor_slp[mindev]);
612 		return (rc);
613 	}
614 
615 	rc = nsc_uwrite(fd, uiop, crp);
616 
617 	if (resv)
618 		nsc_release(fd);
619 
620 	mutex_exit(_nsc_minor_slp[mindev]);
621 	return (rc);
622 }
623 
624 
625 int
626 _nscreserve(dev_t dev, int *rvp)
627 {
628 	minor_t mindev = getminor(dev);
629 	nsc_fd_t *fd;
630 	int rc;
631 
632 	if ((fd = _nsc_minor_fd[mindev]) == 0)
633 		return (EIO);
634 
635 	mutex_enter(_nsc_minor_slp[mindev]);
636 
637 	if (nsc_held(fd)) {
638 		mutex_exit(_nsc_minor_slp[mindev]);
639 		return (EBUSY);
640 	}
641 
642 	if ((rc = nsc_reserve(fd, NSC_PCATCH)) != 0) {
643 		mutex_exit(_nsc_minor_slp[mindev]);
644 		return (rc);
645 	}
646 
647 	*rvp = 0;
648 
649 	mutex_exit(_nsc_minor_slp[mindev]);
650 	return (0);
651 }
652 
653 
654 int
655 _nscrelease(dev_t dev, int *rvp)
656 {
657 	minor_t mindev = getminor(dev);
658 	nsc_fd_t *fd;
659 
660 	if ((fd = _nsc_minor_fd[mindev]) == 0)
661 		return (EIO);
662 
663 	mutex_enter(_nsc_minor_slp[mindev]);
664 
665 	if (!nsc_held(fd)) {
666 		mutex_exit(_nsc_minor_slp[mindev]);
667 		return (EINVAL);
668 	}
669 
670 	nsc_release(fd);
671 
672 	*rvp = 0;
673 
674 	mutex_exit(_nsc_minor_slp[mindev]);
675 	return (0);
676 }
677 
678 
679 int
680 _nscpartsize(dev_t dev, intptr_t arg, int mode)
681 {
682 	struct nscioc_partsize partsize;
683 	minor_t mindev = getminor(dev);
684 	nsc_size_t size;
685 	int rc, resv;
686 	nsc_fd_t *fd;
687 
688 	if ((fd = _nsc_minor_fd[mindev]) == 0)
689 		return (EIO);
690 
691 	mutex_enter(_nsc_minor_slp[mindev]);
692 
693 	resv = (nsc_held(fd) == 0);
694 
695 	if (resv && (rc = nsc_reserve(fd, NSC_PCATCH)) != 0) {
696 		mutex_exit(_nsc_minor_slp[mindev]);
697 		return (rc);
698 	}
699 
700 	rc = nsc_partsize(fd, &size);
701 	partsize.partsize = (uint64_t)size;
702 
703 	if (resv)
704 		nsc_release(fd);
705 
706 	mutex_exit(_nsc_minor_slp[mindev]);
707 
708 	if (ddi_copyout((void *)&partsize, (void *)arg,
709 	    sizeof (partsize), mode) < 0) {
710 		return (EFAULT);
711 	}
712 
713 	return (rc);
714 }
715 
716 
717 /* ARGSUSED */
718 
719 int
720 nscioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *crp, int *rvp)
721 {
722 	struct nscioc_bsize *bsize = NULL;
723 	char *path = NULL;
724 	int rc = 0;
725 
726 	*rvp = 0;
727 
728 	switch (cmd) {
729 	case NSCIOC_OPEN:
730 		rc = _nscopen(dev, arg, mode, rvp);
731 		break;
732 
733 	case NSCIOC_RESERVE:
734 		rc = _nscreserve(dev, rvp);
735 		break;
736 
737 	case NSCIOC_RELEASE:
738 		rc = _nscrelease(dev, rvp);
739 		break;
740 
741 	case NSCIOC_PARTSIZE:
742 		rc = _nscpartsize(dev, arg, mode);
743 		break;
744 
745 	case NSCIOC_FREEZE:
746 		path = nsc_kmem_alloc(NSC_MAXPATH, KM_SLEEP, _nsc_local_mem);
747 		if (path == NULL) {
748 			rc = ENOMEM;
749 			break;
750 		}
751 		if (ddi_copyin((void *)arg, path, NSC_MAXPATH, mode) < 0)
752 			rc = EFAULT;
753 		else {
754 			path[NSC_MAXPATH-1] = 0;
755 			rc = _nsc_frz_start(path, rvp);
756 		}
757 		break;
758 
759 	case NSCIOC_UNFREEZE:
760 		path = nsc_kmem_alloc(NSC_MAXPATH, KM_SLEEP, _nsc_local_mem);
761 		if (path == NULL) {
762 			rc = ENOMEM;
763 			break;
764 		}
765 		if (ddi_copyin((void *)arg, path, NSC_MAXPATH, mode) < 0)
766 			rc = EFAULT;
767 		else {
768 			path[NSC_MAXPATH-1] = 0;
769 			rc = _nsc_frz_stop(path, rvp);
770 		}
771 		break;
772 
773 	case NSCIOC_ISFROZEN:
774 		path = nsc_kmem_alloc(NSC_MAXPATH, KM_SLEEP, _nsc_local_mem);
775 		if (path == NULL) {
776 			rc = ENOMEM;
777 			break;
778 		}
779 		if (ddi_copyin((void *)arg, path, NSC_MAXPATH, mode) < 0)
780 			rc = EFAULT;
781 		else {
782 			path[NSC_MAXPATH-1] = 0;
783 			rc = _nsc_frz_isfrozen(path, rvp);
784 		}
785 		break;
786 
787 #ifdef ENABLE_POWER_MSG
788 	case NSCIOC_POWERMSG:
789 		rc = _nsc_power((void *)arg, rvp);
790 		break;
791 #endif
792 
793 	case NSCIOC_NSKERND:
794 		rc = nskernd_command(arg, mode, rvp);
795 		break;
796 
797 	/* return sizes of global memory segments */
798 	case NSCIOC_GLOBAL_SIZES:
799 		if (!_nsc_init_done) {
800 			rc = EINVAL;
801 			break;
802 		}
803 
804 		rc = _nsc_get_global_sizes((void *)arg, rvp);
805 
806 		break;
807 
808 	/* return contents of global segments */
809 	case NSCIOC_GLOBAL_DATA:
810 		if (!_nsc_init_done) {
811 			rc = EINVAL;
812 			break;
813 		}
814 
815 		rc = _nsc_get_global_data((void *)arg, rvp);
816 		break;
817 
818 	/*
819 	 * nvmem systems:
820 	 * clear the hdr dirty bit to prevent loading from nvme on reboot
821 	 */
822 	case NSCIOC_NVMEM_CLEANF:
823 		rc = _nsc_clear_dirty(1);	/* dont be nice about it */
824 		break;
825 	case NSCIOC_NVMEM_CLEAN:
826 		rc = _nsc_clear_dirty(0);
827 		break;
828 
829 	case NSCIOC_BSIZE:
830 		bsize = nsc_kmem_alloc(sizeof (*bsize), KM_SLEEP,
831 		    _nsc_local_mem);
832 		if (bsize == NULL) {
833 			rc = ENOMEM;
834 			break;
835 		}
836 
837 		if (ddi_copyin((void *)arg, bsize, sizeof (*bsize), mode) < 0) {
838 			rc = EFAULT;
839 			break;
840 		}
841 
842 		rc = nskern_bsize(bsize, rvp);
843 		if (rc == 0) {
844 			if (ddi_copyout(bsize, (void *)arg,
845 			    sizeof (*bsize), mode) < 0) {
846 				rc = EFAULT;
847 				break;
848 			}
849 		}
850 
851 		break;
852 
853 	default:
854 		return (ENOTTY);
855 	}
856 
857 	if (bsize != NULL) {
858 		nsc_kmem_free(bsize, sizeof (*bsize));
859 		bsize = NULL;
860 	}
861 	if (path != NULL) {
862 		nsc_kmem_free(path, NSC_MAXPATH);
863 		path = NULL;
864 	}
865 	return (rc);
866 }
867 
868 
869 int
870 nsc_max_devices(void)
871 {
872 	return (_nsc_max_devices);
873 }
874 
875 
876 /*
877  * Used by _nsc_global_setup() in case nvram is dirty and has saved a different
878  * value for nsc_max_devices. We need to use the saved value, not the new
879  * one configured by the user.
880  */
881 void
882 _nsc_set_max_devices(int maxdev)
883 {
884 	_nsc_max_devices = maxdev;
885 	_nsc_maxdev = _nsc_max_devices;
886 }
887