1 /* $NetBSD: machdep.c,v 1.35 2002/09/27 15:36:25 provos Exp $ */ 2 3 /* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. 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, The Mach Operating System project at 11 * Carnegie-Mellon University and Ralph Campbell. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * @(#)machdep.c 8.3 (Berkeley) 1/12/94 42 */ 43 44 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 45 46 __KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.35 2002/09/27 15:36:25 provos Exp $"); 47 48 /* from: Utah Hdr: machdep.c 1.63 91/04/24 */ 49 50 #include "opt_ddb.h" 51 #include "opt_kgdb.h" 52 53 #include <sys/param.h> 54 #include <sys/signalvar.h> 55 #include <sys/kernel.h> 56 #include <sys/proc.h> 57 #include <sys/buf.h> 58 #include <sys/reboot.h> 59 #include <sys/conf.h> 60 #include <sys/file.h> 61 #include <sys/callout.h> 62 #include <sys/malloc.h> 63 #include <sys/mbuf.h> 64 #include <sys/msgbuf.h> 65 #include <sys/ioctl.h> 66 #include <sys/device.h> 67 #include <sys/user.h> 68 #include <sys/exec.h> 69 #include <sys/mount.h> 70 #include <sys/syscallargs.h> 71 #include <sys/kcore.h> 72 73 #include <uvm/uvm_extern.h> 74 75 #include <ufs/mfs/mfs_extern.h> /* mfs_initminiroot() */ 76 77 #include <machine/cpu.h> 78 #include <machine/reg.h> 79 #include <machine/psl.h> 80 #include <machine/pte.h> 81 82 #ifdef DDB 83 #include <machine/db_machdep.h> 84 #include <ddb/db_extern.h> 85 #endif 86 87 #include <machine/intr.h> 88 #include <machine/mainboard.h> 89 #include <machine/sysconf.h> 90 #include <machine/autoconf.h> 91 #include <machine/bootinfo.h> 92 #include <machine/prom.h> 93 #include <dev/clock_subr.h> 94 #include <dev/cons.h> 95 96 #include <sys/boot_flag.h> 97 98 #include "fs_mfs.h" 99 #include "opt_ddb.h" 100 #include "opt_execfmt.h" 101 102 #include "zsc.h" /* XXX */ 103 #include "com.h" /* XXX */ 104 105 /* the following is used externally (sysctl_hw) */ 106 extern char cpu_model[]; 107 108 /* Our exported CPU info; we can have only one. */ 109 struct cpu_info cpu_info_store; 110 111 /* maps for VM objects */ 112 113 struct vm_map *exec_map = NULL; 114 struct vm_map *mb_map = NULL; 115 struct vm_map *phys_map = NULL; 116 117 int physmem; /* max supported memory, changes to actual */ 118 char *bootinfo = NULL; /* pointer to bootinfo structure */ 119 120 phys_ram_seg_t mem_clusters[VM_PHYSSEG_MAX]; 121 int mem_cluster_cnt; 122 123 void to_monitor __P((int)) __attribute__((__noreturn__)); 124 void prom_halt __P((int)) __attribute__((__noreturn__)); 125 126 #ifdef KGDB 127 void zs_kgdb_init __P((void)); 128 void kgdb_connect __P((int)); 129 #endif 130 131 struct evcnt soft_evcnt[IPL_NSOFT]; 132 133 /* 134 * Local functions. 135 */ 136 int initcpu __P((void)); 137 void configure __P((void)); 138 139 void mach_init __P((int, char *[], char*[], u_int, char *)); 140 int memsize_scan __P((caddr_t)); 141 142 #ifdef DEBUG 143 /* stacktrace code violates prototypes to get callee's registers */ 144 extern void stacktrace __P((void)); /*XXX*/ 145 #endif 146 147 /* 148 * safepri is a safe priority for sleep to set for a spin-wait 149 * during autoconfiguration or after a panic. Used as an argument to splx(). 150 * XXX disables interrupt 5 to disable mips3 on-chip clock, which also 151 * disables mips1 FPU interrupts. 152 */ 153 int safepri = MIPS3_PSL_LOWIPL; /* XXX */ 154 extern struct user *proc0paddr; 155 156 /* locore callback-vector setup */ 157 extern void mips_vector_init __P((void)); 158 extern void prom_init __P((void)); 159 extern void pizazz_init __P((void)); 160 161 /* platform-specific initialization vector */ 162 static void unimpl_cons_init __P((void)); 163 static void unimpl_iointr __P((unsigned, unsigned, unsigned, unsigned)); 164 static int unimpl_memsize __P((caddr_t)); 165 static unsigned unimpl_clkread __P((void)); 166 static void unimpl_todr __P((struct clock_ymdhms *)); 167 static void unimpl_intr_establish __P((int, int (*)__P((void *)), void *)); 168 169 struct platform platform = { 170 "iobus not set", 171 unimpl_cons_init, 172 unimpl_iointr, 173 unimpl_memsize, 174 unimpl_clkread, 175 unimpl_todr, 176 unimpl_todr, 177 unimpl_intr_establish, 178 }; 179 180 struct consdev *cn_tab = NULL; 181 extern struct consdev consdev_prom; 182 extern struct consdev consdev_zs; 183 184 static void null_cnprobe __P((struct consdev *)); 185 static void prom_cninit __P((struct consdev *)); 186 static int prom_cngetc __P((dev_t)); 187 static void prom_cnputc __P((dev_t, int)); 188 static void null_cnpollc __P((dev_t, int)); 189 190 struct consdev consdev_prom = { 191 null_cnprobe, 192 prom_cninit, 193 prom_cngetc, 194 prom_cnputc, 195 null_cnpollc, 196 }; 197 198 199 /* 200 * Do all the stuff that locore normally does before calling main(). 201 * Process arguments passed to us by the prom monitor. 202 * Return the first page address following the system. 203 */ 204 void 205 mach_init(argc, argv, envp, bim, bip) 206 int argc; 207 char *argv[]; 208 char *envp[]; 209 u_int bim; 210 char *bip; 211 { 212 u_long first, last; 213 caddr_t kernend, v; 214 vsize_t size; 215 char *cp; 216 int i, howto; 217 extern char edata[], end[]; 218 char *bi_msg; 219 #ifdef DDB 220 int nsym = 0; 221 caddr_t ssym = 0; 222 caddr_t esym = 0; 223 struct btinfo_symtab *bi_syms; 224 #endif 225 226 227 /* Check for valid bootinfo passed from bootstrap */ 228 if (bim == BOOTINFO_MAGIC) { 229 struct btinfo_magic *bi_magic; 230 231 bootinfo = (char *)BOOTINFO_ADDR; /* XXX */ 232 bi_magic = lookup_bootinfo(BTINFO_MAGIC); 233 if (bi_magic == NULL || bi_magic->magic != BOOTINFO_MAGIC) 234 bi_msg = "invalid bootinfo structure.\n"; 235 else 236 bi_msg = NULL; 237 } else 238 bi_msg = "invalid bootinfo (standalone boot?)\n"; 239 240 /* clear the BSS segment */ 241 kernend = (caddr_t)mips_round_page(end); 242 memset(edata, 0, end - edata); 243 244 #ifdef DDB 245 bi_syms = lookup_bootinfo(BTINFO_SYMTAB); 246 247 /* Load sysmbol table if present */ 248 if (bi_syms != NULL) { 249 nsym = bi_syms->nsym; 250 ssym = (caddr_t)bi_syms->ssym; 251 esym = (caddr_t)bi_syms->esym; 252 kernend = (caddr_t)mips_round_page(esym); 253 } 254 #endif 255 256 prom_init(); 257 consinit(); 258 259 if (bi_msg != NULL) 260 printf(bi_msg); 261 262 /* 263 * Set the VM page size. 264 */ 265 uvm_setpagesize(); 266 267 /* Find out how much memory is available. */ 268 physmem = memsize_scan(kernend); 269 270 /* 271 * Now that we know how much memory we have, initialize the 272 * mem cluster array. 273 */ 274 mem_clusters[0].start = 0; /* XXX is this correct? */ 275 mem_clusters[0].size = ctob(physmem); 276 mem_cluster_cnt = 1; 277 278 /* 279 * Copy exception-dispatch code down to exception vector. 280 * Initialize locore-function vector. 281 * Clear out the I and D caches. 282 */ 283 mips_vector_init(); 284 285 /* Look at argv[0] and compute bootdev */ 286 makebootdev(argv[0]); 287 288 /* 289 * Look at arguments passed to us and compute boothowto. 290 */ 291 boothowto = RB_AUTOBOOT; 292 for (i = 1; i < argc; i++) { 293 for (cp = argv[i]; *cp; cp++) { 294 /* Ignore superfluous '-', if there is one */ 295 if (*cp == '-') 296 continue; 297 298 howto = 0; 299 BOOT_FLAG(*cp, howto); 300 if (! howto) 301 printf("bootflag '%c' not recognised\n", *cp); 302 else 303 boothowto |= howto; 304 } 305 } 306 307 308 #ifdef DDB 309 /* init symbols if present */ 310 if (esym) 311 ddb_init(esym - ssym, ssym, esym); 312 if (boothowto & RB_KDB) 313 Debugger(); 314 #endif 315 #ifdef KGDB 316 zs_kgdb_init(); /* XXX */ 317 if (boothowto & RB_KDB) 318 kgdb_connect(0); 319 #endif 320 321 #ifdef MFS 322 /* 323 * Check to see if a mini-root was loaded into memory. It resides 324 * at the start of the next page just after the end of BSS. 325 */ 326 if (boothowto & RB_MINIROOT) 327 kernend += round_page(mfs_initminiroot(kernend)); 328 #endif 329 330 /* 331 * Load the rest of the available pages into the VM system. 332 */ 333 first = round_page(MIPS_KSEG0_TO_PHYS(kernend)); 334 last = mem_clusters[0].start + mem_clusters[0].size; 335 uvm_page_physload(atop(first), atop(last), atop(first), atop(last), 336 VM_FREELIST_DEFAULT); 337 338 /* 339 * Initialize error message buffer (at end of core). 340 */ 341 mips_init_msgbuf(); 342 343 /* 344 * Compute the size of system data structures. pmap_bootstrap() 345 * needs some of this information. 346 */ 347 size = (vsize_t)allocsys(NULL, NULL); 348 349 /* 350 * Initialize the virtual memory system. 351 */ 352 pmap_bootstrap(); 353 354 /* 355 * Allocate space for proc0's USPACE. 356 */ 357 v = (caddr_t)uvm_pageboot_alloc(USPACE); 358 proc0.p_addr = proc0paddr = (struct user *)v; 359 proc0.p_md.md_regs = (struct frame *)(v + USPACE) - 1; 360 curpcb = &proc0.p_addr->u_pcb; 361 curpcb->pcb_context[11] = MIPS_INT_MASK | MIPS_SR_INT_IE; /* SR */ 362 363 /* 364 * Allocate space for system data structures. These data structures 365 * are allocated here instead of cpu_startup() because physical 366 * memory is directly addressable. We don't have to map these into 367 * virtual address space. 368 */ 369 v = (caddr_t)uvm_pageboot_alloc(size); 370 if ((allocsys(v, NULL) - v) != size) 371 panic("mach_init: table size inconsistency"); 372 /* 373 * Set up interrupt handling and I/O addresses. 374 */ 375 376 pizazz_init(); 377 } 378 379 380 381 /* 382 * cpu_startup: allocate memory for variable-sized tables, 383 * initialize cpu, and do autoconfiguration. 384 */ 385 void 386 cpu_startup() 387 { 388 u_int i, base, residual; 389 vaddr_t minaddr, maxaddr; 390 vsize_t size; 391 char pbuf[9]; 392 #ifdef DEBUG 393 extern int pmapdebug; 394 int opmapdebug = pmapdebug; 395 396 pmapdebug = 0; 397 #endif 398 399 /* 400 * Good {morning,afternoon,evening,night}. 401 */ 402 printf(version); 403 printf("%s\n", cpu_model); 404 format_bytes(pbuf, sizeof(pbuf), ctob(physmem)); 405 printf("total memory = %s\n", pbuf); 406 407 /* 408 * Allocate virtual address space for file I/O buffers. 409 * Note they are different than the array of headers, 'buf', 410 * and usually occupy more virtual memory than physical. 411 */ 412 size = MAXBSIZE * nbuf; 413 if (uvm_map(kernel_map, (vaddr_t *)&buffers, round_page(size), 414 NULL, UVM_UNKNOWN_OFFSET, 0, 415 UVM_MAPFLAG(UVM_PROT_NONE, UVM_PROT_NONE, UVM_INH_NONE, 416 UVM_ADV_NORMAL, 0)) != 0) 417 panic("startup: cannot allocate VM for buffers"); 418 minaddr = (vaddr_t)buffers; 419 base = bufpages / nbuf; 420 residual = bufpages % nbuf; 421 for (i = 0; i < nbuf; i++) { 422 vsize_t curbufsize; 423 vaddr_t curbuf; 424 struct vm_page *pg; 425 426 /* 427 * Each buffer has MAXBSIZE bytes of VM space allocated. Of 428 * that MAXBSIZE space, we allocate and map (base+1) pages 429 * for the first "residual" buffers, and then we allocate 430 * "base" pages for the rest. 431 */ 432 curbuf = (vaddr_t) buffers + (i * MAXBSIZE); 433 curbufsize = NBPG * ((i < residual) ? (base+1) : base); 434 435 while (curbufsize) { 436 pg = uvm_pagealloc(NULL, 0, NULL, 0); 437 if (pg == NULL) 438 panic("cpu_startup: not enough memory for " 439 "buffer cache"); 440 pmap_kenter_pa(curbuf, VM_PAGE_TO_PHYS(pg), 441 VM_PROT_READ|VM_PROT_WRITE); 442 curbuf += PAGE_SIZE; 443 curbufsize -= PAGE_SIZE; 444 } 445 } 446 pmap_update(pmap_kernel()); 447 448 /* 449 * Allocate a submap for exec arguments. This map effectively 450 * limits the number of processes exec'ing at any time. 451 */ 452 exec_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, 453 16 * NCARGS, TRUE, FALSE, NULL); 454 /* 455 * Allocate a submap for physio 456 */ 457 phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, 458 VM_PHYS_SIZE, TRUE, FALSE, NULL); 459 460 /* 461 * No need to allocate an mbuf cluster submap. Mbuf clusters 462 * are allocated via the pool allocator, and we use KSEG to 463 * map those pages. 464 */ 465 466 #ifdef DEBUG 467 pmapdebug = opmapdebug; 468 #endif 469 format_bytes(pbuf, sizeof(pbuf), ptoa(uvmexp.free)); 470 printf("avail memory = %s\n", pbuf); 471 format_bytes(pbuf, sizeof(pbuf), bufpages * NBPG); 472 printf("using %u buffers containing %s of memory\n", nbuf, pbuf); 473 474 /* 475 * Set up buffers, so they can be used to read disk labels. 476 */ 477 bufinit(); 478 } 479 480 /* 481 * Look up information in bootinfo of boot loader. 482 */ 483 void * 484 lookup_bootinfo(type) 485 int type; 486 { 487 struct btinfo_common *bt; 488 char *help = bootinfo; 489 490 /* Check for a bootinfo record first. */ 491 if (help == NULL) 492 return (NULL); 493 494 do { 495 bt = (struct btinfo_common *)help; 496 if (bt->type == type) 497 return ((void *)help); 498 help += bt->next; 499 } while (bt->next != 0 && 500 (size_t)help < (size_t)bootinfo + BOOTINFO_SIZE); 501 502 return (NULL); 503 } 504 505 int waittime = -1; 506 507 /* 508 * call PROM to halt or reboot. 509 */ 510 void 511 prom_halt(howto) 512 int howto; 513 { 514 if (howto & RB_HALT) 515 MIPS_PROM(reinit)(); 516 MIPS_PROM(reboot)(); 517 /* NOTREACHED */ 518 } 519 520 void 521 cpu_reboot(howto, bootstr) 522 volatile int howto; 523 char *bootstr; 524 { 525 /* take a snap shot before clobbering any registers */ 526 if (curproc) 527 savectx((struct user *)curpcb); 528 529 #ifdef DEBUG 530 if (panicstr) 531 stacktrace(); 532 #endif 533 534 /* If system is cold, just halt. */ 535 if (cold) { 536 howto |= RB_HALT; 537 goto haltsys; 538 } 539 540 /* If "always halt" was specified as a boot flag, obey. */ 541 if ((boothowto & RB_HALT) != 0) 542 howto |= RB_HALT; 543 544 boothowto = howto; 545 if ((howto & RB_NOSYNC) == 0 && waittime < 0) { 546 /* 547 * Synchronize the disks.... 548 */ 549 waittime = 0; 550 vfs_shutdown(); 551 552 /* 553 * If we've been adjusting the clock, the todr 554 * will be out of synch; adjust it now. 555 */ 556 resettodr(); 557 } 558 559 /* Disable interrupts. */ 560 splhigh(); 561 562 /* If rebooting and a dump is requested do it. */ 563 #if 0 564 if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP) 565 #else 566 if (howto & RB_DUMP) 567 #endif 568 dumpsys(); 569 570 haltsys: 571 572 /* run any shutdown hooks */ 573 doshutdownhooks(); 574 575 if ((howto & RB_POWERDOWN) == RB_POWERDOWN) 576 prom_halt(0x80); /* rom monitor RB_PWOFF */ 577 578 /* Finally, halt/reboot the system. */ 579 printf("%s\n\n", howto & RB_HALT ? "halted." : "rebooting..."); 580 prom_halt(howto & RB_HALT); 581 /*NOTREACHED*/ 582 } 583 584 /* 585 * Return the best possible estimate of the time in the timeval 586 * to which tvp points. Unfortunately, we can't read the hardware registers. 587 * We guarantee that the time will be greater than the value obtained by a 588 * previous call. 589 */ 590 void 591 microtime(tvp) 592 register struct timeval *tvp; 593 { 594 static struct timeval lasttime; 595 int s = splclock(); 596 597 *tvp = time; 598 599 tvp->tv_usec += (*platform.clkread)(); 600 601 while (tvp->tv_usec >= 1000000) { 602 tvp->tv_usec -= 1000000; 603 tvp->tv_sec++; 604 } 605 606 if (tvp->tv_sec == lasttime.tv_sec && 607 tvp->tv_usec <= lasttime.tv_usec && 608 (tvp->tv_usec = lasttime.tv_usec + 1) > 1000000) { 609 tvp->tv_sec++; 610 tvp->tv_usec -= 1000000; 611 } 612 lasttime = *tvp; 613 splx(s); 614 } 615 616 int 617 initcpu() 618 { 619 spl0(); /* safe to turn interrupts on now */ 620 return 0; 621 } 622 623 static void 624 unimpl_cons_init() 625 { 626 627 panic("sysconf.init didn't set cons_init"); 628 } 629 630 static void 631 unimpl_iointr(mask, pc, statusreg, causereg) 632 u_int mask; 633 u_int pc; 634 u_int statusreg; 635 u_int causereg; 636 { 637 638 panic("sysconf.init didn't set intr"); 639 } 640 641 static int 642 unimpl_memsize(first) 643 caddr_t first; 644 { 645 646 panic("sysconf.init didn't set memsize"); 647 } 648 649 static unsigned 650 unimpl_clkread() 651 { 652 return 0; /* No microtime available */ 653 } 654 655 static void 656 unimpl_todr(dt) 657 struct clock_ymdhms *dt; 658 { 659 panic("sysconf.init didn't init TOD"); 660 } 661 662 void 663 unimpl_intr_establish(level, func, arg) 664 int level; 665 int (*func) __P((void *)); 666 void *arg; 667 { 668 panic("sysconf.init didn't init intr_establish"); 669 } 670 671 void 672 delay(n) 673 int n; 674 { 675 DELAY(n); 676 } 677 678 /* 679 * Find out how much memory is available by testing memory. 680 * Be careful to save and restore the original contents for msgbuf. 681 */ 682 int 683 memsize_scan(first) 684 caddr_t first; 685 { 686 volatile int *vp, *vp0; 687 int mem, tmp, tmp0; 688 689 #define PATTERN1 0xa5a5a5a5 690 #define PATTERN2 ~PATTERN1 691 692 /* 693 * Non destructive scan of memory to determine the size 694 * Use the first page to test for memory aliases. This 695 * also has the side effect of flushing the bus alignment 696 * buffer 697 */ 698 mem = btoc((paddr_t)first - MIPS_KSEG0_START); 699 vp = (int *)MIPS_PHYS_TO_KSEG1(mem << PGSHIFT); 700 vp0 = (int *)MIPS_PHYS_TO_KSEG1(0); /* Start of physical memory */ 701 tmp0 = *vp0; 702 while (vp < (int *)MIPS_MAX_MEM_ADDR) { 703 tmp = *vp; 704 *vp = PATTERN1; 705 *vp0 = PATTERN2; 706 wbflush(); 707 if (*vp != PATTERN1) 708 break; 709 *vp = PATTERN2; 710 *vp0 = PATTERN1; 711 wbflush(); 712 if (*vp != PATTERN2) 713 break; 714 *vp = tmp; 715 vp += NBPG/sizeof(int); 716 mem++; 717 } 718 *vp0 = tmp0; 719 return mem; 720 } 721 722 /* 723 * Console initialization: called early on from main, 724 * before vm init or startup. Do enough configuration 725 * to choose and initialize a console. 726 */ 727 728 static void 729 null_cnprobe(cn) 730 struct consdev *cn; 731 { 732 } 733 734 static void 735 prom_cninit(cn) 736 struct consdev *cn; 737 { 738 extern const struct cdevsw cons_cdevsw; 739 740 cn->cn_dev = makedev(cdevsw_lookup_major(&cons_cdevsw), 0); 741 cn->cn_pri = CN_REMOTE; 742 } 743 744 static int 745 prom_cngetc(dev) 746 dev_t dev; 747 { 748 return MIPS_PROM(getchar)(); 749 } 750 751 static void 752 prom_cnputc(dev, c) 753 dev_t dev; 754 int c; 755 { 756 MIPS_PROM(putchar)(c); 757 } 758 759 static void 760 null_cnpollc(dev, on) 761 dev_t dev; 762 int on; 763 { 764 } 765 766 void 767 consinit() 768 { 769 int zs_unit; 770 771 zs_unit = 0; 772 cn_tab = &consdev_zs; 773 774 (*cn_tab->cn_init)(cn_tab); 775 } 776