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 sprint(buf+i, "%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n", 668 m->cpuidtype, m->cpuidax, m->cpuiddx); 669 print(buf); 670 } 671 672 /* 673 * figure out: 674 * - cpu type 675 * - whether or not we have a TSC (cycle counter) 676 * - whether or not it supports page size extensions 677 * (if so turn it on) 678 * - whether or not it supports machine check exceptions 679 * (if so turn it on) 680 * - whether or not it supports the page global flag 681 * (if so turn it on) 682 */ 683 int 684 cpuidentify(void) 685 { 686 char *p; 687 int family, model, nomce; 688 X86type *t, *tab; 689 ulong cr4; 690 vlong mca, mct; 691 692 cpuid(m->cpuidid, &m->cpuidax, &m->cpuiddx); 693 if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0) 694 tab = x86amd; 695 else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0) 696 tab = x86winchip; 697 else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0) 698 tab = x86sis; 699 else 700 tab = x86intel; 701 702 family = X86FAMILY(m->cpuidax); 703 model = X86MODEL(m->cpuidax); 704 for(t=tab; t->name; t++) 705 if((t->family == family && t->model == model) 706 || (t->family == family && t->model == -1) 707 || (t->family == -1)) 708 break; 709 710 m->cpuidtype = t->name; 711 712 /* 713 * if there is one, set tsc to a known value 714 */ 715 if(m->cpuiddx & 0x10){ 716 m->havetsc = 1; 717 cycles = _cycles; 718 if(m->cpuiddx & 0x20) 719 wrmsr(0x10, 0); 720 } 721 722 /* 723 * use i8253 to guess our cpu speed 724 */ 725 guesscpuhz(t->aalcycles); 726 727 /* 728 * If machine check exception, page size extensions or page global bit 729 * are supported enable them in CR4 and clear any other set extensions. 730 * If machine check was enabled clear out any lingering status. 731 */ 732 if(m->cpuiddx & 0x2088){ 733 cr4 = 0; 734 if(m->cpuiddx & 0x08) 735 cr4 |= 0x10; /* page size extensions */ 736 if(p = getconf("*nomce")) 737 nomce = strtoul(p, 0, 0); 738 else 739 nomce = 0; 740 if((m->cpuiddx & 0x80) && !nomce){ 741 cr4 |= 0x40; /* machine check enable */ 742 if(family == 5){ 743 rdmsr(0x00, &mca); 744 rdmsr(0x01, &mct); 745 } 746 } 747 748 /* 749 * Detect whether the chip supports the global bit 750 * in page directory and page table entries. When set 751 * in a particular entry, it means ``don't bother removing 752 * this from the TLB when CR3 changes.'' 753 * 754 * We flag all kernel pages with this bit. Doing so lessens the 755 * overhead of switching processes on bare hardware, 756 * even more so on VMware. See mmu.c:/^memglobal. 757 * 758 * For future reference, should we ever need to do a 759 * full TLB flush, it can be accomplished by clearing 760 * the PGE bit in CR4, writing to CR3, and then 761 * restoring the PGE bit. 762 */ 763 if(m->cpuiddx & 0x2000){ 764 cr4 |= 0x80; /* page global enable bit */ 765 m->havepge = 1; 766 } 767 768 putcr4(cr4); 769 if(m->cpuiddx & 0x80) 770 rdmsr(0x01, &mct); 771 } 772 773 cputype = t; 774 return t->family; 775 } 776 777 static long 778 cputyperead(Chan*, void *a, long n, vlong offset) 779 { 780 char str[32]; 781 ulong mhz; 782 783 mhz = (m->cpuhz+999999)/1000000; 784 785 snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz); 786 return readstr(offset, a, n, str); 787 } 788 789 static long 790 archctlread(Chan*, void *a, long nn, vlong offset) 791 { 792 char buf[256]; 793 int n; 794 795 n = snprint(buf, sizeof buf, "cpu %s %lud%s\n", 796 cputype->name, (ulong)(m->cpuhz+999999)/1000000, 797 m->havepge ? " pge" : ""); 798 n += snprint(buf+n, sizeof buf-n, "pge %s\n", getcr4()&0x80 ? "on" : "off"); 799 n += snprint(buf+n, sizeof buf-n, "coherence "); 800 if(coherence == mb386) 801 n += snprint(buf+n, sizeof buf-n, "mb386\n"); 802 else if(coherence == mb586) 803 n += snprint(buf+n, sizeof buf-n, "mb586\n"); 804 else if(coherence == nop) 805 n += snprint(buf+n, sizeof buf-n, "nop\n"); 806 else 807 n += snprint(buf+n, sizeof buf-n, "0x%p\n", coherence); 808 n += snprint(buf+n, sizeof buf-n, "cmpswap "); 809 if(cmpswap == cmpswap386) 810 n += snprint(buf+n, sizeof buf-n, "cmpswap386\n"); 811 else if(cmpswap == cmpswap486) 812 n += snprint(buf+n, sizeof buf-n, "cmpswap486\n"); 813 else 814 n += snprint(buf+n, sizeof buf-n, "0x%p\n", cmpswap); 815 n += snprint(buf+n, sizeof buf-n, "i8253set %s\n", doi8253set ? "on" : "off"); 816 buf[n] = 0; 817 return readstr(offset, a, nn, buf); 818 } 819 820 enum 821 { 822 CMpge, 823 CMcoherence, 824 CMi8253set, 825 }; 826 827 static Cmdtab archctlmsg[] = 828 { 829 CMpge, "pge", 2, 830 CMcoherence, "coherence", 2, 831 CMi8253set, "i8253set", 2, 832 }; 833 834 static long 835 archctlwrite(Chan*, void *a, long n, vlong) 836 { 837 Cmdbuf *cb; 838 Cmdtab *ct; 839 840 cb = parsecmd(a, n); 841 if(waserror()){ 842 free(cb); 843 nexterror(); 844 } 845 ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg)); 846 switch(ct->index){ 847 case CMpge: 848 if(!m->havepge) 849 error("processor does not support pge"); 850 if(strcmp(cb->f[1], "on") == 0) 851 putcr4(getcr4() | 0x80); 852 else if(strcmp(cb->f[1], "off") == 0) 853 putcr4(getcr4() & ~0x80); 854 else 855 cmderror(cb, "invalid pge ctl"); 856 break; 857 case CMcoherence: 858 if(strcmp(cb->f[1], "mb386") == 0) 859 coherence = mb386; 860 else if(strcmp(cb->f[1], "mb586") == 0){ 861 if(X86FAMILY(m->cpuidax) < 5) 862 error("invalid coherence ctl on this cpu family"); 863 coherence = mb586; 864 } 865 else if(strcmp(cb->f[1], "nop") == 0){ 866 /* only safe on vmware */ 867 if(conf.nmach > 1) 868 error("cannot disable coherence on a multiprocessor"); 869 coherence = nop; 870 }else 871 cmderror(cb, "invalid coherence ctl"); 872 break; 873 case CMi8253set: 874 if(strcmp(cb->f[1], "on") == 0) 875 doi8253set = 1; 876 else if(strcmp(cb->f[1], "off") == 0){ 877 doi8253set = 0; 878 (*arch->timerset)(0); 879 }else 880 cmderror(cb, "invalid i2853set ctl"); 881 break; 882 } 883 free(cb); 884 poperror(); 885 return n; 886 } 887 888 void 889 archinit(void) 890 { 891 PCArch **p; 892 893 arch = 0; 894 for(p = knownarch; *p; p++){ 895 if((*p)->ident && (*p)->ident() == 0){ 896 arch = *p; 897 break; 898 } 899 } 900 if(arch == 0) 901 arch = &archgeneric; 902 else{ 903 if(arch->id == 0) 904 arch->id = archgeneric.id; 905 if(arch->reset == 0) 906 arch->reset = archgeneric.reset; 907 if(arch->serialpower == 0) 908 arch->serialpower = archgeneric.serialpower; 909 if(arch->modempower == 0) 910 arch->modempower = archgeneric.modempower; 911 if(arch->intrinit == 0) 912 arch->intrinit = archgeneric.intrinit; 913 if(arch->intrenable == 0) 914 arch->intrenable = archgeneric.intrenable; 915 } 916 917 /* 918 * Decide whether to use copy-on-reference (386 and mp). 919 * We get another chance to set it in mpinit() for a 920 * multiprocessor. 921 */ 922 if(X86FAMILY(m->cpuidax) == 3) 923 conf.copymode = 1; 924 925 if(X86FAMILY(m->cpuidax) >= 4) 926 cmpswap = cmpswap486; 927 928 if(X86FAMILY(m->cpuidax) >= 5) 929 coherence = mb586; 930 931 addarchfile("cputype", 0444, cputyperead, nil); 932 addarchfile("archctl", 0664, archctlread, archctlwrite); 933 } 934 935 /* 936 * call either the pcmcia or pccard device setup 937 */ 938 int 939 pcmspecial(char *idstr, ISAConf *isa) 940 { 941 return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1; 942 } 943 944 /* 945 * call either the pcmcia or pccard device teardown 946 */ 947 void 948 pcmspecialclose(int a) 949 { 950 if (_pcmspecialclose != nil) 951 _pcmspecialclose(a); 952 } 953 954 /* 955 * return value and speed of timer set in arch->clockenable 956 */ 957 uvlong 958 fastticks(uvlong *hz) 959 { 960 return (*arch->fastclock)(hz); 961 } 962 963 /* 964 * set next timer interrupt 965 */ 966 void 967 timerset(uvlong x) 968 { 969 if(doi8253set) 970 (*arch->timerset)(x); 971 } 972