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 { 6, 0xF, 16, "Xeon5000-series", }, 584 { 0xF, 1, 16, "P4", }, /* P4 */ 585 { 0xF, 2, 16, "PentiumIV/Xeon", }, 586 587 { 3, -1, 32, "386", }, /* family defaults */ 588 { 4, -1, 22, "486", }, 589 { 5, -1, 23, "P5", }, 590 { 6, -1, 16, "P6", }, 591 { 0xF, -1, 16, "P4", }, /* P4 */ 592 593 { -1, -1, 16, "unknown", }, /* total default */ 594 }; 595 596 /* 597 * The AMD processors all implement the CPUID instruction. 598 * The later ones also return the processor name via functions 599 * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX 600 * and DX: 601 * K5 "AMD-K5(tm) Processor" 602 * K6 "AMD-K6tm w/ multimedia extensions" 603 * K6 3D "AMD-K6(tm) 3D processor" 604 * K6 3D+ ? 605 */ 606 static X86type x86amd[] = 607 { 608 { 5, 0, 23, "AMD-K5", }, /* guesswork */ 609 { 5, 1, 23, "AMD-K5", }, /* guesswork */ 610 { 5, 2, 23, "AMD-K5", }, /* guesswork */ 611 { 5, 3, 23, "AMD-K5", }, /* guesswork */ 612 { 5, 4, 23, "AMD Geode GX1", }, /* guesswork */ 613 { 5, 5, 23, "AMD Geode GX2", }, /* guesswork */ 614 { 5, 6, 11, "AMD-K6", }, /* trial and error */ 615 { 5, 7, 11, "AMD-K6", }, /* trial and error */ 616 { 5, 8, 11, "AMD-K6-2", }, /* trial and error */ 617 { 5, 9, 11, "AMD-K6-III", },/* trial and error */ 618 { 5, 0xa, 23, "AMD Geode LX", }, /* guesswork */ 619 620 { 6, 1, 11, "AMD-Athlon", },/* trial and error */ 621 { 6, 2, 11, "AMD-Athlon", },/* trial and error */ 622 623 { 4, -1, 22, "Am486", }, /* guesswork */ 624 { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */ 625 { 6, -1, 11, "AMD-Athlon", },/* guesswork */ 626 { 0xF, -1, 11, "AMD64", }, /* guesswork */ 627 628 { -1, -1, 11, "unknown", }, /* total default */ 629 }; 630 631 /* 632 * WinChip 240MHz 633 */ 634 static X86type x86winchip[] = 635 { 636 {5, 4, 23, "Winchip",}, /* guesswork */ 637 {6, 7, 23, "Via C3 Samuel 2 or Ezra",}, 638 {6, 8, 23, "Via C3 Ezra-T",}, 639 {6, 9, 23, "Via C3 Eden-N",}, 640 { -1, -1, 23, "unknown", }, /* total default */ 641 }; 642 643 /* 644 * SiS 55x 645 */ 646 static X86type x86sis[] = 647 { 648 {5, 0, 23, "SiS 55x",}, /* guesswork */ 649 { -1, -1, 23, "unknown", }, /* total default */ 650 }; 651 652 static X86type *cputype; 653 654 static void simplecycles(uvlong*); 655 void (*cycles)(uvlong*) = simplecycles; 656 void _cycles(uvlong*); /* in l.s */ 657 658 static void 659 simplecycles(uvlong*x) 660 { 661 *x = m->ticks; 662 } 663 664 void 665 cpuidprint(void) 666 { 667 int i; 668 char buf[128]; 669 670 i = sprint(buf, "cpu%d: %dMHz ", m->machno, m->cpumhz); 671 if(m->cpuidid[0]) 672 i += sprint(buf+i, "%12.12s ", m->cpuidid); 673 seprint(buf+i, buf + sizeof buf - 1, 674 "%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n", 675 m->cpuidtype, m->cpuidax, m->cpuiddx); 676 print(buf); 677 } 678 679 /* 680 * figure out: 681 * - cpu type 682 * - whether or not we have a TSC (cycle counter) 683 * - whether or not it supports page size extensions 684 * (if so turn it on) 685 * - whether or not it supports machine check exceptions 686 * (if so turn it on) 687 * - whether or not it supports the page global flag 688 * (if so turn it on) 689 */ 690 int 691 cpuidentify(void) 692 { 693 char *p; 694 int family, model, nomce; 695 X86type *t, *tab; 696 ulong cr4; 697 vlong mca, mct; 698 699 cpuid(m->cpuidid, &m->cpuidax, &m->cpuiddx); 700 if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0 || 701 strncmp(m->cpuidid, "Geode by NSC", 12) == 0) 702 tab = x86amd; 703 else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0) 704 tab = x86winchip; 705 else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0) 706 tab = x86sis; 707 else 708 tab = x86intel; 709 710 family = X86FAMILY(m->cpuidax); 711 model = X86MODEL(m->cpuidax); 712 for(t=tab; t->name; t++) 713 if((t->family == family && t->model == model) 714 || (t->family == family && t->model == -1) 715 || (t->family == -1)) 716 break; 717 718 m->cpuidtype = t->name; 719 720 /* 721 * if there is one, set tsc to a known value 722 */ 723 if(m->cpuiddx & Tsc){ 724 m->havetsc = 1; 725 cycles = _cycles; 726 if(m->cpuiddx & Cpumsr) 727 wrmsr(0x10, 0); 728 } 729 730 /* 731 * use i8253 to guess our cpu speed 732 */ 733 guesscpuhz(t->aalcycles); 734 735 /* 736 * If machine check exception, page size extensions or page global bit 737 * are supported enable them in CR4 and clear any other set extensions. 738 * If machine check was enabled clear out any lingering status. 739 */ 740 if(m->cpuiddx & (Pge|Mce|0x8)){ 741 cr4 = 0; 742 if(m->cpuiddx & 0x08) 743 cr4 |= 0x10; /* page size extensions */ 744 if(p = getconf("*nomce")) 745 nomce = strtoul(p, 0, 0); 746 else 747 nomce = 0; 748 if((m->cpuiddx & Mce) && !nomce){ 749 cr4 |= 0x40; /* machine check enable */ 750 if(family == 5){ 751 rdmsr(0x00, &mca); 752 rdmsr(0x01, &mct); 753 } 754 } 755 756 /* 757 * Detect whether the chip supports the global bit 758 * in page directory and page table entries. When set 759 * in a particular entry, it means ``don't bother removing 760 * this from the TLB when CR3 changes.'' 761 * 762 * We flag all kernel pages with this bit. Doing so lessens the 763 * overhead of switching processes on bare hardware, 764 * even more so on VMware. See mmu.c:/^memglobal. 765 * 766 * For future reference, should we ever need to do a 767 * full TLB flush, it can be accomplished by clearing 768 * the PGE bit in CR4, writing to CR3, and then 769 * restoring the PGE bit. 770 */ 771 if(m->cpuiddx & Pge){ 772 cr4 |= 0x80; /* page global enable bit */ 773 m->havepge = 1; 774 } 775 776 putcr4(cr4); 777 if(m->cpuiddx & Mce) 778 rdmsr(0x01, &mct); 779 } 780 781 cputype = t; 782 return t->family; 783 } 784 785 static long 786 cputyperead(Chan*, void *a, long n, vlong offset) 787 { 788 char str[32]; 789 ulong mhz; 790 791 mhz = (m->cpuhz+999999)/1000000; 792 793 snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz); 794 return readstr(offset, a, n, str); 795 } 796 797 static long 798 archctlread(Chan*, void *a, long nn, vlong offset) 799 { 800 char buf[256]; 801 int n; 802 803 n = snprint(buf, sizeof buf, "cpu %s %lud%s\n", 804 cputype->name, (ulong)(m->cpuhz+999999)/1000000, 805 m->havepge ? " pge" : ""); 806 n += snprint(buf+n, sizeof buf-n, "pge %s\n", getcr4()&0x80 ? "on" : "off"); 807 n += snprint(buf+n, sizeof buf-n, "coherence "); 808 if(coherence == mb386) 809 n += snprint(buf+n, sizeof buf-n, "mb386\n"); 810 else if(coherence == mb586) 811 n += snprint(buf+n, sizeof buf-n, "mb586\n"); 812 else if(coherence == mfence) 813 n += snprint(buf+n, sizeof buf-n, "mfence\n"); 814 else if(coherence == nop) 815 n += snprint(buf+n, sizeof buf-n, "nop\n"); 816 else 817 n += snprint(buf+n, sizeof buf-n, "0x%p\n", coherence); 818 n += snprint(buf+n, sizeof buf-n, "cmpswap "); 819 if(cmpswap == cmpswap386) 820 n += snprint(buf+n, sizeof buf-n, "cmpswap386\n"); 821 else if(cmpswap == cmpswap486) 822 n += snprint(buf+n, sizeof buf-n, "cmpswap486\n"); 823 else 824 n += snprint(buf+n, sizeof buf-n, "0x%p\n", cmpswap); 825 n += snprint(buf+n, sizeof buf-n, "i8253set %s\n", doi8253set ? "on" : "off"); 826 buf[n] = 0; 827 return readstr(offset, a, nn, buf); 828 } 829 830 enum 831 { 832 CMpge, 833 CMcoherence, 834 CMi8253set, 835 }; 836 837 static Cmdtab archctlmsg[] = 838 { 839 CMpge, "pge", 2, 840 CMcoherence, "coherence", 2, 841 CMi8253set, "i8253set", 2, 842 }; 843 844 static long 845 archctlwrite(Chan*, void *a, long n, vlong) 846 { 847 Cmdbuf *cb; 848 Cmdtab *ct; 849 850 cb = parsecmd(a, n); 851 if(waserror()){ 852 free(cb); 853 nexterror(); 854 } 855 ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg)); 856 switch(ct->index){ 857 case CMpge: 858 if(!m->havepge) 859 error("processor does not support pge"); 860 if(strcmp(cb->f[1], "on") == 0) 861 putcr4(getcr4() | 0x80); 862 else if(strcmp(cb->f[1], "off") == 0) 863 putcr4(getcr4() & ~0x80); 864 else 865 cmderror(cb, "invalid pge ctl"); 866 break; 867 case CMcoherence: 868 if(strcmp(cb->f[1], "mb386") == 0) 869 coherence = mb386; 870 else if(strcmp(cb->f[1], "mb586") == 0){ 871 if(X86FAMILY(m->cpuidax) < 5) 872 error("invalid coherence ctl on this cpu family"); 873 coherence = mb586; 874 }else if(strcmp(cb->f[1], "mfence") == 0){ 875 if((m->cpuiddx & Sse2) == 0) 876 error("invalid coherence ctl on this cpu family"); 877 coherence = mfence; 878 }else if(strcmp(cb->f[1], "nop") == 0){ 879 /* only safe on vmware */ 880 if(conf.nmach > 1) 881 error("cannot disable coherence on a multiprocessor"); 882 coherence = nop; 883 }else 884 cmderror(cb, "invalid coherence ctl"); 885 break; 886 case CMi8253set: 887 if(strcmp(cb->f[1], "on") == 0) 888 doi8253set = 1; 889 else if(strcmp(cb->f[1], "off") == 0){ 890 doi8253set = 0; 891 (*arch->timerset)(0); 892 }else 893 cmderror(cb, "invalid i2853set ctl"); 894 break; 895 } 896 free(cb); 897 poperror(); 898 return n; 899 } 900 901 void 902 archinit(void) 903 { 904 PCArch **p; 905 906 arch = 0; 907 for(p = knownarch; *p; p++){ 908 if((*p)->ident && (*p)->ident() == 0){ 909 arch = *p; 910 break; 911 } 912 } 913 if(arch == 0) 914 arch = &archgeneric; 915 else{ 916 if(arch->id == 0) 917 arch->id = archgeneric.id; 918 if(arch->reset == 0) 919 arch->reset = archgeneric.reset; 920 if(arch->serialpower == 0) 921 arch->serialpower = archgeneric.serialpower; 922 if(arch->modempower == 0) 923 arch->modempower = archgeneric.modempower; 924 if(arch->intrinit == 0) 925 arch->intrinit = archgeneric.intrinit; 926 if(arch->intrenable == 0) 927 arch->intrenable = archgeneric.intrenable; 928 } 929 930 /* 931 * Decide whether to use copy-on-reference (386 and mp). 932 * We get another chance to set it in mpinit() for a 933 * multiprocessor. 934 */ 935 if(X86FAMILY(m->cpuidax) == 3) 936 conf.copymode = 1; 937 938 if(X86FAMILY(m->cpuidax) >= 4) 939 cmpswap = cmpswap486; 940 941 if(X86FAMILY(m->cpuidax) >= 5) 942 coherence = mb586; 943 944 if(m->cpuiddx & Sse2) 945 coherence = mfence; 946 947 addarchfile("cputype", 0444, cputyperead, nil); 948 addarchfile("archctl", 0664, archctlread, archctlwrite); 949 } 950 951 /* 952 * call either the pcmcia or pccard device setup 953 */ 954 int 955 pcmspecial(char *idstr, ISAConf *isa) 956 { 957 return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1; 958 } 959 960 /* 961 * call either the pcmcia or pccard device teardown 962 */ 963 void 964 pcmspecialclose(int a) 965 { 966 if (_pcmspecialclose != nil) 967 _pcmspecialclose(a); 968 } 969 970 /* 971 * return value and speed of timer set in arch->clockenable 972 */ 973 uvlong 974 fastticks(uvlong *hz) 975 { 976 return (*arch->fastclock)(hz); 977 } 978 979 ulong 980 µs(void) 981 { 982 return fastticks2us((*arch->fastclock)(nil)); 983 } 984 985 /* 986 * set next timer interrupt 987 */ 988 void 989 timerset(Tval x) 990 { 991 if(doi8253set) 992 (*arch->timerset)(x); 993 } 994