|
|
1.1 ! root 1: /* $Header: /src386/STREAMS/coh.386/RCS/null.c,v 2.3 93/08/09 13:35:51 bin Exp Locker: bin $ */ ! 2: /* (lgl- ! 3: * The information contained herein is a trade secret of Mark Williams ! 4: * Company, and is confidential information. It is provided under a ! 5: * license agreement, and may be copied or disclosed only under the ! 6: * terms of that agreement. Any reproduction or disclosure of this ! 7: * material without the express written authorization of Mark Williams ! 8: * Company or persuant to the license agreement is unlawful. ! 9: * ! 10: * COHERENT Version 2.3.37 ! 11: * Copyright (c) 1982, 1983, 1984. ! 12: * An unpublished work by Mark Williams Company, Chicago. ! 13: * All rights reserved. ! 14: -lgl) */ ! 15: /* ! 16: * Null and memory driver. ! 17: * Minor device 0 is /dev/null ! 18: * Minor device 1 is /dev/mem, physical memory ! 19: * Minor device 2 is /dev/kmem, kernel data ! 20: * Minor device 3 is /dev/cmos ! 21: * Minor device 4 is /dev/boot_gift ! 22: * Minor device 5 is /dev/clock ! 23: * Minor device 6 is /dev/ps ! 24: * Minor device 7 is /dev/kmemhi, virtual memory 0x8000_0000-0xFFFF_FFFF ! 25: * Minor device 11 is /dev/idle ! 26: * ! 27: * $Log: null.c,v $ ! 28: * Revision 2.3 93/08/09 13:35:51 bin ! 29: * Kernel 82 changes ! 30: * ! 31: * Revision 2.2 93/07/26 14:28:57 nigel ! 32: * Nigel's R80 ! 33: * ! 34: * Revision 1.7 93/04/14 10:06:37 root ! 35: * r75 ! 36: * ! 37: * Revision 1.10 93/03/02 08:16:25 bin ! 38: * kernel 73 update ! 39: * ! 40: * Revision 1.6 92/11/09 17:10:54 root ! 41: * Just before adding vio segs. ! 42: * ! 43: * Revision 1.2 92/01/06 11:59:49 hal ! 44: * Compile with cc.mwc. ! 45: * ! 46: * Revision 1.1 88/03/24 16:14:04 src ! 47: * Initial revision ! 48: * ! 49: */ ! 50: ! 51: /* ! 52: * The symbol "DANGEROUS" should be undefined for a production system. ! 53: */ ! 54: #ifdef TRACER ! 55: #define NULL_IOCTL /* Allow ioctl()s for /dev/kmem. */ ! 56: #define DANGEROUS /* Allow dangerous ioctl()s for /dev/null. */ ! 57: #endif ! 58: #define IDLE_DEV ! 59: ! 60: #include <kernel/typed.h> ! 61: ! 62: #include <sys/coherent.h> ! 63: #include <sys/con.h> ! 64: #include <sys/errno.h> ! 65: #include <sys/stat.h> ! 66: #include <sys/inode.h> ! 67: #include <sys/seg.h> ! 68: #include <sys/coh_ps.h> ! 69: #include <sys/file.h> ! 70: #if defined NULL_IOCTL || defined IDLE_DEV ! 71: #include <sys/null.h> ! 72: #endif /* NULL_IOCTL || IDLE_DEV */ ! 73: ! 74: ! 75: #if TRACER ! 76: #include <sys/buf.h> ! 77: #endif ! 78: ! 79: /* These are minor numbers. */ ! 80: #define DEV_NULL 0 /* /dev/null */ ! 81: #define DEV_MEM 1 /* /dev/mem */ ! 82: #define DEV_KMEM 2 /* /dev/kmem */ ! 83: #define DEV_CMOS 3 /* /dev/cmos */ ! 84: #define DEV_BOOTGIFT 4 /* /dev/bootgift */ ! 85: #define DEV_CLOCK 5 /* /dev/clock */ ! 86: #define DEV_PS 6 /* /dev/ps */ ! 87: #define DEV_KMEMHI 7 /* /dev/kmemhi */ ! 88: #define DEV_IDLE 11 /* /dev/idle */ ! 89: ! 90: #define KMEMHI_BASE 0x80000000 ! 91: #define PXCOPY_LIM 4096 ! 92: ! 93: /* ! 94: * CMOS devices are limited by an 8 bit address. ! 95: */ ! 96: #define MAX_CMOS 255 ! 97: #define CMOS_LEN 256 ! 98: ! 99: /* ! 100: * The first 14 bytes of the CMOS are the clock. ! 101: */ ! 102: #define MAX_CLOCK 13 ! 103: #define CLOCK_LEN 14 ! 104: ! 105: /* ! 106: * These are definitions for mucking with the CMOS clock. ! 107: */ ! 108: #define SRA 10 /* Status Register A */ ! 109: #define SRB 11 /* Status Register B */ ! 110: #define SRC 12 /* Status Register C */ ! 111: #define SRD 13 /* Status Register D */ ! 112: ! 113: #define UIP 0x80 /* Update In Progress bit of SRA. */ ! 114: #define NO_UPD 0x80 /* No Update bit of SRB. */ ! 115: ! 116: /* ! 117: * Functions for configuration. ! 118: */ ! 119: void nlopen(); ! 120: void nlclose(); ! 121: void nlread(); ! 122: void nlwrite(); ! 123: int nlioctl(); ! 124: int nulldev(); ! 125: int nonedev(); ! 126: ! 127: /* ! 128: * Configuration table. ! 129: */ ! 130: CON nlcon ={ ! 131: DFCHR, /* Flags */ ! 132: 0, /* Major index */ ! 133: nlopen, /* Open */ ! 134: nlclose, /* Close */ ! 135: nulldev, /* Block */ ! 136: nlread, /* Read */ ! 137: nlwrite, /* Write */ ! 138: #if defined NULL_IOCTL || defined IDLE_DEV ! 139: nlioctl, /* Ioctl */ ! 140: #else /* NULL_IOCTL || IDLE_DEV */ ! 141: nonedev, /* Ioctl */ ! 142: #endif /* NULL_IOCTL || IDLE_DEV */ ! 143: nulldev, /* Powerfail */ ! 144: nulldev, /* Timeout */ ! 145: nulldev, /* Load */ ! 146: nulldev /* Unload */ ! 147: }; ! 148: ! 149: int lock_clock(); ! 150: void unlock_clock(); ! 151: ! 152: /* ! 153: * Null/memory open routine. ! 154: */ ! 155: void ! 156: nlopen(dev, mode) ! 157: dev_t dev; ! 158: int mode; ! 159: { ! 160: switch (minor(dev)) { ! 161: #ifdef IDLE_DEV ! 162: case DEV_IDLE: ! 163: #endif ! 164: case DEV_PS: ! 165: /* /dev/ps is read only */ ! 166: if (IPR != (IPR & mode)) ! 167: SET_U_ERROR( EACCES, "/dev/ps is read only" ); ! 168: break; ! 169: default: ! 170: /* ! 171: * For minor devices on NULL there is ! 172: * usually no action for open(). ! 173: */ ! 174: break; ! 175: } ! 176: return; ! 177: } /* nlopen() */ ! 178: ! 179: /* ! 180: * Null/memory close routine. ! 181: */ ! 182: void ! 183: nlclose(dev, mode) ! 184: dev_t dev; ! 185: int mode; ! 186: { ! 187: /* ! 188: * For minor devices on NULL there is ! 189: * Usually no action for close(). ! 190: */ ! 191: return; ! 192: } /* nlclose() */ ! 193: ! 194: /* ! 195: * Null/memory read routine. ! 196: */ ! 197: void ! 198: nlread(dev, iop) ! 199: dev_t dev; ! 200: register IO *iop; ! 201: { ! 202: register unsigned bytesRead; ! 203: register PROC *pp1; /* */ ! 204: char psBuf[ARGSZ]; /* buffer for command line ! 205: * arguments for ps. */ ! 206: stMonitor psData; /* All process data for */ ! 207: UPROC *uprc; /* pointer to u area */ ! 208: int ndpUseg; /* System global address ! 209: * of U segment */ ! 210: unsigned int seek; ! 211: unsigned char read_cmos(); ! 212: extern typed_space boot_gift; ! 213: ! 214: switch (minor (dev)) { ! 215: case DEV_NULL: ! 216: /* ! 217: * Read nothing. ! 218: * Do NOT update iop->io_ioc. ! 219: * This way, caller knows 0 bytes were read. ! 220: */ ! 221: break; ! 222: ! 223: case DEV_MEM: { ! 224: int src = iop->io_seek; ! 225: int dest = iop->io.pbase; ! 226: ! 227: while (iop->io_ioc) { ! 228: int numBytes = PXCOPY_LIM; ! 229: if (numBytes > iop->io_ioc) ! 230: numBytes = iop->io_ioc; ! 231: ! 232: bytesRead = pxcopy (src, dest, numBytes, ! 233: SEG_386_UD | R_USR); ! 234: src += bytesRead; ! 235: dest += bytesRead; ! 236: iop->io_ioc -= bytesRead; ! 237: if (u.u_error == EFAULT) { ! 238: u.u_error = 0; ! 239: break; ! 240: } ! 241: } ! 242: break; ! 243: } ! 244: ! 245: case DEV_KMEM: ! 246: iowrite (iop, iop->io_seek, iop->io_ioc); ! 247: if (u.u_error == EFAULT) ! 248: u.u_error = 0; ! 249: break; ! 250: ! 251: case DEV_CLOCK: ! 252: /* ! 253: * Don't go past the end of the CLOCK. ! 254: */ ! 255: if (iop->io_seek >= CLOCK_LEN) ! 256: break; ! 257: ! 258: /* ! 259: * Lock the clock before any reading. ! 260: */ ! 261: ! 262: if (lock_clock () == 0) { ! 263: SET_U_ERROR (EIO, "RT clock will not settle."); ! 264: break; ! 265: } ! 266: ! 267: /* ! 268: * Read the requested data out of the CMOS. ! 269: */ ! 270: for (seek = iop->io_seek; seek < CLOCK_LEN; seek++) { ! 271: if(ioputc(read_cmos(seek), iop) == -1) ! 272: break; ! 273: } ! 274: ! 275: /* ! 276: * Now that we are done reading the CMOS, let ! 277: * the clock loose. ! 278: */ ! 279: unlock_clock(); ! 280: break; ! 281: ! 282: case DEV_CMOS: ! 283: /* ! 284: * Don't go past the end of the CMOS. ! 285: */ ! 286: if (iop->io_seek >= CMOS_LEN) ! 287: break; ! 288: ! 289: /* ! 290: * Read the requested data out of the CMOS. ! 291: */ ! 292: for (seek = iop->io_seek; seek < CMOS_LEN; seek++) { ! 293: if(ioputc(read_cmos(seek), iop) == -1) ! 294: break; ! 295: } ! 296: break; ! 297: ! 298: case DEV_BOOTGIFT: ! 299: /* ! 300: * Reads all from the data structure boot_gift. ! 301: */ ! 302: if (iop->io_seek < BG_LEN) { ! 303: bytesRead = iop->io_ioc; ! 304: ! 305: /* ! 306: * Copy no more than to the end of boot_gift. ! 307: */ ! 308: if (iop->io_seek + bytesRead > BG_LEN) ! 309: bytesRead = BG_LEN - iop->io_seek; ! 310: ! 311: iowrite (iop, (char *) (& boot_gift) + iop->io_seek, ! 312: bytesRead); ! 313: } ! 314: break; ! 315: ! 316: case DEV_PS: ! 317: /* Lock the process table. It allows to have an atomic ps. */ ! 318: lock (pnxgate); ! 319: /* Main driver loop. Go through all processes. Fill struct PS ! 320: * and send put to user buffer. ! 321: */ ! 322: for (pp1 = & procq; (pp1 = pp1->p_nforw) != & procq; ) { ! 323: register int i; /* loop index */ ! 324: register unsigned uLen, /* Process size */ ! 325: uLenR; /* Real process size */ ! 326: register SEG *sp; /* u area segment */ ! 327: int work; /* virtual click number */ ! 328: ! 329: /* Check if driver can send next proc data */ ! 330: if (iop->io_ioc < sizeof (stMonitor)) ! 331: break; ! 332: ! 333: /* Calculate the size of process. */ ! 334: uLen = uLenR = 0; ! 335: for (i = 0 ; i < NUSEG ; i++) { ! 336: if ((sp = pp1->p_segp [i]) == NULL) ! 337: continue; ! 338: uLenR += sp->s_size; ! 339: if (i == SIUSERP || i == SIAUXIL) ! 340: continue; ! 341: uLen += sp->s_size; ! 342: ! 343: } ! 344: ! 345: /* Find u area for process pp1 */ ! 346: sp = pp1->p_segp [SIUSERP]; ! 347: ndpUseg = MAPIO (sp->s_vmem, U_OFFSET); ! 348: work = workAlloc (); ! 349: ptable1_v [work] = ! 350: sysmem.u.pbase [btocrd (ndpUseg)] | SEG_RW; ! 351: uprc = (UPROC *) (ctob (work) + U_OFFSET); ! 352: memcpy (psData.u_comm, uprc->u_comm, ARGSZ); ! 353: memcpy (psData.u_sleep, uprc->u_sleep, U_SLEEP_LEN); ! 354: workFree (work); ! 355: ! 356: #ifdef TRACER ! 357: if (strncmp (psData.u_sleep, "lock", ! 358: U_SLEEP_LEN) == 0) { ! 359: GATE * g = pp1->p_event; ! 360: printf ("[%d] locked at %x lock %s (%d) = %x\n", ! 361: pp1->p_pid, g, g->_name, g->_count, ! 362: g->_lock [0]); ! 363: } ! 364: if (strncmp (psData.u_sleep, "bpwait", ! 365: U_SLEEP_LEN) == 0) { ! 366: BUF * bp = pp1->p_event; ! 367: printf ("[%d] blocked on %x flags = %x\n", ! 368: pp1->p_pid, bp, bp->b_flag); ! 369: } ! 370: #endif ! 371: ! 372: /* fill up stMonitor */ ! 373: psData.p_pid = pp1->p_pid; ! 374: psData.p_ppid = pp1->p_ppid; ! 375: psData.p_uid = pp1->p_uid; ! 376: psData.p_ruid = pp1->p_ruid; ! 377: psData.p_rgid = pp1->p_rgid; ! 378: psData.p_state = pp1->p_state; ! 379: psData.p_flags = pp1->p_flags; ! 380: psData.rrun = (char *) pp1 != pp1->p_event; ! 381: psData.p_event = pp1->p_event; ! 382: psData.p_ttdev = pp1->p_ttdev; ! 383: psData.p_nice = pp1->p_nice; ! 384: psData.size = (short) (uLen >> 10); ! 385: psData.rsize = (short) (uLenR >> 10); ! 386: psData.p_schedPri = pp1->p_schedPri; ! 387: psData.p_utime = pp1->p_utime; ! 388: psData.p_stime = pp1->p_stime; ! 389: ! 390: memcpy (psData.pr_argv, psBuf, ARGSZ); ! 391: ! 392: /* send data to user */ ! 393: iowrite (iop, (char *) & psData, sizeof (stMonitor)); ! 394: } ! 395: unlock (pnxgate); ! 396: break; ! 397: ! 398: case DEV_KMEMHI: ! 399: iowrite (iop, iop->io_seek - KMEMHI_BASE, iop->io_ioc); ! 400: if (u.u_error == EFAULT) ! 401: u.u_error = 0; ! 402: break; ! 403: ! 404: default: ! 405: SET_U_ERROR (ENXIO, "nlread(): illegal minor device for null"); ! 406: } ! 407: return; ! 408: } ! 409: ! 410: /* ! 411: * Null/memory write routine. ! 412: */ ! 413: void ! 414: nlwrite(dev, iop) ! 415: dev_t dev; ! 416: register IO *iop; ! 417: { ! 418: register unsigned bytesWrit; ! 419: unsigned write_cmos(); ! 420: unsigned seek; ! 421: int ch; ! 422: ! 423: switch (minor (dev)) { ! 424: case DEV_NULL: ! 425: /* ! 426: * Tell caller all bytes were written. ! 427: */ ! 428: iop->io_ioc = 0; ! 429: break; ! 430: ! 431: case DEV_MEM: ! 432: while (iop->io_ioc) { ! 433: int src = iop->io.pbase; ! 434: int dest = iop->io_seek; ! 435: int numBytes = PXCOPY_LIM; ! 436: if (numBytes > iop->io_ioc) ! 437: numBytes = iop->io_ioc; ! 438: ! 439: bytesWrit = xpcopy (src, dest, numBytes, ! 440: SEG_386_UD | R_USR); ! 441: src += bytesWrit; ! 442: dest += bytesWrit; ! 443: iop->io_ioc -= bytesWrit; ! 444: if (u.u_error == EFAULT) { ! 445: u.u_error = 0; ! 446: break; ! 447: } ! 448: } ! 449: break; ! 450: ! 451: case DEV_KMEM: ! 452: ioread (iop, iop->io_seek, iop->io_ioc); ! 453: break; ! 454: ! 455: case DEV_CLOCK: ! 456: /* ! 457: * Don't go past the end of the CLOCK. ! 458: */ ! 459: if (iop->io_seek >= CLOCK_LEN) ! 460: break; ! 461: ! 462: /* ! 463: * Lock the clock before any writing. ! 464: */ ! 465: if (lock_clock () == 0) { ! 466: SET_U_ERROR (EIO, "RT clock will not settle."); ! 467: break; ! 468: } ! 469: ! 470: /* ! 471: * Write the requested data into the CMOS. ! 472: */ ! 473: ! 474: for (seek = iop->io_seek ; seek < CLOCK_LEN ; seek ++) { ! 475: if ((ch = iogetc (iop)) == -1) ! 476: break; ! 477: write_cmos (seek, ch); ! 478: } ! 479: ! 480: /* ! 481: * Now that we are done writing the CMOS, let ! 482: * the clock loose. ! 483: */ ! 484: unlock_clock (); ! 485: break; ! 486: ! 487: case DEV_CMOS: ! 488: /* ! 489: * Don't go past the end of the CMOS. ! 490: */ ! 491: if (iop->io_seek >= CMOS_LEN) ! 492: break; ! 493: ! 494: /* ! 495: * Write the requested data into the CMOS. ! 496: */ ! 497: for (seek = iop->io_seek ; seek < CMOS_LEN ; seek ++) { ! 498: if ((ch = iogetc (iop)) == -1) ! 499: break; ! 500: write_cmos (seek, ch); ! 501: } ! 502: break; ! 503: ! 504: case DEV_BOOTGIFT: ! 505: /* ! 506: * /dev/bootgift is not writable. ! 507: */ ! 508: break; ! 509: ! 510: case DEV_PS: ! 511: /* ! 512: * We should not be able to open /dev/ps to write. ! 513: * Just paranoya. ! 514: */ ! 515: break; ! 516: ! 517: case DEV_KMEMHI: ! 518: ioread (iop, iop->io_seek - KMEMHI_BASE, iop->io_ioc); ! 519: break; ! 520: ! 521: default: ! 522: SET_U_ERROR (ENXIO, ! 523: "nlwrite(): illegal minor device for null"); ! 524: } ! 525: return; ! 526: } ! 527: ! 528: #if defined NULL_IOCTL || defined IDLE_DEV /* Includes all of nlioctl(). */ ! 529: ! 530: /* ! 531: * Do an ioctl call for /dev/null. ! 532: */ ! 533: int ! 534: nlioctl(dev, cmd, vec) ! 535: dev_t dev; ! 536: int cmd; ! 537: char * vec; ! 538: { ! 539: /* Only /dev/kmem and /dev/idle have an ioctl. */ ! 540: switch (minor (dev)) { ! 541: #ifdef NULL_IOCTL ! 542: case DEV_KMEM: ! 543: switch (cmd) { ! 544: #ifdef DANGEROUS ! 545: case NLCALL: /* Call a function. */ ! 546: return docall (vec); ! 547: #endif /* DANGEROUS */ ! 548: default: ! 549: SET_U_ERROR (EINVAL, ! 550: "nioctl(): illegal command for kmem"); ! 551: return -1; ! 552: } ! 553: #endif /* NULL_IOCTL */ ! 554: #ifdef IDLE_DEV ! 555: case DEV_IDLE: ! 556: if (cmd != NLIDLE) { ! 557: SET_U_ERROR (EINVAL, ! 558: "nioctl(): illegal command for idle"); ! 559: return -1; ! 560: } else { ! 561: register PROC *pp; ! 562: register int *mem = vec; ! 563: ! 564: ! 565: pp = & procq; /* point to process queue */ ! 566: ! 567: if (pp->p_pid != 0) { ! 568: while ((pp = pp->p_nforw) != &procq) ! 569: if (pp->p_pid == 0) /* idle process ? */ ! 570: break; ! 571: } ! 572: ! 573: /* ! 574: * At this point, pp->p_utime and pp->p_stime contain ! 575: * the idle time of the system process ! 576: */ ! 577: ! 578: if (pp->p_pid != 0) ! 579: putuwd (mem ++, 0); ! 580: else ! 581: putuwd (mem ++, pp->p_utime + pp->p_stime); ! 582: putuwd (mem, lbolt); ! 583: return 1; ! 584: } ! 585: ! 586: #endif /* IDLE_DEV */ ! 587: default: ! 588: SET_U_ERROR(EINVAL, "illegal minor device for null ioctl"); ! 589: return -1; ! 590: } /* switch on minor device */ ! 591: ! 592: } /* nlioctl() */ ! 593: ! 594: #endif /* NULL_IOCTL || IDLE_DEV */ ! 595: ! 596: #ifdef DANGEROUS /* Includes all of docall(). */ ! 597: /* ! 598: * MASSIVE SECURITY HOLE! This should NOT be included in a distribution ! 599: * system. Among other problems, it becomes possible to do "setuid(0)". ! 600: * ! 601: * Call a function with arguments. ! 602: * ! 603: * Takes an array of unsigned ints. The first element is the length of ! 604: * the whole array, the second element is a pointer to the function to ! 605: * call, all other elements are arguments. At most 5 arguments may be ! 606: * passed. ! 607: * ! 608: * Returns the return value of the called fuction in uvec[0]. ! 609: */ ! 610: int ! 611: docall(uvec) ! 612: unsigned uvec[]; ! 613: { ! 614: int (* func)(); ! 615: unsigned kvec[7]; ! 616: int retval; ! 617: ! 618: printf("NLCALL security hole.\n"); ! 619: ! 620: /* Fetch the first element of vec. */ ! 621: ukcopy (uvec, kvec, sizeof (unsigned)); ! 622: ! 623: if (kvec [0] < 2 || kvec[0] > 7) { ! 624: /* Invalid number of elements in uvec. */ ! 625: SET_U_ERROR (EINVAL, "Invalid number of elements in uvec"); ! 626: return -1; ! 627: } ! 628: ! 629: /* Fetch the whole vector. */ ! 630: ukcopy (uvec, kvec, kvec [0] * sizeof (unsigned)); ! 631: ! 632: /* Extract the function. */ ! 633: func = (int (*)()) kvec [1]; ! 634: ! 635: /* Call the function with all arguments. */ ! 636: retval = (* func) (kvec [2], kvec [3], kvec [4], kvec [5], kvec [6]); ! 637: ! 638: kucopy (& retval, uvec, sizeof (unsigned)); ! 639: } /* docall() */ ! 640: ! 641: #endif /* DANGEROUS */ ! 642: ! 643: /* ! 644: * int lock_clock() -- Stop the update cycle on the CMOS RT clock and ! 645: * wait for it to settle. Returns 0 if the clock would not settle ! 646: * in time. ! 647: */ ! 648: int ! 649: lock_clock() ! 650: { ! 651: register int i; ! 652: ! 653: /* ! 654: * Wait for the clock to settle. If it does not settle in ! 655: * a reasonable amount of time, give up. ! 656: */ ! 657: ! 658: i = 65536; /* Loop for a longish time. */ ! 659: while (-- i > 0) { ! 660: if (0 == (UIP & read_cmos (SRA))) { ! 661: break; /* Break if there is no update in progress. */ ! 662: } ! 663: } ! 664: ! 665: if (0 == i) { ! 666: /* The clock would not settle. */ ! 667: return 0; ! 668: } ! 669: ! 670: /* ! 671: * There is a tiny race here--an interrupt could conceivably ! 672: * come here, thus allowing enough delay for another update to ! 673: * begin. But if we take interrupts that take a full second ! 674: * to process, other things are going to break horribly. ! 675: */ ! 676: ! 677: /* ! 678: * Lock out updates. ! 679: * We set the No Updates bit in Clock Status Register B. ! 680: */ ! 681: write_cmos (SRB, read_cmos (SRB) | NO_UPD); ! 682: return 1; ! 683: } /* lock_clock() */ ! 684: ! 685: /* ! 686: * void unlock_clock() -- Restart the update cycle on the CMOS RT clock. ! 687: */ ! 688: void ! 689: unlock_clock() ! 690: { ! 691: /* ! 692: * We clear the No Updates bit in Clock Status Register B. ! 693: */ ! 694: write_cmos (SRB, read_cmos(SRB) & ~ NO_UPD); ! 695: } /* unlock_clock() */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.