1 #include "u.h" 2 #include "../port/lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "io.h" 7 #include "ureg.h" 8 #include "../port/error.h" 9 10 typedef struct IOMap IOMap; 11 struct IOMap 12 { 13 IOMap *next; 14 int reserved; 15 char tag[13]; 16 ulong start; 17 ulong end; 18 }; 19 20 static struct 21 { 22 Lock; 23 IOMap *m; 24 IOMap *free; 25 IOMap maps[32]; /* some initial free maps */ 26 27 QLock ql; /* lock for reading map */ 28 } iomap; 29 30 enum { 31 Qdir = 0, 32 Qioalloc = 1, 33 Qiob, 34 Qiow, 35 Qiol, 36 Qbase, 37 38 Qmax = 16, 39 }; 40 41 typedef long Rdwrfn(Chan*, void*, long, vlong); 42 43 static Rdwrfn *readfn[Qmax]; 44 static Rdwrfn *writefn[Qmax]; 45 46 static Dirtab archdir[Qmax] = { 47 ".", { Qdir, 0, QTDIR }, 0, 0555, 48 "ioalloc", { Qioalloc, 0 }, 0, 0444, 49 "iob", { Qiob, 0 }, 0, 0660, 50 "iow", { Qiow, 0 }, 0, 0660, 51 "iol", { Qiol, 0 }, 0, 0660, 52 }; 53 Lock archwlock; /* the lock is only for changing archdir */ 54 int narchdir = Qbase; 55 int (*_pcmspecial)(char*, ISAConf*); 56 void (*_pcmspecialclose)(int); 57 58 static int doi8253set = 1; 59 60 /* 61 * Add a file to the #P listing. Once added, you can't delete it. 62 * You can't add a file with the same name as one already there, 63 * and you get a pointer to the Dirtab entry so you can do things 64 * like change the Qid version. Changing the Qid path is disallowed. 65 */ 66 Dirtab* 67 addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn) 68 { 69 int i; 70 Dirtab d; 71 Dirtab *dp; 72 73 memset(&d, 0, sizeof d); 74 strcpy(d.name, name); 75 d.perm = perm; 76 77 lock(&archwlock); 78 if(narchdir >= Qmax){ 79 unlock(&archwlock); 80 return nil; 81 } 82 83 for(i=0; i<narchdir; i++) 84 if(strcmp(archdir[i].name, name) == 0){ 85 unlock(&archwlock); 86 return nil; 87 } 88 89 d.qid.path = narchdir; 90 archdir[narchdir] = d; 91 readfn[narchdir] = rdfn; 92 writefn[narchdir] = wrfn; 93 dp = &archdir[narchdir++]; 94 unlock(&archwlock); 95 96 return dp; 97 } 98 99 void 100 ioinit(void) 101 { 102 char *excluded; 103 int i; 104 105 for(i = 0; i < nelem(iomap.maps)-1; i++) 106 iomap.maps[i].next = &iomap.maps[i+1]; 107 iomap.maps[i].next = nil; 108 iomap.free = iomap.maps; 109 110 /* 111 * This is necessary to make the IBM X20 boot. 112 * Have not tracked down the reason. 113 * i82557 is at 0x1000, the dummy entry is needed for swappable devs. 114 */ 115 ioalloc(0x0fff, 1, 0, "dummy"); 116 117 if ((excluded = getconf("ioexclude")) != nil) { 118 char *s; 119 120 s = excluded; 121 while (s && *s != '\0' && *s != '\n') { 122 char *ends; 123 int io_s, io_e; 124 125 io_s = (int)strtol(s, &ends, 0); 126 if (ends == nil || ends == s || *ends != '-') { 127 print("ioinit: cannot parse option string\n"); 128 break; 129 } 130 s = ++ends; 131 132 io_e = (int)strtol(s, &ends, 0); 133 if (ends && *ends == ',') 134 *ends++ = '\0'; 135 s = ends; 136 137 ioalloc(io_s, io_e - io_s + 1, 0, "pre-allocated"); 138 } 139 } 140 141 } 142 143 /* 144 * Reserve a range to be ioalloced later. 145 * This is in particular useful for exchangable cards, such 146 * as pcmcia and cardbus cards. 147 */ 148 int 149 ioreserve(int, int size, int align, char *tag) 150 { 151 IOMap *m, **l; 152 int i, port; 153 154 lock(&iomap); 155 /* find a free port above 0x400 and below 0x1000 */ 156 port = 0x400; 157 for(l = &iomap.m; *l; l = &(*l)->next){ 158 m = *l; 159 if (m->start < 0x400) continue; 160 i = m->start - port; 161 if(i > size) 162 break; 163 if(align > 0) 164 port = ((port+align-1)/align)*align; 165 else 166 port = m->end; 167 } 168 if(*l == nil){ 169 unlock(&iomap); 170 return -1; 171 } 172 m = iomap.free; 173 if(m == nil){ 174 print("ioalloc: out of maps"); 175 unlock(&iomap); 176 return port; 177 } 178 iomap.free = m->next; 179 m->next = *l; 180 m->start = port; 181 m->end = port + size; 182 m->reserved = 1; 183 strncpy(m->tag, tag, sizeof(m->tag)); 184 m->tag[sizeof(m->tag)-1] = 0; 185 *l = m; 186 187 archdir[0].qid.vers++; 188 189 unlock(&iomap); 190 return m->start; 191 } 192 193 /* 194 * alloc some io port space and remember who it was 195 * alloced to. if port < 0, find a free region. 196 */ 197 int 198 ioalloc(int port, int size, int align, char *tag) 199 { 200 IOMap *m, **l; 201 int i; 202 203 lock(&iomap); 204 if(port < 0){ 205 /* find a free port above 0x400 and below 0x1000 */ 206 port = 0x400; 207 for(l = &iomap.m; *l; l = &(*l)->next){ 208 m = *l; 209 if (m->start < 0x400) continue; 210 i = m->start - port; 211 if(i > size) 212 break; 213 if(align > 0) 214 port = ((port+align-1)/align)*align; 215 else 216 port = m->end; 217 } 218 if(*l == nil){ 219 unlock(&iomap); 220 return -1; 221 } 222 } else { 223 /* Only 64KB I/O space on the x86. */ 224 if((port+size) > 0x10000){ 225 unlock(&iomap); 226 return -1; 227 } 228 /* see if the space clashes with previously allocated ports */ 229 for(l = &iomap.m; *l; l = &(*l)->next){ 230 m = *l; 231 if(m->end <= port) 232 continue; 233 if(m->reserved && m->start == port && m->end == port + size) { 234 m->reserved = 0; 235 unlock(&iomap); 236 return m->start; 237 } 238 if(m->start >= port+size) 239 break; 240 unlock(&iomap); 241 return -1; 242 } 243 } 244 m = iomap.free; 245 if(m == nil){ 246 print("ioalloc: out of maps"); 247 unlock(&iomap); 248 return port; 249 } 250 iomap.free = m->next; 251 m->next = *l; 252 m->start = port; 253 m->end = port + size; 254 strncpy(m->tag, tag, sizeof(m->tag)); 255 m->tag[sizeof(m->tag)-1] = 0; 256 *l = m; 257 258 archdir[0].qid.vers++; 259 260 unlock(&iomap); 261 return m->start; 262 } 263 264 void 265 iofree(int port) 266 { 267 IOMap *m, **l; 268 269 lock(&iomap); 270 for(l = &iomap.m; *l; l = &(*l)->next){ 271 if((*l)->start == port){ 272 m = *l; 273 *l = m->next; 274 m->next = iomap.free; 275 iomap.free = m; 276 break; 277 } 278 if((*l)->start > port) 279 break; 280 } 281 archdir[0].qid.vers++; 282 unlock(&iomap); 283 } 284 285 int 286 iounused(int start, int end) 287 { 288 IOMap *m; 289 290 for(m = iomap.m; m; m = m->next){ 291 if(start >= m->start && start < m->end 292 || start <= m->start && end > m->start) 293 return 0; 294 } 295 return 1; 296 } 297 298 static void 299 checkport(int start, int end) 300 { 301 /* standard vga regs are OK */ 302 if(start >= 0x2b0 && end <= 0x2df+1) 303 return; 304 if(start >= 0x3c0 && end <= 0x3da+1) 305 return; 306 307 if(iounused(start, end)) 308 return; 309 error(Eperm); 310 } 311 312 static Chan* 313 archattach(char* spec) 314 { 315 return devattach('P', spec); 316 } 317 318 Walkqid* 319 archwalk(Chan* c, Chan *nc, char** name, int nname) 320 { 321 return devwalk(c, nc, name, nname, archdir, narchdir, devgen); 322 } 323 324 static int 325 archstat(Chan* c, uchar* dp, int n) 326 { 327 return devstat(c, dp, n, archdir, narchdir, devgen); 328 } 329 330 static Chan* 331 archopen(Chan* c, int omode) 332 { 333 return devopen(c, omode, archdir, narchdir, devgen); 334 } 335 336 static void 337 archclose(Chan*) 338 { 339 } 340 341 enum 342 { 343 Linelen= 31, 344 }; 345 346 static long 347 archread(Chan *c, void *a, long n, vlong offset) 348 { 349 char *buf, *p; 350 int port; 351 ushort *sp; 352 ulong *lp; 353 IOMap *m; 354 Rdwrfn *fn; 355 356 switch((ulong)c->qid.path){ 357 358 case Qdir: 359 return devdirread(c, a, n, archdir, narchdir, devgen); 360 361 case Qiob: 362 port = offset; 363 checkport(offset, offset+n); 364 for(p = a; port < offset+n; port++) 365 *p++ = inb(port); 366 return n; 367 368 case Qiow: 369 if(n & 1) 370 error(Ebadarg); 371 checkport(offset, offset+n); 372 sp = a; 373 for(port = offset; port < offset+n; port += 2) 374 *sp++ = ins(port); 375 return n; 376 377 case Qiol: 378 if(n & 3) 379 error(Ebadarg); 380 checkport(offset, offset+n); 381 lp = a; 382 for(port = offset; port < offset+n; port += 4) 383 *lp++ = inl(port); 384 return n; 385 386 case Qioalloc: 387 break; 388 389 default: 390 if(c->qid.path < narchdir && (fn = readfn[c->qid.path])) 391 return fn(c, a, n, offset); 392 error(Eperm); 393 break; 394 } 395 396 if((buf = malloc(n)) == nil) 397 error(Enomem); 398 p = buf; 399 n = n/Linelen; 400 offset = offset/Linelen; 401 402 lock(&iomap); 403 for(m = iomap.m; n > 0 && m != nil; m = m->next){ 404 if(offset-- > 0) 405 continue; 406 sprint(p, "%8lux %8lux %-12.12s\n", m->start, m->end-1, m->tag); 407 p += Linelen; 408 n--; 409 } 410 unlock(&iomap); 411 412 n = p - buf; 413 memmove(a, buf, n); 414 free(buf); 415 416 return n; 417 } 418 419 static long 420 archwrite(Chan *c, void *a, long n, vlong offset) 421 { 422 char *p; 423 int port; 424 ushort *sp; 425 ulong *lp; 426 Rdwrfn *fn; 427 428 switch((ulong)c->qid.path){ 429 430 case Qiob: 431 p = a; 432 checkport(offset, offset+n); 433 for(port = offset; port < offset+n; port++) 434 outb(port, *p++); 435 return n; 436 437 case Qiow: 438 if(n & 1) 439 error(Ebadarg); 440 checkport(offset, offset+n); 441 sp = a; 442 for(port = offset; port < offset+n; port += 2) 443 outs(port, *sp++); 444 return n; 445 446 case Qiol: 447 if(n & 3) 448 error(Ebadarg); 449 checkport(offset, offset+n); 450 lp = a; 451 for(port = offset; port < offset+n; port += 4) 452 outl(port, *lp++); 453 return n; 454 455 default: 456 if(c->qid.path < narchdir && (fn = writefn[c->qid.path])) 457 return fn(c, a, n, offset); 458 error(Eperm); 459 break; 460 } 461 return 0; 462 } 463 464 Dev archdevtab = { 465 'P', 466 "arch", 467 468 devreset, 469 devinit, 470 devshutdown, 471 archattach, 472 archwalk, 473 archstat, 474 archopen, 475 devcreate, 476 archclose, 477 archread, 478 devbread, 479 archwrite, 480 devbwrite, 481 devremove, 482 devwstat, 483 }; 484 485 /* 486 * the following is a generic version of the 487 * architecture specific stuff 488 */ 489 490 static int 491 unimplemented(int) 492 { 493 return 0; 494 } 495 496 static void 497 nop(void) 498 { 499 } 500 501 /* 502 * 386 has no compare-and-swap instruction. 503 * Run it with interrupts turned off instead. 504 */ 505 static int 506 cmpswap386(long *addr, long old, long new) 507 { 508 int r, s; 509 510 s = splhi(); 511 if(r = (*addr == old)) 512 *addr = new; 513 splx(s); 514 return r; 515 } 516 517 /* 518 * On a uniprocessor, you'd think that coherence could be nop, 519 * but it can't. We still need a barrier when using coherence() in 520 * device drivers. 521 * 522 * On VMware, it's safe (and a huge win) to set this to nop. 523 * Aux/vmware does this via the #P/archctl file. 524 */ 525 void (*coherence)(void) = nop; 526 527 int (*cmpswap)(long*, long, long) = cmpswap386; 528 529 PCArch* arch; 530 extern PCArch* knownarch[]; 531 532 PCArch archgeneric = { 533 .id= "generic", 534 .ident= 0, 535 .reset= i8042reset, 536 .serialpower= unimplemented, 537 .modempower= unimplemented, 538 539 .intrinit= i8259init, 540 .intrenable= i8259enable, 541 .intrvecno= i8259vecno, 542 .intrdisable= i8259disable, 543 .intron= i8259on, 544 .introff= i8259off, 545 546 .clockenable= i8253enable, 547 .fastclock= i8253read, 548 .timerset= i8253timerset, 549 }; 550 551 typedef struct X86type X86type; 552 struct X86type { 553 int family; 554 int model; 555 int aalcycles; 556 char* name; 557 }; 558 559 static X86type x86intel[] = 560 { 561 { 4, 0, 22, "486DX", }, /* known chips */ 562 { 4, 1, 22, "486DX50", }, 563 { 4, 2, 22, "486SX", }, 564 { 4, 3, 22, "486DX2", }, 565 { 4, 4, 22, "486SL", }, 566 { 4, 5, 22, "486SX2", }, 567 { 4, 7, 22, "DX2WB", }, /* P24D */ 568 { 4, 8, 22, "DX4", }, /* P24C */ 569 { 4, 9, 22, "DX4WB", }, /* P24CT */ 570 { 5, 0, 23, "P5", }, 571 { 5, 1, 23, "P5", }, 572 { 5, 2, 23, "P54C", }, 573 { 5, 3, 23, "P24T", }, 574 { 5, 4, 23, "P55C MMX", }, 575 { 5, 7, 23, "P54C VRT", }, 576 { 6, 1, 16, "PentiumPro", },/* trial and error */ 577 { 6, 3, 16, "PentiumII", }, 578 { 6, 5, 16, "PentiumII/Xeon", }, 579 { 6, 6, 16, "Celeron", }, 580 { 6, 7, 16, "PentiumIII/Xeon", }, 581 { 6, 8, 16, "PentiumIII/Xeon", }, 582 { 6, 0xB, 16, "PentiumIII/Xeon", }, 583 { 0xF, 1, 16, "P4", }, /* P4 */ 584 { 0xF, 2, 16, "PentiumIV/Xeon", }, 585 586 { 3, -1, 32, "386", }, /* family defaults */ 587 { 4, -1, 22, "486", }, 588 { 5, -1, 23, "P5", }, 589 { 6, -1, 16, "P6", }, 590 { 0xF, -1, 16, "P4", }, /* P4 */ 591 592 { -1, -1, 16, "unknown", }, /* total default */ 593 }; 594 595 /* 596 * The AMD processors all implement the CPUID instruction. 597 * The later ones also return the processor name via functions 598 * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX 599 * and DX: 600 * K5 "AMD-K5(tm) Processor" 601 * K6 "AMD-K6tm w/ multimedia extensions" 602 * K6 3D "AMD-K6(tm) 3D processor" 603 * K6 3D+ ? 604 */ 605 static X86type x86amd[] = 606 { 607 { 5, 0, 23, "AMD-K5", }, /* guesswork */ 608 { 5, 1, 23, "AMD-K5", }, /* guesswork */ 609 { 5, 2, 23, "AMD-K5", }, /* guesswork */ 610 { 5, 3, 23, "AMD-K5", }, /* guesswork */ 611 { 5, 6, 11, "AMD-K6", }, /* trial and error */ 612 { 5, 7, 11, "AMD-K6", }, /* trial and error */ 613 { 5, 8, 11, "AMD-K6-2", }, /* trial and error */ 614 { 5, 9, 11, "AMD-K6-III", },/* trial and error */ 615 616 { 6, 1, 11, "AMD-Athlon", },/* trial and error */ 617 { 6, 2, 11, "AMD-Athlon", },/* trial and error */ 618 619 { 4, -1, 22, "Am486", }, /* guesswork */ 620 { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */ 621 { 6, -1, 11, "AMD-Athlon", },/* guesswork */ 622 { 0xF, -1, 11, "AMD64", }, /* guesswork */ 623 624 { -1, -1, 11, "unknown", }, /* total default */ 625 }; 626 627 /* 628 * WinChip 240MHz 629 */ 630 static X86type x86winchip[] = 631 { 632 {5, 4, 23, "Winchip",}, /* guesswork */ 633 {6, 7, 23, "Via C3 Samuel 2 or Ezra",}, 634 {6, 8, 23, "Via C3 Ezra-T",}, 635 {6, 9, 23, "Via C3 Eden-N",}, 636 { -1, -1, 23, "unknown", }, /* total default */ 637 }; 638 639 /* 640 * SiS 55x 641 */ 642 static X86type x86sis[] = 643 { 644 {5, 0, 23, "SiS 55x",}, /* guesswork */ 645 { -1, -1, 23, "unknown", }, /* total default */ 646 }; 647 648 static X86type *cputype; 649 650 static void simplecycles(uvlong*); 651 void (*cycles)(uvlong*) = simplecycles; 652 void _cycles(uvlong*); /* in l.s */ 653 654 static void 655 simplecycles(uvlong*x) 656 { 657 *x = m->ticks; 658 } 659 660 void 661 cpuidprint(void) 662 { 663 int i; 664 char buf[128]; 665 666 i = sprint(buf, "cpu%d: %dMHz ", m->machno, m->cpumhz); 667 if(m->cpuidid[0]) 668 i += sprint(buf+i, "%12.12s ", m->cpuidid); 669 seprint(buf+i, buf + sizeof buf - 1, 670 "%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n", 671 m->cpuidtype, m->cpuidax, m->cpuiddx); 672 print(buf); 673 } 674 675 /* 676 * figure out: 677 * - cpu type 678 * - whether or not we have a TSC (cycle counter) 679 * - whether or not it supports page size extensions 680 * (if so turn it on) 681 * - whether or not it supports machine check exceptions 682 * (if so turn it on) 683 * - whether or not it supports the page global flag 684 * (if so turn it on) 685 */ 686 int 687 cpuidentify(void) 688 { 689 char *p; 690 int family, model, nomce; 691 X86type *t, *tab; 692 ulong cr4; 693 vlong mca, mct; 694 695 cpuid(m->cpuidid, &m->cpuidax, &m->cpuiddx); 696 if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0) 697 tab = x86amd; 698 else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0) 699 tab = x86winchip; 700 else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0) 701 tab = x86sis; 702 else 703 tab = x86intel; 704 705 family = X86FAMILY(m->cpuidax); 706 model = X86MODEL(m->cpuidax); 707 for(t=tab; t->name; t++) 708 if((t->family == family && t->model == model) 709 || (t->family == family && t->model == -1) 710 || (t->family == -1)) 711 break; 712 713 m->cpuidtype = t->name; 714 715 /* 716 * if there is one, set tsc to a known value 717 */ 718 if(m->cpuiddx & 0x10){ 719 m->havetsc = 1; 720 cycles = _cycles; 721 if(m->cpuiddx & 0x20) 722 wrmsr(0x10, 0); 723 } 724 725 /* 726 * use i8253 to guess our cpu speed 727 */ 728 guesscpuhz(t->aalcycles); 729 730 /* 731 * If machine check exception, page size extensions or page global bit 732 * are supported enable them in CR4 and clear any other set extensions. 733 * If machine check was enabled clear out any lingering status. 734 */ 735 if(m->cpuiddx & 0x2088){ 736 cr4 = 0; 737 if(m->cpuiddx & 0x08) 738 cr4 |= 0x10; /* page size extensions */ 739 if(p = getconf("*nomce")) 740 nomce = strtoul(p, 0, 0); 741 else 742 nomce = 0; 743 if((m->cpuiddx & 0x80) && !nomce){ 744 cr4 |= 0x40; /* machine check enable */ 745 if(family == 5){ 746 rdmsr(0x00, &mca); 747 rdmsr(0x01, &mct); 748 } 749 } 750 751 /* 752 * Detect whether the chip supports the global bit 753 * in page directory and page table entries. When set 754 * in a particular entry, it means ``don't bother removing 755 * this from the TLB when CR3 changes.'' 756 * 757 * We flag all kernel pages with this bit. Doing so lessens the 758 * overhead of switching processes on bare hardware, 759 * even more so on VMware. See mmu.c:/^memglobal. 760 * 761 * For future reference, should we ever need to do a 762 * full TLB flush, it can be accomplished by clearing 763 * the PGE bit in CR4, writing to CR3, and then 764 * restoring the PGE bit. 765 */ 766 if(m->cpuiddx & 0x2000){ 767 cr4 |= 0x80; /* page global enable bit */ 768 m->havepge = 1; 769 } 770 771 putcr4(cr4); 772 if(m->cpuiddx & 0x80) 773 rdmsr(0x01, &mct); 774 } 775 776 cputype = t; 777 return t->family; 778 } 779 780 static long 781 cputyperead(Chan*, void *a, long n, vlong offset) 782 { 783 char str[32]; 784 ulong mhz; 785 786 mhz = (m->cpuhz+999999)/1000000; 787 788 snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz); 789 return readstr(offset, a, n, str); 790 } 791 792 static long 793 archctlread(Chan*, void *a, long nn, vlong offset) 794 { 795 char buf[256]; 796 int n; 797 798 n = snprint(buf, sizeof buf, "cpu %s %lud%s\n", 799 cputype->name, (ulong)(m->cpuhz+999999)/1000000, 800 m->havepge ? " pge" : ""); 801 n += snprint(buf+n, sizeof buf-n, "pge %s\n", getcr4()&0x80 ? "on" : "off"); 802 n += snprint(buf+n, sizeof buf-n, "coherence "); 803 if(coherence == mb386) 804 n += snprint(buf+n, sizeof buf-n, "mb386\n"); 805 else if(coherence == mb586) 806 n += snprint(buf+n, sizeof buf-n, "mb586\n"); 807 else if(coherence == nop) 808 n += snprint(buf+n, sizeof buf-n, "nop\n"); 809 else 810 n += snprint(buf+n, sizeof buf-n, "0x%p\n", coherence); 811 n += snprint(buf+n, sizeof buf-n, "cmpswap "); 812 if(cmpswap == cmpswap386) 813 n += snprint(buf+n, sizeof buf-n, "cmpswap386\n"); 814 else if(cmpswap == cmpswap486) 815 n += snprint(buf+n, sizeof buf-n, "cmpswap486\n"); 816 else 817 n += snprint(buf+n, sizeof buf-n, "0x%p\n", cmpswap); 818 n += snprint(buf+n, sizeof buf-n, "i8253set %s\n", doi8253set ? "on" : "off"); 819 buf[n] = 0; 820 return readstr(offset, a, nn, buf); 821 } 822 823 enum 824 { 825 CMpge, 826 CMcoherence, 827 CMi8253set, 828 }; 829 830 static Cmdtab archctlmsg[] = 831 { 832 CMpge, "pge", 2, 833 CMcoherence, "coherence", 2, 834 CMi8253set, "i8253set", 2, 835 }; 836 837 static long 838 archctlwrite(Chan*, void *a, long n, vlong) 839 { 840 Cmdbuf *cb; 841 Cmdtab *ct; 842 843 cb = parsecmd(a, n); 844 if(waserror()){ 845 free(cb); 846 nexterror(); 847 } 848 ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg)); 849 switch(ct->index){ 850 case CMpge: 851 if(!m->havepge) 852 error("processor does not support pge"); 853 if(strcmp(cb->f[1], "on") == 0) 854 putcr4(getcr4() | 0x80); 855 else if(strcmp(cb->f[1], "off") == 0) 856 putcr4(getcr4() & ~0x80); 857 else 858 cmderror(cb, "invalid pge ctl"); 859 break; 860 case CMcoherence: 861 if(strcmp(cb->f[1], "mb386") == 0) 862 coherence = mb386; 863 else if(strcmp(cb->f[1], "mb586") == 0){ 864 if(X86FAMILY(m->cpuidax) < 5) 865 error("invalid coherence ctl on this cpu family"); 866 coherence = mb586; 867 } 868 else if(strcmp(cb->f[1], "nop") == 0){ 869 /* only safe on vmware */ 870 if(conf.nmach > 1) 871 error("cannot disable coherence on a multiprocessor"); 872 coherence = nop; 873 }else 874 cmderror(cb, "invalid coherence ctl"); 875 break; 876 case CMi8253set: 877 if(strcmp(cb->f[1], "on") == 0) 878 doi8253set = 1; 879 else if(strcmp(cb->f[1], "off") == 0){ 880 doi8253set = 0; 881 (*arch->timerset)(0); 882 }else 883 cmderror(cb, "invalid i2853set ctl"); 884 break; 885 } 886 free(cb); 887 poperror(); 888 return n; 889 } 890 891 void 892 archinit(void) 893 { 894 PCArch **p; 895 896 arch = 0; 897 for(p = knownarch; *p; p++){ 898 if((*p)->ident && (*p)->ident() == 0){ 899 arch = *p; 900 break; 901 } 902 } 903 if(arch == 0) 904 arch = &archgeneric; 905 else{ 906 if(arch->id == 0) 907 arch->id = archgeneric.id; 908 if(arch->reset == 0) 909 arch->reset = archgeneric.reset; 910 if(arch->serialpower == 0) 911 arch->serialpower = archgeneric.serialpower; 912 if(arch->modempower == 0) 913 arch->modempower = archgeneric.modempower; 914 if(arch->intrinit == 0) 915 arch->intrinit = archgeneric.intrinit; 916 if(arch->intrenable == 0) 917 arch->intrenable = archgeneric.intrenable; 918 } 919 920 /* 921 * Decide whether to use copy-on-reference (386 and mp). 922 * We get another chance to set it in mpinit() for a 923 * multiprocessor. 924 */ 925 if(X86FAMILY(m->cpuidax) == 3) 926 conf.copymode = 1; 927 928 if(X86FAMILY(m->cpuidax) >= 4) 929 cmpswap = cmpswap486; 930 931 if(X86FAMILY(m->cpuidax) >= 5) 932 coherence = mb586; 933 934 addarchfile("cputype", 0444, cputyperead, nil); 935 addarchfile("archctl", 0664, archctlread, archctlwrite); 936 } 937 938 /* 939 * call either the pcmcia or pccard device setup 940 */ 941 int 942 pcmspecial(char *idstr, ISAConf *isa) 943 { 944 return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1; 945 } 946 947 /* 948 * call either the pcmcia or pccard device teardown 949 */ 950 void 951 pcmspecialclose(int a) 952 { 953 if (_pcmspecialclose != nil) 954 _pcmspecialclose(a); 955 } 956 957 /* 958 * return value and speed of timer set in arch->clockenable 959 */ 960 uvlong 961 fastticks(uvlong *hz) 962 { 963 return (*arch->fastclock)(hz); 964 } 965 966 ulong 967 µs(void) 968 { 969 return fastticks2us((*arch->fastclock)(nil)); 970 } 971 972 /* 973 * set next timer interrupt 974 */ 975 void 976 timerset(uvlong x) 977 { 978 if(doi8253set) 979 (*arch->timerset)(x); 980 } 981