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