1/*	$NetBSD: machdep.c,v 1.191 2024/03/05 14:15:29 thorpej Exp $	*/
2
3/*
4 * Copyright (c) 1988 University of Utah.
5 * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * the Systems Programming Group of the University of Utah Computer
10 * Science Department.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * from: Utah $Hdr: machdep.c 1.63 91/04/24$
37 *
38 *	@(#)machdep.c	7.16 (Berkeley) 6/3/91
39 */
40
41#include <sys/cdefs.h>
42__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.191 2024/03/05 14:15:29 thorpej Exp $");
43
44#include "opt_ddb.h"
45#include "opt_compat_netbsd.h"
46#include "opt_mbtype.h"
47#include "opt_modular.h"
48#include "opt_panicbutton.h"
49
50#include <sys/param.h>
51#include <sys/systm.h>
52#include <sys/signalvar.h>
53#include <sys/kernel.h>
54#include <sys/proc.h>
55#include <sys/buf.h>
56#include <sys/reboot.h>
57#include <sys/conf.h>
58#include <sys/file.h>
59#include <sys/device.h>
60#include <sys/kmem.h>
61#include <sys/mbuf.h>
62#include <sys/msgbuf.h>
63#include <sys/vnode.h>
64#include <sys/queue.h>
65#include <sys/mount.h>
66#include <sys/syscallargs.h>
67#include <sys/ksyms.h>
68#include <sys/module.h>
69#include <sys/intr.h>
70#include <sys/exec.h>
71#include <sys/exec_aout.h>
72#include <sys/cpu.h>
73#include <sys/exec_elf.h>
74#include <sys/bus.h>
75
76#include <uvm/uvm_extern.h>
77
78#include <sys/sysctl.h>
79
80#include <machine/db_machdep.h>
81#include <ddb/db_sym.h>
82#include <ddb/db_extern.h>
83
84#include <machine/reg.h>
85#include <machine/psl.h>
86#include <machine/pte.h>
87
88#include <dev/cons.h>
89#include <dev/mm.h>
90
91#include "ksyms.h"
92#include "nvr.h"
93
94#define	DEV_NVRAM	11	/* Nvram minor-number */
95
96static void bootsync(void);
97static void call_sicallbacks(void);
98static void identifycpu(void);
99void	straymfpint(int, u_short);
100void	straytrap(int, u_short);
101
102#ifdef _MILANHW_
103void	nmihandler(void);
104#endif
105
106struct vm_map *phys_map = NULL;
107
108void *	msgbufaddr;
109vaddr_t	msgbufpa;
110
111extern  int   freebufspace;
112extern	u_int lowram;
113
114/*
115 * For the fpu emulation and the fpu driver
116 */
117int	fputype = 0;
118
119/* the following is used externally (sysctl_hw) */
120char	machine[] = MACHINE;	/* from <machine/param.h> */
121
122/* Our exported CPU info; we can have only one. */
123struct cpu_info cpu_info_store;
124
125 /*
126 * Console initialization: called early on from main,
127 * before vm init or startup.  Do enough configuration
128 * to choose and initialize a console.
129 */
130void
131consinit(void)
132{
133	int i;
134
135	/*
136	 * Initialize error message buffer. pmap_bootstrap() has
137	 * positioned this at the end of kernel memory segment - map
138	 * and initialize it now.
139	 */
140	for (i = 0; i < btoc(MSGBUFSIZE); i++)
141		pmap_kenter_pa((vaddr_t)msgbufaddr + i * PAGE_SIZE,
142		    msgbufpa + i * PAGE_SIZE, VM_PROT_READ|VM_PROT_WRITE, 0);
143	pmap_update(pmap_kernel());
144	initmsgbuf(msgbufaddr, m68k_round_page(MSGBUFSIZE));
145
146	/*
147	 * Initialize hardware that support various console types like
148	 * the grf and PCI busses.
149	 */
150	config_console();
151
152	/*
153	 * Now pick the best console candidate.
154	 */
155	cninit();
156
157#if NKSYMS || defined(DDB) || defined(MODULAR)
158	{
159		extern int end;
160		extern int *esym;
161
162		ksyms_addsyms_elf((int)esym - (int)&end - sizeof(Elf32_Ehdr),
163		    (void *)&end, esym);
164	}
165#endif
166#if defined (DDB)
167	if (boothowto & RB_KDB)
168		Debugger();
169#endif
170}
171
172/*
173 * cpu_startup: allocate memory for variable-sized tables,
174 * initialize CPU, and do autoconfiguration.
175 */
176void
177cpu_startup(void)
178{
179	char pbuf[9];
180#ifdef DEBUG
181	extern int pmapdebug;
182	int opmapdebug = pmapdebug;
183#endif
184	vaddr_t minaddr, maxaddr;
185	extern vsize_t mem_size;	/* from pmap.c */
186
187#ifdef DEBUG
188	pmapdebug = 0;
189#endif
190
191	if (fputype != FPU_NONE)
192		m68k_make_fpu_idle_frame();
193
194	/*
195	 * Good {morning,afternoon,evening,night}.
196	 */
197	printf("%s%s", copyright, version);
198	identifycpu();
199
200	format_bytes(pbuf, sizeof(pbuf), mem_size);
201	printf("total memory = %s\n", pbuf);
202
203	minaddr = 0;
204
205	/*
206	 * Allocate a submap for physio
207	 */
208	phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
209	    VM_PHYS_SIZE, 0, false, NULL);
210
211#ifdef DEBUG
212	pmapdebug = opmapdebug;
213#endif
214	format_bytes(pbuf, sizeof(pbuf), ptoa(uvm_availmem(false)));
215	printf("avail memory = %s\n", pbuf);
216}
217
218/*
219 * Info for CTL_HW
220 */
221static void
222identifycpu(void)
223{
224	const char *mach, *mmu, *fpu, *cpu;
225	uint32_t pcr;
226	char cputxt[30];
227
228	switch (machineid & ATARI_ANYMACH) {
229	case ATARI_TT:
230		mach = "Atari TT";
231		break;
232	case ATARI_FALCON:
233		mach = "Atari Falcon";
234		break;
235	case ATARI_HADES:
236		mach = "Atari Hades";
237		break;
238	case ATARI_MILAN:
239		mach = "Atari Milan";
240		break;
241	default:
242		mach = "Atari UNKNOWN";
243		break;
244	}
245
246	cpu     = "m68k";
247	fputype = fpu_probe();
248	fpu     = fpu_describe(fputype);
249
250	switch (cputype) {
251	case CPU_68060:
252		__asm(".word 0x4e7a,0x0808;"
253		    "movl %%d0,%0" : "=d"(pcr) : : "d0");
254		snprintf(cputxt, sizeof(cputxt), "MC68%s060 rev.%d",
255		    pcr & 0x10000 ? "LC/EC" : "", (pcr >> 8) & 0xff);
256		cpu = cputxt;
257		mmu = "/MMU";
258		break;
259	case CPU_68040:
260		cpu = "MC68040";
261		mmu = "/MMU";
262		break;
263	case CPU_68030:
264		cpu = "MC68030";
265		mmu = "/MMU";
266		break;
267	default: /* XXX */
268		cpu = "MC68020";
269		mmu = " MC68851 MMU";
270	}
271	cpu_setmodel("%s (%s CPU%s%sFPU)", mach, cpu, mmu, fpu);
272	printf("%s\n", cpu_getmodel());
273}
274
275/*
276 * machine dependent system variables.
277 */
278SYSCTL_SETUP(sysctl_machdep_setup, "sysctl machdep subtree setup")
279{
280
281	sysctl_createv(clog, 0, NULL, NULL,
282		       CTLFLAG_PERMANENT,
283		       CTLTYPE_NODE, "machdep", NULL,
284		       NULL, 0, NULL, 0,
285		       CTL_MACHDEP, CTL_EOL);
286
287	sysctl_createv(clog, 0, NULL, NULL,
288		       CTLFLAG_PERMANENT,
289		       CTLTYPE_STRUCT, "console_device", NULL,
290		       sysctl_consdev, 0, NULL, sizeof(dev_t),
291		       CTL_MACHDEP, CPU_CONSDEV, CTL_EOL);
292}
293
294static int waittime = -1;
295
296static void
297bootsync(void)
298{
299	if (waittime < 0) {
300		waittime = 0;
301		vfs_shutdown();
302	}
303}
304
305void
306cpu_reboot(int howto, char *bootstr)
307{
308	struct pcb *pcb = lwp_getpcb(curlwp);
309
310	/* take a snap shot before clobbering any registers */
311	if (pcb != NULL)
312		savectx(pcb);
313
314	boothowto = howto;
315	if ((howto & RB_NOSYNC) == 0)
316		bootsync();
317
318	/*
319	 * Call shutdown hooks. Do this _before_ anything might be
320	 * asked to the user in case nobody is there....
321	 */
322	doshutdownhooks();
323
324	pmf_system_shutdown(boothowto);
325
326	splhigh();			/* extreme priority */
327	if (howto & RB_HALT) {
328		printf("halted\n\n");
329		__asm("	stop	#0x2700");
330	} else {
331		if (howto & RB_DUMP)
332			dumpsys();
333
334		doboot();
335		/*NOTREACHED*/
336	}
337	panic("Boot() should never come here");
338	/*NOTREACHED*/
339}
340
341#define	BYTES_PER_DUMP	PAGE_SIZE	/* Must be a multiple of PAGE_SIZE */
342static vaddr_t	dumpspace;	/* Virt. space to map dumppages	*/
343
344/*
345 * Reserve _virtual_ memory to map in the page to be dumped
346 */
347vaddr_t
348reserve_dumppages(vaddr_t p)
349{
350
351	dumpspace = p;
352	return p + BYTES_PER_DUMP;
353}
354
355uint32_t	dumpmag  = 0x8fca0101;	/* magic number for savecore	*/
356int		dumpsize = 0;		/* also for savecore (pages)	*/
357long		dumplo   = 0;		/* (disk blocks)		*/
358
359void
360cpu_dumpconf(void)
361{
362	int	nblks, i;
363
364	for (i = dumpsize = 0; i < NMEM_SEGS; i++) {
365		if (boot_segs[i].start == boot_segs[i].end)
366			break;
367		dumpsize += boot_segs[i].end - boot_segs[i].start;
368	}
369	dumpsize = btoc(dumpsize);
370
371	if (dumpdev != NODEV) {
372		nblks = bdev_size(dumpdev);
373		if (nblks > 0) {
374			if (dumpsize > btoc(dbtob(nblks - dumplo)))
375				dumpsize = btoc(dbtob(nblks - dumplo));
376			else if (dumplo == 0)
377				dumplo = nblks - btodb(ctob(dumpsize));
378		}
379	}
380	dumplo -= cpu_dumpsize();
381
382	/*
383	 * Don't dump on the first PAGE_SIZE (why PAGE_SIZE?)
384	 * in case the dump device includes a disk label.
385	 */
386	if (dumplo < btodb(PAGE_SIZE))
387		dumplo = btodb(PAGE_SIZE);
388}
389
390/*
391 * Doadump comes here after turning off memory management and
392 * getting on the dump stack, either when called above, or by
393 * the auto-restart code.
394 */
395void
396dumpsys(void)
397{
398	const struct bdevsw *bdev;
399	daddr_t	blkno;		/* Current block to write	*/
400	int	(*dump)(dev_t, daddr_t, void *, size_t);
401				/* Dumping function		*/
402	u_long	maddr;		/* PA being dumped		*/
403	int	segbytes;	/* Number of bytes in this seg.	*/
404	int	segnum;		/* Segment we are dumping	*/
405	int	nbytes;		/* Bytes left to dump		*/
406	int	i, n, error;
407
408	error = segnum = 0;
409	if (dumpdev == NODEV)
410		return;
411	bdev = bdevsw_lookup(dumpdev);
412	if (bdev == NULL)
413		return;
414	/*
415	 * For dumps during autoconfiguration,
416	 * if dump device has already configured...
417	 */
418	if (dumpsize == 0)
419		cpu_dumpconf();
420	if (dumplo <= 0) {
421		printf("\ndump to dev %u,%u not possible\n",
422		    major(dumpdev), minor(dumpdev));
423		return;
424	}
425	printf("\ndumping to dev %u,%u offset %ld\n",
426	    major(dumpdev), minor(dumpdev), dumplo);
427
428#if defined(DDB) || defined(PANICWAIT)
429	printf("Do you want to dump memory? [y]");
430	cnpollc(1);
431	i = cngetc();
432	cnpollc(0);
433	cnputc(i);
434	switch (i) {
435	case 'n':
436	case 'N':
437		return;
438	case '\n':
439		break;
440	default :
441		cnputc('\n');
442	}
443#endif /* defined(DDB) || defined(PANICWAIT) */
444
445	maddr    = 0;
446	segbytes = boot_segs[0].end;
447	blkno    = dumplo;
448	dump     = bdev->d_dump;
449	nbytes   = dumpsize * PAGE_SIZE;
450
451	printf("dump ");
452
453	error = cpu_dump(dump, &blkno);
454	if (!error) {
455		for (i = 0; i < nbytes; i += n, segbytes -= n) {
456			/*
457			 * Skip the hole
458			 */
459			if (segbytes == 0) {
460				segnum++;
461				maddr    = boot_segs[segnum].start;
462				segbytes = boot_segs[segnum].end -
463				    boot_segs[segnum].start;
464			}
465			/*
466			 * Print Mb's to go
467			 */
468			n = nbytes - i;
469			if (n && (n % (1024 * 1024)) == 0)
470				printf_nolog("%d ", n / (1024 * 1024));
471
472			/*
473			 * Limit transfer to BYTES_PER_DUMP
474			 */
475			if (n > BYTES_PER_DUMP)
476				n = BYTES_PER_DUMP;
477
478			/*
479			 * Map to a VA and write it
480			 */
481			if (maddr != 0) { /* XXX kvtop chokes on this	*/
482				(void)pmap_map(dumpspace, maddr, maddr + n,
483				    VM_PROT_READ);
484				error = (*dump)(dumpdev, blkno,
485				    (void *)dumpspace, n);
486				if (error)
487					break;
488			}
489
490			maddr += n;
491			blkno += btodb(n);
492		}
493	}
494	switch (error) {
495
496	case ENXIO:
497		printf("device bad\n");
498		break;
499
500	case EFAULT:
501		printf("device not ready\n");
502		break;
503
504	case EINVAL:
505		printf("area improper\n");
506		break;
507
508	case EIO:
509		printf("i/o error\n");
510		break;
511
512	default:
513		printf("succeeded\n");
514		break;
515	}
516	printf("\n\n");
517	delay(5000000);		/* 5 seconds */
518}
519
520void
521straytrap(int pc, u_short evec)
522{
523	static int prev_evec;
524
525	printf("unexpected trap (vector offset 0x%x) from 0x%x\n",
526	    evec & 0xFFF, pc);
527
528	if (prev_evec == evec) {
529		delay(1000000);
530		prev_evec = 0;
531	} else
532		prev_evec = evec;
533}
534
535void
536straymfpint(int pc, u_short evec)
537{
538
539	printf("unexpected mfp-interrupt (vector offset 0x%x) from 0x%x\n",
540	       evec & 0xFFF, pc);
541}
542
543int	*nofault;
544
545int
546badbaddr(void *addr, int size)
547{
548	int i;
549	label_t	faultbuf;
550
551	nofault = (int *) &faultbuf;
552	if (setjmp((label_t *)nofault)) {
553		nofault = (int *) 0;
554		return 1;
555	}
556	switch (size) {
557	case 1:
558		i = *(volatile uint8_t *)addr;
559		break;
560	case 2:
561		i = *(volatile uint16_t *)addr;
562		break;
563	case 4:
564		i = *(volatile uint32_t *)addr;
565		break;
566	default:
567		panic("badbaddr: unknown size");
568	}
569	__USE(i);
570	nofault = (int *)0;
571	return 0;
572}
573
574/*
575 * this is a handy package to have asynchronously executed
576 * function calls executed at very low interrupt priority.
577 * Example for use is keyboard repeat, where the repeat
578 * handler running at splclock() triggers such a (hardware
579 * aided) software interrupt.
580 * Note: the installed functions are currently called in a
581 * LIFO fashion, might want to change this to FIFO
582 * later.
583 *
584 * XXX: Some of functions which use this callback should be rewritten
585 * XXX: to use MI softintr(9) directly.
586 */
587struct si_callback {
588	struct si_callback *next;
589	void (*function)(void *rock1, void *rock2);
590	void *rock1, *rock2;
591};
592static void *si_callback_cookie;
593static struct si_callback *si_callbacks;
594static struct si_callback *si_free;
595#ifdef DIAGNOSTIC
596static int ncbd;	/* number of callback blocks dynamically allocated */
597#endif
598
599void
600init_sicallback(void)
601{
602
603	si_callback_cookie = softint_establish(SOFTINT_NET,
604	    (void (*)(void *))call_sicallbacks, NULL);
605}
606
607void
608add_sicallback(void (*function)(void *, void *), void *rock1, void *rock2)
609{
610	struct si_callback *si;
611	int s;
612
613	/*
614	 * this function may be called from high-priority interrupt handlers.
615	 * We may NOT block for  memory-allocation in here!.
616	 */
617	s = splhigh();
618	if ((si = si_free) != NULL)
619		si_free = si->next;
620	splx(s);
621
622	if (si == NULL) {
623		si = kmem_intr_alloc(sizeof(*si), KM_NOSLEEP);
624#ifdef DIAGNOSTIC
625		if (si)
626			++ncbd;		/* count # dynamically allocated */
627#endif
628		if (si == NULL)
629			return;
630	}
631
632	si->function = function;
633	si->rock1    = rock1;
634	si->rock2    = rock2;
635
636	s = splhigh();
637	si->next     = si_callbacks;
638	si_callbacks = si;
639	splx(s);
640
641	/*
642	 * and cause a software interrupt (spl1). This interrupt might
643	 * happen immediately, or after returning to a safe enough level.
644	 *
645	 * XXX:
646	 * According to <machine/scu.h> and lev1intr() handler in locore.s,
647	 * at least _ATARIHW_ machines (ATARITT and HADES?) seem to have
648	 * some hardware support which can initiate real hardware interrupt
649	 * at ipl 1 for software interrupt. But as per <machine/mtpr.h>,
650	 * this feature was not used at all on setsoft*() calls and
651	 * traditional hp300 derived ssir (simulated software interrupt
652	 * request) on VAX REI emulation in locore.s is used.
653	 */
654	softint_schedule(si_callback_cookie);
655}
656
657void
658rem_sicallback(void (*function)(void *rock1, void *rock2))
659{
660	struct si_callback *si, *psi, *nsi;
661	int s;
662
663	s = splhigh();
664	for (psi = 0, si = si_callbacks; si; ) {
665		nsi = si->next;
666
667		if (si->function != function)
668			psi = si;
669		else {
670			si->next = si_free;
671			si_free  = si;
672			if (psi != NULL)
673				psi->next = nsi;
674			else
675				si_callbacks = nsi;
676		}
677		si = nsi;
678	}
679	splx(s);
680}
681
682/* purge the list */
683static void
684call_sicallbacks(void)
685{
686	struct si_callback *si;
687	int s;
688	void *rock1, *rock2;
689	void (*function)(void *, void *);
690
691	do {
692		s = splhigh();
693		if ((si = si_callbacks) != NULL)
694			si_callbacks = si->next;
695		splx(s);
696
697		if (si != NULL) {
698			function = si->function;
699			rock1    = si->rock1;
700			rock2    = si->rock2;
701			s = splhigh();
702			if (si_callbacks)
703				softint_schedule(si_callback_cookie);
704			si->next = si_free;
705			si_free  = si;
706			splx(s);
707
708			/*
709			 * Raise spl for BASEPRI() checks to see
710			 * nested interrupts in some drivers using callbacks
711			 * since modern MI softint(9) doesn't seem to do it
712			 * in !__HAVE_FAST_SOFTINTS case.
713			 *
714			 * XXX: This is just a workaround hack.
715			 *      Each driver should raise spl in its handler
716			 *      to avoid nested interrupts if necessary.
717			 */
718			s = splsoftnet();	/* XXX */
719			function(rock1, rock2);
720			splx(s);
721		}
722	} while (si != NULL);
723#ifdef DIAGNOSTIC
724	if (ncbd) {
725#ifdef DEBUG
726		printf("call_sicallback: %d more dynamic structures\n", ncbd);
727#endif
728		ncbd = 0;
729	}
730#endif
731}
732
733#if defined(DEBUG) && !defined(PANICBUTTON)
734#define PANICBUTTON
735#endif
736
737#ifdef PANICBUTTON
738int panicbutton = 1;	/* non-zero if panic buttons are enabled */
739int crashandburn = 0;
740int candbdelay = 50;	/* give em half a second */
741
742void candbtimer(void);
743
744void
745candbtimer(void)
746{
747
748	crashandburn = 0;
749}
750#endif
751
752/*
753 * should only get here, if no standard executable. This can currently
754 * only mean, we're reading an old ZMAGIC file without MID, but since Atari
755 * ZMAGIC always worked the `right' way (;-)) just ignore the missing
756 * MID and proceed to new zmagic code ;-)
757 */
758int
759cpu_exec_aout_makecmds(struct lwp *l, struct exec_package *epp)
760{
761	int error = ENOEXEC;
762#ifdef COMPAT_NOMID
763	struct exec *execp = epp->ep_hdr;
764#endif
765
766#ifdef COMPAT_NOMID
767	if (!((execp->a_midmag >> 16) & 0x0fff)
768	    && execp->a_midmag == ZMAGIC)
769		return exec_aout_prep_zmagic(l->l_proc, epp);
770#endif
771	return error;
772}
773
774#ifdef MODULAR
775/*
776 * Push any modules loaded by the bootloader etc.
777 */
778void
779module_init_md(void)
780{
781}
782#endif
783
784#ifdef _MILANHW_
785
786/*
787 * Currently the only source of NMI interrupts on the Milan is the PLX9080.
788 * On access errors to the PCI bus, an NMI is generated. This NMI is shorted
789 * in locore in case of a PCI config cycle to a non-existing address to allow
790 * for probes. On other occasions, it ShouldNotHappen(TM).
791 * Note: The handler in locore clears the errors, to make further PCI access
792 * possible.
793 */
794void
795nmihandler(void)
796{
797	extern unsigned long	plx_status;
798
799	printf("nmihandler: plx_status = 0x%08lx\n", plx_status);
800}
801#endif
802
803int
804mm_md_physacc(paddr_t pa, vm_prot_t prot)
805{
806	struct memseg *ms;
807
808	for (ms = boot_segs; ms->start != ms->end; ms++) {
809		if ((pa >= ms->start) && (pa < ms->end))
810			return 0;
811	}
812	return EFAULT;
813}
814
815int
816mm_md_readwrite(dev_t dev, struct uio *uio)
817{
818	switch (minor(dev)) {
819	case DEV_NVRAM:
820#if NNVR > 0
821		return nvram_uio(uio);
822#else
823		return ENXIO;
824#endif
825	default:
826		return ENXIO;
827	}
828}
829