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