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